From af70b6b6897054be73e5e6ffb9b585ac3d3a264a Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Thu, 19 Jan 2023 12:07:50 +0000 Subject: [PATCH 001/163] Revert "[ci]Fix dead links for WiX 3 binaries" (#23433) This reverts commit 07f3507f11abb2eb7aa5e1d80a6a6d0413dce655. --- .pipelines/installWiX.ps1 | 4 ++-- doc/devdocs/readme.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pipelines/installWiX.ps1 b/.pipelines/installWiX.ps1 index 18a81c93d3..ab770b8bf5 100644 --- a/.pipelines/installWiX.ps1 +++ b/.pipelines/installWiX.ps1 @@ -1,7 +1,7 @@ $ProgressPreference = 'SilentlyContinue' -$WixDownloadUrl = "https://github.com/JaneaSystems/wix3/releases/download/wix3-3.14.0.6526/wix314.exe" -$WixBinariesDownloadUrl = "https://github.com/JaneaSystems/wix3/releases/download/wix3-3.14.0.6526/wix314-binaries.zip" +$WixDownloadUrl = "https://wixtoolset.org/downloads/v3.14.0.6526/wix314.exe" +$WixBinariesDownloadUrl = "https://wixtoolset.org/downloads/v3.14.0.6526/wix314-binaries.zip" # Download WiX binaries and verify their hash sums Invoke-WebRequest -Uri $WixDownloadUrl -OutFile "$($ENV:Temp)\wix314.exe" diff --git a/doc/devdocs/readme.md b/doc/devdocs/readme.md index 88b0867bd7..cfc328aeb6 100644 --- a/doc/devdocs/readme.md +++ b/doc/devdocs/readme.md @@ -73,8 +73,8 @@ The installer can only be compiled in `Release` mode, step 1 and 2 must be done ### Prerequisites for building the MSI installer 1. Install the [WiX Toolset Visual Studio 2022 Extension](https://marketplace.visualstudio.com/items?itemName=WixToolset.WixToolsetVisualStudio2022Extension). -1. Install the [WiX Toolset build tools](https://wixtoolset.org/releases/v3-14-0-6526/). The links to the binaries are not working, so we've created a [fork here](https://github.com/JaneaSystems/wix3/releases/tag/wix3-3.14.0.6526) where the WiX Toolset can be downloaded from. -1. Download [WiX binaries](https://github.com/JaneaSystems/wix3/releases/download/wix3-3.14.0.6526/wix314-binaries.zip) and extract `wix.targets` to `C:\Program Files (x86)\WiX Toolset v3.14`. +1. Install the [WiX Toolset build tools](https://wixtoolset.org/releases/v3-14-0-6526/). +1. Download [WiX binaries](https://wixtoolset.org/downloads/v3.14.0.6526/wix314-binaries.zip) and extract `wix.targets` to `C:\Program Files (x86)\WiX Toolset v3.14`. ### Locally building the installer prerequisite projects all at once from the command-line From cc5633db3038aa70454e6b0ff4dd2bb6954c1f66 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Mon, 23 Jan 2023 15:59:18 +0100 Subject: [PATCH 002/163] [installer] Extract module related stuff from Product.wxs to per-module .wxs file (#23378) * Extract module related stuff from Product.wxs to per-module .wxs file * Spellcheck * Minor changes --- .github/actions/spell-check/expect.txt | 28 +- installer/PowerToysSetup/AlwaysOnTop.wxs | 25 + installer/PowerToysSetup/Awake.wxs | 33 + installer/PowerToysSetup/ColorPicker.wxs | 37 + installer/PowerToysSetup/Common.wxi | 27 + installer/PowerToysSetup/Core.wxs | 142 ++ installer/PowerToysSetup/FancyZones.wxs | 25 + .../PowerToysSetup/FileExplorerPreview.wxs | 35 + installer/PowerToysSetup/FileLocksmith.wxs | 74 + installer/PowerToysSetup/Hosts.wxs | 71 + installer/PowerToysSetup/ImageResizer.wxs | 109 + installer/PowerToysSetup/KeyboardManager.wxs | 36 + installer/PowerToysSetup/MeasureTool.wxs | 46 + installer/PowerToysSetup/MouseUtils.wxs | 28 + installer/PowerToysSetup/PowerAccent.wxs | 26 + installer/PowerToysSetup/PowerRename.wxs | 71 + .../PowerToysSetup/PowerToysInstaller.wixproj | 27 +- installer/PowerToysSetup/Product.wxs | 1855 +---------------- installer/PowerToysSetup/Resources.wxs | 318 +++ installer/PowerToysSetup/Run.wxs | 311 +++ installer/PowerToysSetup/Settings.wxs | 105 + installer/PowerToysSetup/ShortcutGuide.wxs | 43 + installer/PowerToysSetup/TextExtractor.wxs | 25 + installer/PowerToysSetup/Tools.wxs | 27 + installer/PowerToysSetup/VideoConference.wxs | 48 + installer/PowerToysSetup/WinAppSDK.wxs | 364 ++++ .../PowerToysSetupCustomActions.vcxproj | 4 +- 27 files changed, 2093 insertions(+), 1847 deletions(-) create mode 100644 installer/PowerToysSetup/AlwaysOnTop.wxs create mode 100644 installer/PowerToysSetup/Awake.wxs create mode 100644 installer/PowerToysSetup/ColorPicker.wxs create mode 100644 installer/PowerToysSetup/Common.wxi create mode 100644 installer/PowerToysSetup/Core.wxs create mode 100644 installer/PowerToysSetup/FancyZones.wxs create mode 100644 installer/PowerToysSetup/FileExplorerPreview.wxs create mode 100644 installer/PowerToysSetup/FileLocksmith.wxs create mode 100644 installer/PowerToysSetup/Hosts.wxs create mode 100644 installer/PowerToysSetup/ImageResizer.wxs create mode 100644 installer/PowerToysSetup/KeyboardManager.wxs create mode 100644 installer/PowerToysSetup/MeasureTool.wxs create mode 100644 installer/PowerToysSetup/MouseUtils.wxs create mode 100644 installer/PowerToysSetup/PowerAccent.wxs create mode 100644 installer/PowerToysSetup/PowerRename.wxs create mode 100644 installer/PowerToysSetup/Resources.wxs create mode 100644 installer/PowerToysSetup/Run.wxs create mode 100644 installer/PowerToysSetup/Settings.wxs create mode 100644 installer/PowerToysSetup/ShortcutGuide.wxs create mode 100644 installer/PowerToysSetup/TextExtractor.wxs create mode 100644 installer/PowerToysSetup/Tools.wxs create mode 100644 installer/PowerToysSetup/VideoConference.wxs create mode 100644 installer/PowerToysSetup/WinAppSDK.wxs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 9a5ad6f8cd..893b44edef 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -92,7 +92,6 @@ Artsakh asdf AShortcut ASingle -Asn ASSOCCHANGED ASYNCWINDOWPLACEMENT ASYNCWINDOWPOS @@ -161,7 +160,6 @@ bpmf bpp bricelam BRIGHTGREEN -Brotli Browsable bsd bstr @@ -222,10 +220,6 @@ CLIPCHILDREN Clipperton CLIPSIBLINGS clrcall -clrcompression -clretwrc -clrgc -clrjit Cls CLSCTX clsid @@ -240,7 +234,6 @@ CMock CMONITORS cmpgt cmyk -Cng cnt Cocklebiddy coclass @@ -286,7 +279,6 @@ CONTROLL CONTROLPARENT Controlz copiedcolorrepresentation -coreclr corewebview cortana cotaskmem @@ -319,6 +311,7 @@ Ctx CUI Cunha currentculture +CURRENTDIR CURSORINFO cursorpos customaction @@ -350,7 +343,6 @@ davidegiacometti Dayof Dbg Dbghelp -dbgshim DBLCLKS DBLEPSILON DCapture @@ -359,7 +351,6 @@ DCOM dcommon dcomp dcompi -DCompiler DComposition DCR DDevice @@ -685,8 +676,6 @@ HOMEPATH homljgmgpmcbpjbnjpfijnhipfkiclkd HOOKPROC Hostbackdropbrush -hostfxr -hostpolicy hotkeycontrol hotkeys hotlight @@ -758,7 +747,6 @@ IMAGERESIZEREXT imageresizerinput imageresizersettings imagingdevices -Imc ime imeutil inetcpl @@ -795,7 +783,6 @@ Intelli interactable Interlop INTRESOURCE -Intrinsics INVALIDARG invalidoperatioexception ipc @@ -833,7 +820,6 @@ jobject jpe jpn jpnime -JSONOf Jsons jsonval junja @@ -871,7 +857,6 @@ Kyrgyzstan Kyzylorda LAlt Lambson -LANGID langword Lastdevice Laute @@ -1085,10 +1070,7 @@ mru msbuild msc msclr -mscordaccore -mscordbi mscorlib -mscorrc msdata msedge MSGFLT @@ -1101,7 +1083,6 @@ MSIXCA MSLLHOOKSTRUCT Mso msp -msquic msrc msstore mst @@ -1151,7 +1132,6 @@ netcpl netframework netsetup netsh -netstandard Neue newcolor newdev @@ -1328,7 +1308,6 @@ pinvoke pipename Pitcairn PKBDLLHOOKSTRUCT -Pkcs PKEY plib PLK @@ -1430,7 +1409,6 @@ QUERYENDSESSION queryfocus QUERYOPEN QUEUESYNC -Quic Quickime QUNS qwertyuiopasdfghjklzxcvbnm @@ -1537,7 +1515,6 @@ Rsp Rstrtmgr RTB RTLREADING -RTSS ruleset runas rundll @@ -1701,7 +1678,6 @@ srw srwlock sse ssf -Ssl STACKFRAME stackoverflow stackpanel @@ -2091,7 +2067,6 @@ workspaces wox wparam wpf -wpfgfx wpftmp wpr wprp @@ -2158,6 +2133,5 @@ zonable zoneset Zoneszonabletester Zonev -zopfli Zykova zzz diff --git a/installer/PowerToysSetup/AlwaysOnTop.wxs b/installer/PowerToysSetup/AlwaysOnTop.wxs new file mode 100644 index 0000000000..3ffc1b7f36 --- /dev/null +++ b/installer/PowerToysSetup/AlwaysOnTop.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Awake.wxs b/installer/PowerToysSetup/Awake.wxs new file mode 100644 index 0000000000..49c8304fe7 --- /dev/null +++ b/installer/PowerToysSetup/Awake.wxs @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/ColorPicker.wxs b/installer/PowerToysSetup/ColorPicker.wxs new file mode 100644 index 0000000000..9c8606b52f --- /dev/null +++ b/installer/PowerToysSetup/ColorPicker.wxs @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Common.wxi b/installer/PowerToysSetup/Common.wxi new file mode 100644 index 0000000000..6aad3632b7 --- /dev/null +++ b/installer/PowerToysSetup/Common.wxi @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Core.wxs b/installer/PowerToysSetup/Core.wxs new file mode 100644 index 0000000000..601500ef97 --- /dev/null +++ b/installer/PowerToysSetup/Core.wxs @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSTALLDESKTOPSHORTCUT + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/FancyZones.wxs b/installer/PowerToysSetup/FancyZones.wxs new file mode 100644 index 0000000000..6e07dcc4ff --- /dev/null +++ b/installer/PowerToysSetup/FancyZones.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/FileExplorerPreview.wxs b/installer/PowerToysSetup/FileExplorerPreview.wxs new file mode 100644 index 0000000000..1691569c7b --- /dev/null +++ b/installer/PowerToysSetup/FileExplorerPreview.wxs @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/FileLocksmith.wxs b/installer/PowerToysSetup/FileLocksmith.wxs new file mode 100644 index 0000000000..1f9edbe93b --- /dev/null +++ b/installer/PowerToysSetup/FileLocksmith.wxs @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Hosts.wxs b/installer/PowerToysSetup/Hosts.wxs new file mode 100644 index 0000000000..530bafaa3a --- /dev/null +++ b/installer/PowerToysSetup/Hosts.wxs @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/ImageResizer.wxs b/installer/PowerToysSetup/ImageResizer.wxs new file mode 100644 index 0000000000..bc0de24cce --- /dev/null +++ b/installer/PowerToysSetup/ImageResizer.wxs @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/KeyboardManager.wxs b/installer/PowerToysSetup/KeyboardManager.wxs new file mode 100644 index 0000000000..1bb67443b2 --- /dev/null +++ b/installer/PowerToysSetup/KeyboardManager.wxs @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/MeasureTool.wxs b/installer/PowerToysSetup/MeasureTool.wxs new file mode 100644 index 0000000000..6f5412917a --- /dev/null +++ b/installer/PowerToysSetup/MeasureTool.wxs @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/MouseUtils.wxs b/installer/PowerToysSetup/MouseUtils.wxs new file mode 100644 index 0000000000..0d8ed66f1a --- /dev/null +++ b/installer/PowerToysSetup/MouseUtils.wxs @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/PowerAccent.wxs b/installer/PowerToysSetup/PowerAccent.wxs new file mode 100644 index 0000000000..a74b374113 --- /dev/null +++ b/installer/PowerToysSetup/PowerAccent.wxs @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/PowerRename.wxs b/installer/PowerToysSetup/PowerRename.wxs new file mode 100644 index 0000000000..0386d7aaff --- /dev/null +++ b/installer/PowerToysSetup/PowerRename.wxs @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj index d61e1f0c05..e4c7dd649e 100644 --- a/installer/PowerToysSetup/PowerToysInstaller.wixproj +++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj @@ -27,7 +27,7 @@ call "..\..\publish.cmd" arm64 Always - call move /Y ..\..\Product.wxs.bk ..\..\Product.wxs + call move /Y ..\..\Core.wxs.bk ..\..\Core.wxs call move /Y ..\..\..\PowerToysSetupCustomActions\DepsFilesLists.h.bk ..\..\..\PowerToysSetupCustomActions\DepsFilesLists.h @@ -62,7 +62,32 @@ call "..\..\publish.cmd" arm64 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index c14f9f2102..9d277676d4 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -2,33 +2,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + Installed AND NOT (_REMOVE_ALL="Yes") Installed AND NOT (_REMOVE_ALL="Yes") + @@ -527,6 +420,7 @@ + @@ -549,9 +443,9 @@ - - - + + + @@ -698,1689 +592,16 @@ - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WINDOWSBUILDNUMBER >= 19041 - - - - - - - - - - - - - - WINDOWSBUILDNUMBER >= 19041 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - INSTALLDESKTOPSHORTCUT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/installer/PowerToysSetup/Resources.wxs b/installer/PowerToysSetup/Resources.wxs new file mode 100644 index 0000000000..a771c6d8b6 --- /dev/null +++ b/installer/PowerToysSetup/Resources.wxs @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Run.wxs b/installer/PowerToysSetup/Run.wxs new file mode 100644 index 0000000000..27ac9e0673 --- /dev/null +++ b/installer/PowerToysSetup/Run.wxs @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs new file mode 100644 index 0000000000..849fb081a0 --- /dev/null +++ b/installer/PowerToysSetup/Settings.wxs @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/ShortcutGuide.wxs b/installer/PowerToysSetup/ShortcutGuide.wxs new file mode 100644 index 0000000000..9f425289db --- /dev/null +++ b/installer/PowerToysSetup/ShortcutGuide.wxs @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/TextExtractor.wxs b/installer/PowerToysSetup/TextExtractor.wxs new file mode 100644 index 0000000000..f392ad7d37 --- /dev/null +++ b/installer/PowerToysSetup/TextExtractor.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Tools.wxs b/installer/PowerToysSetup/Tools.wxs new file mode 100644 index 0000000000..23aea33d13 --- /dev/null +++ b/installer/PowerToysSetup/Tools.wxs @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/VideoConference.wxs b/installer/PowerToysSetup/VideoConference.wxs new file mode 100644 index 0000000000..231ed04f8f --- /dev/null +++ b/installer/PowerToysSetup/VideoConference.wxs @@ -0,0 +1,48 @@ + + + + + + + + + + WINDOWSBUILDNUMBER >= 19041 + + + + + + + + + + + + + + WINDOWSBUILDNUMBER >= 19041 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/WinAppSDK.wxs b/installer/PowerToysSetup/WinAppSDK.wxs new file mode 100644 index 0000000000..b1b7203bc9 --- /dev/null +++ b/installer/PowerToysSetup/WinAppSDK.wxs @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj index 07b2a3a065..6b4f623b12 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj @@ -47,8 +47,8 @@ call cmd /C "copy ""$(ProjectDir)DepsFilesLists.h"" ""$(ProjectDir)DepsFilesLists.h.bk""" - call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Product.wxs"" ""$(ProjectDir)..\PowerToysSetup\Product.wxs.bk"""" - call powershell.exe -File parseRuntimes.ps1 -depsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json" -depsfileslistspath "$(ProjectDir)DepsFilesLists.h" -productwxspath "$(ProjectDir)..\PowerToysSetup\Product.wxs" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Core.wxs"" ""$(ProjectDir)..\PowerToysSetup\Core.wxs.bk"""" + call powershell.exe -File parseRuntimes.ps1 -depsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json" -depsfileslistspath "$(ProjectDir)DepsFilesLists.h" -productwxspath "$(ProjectDir)..\PowerToysSetup\Core.wxs" Backing up original files and populating .NET and WPF Runtime dependencies From 6f0b16de496bbff821985cec5ecd28c77f2d7a0d Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Mon, 23 Jan 2023 20:13:05 +0300 Subject: [PATCH 003/163] [FancyZones] Improve code quality (part 2: WorkArea init) (#23030) * init WorkArea with a rectangle * keep the highlighted zones state in a separate class --- .../fancyzones/FancyZonesLib/FancyZones.cpp | 12 +- .../FancyZonesLib/FancyZonesLib.vcxproj | 2 + .../FancyZonesLib.vcxproj.filters | 6 + .../FancyZonesLib/HighlightedZones.cpp | 55 ++++++++ .../FancyZonesLib/HighlightedZones.h | 22 ++++ .../fancyzones/FancyZonesLib/MonitorUtils.cpp | 15 +++ .../fancyzones/FancyZonesLib/MonitorUtils.h | 3 + .../fancyzones/FancyZonesLib/WorkArea.cpp | 72 +++-------- .../fancyzones/FancyZonesLib/WorkArea.h | 55 ++------ .../UnitTests/WorkArea.Spec.cpp | 121 +++++++++--------- 10 files changed, 201 insertions(+), 162 deletions(-) create mode 100644 src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp create mode 100644 src/modules/fancyzones/FancyZonesLib/HighlightedZones.h diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 599f96b342..cf2c569a8f 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -733,7 +733,17 @@ void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAr parentId = parentArea->UniqueId(); } - auto workArea = MakeWorkArea(m_hinstance, monitor, id, parentId); + FancyZonesUtils::Rect rect{}; + if (monitor) + { + rect = MonitorUtils::GetWorkAreaRect(monitor); + } + else + { + rect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>(); + } + + auto workArea = MakeWorkArea(m_hinstance, id, parentId, rect); if (workArea) { m_workAreaHandler.AddWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor, workArea); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index c26b30d52e..224779b0fa 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -75,6 +75,7 @@ + @@ -124,6 +125,7 @@ + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index 29ed814973..d14e7a6a4a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -153,6 +153,9 @@ Header Files\FancyZonesData + + Header Files + Header Files @@ -251,6 +254,9 @@ Source Files + + Source Files + diff --git a/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp new file mode 100644 index 0000000000..9c6e7d1cca --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp @@ -0,0 +1,55 @@ +#include "pch.h" +#include "HighlightedZones.h" + +#include + +HighlightedZones::HighlightedZones() +{ +} + +const ZoneIndexSet& HighlightedZones::Zones() const noexcept +{ + return m_highlightZone; +} + +bool HighlightedZones::Empty() const noexcept +{ + return m_highlightZone.empty(); +} + +bool HighlightedZones::Update(const Layout* layout, POINT const& point, bool selectManyZones) noexcept +{ + if (!layout) + { + return false; + } + + auto highlightZone = layout->ZonesFromPoint(point); + + if (selectManyZones) + { + if (m_initialHighlightZone.empty()) + { + // first time + m_initialHighlightZone = highlightZone; + } + else + { + highlightZone = layout->GetCombinedZoneRange(m_initialHighlightZone, highlightZone); + } + } + else + { + m_initialHighlightZone = {}; + } + + const bool updated = (highlightZone != m_highlightZone); + m_highlightZone = std::move(highlightZone); + return updated; +} + +void HighlightedZones::Reset() noexcept +{ + m_highlightZone = {}; + m_initialHighlightZone = {}; +} diff --git a/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h new file mode 100644 index 0000000000..e6a5b22909 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class Layout; + +class HighlightedZones +{ +public: + HighlightedZones(); + ~HighlightedZones() = default; + + const ZoneIndexSet& Zones() const noexcept; + bool Empty() const noexcept; + + bool Update(const Layout* layout, POINT const& point, bool selectManyZones) noexcept; + void Reset() noexcept; + +private: + ZoneIndexSet m_initialHighlightZone; + ZoneIndexSet m_highlightZone; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp index 70209a16c0..9f8281804b 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp @@ -383,4 +383,19 @@ namespace MonitorUtils return displays; } + + FancyZonesUtils::Rect GetWorkAreaRect(HMONITOR monitor) + { + if (monitor) + { + MONITORINFO mi{}; + mi.cbSize = sizeof(mi); + if (GetMonitorInfoW(monitor, &mi)) + { + return FancyZonesUtils::Rect(mi.rcWork); + } + } + + return FancyZonesUtils::Rect{}; + } } \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.h b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.h index a16822b340..dec872f40b 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.h +++ b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace MonitorUtils { @@ -19,4 +20,6 @@ namespace MonitorUtils std::vector IdentifyMonitors() noexcept; void OpenWindowOnActiveMonitor(HWND window, HMONITOR monitor) noexcept; + + FancyZonesUtils::Rect GetWorkAreaRect(HMONITOR monitor); }; diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index 6b83fee0e5..69e19cc5f9 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -109,8 +109,9 @@ namespace WindowPool windowPool; } -WorkArea::WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId) : - m_uniqueId(uniqueId) +WorkArea::WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesUtils::Rect& workAreaRect) : + m_uniqueId(uniqueId), + m_workAreaRect(workAreaRect) { WNDCLASSEXW wcex{}; wcex.cbSize = sizeof(WNDCLASSEX); @@ -129,8 +130,7 @@ WorkArea::~WorkArea() HRESULT WorkArea::MoveSizeEnter(HWND window) noexcept { m_windowMoveSize = window; - m_highlightZone = {}; - m_initialHighlightZone = {}; + m_highlightedZones.Reset(); ShowZonesOverlay(); Trace::WorkArea::MoveOrResizeStarted(m_layout.get(), m_layoutWindows.get()); return S_OK; @@ -144,42 +144,23 @@ HRESULT WorkArea::MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool s } bool redraw = false; - POINT ptClient = ptScreen; - MapWindowPoints(nullptr, m_window, &ptClient, 1); if (dragEnabled) { - auto highlightZone = ZonesFromPoint(ptClient); + POINT ptClient = ptScreen; + MapWindowPoints(nullptr, m_window, &ptClient, 1); - if (selectManyZones) - { - if (m_initialHighlightZone.empty()) - { - // first time - m_initialHighlightZone = highlightZone; - } - else - { - highlightZone = m_layout->GetCombinedZoneRange(m_initialHighlightZone, highlightZone); - } - } - else - { - m_initialHighlightZone = {}; - } - - redraw = (highlightZone != m_highlightZone); - m_highlightZone = std::move(highlightZone); + redraw = m_highlightedZones.Update(m_layout.get(), ptClient, selectManyZones); } - else if (m_highlightZone.size()) + else if (!m_highlightedZones.Empty()) { - m_highlightZone = {}; + m_highlightedZones.Reset(); redraw = true; } if (redraw && m_zonesOverlay) { - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightedZones.Zones(), Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); } return S_OK; @@ -192,7 +173,8 @@ HRESULT WorkArea::MoveSizeEnd(HWND window) noexcept return E_INVALIDARG; } - MoveWindowIntoZoneByIndexSet(window, m_highlightZone); + MoveWindowIntoZoneByIndexSet(window, m_highlightedZones.Zones()); + m_highlightedZones.Reset(); Trace::WorkArea::MoveOrResizeEnd(m_layout.get(), m_layoutWindows.get()); @@ -505,7 +487,7 @@ void WorkArea::ShowZonesOverlay() noexcept if (m_window && m_layout) { SetAsTopmostWindow(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightedZones.Zones(), Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); m_zonesOverlay->Show(); } } @@ -515,9 +497,7 @@ void WorkArea::HideZonesOverlay() noexcept if (m_window) { m_zonesOverlay->Hide(); - m_keyLast = 0; m_windowMoveSize = nullptr; - m_highlightZone = {}; } } @@ -532,8 +512,7 @@ void WorkArea::UpdateActiveZoneSet() noexcept CalculateZoneSet(); if (m_window && m_layout) { - m_highlightZone.clear(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); } } @@ -547,10 +526,10 @@ void WorkArea::CycleWindows(HWND window, bool reverse) noexcept void WorkArea::ClearSelectedZones() noexcept { - if (m_highlightZone.size() && m_layout) + if (!m_highlightedZones.Empty() && m_layout) { - m_highlightZone.clear(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + m_highlightedZones.Reset(); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); } } @@ -609,7 +588,7 @@ void WorkArea::CalculateZoneSet() noexcept } m_layout = std::make_unique(appliedLayout.value()); - m_layout->Init(m_workAreaRect, m_monitor); + m_layout->Init(m_workAreaRect, m_uniqueId.monitorId.monitor); if (!m_layoutWindows) { @@ -639,16 +618,6 @@ LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept return 0; } -ZoneIndexSet WorkArea::ZonesFromPoint(POINT pt) noexcept -{ - if (m_layout) - { - return m_layout->ZonesFromPoint(pt); - } - - return {}; -} - void WorkArea::SetAsTopmostWindow() noexcept { if (!m_window) @@ -667,11 +636,6 @@ void WorkArea::SetAsTopmostWindow() noexcept SetWindowPos(m_window, windowInsertAfter, 0, 0, 0, 0, flags); } -void WorkArea::LogInitializationError() -{ - Logger::error(L"Unable to get monitor info, {}", get_last_error_or_default(GetLastError())); -} - #pragma endregion LRESULT CALLBACK WorkArea::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.h b/src/modules/fancyzones/FancyZonesLib/WorkArea.h index 61458f7179..27181dd4dd 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.h +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -10,7 +11,7 @@ class ZonesOverlay; class WorkArea { public: - WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId); + WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesUtils::Rect& workAreaRect); ~WorkArea(); public: @@ -26,34 +27,6 @@ public: return true; } - inline bool InitWorkAreaRect(HMONITOR monitor) - { - m_monitor = monitor; - -#if defined(UNIT_TESTS) - m_workAreaRect = FancyZonesUtils::Rect({ 0, 0, 1920, 1080 }); -#else - - if (monitor) - { - MONITORINFO mi{}; - mi.cbSize = sizeof(mi); - if (!GetMonitorInfoW(monitor, &mi)) - { - return false; - } - - m_workAreaRect = FancyZonesUtils::Rect(mi.rcWork); - } - else - { - m_workAreaRect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>(); - } -#endif - - return true; - } - FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; } const std::unique_ptr& GetLayout() const noexcept { return m_layout; } const std::unique_ptr& GetLayoutWindows() const noexcept { return m_layoutWindows; } @@ -79,8 +52,6 @@ public: void ClearSelectedZones() noexcept; void CycleWindows(HWND window, bool reverse) noexcept; - - void LogInitializationError(); protected: static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept; @@ -90,32 +61,22 @@ private: void InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept; void CalculateZoneSet() noexcept; LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept; - ZoneIndexSet ZonesFromPoint(POINT pt) noexcept; void SetAsTopmostWindow() noexcept; - HMONITOR m_monitor{}; - FancyZonesUtils::Rect m_workAreaRect{}; + const FancyZonesUtils::Rect m_workAreaRect{}; const FancyZonesDataTypes::WorkAreaId m_uniqueId; HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area. - HWND m_windowMoveSize{}; std::unique_ptr m_layout; std::unique_ptr m_layoutWindows; - ZoneIndexSet m_initialHighlightZone; - ZoneIndexSet m_highlightZone; - WPARAM m_keyLast{}; - size_t m_keyCycle{}; std::unique_ptr m_zonesOverlay; + HighlightedZones m_highlightedZones; + + HWND m_windowMoveSize{}; }; -inline std::shared_ptr MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept +inline std::shared_ptr MakeWorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesDataTypes::WorkAreaId& parentUniqueId, const FancyZonesUtils::Rect& workAreaRect) { - auto self = std::make_shared(hinstance, uniqueId); - if (!self->InitWorkAreaRect(monitor)) - { - self->LogInitializationError(); - return nullptr; - } - + auto self = std::make_shared(hinstance, uniqueId, workAreaRect); if (!self->Init(hinstance, parentUniqueId)) { return nullptr; diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp index edfeaf80f4..6979112402 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp @@ -22,11 +22,12 @@ namespace FancyZonesUnitTests { FancyZonesDataTypes::WorkAreaId m_uniqueId; FancyZonesDataTypes::WorkAreaId m_emptyUniqueId; + FancyZonesUtils::Rect m_workAreaRect{ RECT(0,0,1920,1080) }; HINSTANCE m_hInst{}; HMONITOR m_monitor{}; - TEST_METHOD_INITIALIZE(Init) + TEST_METHOD_INITIALIZE(Init) noexcept { m_uniqueId.monitorId.deviceId.id = L"DELA026"; m_uniqueId.monitorId.deviceId.instanceId = L"5&10a58c63&0&UID16777488"; @@ -38,7 +39,7 @@ namespace FancyZonesUnitTests DefaultLayouts::instance().LoadData(); } - TEST_METHOD_CLEANUP(CleanUp) + TEST_METHOD_CLEANUP(CleanUp) noexcept { std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); @@ -50,7 +51,7 @@ namespace FancyZonesUnitTests { const auto defaultLayout = DefaultLayouts::instance().GetDefaultLayout(); - auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId); + auto workArea = MakeWorkArea({}, m_uniqueId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); Assert::IsTrue(m_uniqueId == workArea->UniqueId()); @@ -65,7 +66,7 @@ namespace FancyZonesUnitTests { const auto defaultLayout = DefaultLayouts::instance().GetDefaultLayout(); - auto workArea = MakeWorkArea({}, {}, m_uniqueId, m_emptyUniqueId); + auto workArea = MakeWorkArea({}, m_uniqueId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); Assert::IsTrue(m_uniqueId == workArea->UniqueId()); @@ -95,10 +96,10 @@ namespace FancyZonesUnitTests .sensitivityRadius = 20, }; - auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, parentUniqueId, m_emptyUniqueId); + auto parentWorkArea = MakeWorkArea(m_hInst, parentUniqueId, m_emptyUniqueId, m_workAreaRect); AppliedLayouts::instance().ApplyLayout(parentUniqueId, layout); - auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, parentUniqueId); + auto actualWorkArea = MakeWorkArea(m_hInst, m_uniqueId, parentUniqueId, m_workAreaRect); Assert::IsNotNull(actualWorkArea->GetLayout().get()); Assert::IsNotNull(actualWorkArea->GetLayoutWindows().get()); @@ -131,7 +132,7 @@ namespace FancyZonesUnitTests DefaultLayouts::instance().LoadData(); // test - auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId); + auto workArea = MakeWorkArea({}, m_uniqueId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); Assert::IsTrue(m_uniqueId == workArea->UniqueId()); @@ -164,7 +165,7 @@ namespace FancyZonesUnitTests DefaultLayouts::instance().LoadData(); // test - auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId); + auto workArea = MakeWorkArea({}, m_uniqueId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); Assert::IsTrue(m_uniqueId == workArea->UniqueId()); @@ -181,11 +182,11 @@ namespace FancyZonesUnitTests { FancyZonesDataTypes::WorkAreaId m_uniqueId; FancyZonesDataTypes::WorkAreaId m_parentUniqueId; // default empty + FancyZonesUtils::Rect m_workAreaRect{ RECT(0, 0, 1920, 1080) }; HINSTANCE m_hInst{}; - HMONITOR m_monitor{}; - TEST_METHOD_INITIALIZE(Init) + TEST_METHOD_INITIALIZE(Init) noexcept { m_uniqueId.monitorId.deviceId.id = L"DELA026"; m_uniqueId.monitorId.deviceId.instanceId = L"5&10a58c63&0&UID16777488"; @@ -196,7 +197,7 @@ namespace FancyZonesUnitTests AppliedLayouts::instance().LoadData(); } - TEST_METHOD_CLEANUP(CleanUp) + TEST_METHOD_CLEANUP(CleanUp) noexcept { std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); @@ -205,9 +206,9 @@ namespace FancyZonesUnitTests public: TEST_METHOD (MoveSizeEnter) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - const auto expected = S_OK; + constexpr auto expected = S_OK; const auto actual = workArea->MoveSizeEnter(Mocks::Window()); Assert::AreEqual(expected, actual); @@ -215,9 +216,9 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEnterTwice) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - const auto expected = S_OK; + constexpr auto expected = S_OK; workArea->MoveSizeEnter(Mocks::Window()); const auto actual = workArea->MoveSizeEnter(Mocks::Window()); @@ -227,9 +228,9 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeUpdate) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - const auto expected = S_OK; + constexpr auto expected = S_OK; const auto actual = workArea->MoveSizeUpdate(POINT{ 0, 0 }, true, false); Assert::AreEqual(expected, actual); @@ -237,9 +238,9 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeUpdatePointNegativeCoordinates) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - const auto expected = S_OK; + constexpr auto expected = S_OK; const auto actual = workArea->MoveSizeUpdate(POINT{ -10, -10 }, true, false); Assert::AreEqual(expected, actual); @@ -247,9 +248,9 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeUpdatePointBigCoordinates) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - const auto expected = S_OK; + constexpr auto expected = S_OK; const auto actual = workArea->MoveSizeUpdate(POINT{ LONG_MAX, LONG_MAX }, true, false); Assert::AreEqual(expected, actual); @@ -257,13 +258,13 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEnd) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::Window(); workArea->MoveSizeEnter(window); workArea->MoveSizeUpdate({ 20, 20 }, true, true); - const auto expected = S_OK; + constexpr auto expected = S_OK; const auto actual = workArea->MoveSizeEnd(window); Assert::AreEqual(expected, actual); @@ -274,12 +275,12 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEndDifferentWindows) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::Window(); workArea->MoveSizeEnter(window); - const auto expected = E_INVALIDARG; + constexpr auto expected = E_INVALIDARG; const auto actual = workArea->MoveSizeEnd(Mocks::Window()); Assert::AreEqual(expected, actual); @@ -287,9 +288,9 @@ namespace FancyZonesUnitTests TEST_METHOD (MoveSizeEndWindowNotSet) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - const auto expected = E_INVALIDARG; + constexpr auto expected = E_INVALIDARG; const auto actual = workArea->MoveSizeEnd(Mocks::Window()); Assert::AreEqual(expected, actual); @@ -297,7 +298,7 @@ namespace FancyZonesUnitTests TEST_METHOD (SaveWindowProcessToZoneIndexNullptrWindow) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); workArea->SaveWindowProcessToZoneIndex(nullptr); @@ -307,7 +308,7 @@ namespace FancyZonesUnitTests TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAdded) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); auto window = Mocks::WindowCreate(m_hInst); workArea->GetLayout()->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); @@ -320,7 +321,7 @@ namespace FancyZonesUnitTests TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAddedWithFilledAppZoneHistory) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); const auto processPath = get_process_path(window); @@ -332,7 +333,7 @@ namespace FancyZonesUnitTests Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); const auto& appHistoryArray1 = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); Assert::AreEqual((size_t)1, appHistoryArray1.size()); - Assert::IsTrue(std::vector{ 0 } == appHistoryArray1[0].zoneIndexSet); + Assert::IsTrue(std::vector{ 0 } == appHistoryArray1.at(0).zoneIndexSet); // add zone without window workArea->GetLayout()->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); @@ -341,12 +342,12 @@ namespace FancyZonesUnitTests Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); const auto& appHistoryArray2 = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); Assert::AreEqual((size_t)1, appHistoryArray2.size()); - Assert::IsTrue(std::vector{ 0 } == appHistoryArray2[0].zoneIndexSet); + Assert::IsTrue(std::vector{ 0 } == appHistoryArray2.at(0).zoneIndexSet); } TEST_METHOD (SaveWindowProcessToZoneIndexWindowAdded) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); auto window = Mocks::WindowCreate(m_hInst); const auto processPath = get_process_path(window); @@ -360,25 +361,25 @@ namespace FancyZonesUnitTests Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); const auto& appHistoryArray = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); Assert::AreEqual((size_t)1, appHistoryArray.size()); - Assert::IsTrue(std::vector{ 2 } == appHistoryArray[0].zoneIndexSet); + Assert::IsTrue(std::vector{ 2 } == appHistoryArray.at(0).zoneIndexSet); workArea->SaveWindowProcessToZoneIndex(window); const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory(); Assert::AreEqual((size_t)1, actualAppZoneHistory.size()); const auto& expected = workArea->GetLayoutWindows()->GetZoneIndexSetFromWindow(window); - const auto& actual = appHistoryArray[0].zoneIndexSet; + const auto& actual = appHistoryArray.at(0).zoneIndexSet; Assert::IsTrue(expected == actual); } TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt) { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); auto window = Mocks::WindowCreate(m_hInst); - int originalWidth = 450; - int originalHeight = 550; + const int originalWidth = 450; + const int originalHeight = 550; SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW); SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX); @@ -412,7 +413,7 @@ namespace FancyZonesUnitTests FancyZonesDataTypes::WorkAreaId m_parentUniqueId; // default empty HINSTANCE m_hInst{}; - HMONITOR m_monitor{}; + FancyZonesUtils::Rect m_workAreaRect{ RECT(0, 0, 1920, 1080) }; void PrepareEmptyLayout() { @@ -482,13 +483,13 @@ namespace FancyZonesUnitTests AppliedLayouts::instance().LoadData(); } - TEST_METHOD_INITIALIZE(Init) + TEST_METHOD_INITIALIZE(Init) noexcept { AppZoneHistory::instance().LoadData(); AppliedLayouts::instance().LoadData(); } - TEST_METHOD_CLEANUP(CleanUp) + TEST_METHOD_CLEANUP(CleanUp) noexcept { std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); @@ -498,7 +499,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -515,7 +516,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -532,7 +533,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -549,7 +550,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -566,7 +567,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); const auto& layoutWindows = workArea->GetLayoutWindows(); @@ -585,7 +586,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -596,14 +597,14 @@ namespace FancyZonesUnitTests Assert::AreEqual((size_t)1, actualAppZoneHistory.size()); const auto& layoutWindows = workArea->GetLayoutWindows(); - Assert::IsTrue(ZoneIndexSet{ (ZoneIndex)workArea->GetLayout()->Zones().size() - 1 } == layoutWindows->GetZoneIndexSetFromWindow(window)); + Assert::IsTrue(ZoneIndexSet{ static_cast(workArea->GetLayout()->Zones().size()) - 1 } == layoutWindows->GetZoneIndexSetFromWindow(window)); } TEST_METHOD (MoveAppliedWindowByIndexNoCycle) { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -621,7 +622,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -638,7 +639,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -655,7 +656,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -672,7 +673,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -690,7 +691,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -708,7 +709,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -726,7 +727,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -744,7 +745,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); const auto& layoutWindows = workArea->GetLayoutWindows(); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -763,7 +764,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -781,7 +782,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -799,7 +800,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone From 97e5e22b4eaf6b6e4e542e9ade99dfb069009762 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Tue, 24 Jan 2023 13:10:31 +0000 Subject: [PATCH 004/163] [Awake][Settings]Don't hide "Keep screen on" (#23575) * [Awake][Settings]Don't hide "Keep screen on" * Change tray menu as well --- src/modules/awake/Awake/Core/TrayHelper.cs | 2 +- src/settings-ui/Settings.UI/Strings/en-us/Resources.resw | 3 +++ src/settings-ui/Settings.UI/Views/AwakePage.xaml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/awake/Awake/Core/TrayHelper.cs b/src/modules/awake/Awake/Core/TrayHelper.cs index 171da89ff2..a0f908ce2a 100644 --- a/src/modules/awake/Awake/Core/TrayHelper.cs +++ b/src/modules/awake/Awake/Core/TrayHelper.cs @@ -106,7 +106,7 @@ namespace Awake.Core PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_SEPARATOR, 0, string.Empty); } - PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (keepDisplayOn ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on"); + PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (keepDisplayOn ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED) | (mode == AwakeMode.PASSIVE ? MENU_ITEM_FLAGS.MF_DISABLED : MENU_ITEM_FLAGS.MF_ENABLED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on"); } // In case there are no tray shortcuts defined for the application default to a diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 18f72d0a5b..04a8c64b48 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -1822,6 +1822,9 @@ From there, simply click on one of the supported files in the File Explorer and Keep screen on + + This setting is only available when keeping the PC awake + Mode diff --git a/src/settings-ui/Settings.UI/Views/AwakePage.xaml b/src/settings-ui/Settings.UI/Views/AwakePage.xaml index a084485e2c..74030677b3 100644 --- a/src/settings-ui/Settings.UI/Views/AwakePage.xaml +++ b/src/settings-ui/Settings.UI/Views/AwakePage.xaml @@ -83,7 +83,7 @@ + IsEnabled="{x:Bind ViewModel.IsScreenConfigurationPossibleEnabled, Mode=OneWay}"> From 67ae9b037639e4bd3e5e5ad7ae9b79bd35ae24a4 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Wed, 25 Jan 2023 18:36:25 +0100 Subject: [PATCH 005/163] [devdocs] Add disk usage footprint doc (#23248) * [devdocs] Add disk usage footprint doc * Spelling * Add conclusion * Minor fix * Add version * Add more details * Update disk-usage-footprint.md (#23280) * Update disk-usage-footprint.md * Update disk-usage-footprint.md * Update expect.txt * Update doc/devdocs/disk-usage-footprint.md Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> * Update doc/devdocs/disk-usage-footprint.md Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> * Update doc/devdocs/disk-usage-footprint.md Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> * Update doc/devdocs/disk-usage-footprint.md Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> * Update doc/devdocs/disk-usage-footprint.md Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> * Update doc/devdocs/disk-usage-footprint.md Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> * Update doc/devdocs/disk-usage-footprint.md Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> Co-authored-by: Clint Rutkas Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com> --- .github/actions/spell-check/expect.txt | 4 +- doc/devdocs/disk-usage-footprint.md | 82 ++++++++++++++++++ .../disk-usage/PowerToys_install_dir.png | Bin 0 -> 12465 bytes .../disk-usage/add_remove_size_v0.66.png | Bin 0 -> 5739 bytes doc/images/disk-usage/empty_disk_details.png | Bin 0 -> 57991 bytes .../disk-usage/install_dir_size_v0.66.png | Bin 0 -> 53723 bytes doc/images/disk-usage/pwsh_v0.66.png | Bin 0 -> 29284 bytes .../disk-usage/used_disk_space_v0.66.png | Bin 0 -> 59176 bytes 8 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 doc/devdocs/disk-usage-footprint.md create mode 100644 doc/images/disk-usage/PowerToys_install_dir.png create mode 100644 doc/images/disk-usage/add_remove_size_v0.66.png create mode 100644 doc/images/disk-usage/empty_disk_details.png create mode 100644 doc/images/disk-usage/install_dir_size_v0.66.png create mode 100644 doc/images/disk-usage/pwsh_v0.66.png create mode 100644 doc/images/disk-usage/used_disk_space_v0.66.png diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 893b44edef..5d134a28f7 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1194,7 +1194,7 @@ NPH NResize NTAPI ntdll -NTFS +ntfs NTSTATUS nugets nullonfailure @@ -1437,7 +1437,7 @@ rectp rects recyclebin redirectedfrom -Redist +redist redistributable reencode reencoded diff --git a/doc/devdocs/disk-usage-footprint.md b/doc/devdocs/disk-usage-footprint.md new file mode 100644 index 0000000000..08de4bd7bd --- /dev/null +++ b/doc/devdocs/disk-usage-footprint.md @@ -0,0 +1,82 @@ +# PowerToys disk usage footprint overview + +As of the v0.66 of PowerToys, core runtime dlls are being shipped self-contained as a part of PowerToys. During the installation process, hard-links are being created for every module that needs any of these shared libraries. + +## Why do this work? + +Our main motivations for doing are the following: + +1. Be able to install PowerToys without UAC elevation +2. PowerToys has a reduced disk space when components are shared. +3. Reduce additional downloads for dependencies + +## Current self-contained runtimes + +These dll's are installed in `/dll/` folder structure. + +- [Windows Application SDK runtime](https://learn.microsoft.com/windows/apps/windows-app-sdk/downloads) +- [.NET Desktop Runtime 7](https://dotnet.microsoft.com/download/dotnet/7.0) +- [Microsoft Visual C++ Runtime](https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist?view=msvc-170) + +## So why does PowerToys have a larger footprint now? + +Before the hard-link work was done, each of these runtimes had to be installed, so there were more items being installed outside just the PowerToys install directory. This space would not have been accounted. + +File Explorer treats hard-links the same as "original"/regular files/directories (https://learn.microsoft.com/troubleshoot/windows-server/backup-and-storage/disk-space-problems-on-ntfs-volumes#other-ntfs-features-that-may-cause-file-allocation-confusion), not as links. This results in File Explorer reporting size of PowerToys installation directory bigger than it is (more than 2GB). + +## How much space is actually being used + +As of v0.66 of PowerToys, the installed footprint is ~660MB. + +When we were prototyping and validating, here was our running table of results and why we took the route we did. + +| Moment | Installer size | App size | Installation dir size | Real size | Deps downloaded and installed during PT install | Total (installer size + real size + deps downloaded and installed during PT install) | +|---|---|---|---|---|---|---| +| v0.62.0.1
no deps self-contained | 125 MB | 817 MB | 529 MB | 534 MB | 418 MB | 1,077 MB | +| v0.63.0
WAS and VCRedist | 83 MB | 587 MB | 567 MB | 396 MB | 394 MB | 873 MB | +| v0.63.0
WAS1.2preview and VCRedist| 80 MB | 574 MB | 539 MB | 385 MB | 394 MB | 859 MB | +| Full Test
WAS, VCRedist, .NET | 149 MB | 705 MB | 1,760 MB | 557 MB | 0 MB | 706 MB | + +## Our process to verify current install footprint + +We created a quick virtual machine. We chose to use our fresh Windows 10 test virtual machines here. + +### Empty disk details + + + +### Install PowerToys to empty disk + + + +### PowerToys installation directory size shown by File Explorer + +As mentioned above, File Explorer shows size of PowerToys installation dir as every hard-link is a regular file for itself + + + +### PowerToys size shown by App->Installed apps + + + +### Disk usage with PowerToys installed + +Real disk usage of PowerToys is shown by inspecting disk usage after installing PowerToys. Used space is now 695MB, comparing to ~35MB used space for empty disk gives us the size of ~660MB for PowerToys installation dir. + + + +### PowerShell command calculating size of non-hardlinks files + +Size of regular files (non-hard-links) and hard-links can also be obtained by running following PowerShell command in PowerToys installation dir: + +``` +Regular files: +ls -Recurse -File -force -ErrorAction SilentlyContinue | ? LinkType -ne HardLink | Measure-Object -Property Length -Sum + +Hard-links +ls -Recurse -File -force -ErrorAction SilentlyContinue | ? LinkType -e HardLink | Measure-Object -Property Length -Sum +``` + +Running these commands for PowerToys v0.66 shows that size of regular files is way less than size of hard-links pointing to some of those regular files because PowerShell (same as File Explorer) also takes the size of hard-links as of regular files. + + diff --git a/doc/images/disk-usage/PowerToys_install_dir.png b/doc/images/disk-usage/PowerToys_install_dir.png new file mode 100644 index 0000000000000000000000000000000000000000..d8559a4b28df4a08f5032e121ee1a866c843da00 GIT binary patch literal 12465 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 p#COYzXFnUM?;f;2+&QQDqOQ-IHVg`23pGS~ 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⪼3e1|C6`snLZcB?n)IzKd{7}p2?%HPXv!+P?OYsS8q`nqw}ivCC` z$Uijd+)5M4%JRARW3|}yg1`~u=}s$8pF@kOJ!-Jp4~L3}!+ZZmdT=gEL9FY+_V;-w zcYmmR6w22rUx7f2m#NNU7y0|ua@%>V%~KkIXm_5oDZ;FL?RyoKnlXiJ*JsFBtRlDM z26(HDsLkoniJ=cywhs7Q^TgSKN)-CNh@)nZvsI5;Ma1G39KxK5m6MF)%Vn+~LdZ_{ zyrZ@U%OxMLZwCpv@Rz|G$bb0p#L>&tEld z2%yje|D$^SFGBRcM5p5>xDo)2ylhzitTF#*o_P7-+dk;snqT>9kT{Vih=FTYt*%s> Hcs%?!W0y;% literal 0 HcmV?d00001 diff --git a/doc/images/disk-usage/empty_disk_details.png b/doc/images/disk-usage/empty_disk_details.png new file mode 100644 index 0000000000000000000000000000000000000000..376320830dba3902c5bb23f4ebd99e3647ba64fb GIT binary patch literal 57991 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

MC(zQKb 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 literal 0 HcmV?d00001 diff --git a/doc/images/disk-usage/pwsh_v0.66.png b/doc/images/disk-usage/pwsh_v0.66.png new file mode 100644 index 0000000000000000000000000000000000000000..854c154c054847e0473048273653ee9fde6124d1 GIT binary patch literal 29284 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 Oi}DXuq>CRI1^zE4|6Z~H literal 0 HcmV?d00001 diff --git a/doc/images/disk-usage/used_disk_space_v0.66.png b/doc/images/disk-usage/used_disk_space_v0.66.png new file mode 100644 index 0000000000000000000000000000000000000000..af687d9e43ab9fc384e07dd0fb6af85d5d7f6198 GIT binary patch literal 59176 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%0DM+&T2_x6@VV6f)j8Q$7^#qibvy!Tib-u6lT` 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 literal 0 HcmV?d00001 From 1f307ba2f03d23aee013bd87d4fca825a7bbefc4 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Thu, 26 Jan 2023 13:33:12 +0100 Subject: [PATCH 006/163] [Launcher][WindowWalker]Show all open windows with action keyword (#23307) --- .../Components/SearchController.cs | 43 ++++++++++++------- .../Components/SearchResult.cs | 12 ++++++ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs index d7962f0286..4ec3f4025f 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs @@ -101,7 +101,7 @@ namespace Microsoft.Plugin.WindowWalker.Components if (string.IsNullOrWhiteSpace(SearchText)) { - searchMatches = new List(); + searchMatches = AllOpenWindows(snapshotOfOpenWindows); } else { @@ -117,23 +117,16 @@ namespace Microsoft.Plugin.WindowWalker.Components private List FuzzySearchOpenWindows(List openWindows) { List result = new List(); - List searchStrings = new List(); + var searchStrings = new SearchString(searchText, SearchResult.SearchType.Fuzzy); - searchStrings.Add(new SearchString(searchText, SearchResult.SearchType.Fuzzy)); - - foreach (var searchString in searchStrings) + foreach (var window in openWindows) { - foreach (var window in openWindows) - { - var titleMatch = FuzzyMatching.FindBestFuzzyMatch(window.Title, searchString.SearchText); - var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.Process.Name, searchString.SearchText); + var titleMatch = FuzzyMatching.FindBestFuzzyMatch(window.Title, searchStrings.SearchText); + var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.Process.Name, searchStrings.SearchText); - if ((titleMatch.Count != 0 || processMatch.Count != 0) && - window.Title.Length != 0) - { - var temp = new SearchResult(window, titleMatch, processMatch, searchString.SearchType); - result.Add(temp); - } + if ((titleMatch.Count != 0 || processMatch.Count != 0) && window.Title.Length != 0) + { + result.Add(new SearchResult(window, titleMatch, processMatch, searchStrings.SearchType)); } } @@ -142,6 +135,26 @@ namespace Microsoft.Plugin.WindowWalker.Components return result; } + ///

+ /// Search method that matches all the windows with a title + /// + /// what windows are open + /// Returns search results + private List AllOpenWindows(List openWindows) + { + List result = new List(); + + foreach (var window in openWindows) + { + if (window.Title.Length != 0) + { + result.Add(new SearchResult(window)); + } + } + + return result.OrderBy(w => w.Result.Title).ToList(); + } + /// /// Event args for a window list update event /// diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchResult.cs index 9d4be2cf33..168f11d2f5 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchResult.cs @@ -80,6 +80,18 @@ namespace Microsoft.Plugin.WindowWalker.Components CalculateScore(); } + /// + /// Initializes a new instance of the class. + /// + internal SearchResult(Window window) + { + Result = window; + SearchMatchesInTitle = new List(); + SearchMatchesInProcessName = new List(); + SearchResultMatchType = SearchType.Empty; + CalculateScore(); + } + /// /// Calculates the score for how closely this window matches the search string /// From 8a0cf5ed9732becd5daae0f755dd9246f6d82c92 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Thu, 26 Jan 2023 16:39:45 +0000 Subject: [PATCH 007/163] [build][installer]Call powershell with unrestricted flag (#23619) --- .../PowerToysSetupCustomActions.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj index 6b4f623b12..e4802e286a 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj @@ -48,7 +48,7 @@ call cmd /C "copy ""$(ProjectDir)DepsFilesLists.h"" ""$(ProjectDir)DepsFilesLists.h.bk""" call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Core.wxs"" ""$(ProjectDir)..\PowerToysSetup\Core.wxs.bk"""" - call powershell.exe -File parseRuntimes.ps1 -depsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json" -depsfileslistspath "$(ProjectDir)DepsFilesLists.h" -productwxspath "$(ProjectDir)..\PowerToysSetup\Core.wxs" + call powershell.exe -NonInteractive -executionpolicy Unrestricted -File parseRuntimes.ps1 -depsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json" -depsfileslistspath "$(ProjectDir)DepsFilesLists.h" -productwxspath "$(ProjectDir)..\PowerToysSetup\Core.wxs" Backing up original files and populating .NET and WPF Runtime dependencies From 923c220338886194856e76de2f31e1d5d98dbbca Mon Sep 17 00:00:00 2001 From: Jeremy Sinclair <4016293+snickler@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:40:15 -0500 Subject: [PATCH 008/163] [ci]Enhance ARM64 configuration verification (#23509) * Use case-sensitive equality check for platform name * Add missing platform message. * Update spelling --- .github/actions/spell-check/expect.txt | 2 ++ .pipelines/verifyArm64Configuration.ps1 | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 5d134a28f7..03f9f0a34d 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -192,6 +192,7 @@ CDeclaration CDEF cdpx CENTERALIGN +ceq cguid changecursor Changemove @@ -234,6 +235,7 @@ CMock CMONITORS cmpgt cmyk +cne cnt Cocklebiddy coclass diff --git a/.pipelines/verifyArm64Configuration.ps1 b/.pipelines/verifyArm64Configuration.ps1 index 6a9cdd3986..56e06c8f65 100644 --- a/.pipelines/verifyArm64Configuration.ps1 +++ b/.pipelines/verifyArm64Configuration.ps1 @@ -22,7 +22,7 @@ catch { $solutionFile = [Microsoft.Build.Construction.SolutionFile]::Parse($solution); $arm64SlnConfigs = $solutionFile.SolutionConfigurations | Where-Object { - $_.PlatformName -eq "ARM64" + $_.PlatformName -ceq "ARM64" }; # Should have two configurations. Debug and Release. @@ -39,9 +39,9 @@ $projects = $solutionFile.ProjectsInOrder | Where-Object { # Enumerate through the projects and add any project with a mismatched platform and project configuration foreach ($project in $projects) { foreach ($slnConfig in $arm64SlnConfigs.FullName) { - if ($project.ProjectConfigurations.$slnConfig.FullName -ne $slnConfig) { - $errorTable[$project.ProjectName] += @("" - | Select-Object @{n = "Configuration"; e = { $project.ProjectConfigurations.$slnConfig.FullName } }, + if ($project.ProjectConfigurations.$slnConfig.FullName -cne $slnConfig) { + $errorTable[$project.ProjectName] += @(""` + | Select-Object @{n = "Configuration"; e = { $project.ProjectConfigurations.$slnConfig.FullName ?? "Missing platform" } }, @{n = "ExpectedConfiguration"; e = { $slnConfig } }) } } From dbcf1fca1511f3f9a77b063aee053d4f3a01721f Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Thu, 26 Jan 2023 20:02:46 +0300 Subject: [PATCH 009/163] [FancyZones] Improve code quality (part 3: window dragging) (#23040) --- .../FancyZonesLib/DraggingState.cpp | 83 +++++++++++++++++++ .../fancyzones/FancyZonesLib/DraggingState.h | 30 +++++++ .../fancyzones/FancyZonesLib/FancyZones.cpp | 28 ++++--- .../FancyZonesLib/FancyZonesLib.vcxproj | 2 + .../FancyZonesLib.vcxproj.filters | 9 ++ .../FancyZonesLib/WindowMoveHandler.cpp | 77 +++-------------- .../FancyZonesLib/WindowMoveHandler.h | 31 +------ .../fancyzones/FancyZonesLib/WorkArea.cpp | 2 +- 8 files changed, 156 insertions(+), 106 deletions(-) create mode 100644 src/modules/fancyzones/FancyZonesLib/DraggingState.cpp create mode 100644 src/modules/fancyzones/FancyZonesLib/DraggingState.h diff --git a/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp b/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp new file mode 100644 index 0000000000..03cc789e1e --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp @@ -0,0 +1,83 @@ +#include "pch.h" +#include "DraggingState.h" + +#include +#include + +DraggingState::DraggingState(const std::function& keyUpdateCallback) : + m_mouseState(false), + m_mouseHook(std::bind(&DraggingState::OnMouseDown, this)), + m_leftShiftKeyState(keyUpdateCallback), + m_rightShiftKeyState(keyUpdateCallback), + m_ctrlKeyState(keyUpdateCallback), + m_keyUpdateCallback(keyUpdateCallback) +{ +} + +void DraggingState::Enable() +{ + if (FancyZonesSettings::settings().mouseSwitch) + { + m_mouseHook.enable(); + } + + m_leftShiftKeyState.enable(); + m_rightShiftKeyState.enable(); + m_ctrlKeyState.enable(); +} + +void DraggingState::Disable() +{ + bool leftShiftPressed = m_leftShiftKeyState.state(); + bool rightShiftPressed = m_rightShiftKeyState.state(); + + if (FancyZonesSettings::settings().shiftDrag) + { + if (leftShiftPressed) + { + FancyZonesUtils::SwallowKey(VK_LSHIFT); + } + + if (rightShiftPressed) + { + FancyZonesUtils::SwallowKey(VK_RSHIFT); + } + } + + m_dragging = false; + m_mouseState = false; + + m_mouseHook.disable(); + m_leftShiftKeyState.disable(); + m_rightShiftKeyState.disable(); + m_ctrlKeyState.disable(); +} + +void DraggingState::UpdateDraggingState() noexcept +{ + // This updates m_dragEnabled depending on if the shift key is being held down + if (FancyZonesSettings::settings().shiftDrag) + { + m_dragging = ((m_leftShiftKeyState.state() || m_rightShiftKeyState.state()) ^ m_mouseState); + } + else + { + m_dragging = !((m_leftShiftKeyState.state() || m_rightShiftKeyState.state()) ^ m_mouseState); + } +} + +void DraggingState::OnMouseDown() +{ + m_mouseState = !m_mouseState; + m_keyUpdateCallback(); +} + +bool DraggingState::IsDragging() const noexcept +{ + return m_dragging; +} + +bool DraggingState::IsSelectManyZonesState() const noexcept +{ + return m_ctrlKeyState.state(); +} diff --git a/src/modules/fancyzones/FancyZonesLib/DraggingState.h b/src/modules/fancyzones/FancyZonesLib/DraggingState.h new file mode 100644 index 0000000000..cb6873a1be --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/DraggingState.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +class DraggingState +{ +public: + DraggingState(const std::function& keyUpdateCallback); + ~DraggingState() = default; + + void Enable(); + void Disable(); + void UpdateDraggingState() noexcept; + + bool IsDragging() const noexcept; + bool IsSelectManyZonesState() const noexcept; + +private: + void OnMouseDown(); + + std::atomic m_mouseState; + SecondaryMouseButtonsHook m_mouseHook; + KeyState m_leftShiftKeyState; + KeyState m_rightShiftKeyState; + KeyState m_ctrlKeyState; + std::function m_keyUpdateCallback; + + bool m_dragging{}; // True if we should be showing zone hints while dragging +}; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index cf2c569a8f..940232b38e 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -59,7 +60,8 @@ public: FancyZones(HINSTANCE hinstance, std::function disableModuleCallbackFunction) noexcept : SettingsObserver({ SettingId::EditorHotkey, SettingId::PrevTabHotkey, SettingId::NextTabHotkey, SettingId::SpanZonesAcrossMonitors }), m_hinstance(hinstance), - m_windowMoveHandler([this]() { + m_windowMoveHandler(), + m_draggingState([this]() { PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL); }) { @@ -89,7 +91,9 @@ public: monitor = NULL; } - m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); + m_draggingState.Enable(); + m_draggingState.UpdateDraggingState(); + m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()), m_draggingState.IsDragging()); } void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept @@ -98,12 +102,16 @@ public: { monitor = NULL; } - m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); + + m_draggingState.UpdateDraggingState(); + m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()), m_draggingState.IsDragging(), m_draggingState.IsSelectManyZonesState()); } void MoveSizeEnd(HWND window) noexcept { + m_draggingState.UpdateDraggingState(); m_windowMoveHandler.MoveSizeEnd(window, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); + m_draggingState.Disable(); } IFACEMETHODIMP_(void) @@ -185,6 +193,7 @@ private: HWND m_window{}; WindowMoveHandler m_windowMoveHandler; MonitorWorkAreaHandler m_workAreaHandler; + DraggingState m_draggingState; wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on @@ -478,7 +487,7 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept digitPressed = info->vkCode - VK_NUMPAD0; } - bool dragging = m_windowMoveHandler.InDragging(); + bool dragging = m_draggingState.IsDragging(); bool changeLayoutWhileNotDragging = !dragging && !shift && win && ctrl && alt && digitPressed != -1; bool changeLayoutWhileDragging = dragging && digitPressed != -1; @@ -494,7 +503,7 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept } } - if (m_windowMoveHandler.IsDragEnabled() && shift) + if (m_draggingState.IsDragging() && shift) { return true; } @@ -638,12 +647,9 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa } else if (message == WM_PRIV_LOCATIONCHANGE) { - if (m_windowMoveHandler.InDragging()) + if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL)) { - if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL)) - { - MoveSizeUpdate(monitor, ptScreen); - } + MoveSizeUpdate(monitor, ptScreen); } } else if (message == WM_PRIV_WINDOWCREATED) @@ -1197,7 +1203,7 @@ void FancyZones::ApplyQuickLayout(int key) noexcept void FancyZones::FlashZones() noexcept { - if (FancyZonesSettings::settings().flashZonesOnQuickSwitch && !m_windowMoveHandler.IsDragEnabled()) + if (FancyZonesSettings::settings().flashZonesOnQuickSwitch && !m_draggingState.IsDragging()) { for (auto [monitor, workArea] : m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())) { diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index 224779b0fa..a9969328c6 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -35,6 +35,7 @@ + @@ -82,6 +83,7 @@ + ../pch.h diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index d14e7a6a4a..50f82600b4 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -159,6 +159,12 @@ Header Files + + Header Files + + + Header Files + @@ -257,6 +263,9 @@ Source Files + + Source Files + diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp index 1e2867513e..7f314f2d6a 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp @@ -13,17 +13,12 @@ #include #include -WindowMoveHandler::WindowMoveHandler(const std::function& keyUpdateCallback) : - m_mouseState(false), - m_mouseHook(std::bind(&WindowMoveHandler::OnMouseDown, this)), - m_leftShiftKeyState(keyUpdateCallback), - m_rightShiftKeyState(keyUpdateCallback), - m_ctrlKeyState(keyUpdateCallback), - m_keyUpdateCallback(keyUpdateCallback) + +WindowMoveHandler::WindowMoveHandler() { } -void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& /*ptScreen*/, const std::unordered_map>& workAreaMap) noexcept +void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& /*ptScreen*/, const std::unordered_map>& workAreaMap, bool dragEnabled) noexcept { if (!FancyZonesWindowProcessing::IsProcessable(window)) { @@ -47,26 +42,13 @@ void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const m_draggedWindow = window; - if (FancyZonesSettings::settings().mouseSwitch) - { - m_mouseHook.enable(); - } - - m_leftShiftKeyState.enable(); - m_rightShiftKeyState.enable(); - m_ctrlKeyState.enable(); - - // This updates m_dragEnabled depending on if the shift key is being held down - UpdateDragState(); - if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window)) { // Notifies user if unable to drag elevated window FancyZonesNotifications::WarnIfElevationIsRequired(); - m_dragEnabled = false; } - if (m_dragEnabled) + if (dragEnabled) { m_draggedWindowWorkArea = iter->second; SetWindowTransparency(m_draggedWindow); @@ -105,20 +87,17 @@ void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const } } -void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept +void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap, bool dragEnabled, bool multipleZones) noexcept { if (!m_inDragging) { return; } - // This updates m_dragEnabled depending on if the shift key is being held down. - UpdateDragState(); - if (m_draggedWindowWorkArea) { // Update the WorkArea already handling move/size - if (!m_dragEnabled) + if (!dragEnabled) { // Drag got disabled, tell it to cancel and hide all windows m_draggedWindowWorkArea = nullptr; @@ -152,22 +131,22 @@ void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, for (auto [keyMonitor, workArea] : workAreaMap) { - workArea->MoveSizeUpdate(ptScreen, m_dragEnabled, m_ctrlKeyState.state()); + workArea->MoveSizeUpdate(ptScreen, dragEnabled, multipleZones); } } } } - else if (m_dragEnabled) + else if (dragEnabled) { // We'll get here if the user presses/releases shift while dragging. // Restart the drag on the WorkArea that m_draggedWindow is on - MoveSizeStart(m_draggedWindow, monitor, ptScreen, workAreaMap); + MoveSizeStart(m_draggedWindow, monitor, ptScreen, workAreaMap, dragEnabled); // m_dragEnabled could get set to false if we're moving an elevated window. // In that case do not proceed. - if (m_dragEnabled) + if (dragEnabled) { - MoveSizeUpdate(monitor, ptScreen, workAreaMap); + MoveSizeUpdate(monitor, ptScreen, workAreaMap, dragEnabled, multipleZones); } } } @@ -179,14 +158,6 @@ void WindowMoveHandler::MoveSizeEnd(HWND window, const std::unordered_mapMoveSizeEnd(m_draggedWindow); } } @@ -261,8 +219,6 @@ void WindowMoveHandler::MoveSizeEnd(HWND window, const std::unordered_map& keyUpdateCallback); + WindowMoveHandler(); - void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept; - void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept; + void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap, bool dragEnabled) noexcept; + void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap, bool dragEnabled, bool multipleZones) noexcept; void MoveSizeEnd(HWND window, const std::unordered_map>& workAreaMap) noexcept; void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, std::shared_ptr workArea) noexcept; @@ -25,22 +25,6 @@ public: void AssignWindowsToZones(const std::unordered_map>& activeWorkAreas, bool updatePositions) noexcept; - inline void OnMouseDown() noexcept - { - m_mouseState = !m_mouseState; - m_keyUpdateCallback(); - } - - inline bool IsDragEnabled() const noexcept - { - return m_dragEnabled; - } - - inline bool InDragging() const noexcept - { - return m_inDragging; - } - private: struct WindowTransparencyProperties { @@ -60,8 +44,6 @@ private: bool hasNoVisibleOwner = false; }; - void UpdateDragState() noexcept; - void SetWindowTransparency(HWND window) noexcept; void ResetWindowTransparency() noexcept; @@ -69,14 +51,7 @@ private: HWND m_draggedWindow{}; // The window that is being moved/sized MoveSizeWindowInfo m_draggedWindowInfo; // MoveSizeWindowInfo of the window at the moment when dragging started std::shared_ptr m_draggedWindowWorkArea; // "Active" WorkArea, where the move/size is happening. Will update as drag moves between monitors. - bool m_dragEnabled{}; // True if we should be showing zone hints while dragging WindowTransparencyProperties m_windowTransparencyProperties; - std::atomic m_mouseState; - SecondaryMouseButtonsHook m_mouseHook; - KeyState m_leftShiftKeyState; - KeyState m_rightShiftKeyState; - KeyState m_ctrlKeyState; - std::function m_keyUpdateCallback; }; diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index 69e19cc5f9..bf9c2252ce 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -650,4 +650,4 @@ LRESULT CALLBACK WorkArea::s_WndProc(HWND window, UINT message, WPARAM wparam, L return (thisRef != nullptr) ? thisRef->WndProc(message, wparam, lparam) : DefWindowProc(window, message, wparam, lparam); -} +} \ No newline at end of file From c94c79a0573b60c7b1162e674ebcaa884fd72a2e Mon Sep 17 00:00:00 2001 From: Jeremy Sinclair <4016293+snickler@users.noreply.github.com> Date: Fri, 27 Jan 2023 10:08:15 -0500 Subject: [PATCH 010/163] [Build][Installer] Fix Runtime Parsing for .NET Runtime and WPF dependencies (#23626) * Update parseRuntimes to use separate JSON for .NET Runtime / WPF * Update parseRuntimes script to filter duplicate dlls from WPF Runtime List * Fix spelling * Override dlls --------- Co-authored-by: Stefan Markovic --- .github/actions/spell-check/expect.txt | 2 + installer/PowerToysSetup/Awake.wxs | 2 +- installer/PowerToysSetup/ColorPicker.wxs | 2 +- installer/PowerToysSetup/FancyZones.wxs | 2 +- .../PowerToysSetup/FileExplorerPreview.wxs | 4 +- installer/PowerToysSetup/FileLocksmith.wxs | 2 +- installer/PowerToysSetup/Hosts.wxs | 2 +- installer/PowerToysSetup/ImageResizer.wxs | 2 +- installer/PowerToysSetup/MeasureTool.wxs | 2 +- installer/PowerToysSetup/PowerAccent.wxs | 2 +- installer/PowerToysSetup/Run.wxs | 2 +- installer/PowerToysSetup/Settings.wxs | 2 +- installer/PowerToysSetup/TextExtractor.wxs | 2 +- .../PowerToysSetupCustomActions.vcxproj | 2 +- .../parseRuntimes.ps1 | 64 ++++++++++++------- 15 files changed, 56 insertions(+), 38 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 03f9f0a34d..d09e6d483e 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1525,6 +1525,7 @@ RUNLEVEL runsettings runtimeclass runtimeconfig +runtimedepsjsonpath runtimeobject runtimepack runtimes @@ -2069,6 +2070,7 @@ workspaces wox wparam wpf +wpfdepsjsonpath wpftmp wpr wprp diff --git a/installer/PowerToysSetup/Awake.wxs b/installer/PowerToysSetup/Awake.wxs index 49c8304fe7..645cb61cf5 100644 --- a/installer/PowerToysSetup/Awake.wxs +++ b/installer/PowerToysSetup/Awake.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/ColorPicker.wxs b/installer/PowerToysSetup/ColorPicker.wxs index 9c8606b52f..85beb55268 100644 --- a/installer/PowerToysSetup/ColorPicker.wxs +++ b/installer/PowerToysSetup/ColorPicker.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/FancyZones.wxs b/installer/PowerToysSetup/FancyZones.wxs index 6e07dcc4ff..555c46057c 100644 --- a/installer/PowerToysSetup/FancyZones.wxs +++ b/installer/PowerToysSetup/FancyZones.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/FileExplorerPreview.wxs b/installer/PowerToysSetup/FileExplorerPreview.wxs index 1691569c7b..88f8e97d2b 100644 --- a/installer/PowerToysSetup/FileExplorerPreview.wxs +++ b/installer/PowerToysSetup/FileExplorerPreview.wxs @@ -5,9 +5,9 @@ - + - + diff --git a/installer/PowerToysSetup/FileLocksmith.wxs b/installer/PowerToysSetup/FileLocksmith.wxs index 1f9edbe93b..c2ee5f2571 100644 --- a/installer/PowerToysSetup/FileLocksmith.wxs +++ b/installer/PowerToysSetup/FileLocksmith.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/Hosts.wxs b/installer/PowerToysSetup/Hosts.wxs index 530bafaa3a..217251e054 100644 --- a/installer/PowerToysSetup/Hosts.wxs +++ b/installer/PowerToysSetup/Hosts.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/ImageResizer.wxs b/installer/PowerToysSetup/ImageResizer.wxs index bc0de24cce..88a1fea3e7 100644 --- a/installer/PowerToysSetup/ImageResizer.wxs +++ b/installer/PowerToysSetup/ImageResizer.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/MeasureTool.wxs b/installer/PowerToysSetup/MeasureTool.wxs index 6f5412917a..d6223c11ea 100644 --- a/installer/PowerToysSetup/MeasureTool.wxs +++ b/installer/PowerToysSetup/MeasureTool.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/PowerAccent.wxs b/installer/PowerToysSetup/PowerAccent.wxs index a74b374113..928a3ca488 100644 --- a/installer/PowerToysSetup/PowerAccent.wxs +++ b/installer/PowerToysSetup/PowerAccent.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/Run.wxs b/installer/PowerToysSetup/Run.wxs index 27ac9e0673..e8df425b90 100644 --- a/installer/PowerToysSetup/Run.wxs +++ b/installer/PowerToysSetup/Run.wxs @@ -45,7 +45,7 @@ - + diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs index 849fb081a0..c476277736 100644 --- a/installer/PowerToysSetup/Settings.wxs +++ b/installer/PowerToysSetup/Settings.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/TextExtractor.wxs b/installer/PowerToysSetup/TextExtractor.wxs index f392ad7d37..f4f4c725b0 100644 --- a/installer/PowerToysSetup/TextExtractor.wxs +++ b/installer/PowerToysSetup/TextExtractor.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj index e4802e286a..1e5e6c2295 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj @@ -48,7 +48,7 @@ call cmd /C "copy ""$(ProjectDir)DepsFilesLists.h"" ""$(ProjectDir)DepsFilesLists.h.bk""" call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Core.wxs"" ""$(ProjectDir)..\PowerToysSetup\Core.wxs.bk"""" - call powershell.exe -NonInteractive -executionpolicy Unrestricted -File parseRuntimes.ps1 -depsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json" -depsfileslistspath "$(ProjectDir)DepsFilesLists.h" -productwxspath "$(ProjectDir)..\PowerToysSetup\Core.wxs" + call powershell.exe -NonInteractive -executionpolicy Unrestricted -File parseRuntimes.ps1 -runtimedepsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\Settings\PowerToys.Settings.deps.json" -wpfdepsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json" -depsfileslistspath "$(ProjectDir)DepsFilesLists.h" -productwxspath "$(ProjectDir)..\PowerToysSetup\Core.wxs" Backing up original files and populating .NET and WPF Runtime dependencies diff --git a/installer/PowerToysSetupCustomActions/parseRuntimes.ps1 b/installer/PowerToysSetupCustomActions/parseRuntimes.ps1 index f031c4429d..1ea58c268c 100644 --- a/installer/PowerToysSetupCustomActions/parseRuntimes.ps1 +++ b/installer/PowerToysSetupCustomActions/parseRuntimes.ps1 @@ -1,13 +1,46 @@ [CmdletBinding()] Param( [Parameter(Mandatory = $True, Position = 1)] - [string]$depsjsonpath, + [string]$runtimedepsjsonpath, [Parameter(Mandatory = $True, Position = 2)] - [string]$depsfileslistspath, + [string]$wpfdepsjsonpath, [Parameter(Mandatory = $True, Position = 3)] + [string]$depsfileslistspath, + [Parameter(Mandatory = $True, Position = 4)] [string]$productwxspath ) +function Get-RuntimePack ($depsJsonFile, $runtimeName) { + Write-Host "Parsing $runtimeName Runtime" + $runtimePackList = ([array]$depsJsonFile.targets.PSObject.Properties)[-1].Value.PSObject.Properties | Where-Object { $_.Name -match "runtimepack.$runtimeName" }; + + if ($runtimePackList.Length -eq 0) { + Write-Host -ForegroundColor Red "$runtimeName has not been found" + exit 1 + } + + # Enumerate through array of custom objects and parse the names of the property values into a HashTable + $runtimePackList | ForEach-Object { + $runtimes += @{"$($_.Name -replace "runtimepack\.(\S+)\.\S+/\S+",'$1')" = $_.Value.PSObject.Properties.Value | ForEach-Object { + $_.PSObject.Properties.Name + } + } + } + Write-Output $runtimes; +} + +function Update-RuntimeHashTable () { + $runtimes = Get-RuntimePack $runtimeFile "Microsoft.NETCore.App.Runtime" + $runtimes = Get-RuntimePack $wpfRuntimeFile "Microsoft.WindowsDesktop.App.Runtime" + + # Find the dlls that exist in both the .NET Runtime and WPF Runtime deps list and filter out of WPF + $runtimeFileComparison = Compare-Object -ReferenceObject $runtimes["Microsoft.NETCore.App.Runtime"] -DifferenceObject $runtimes["Microsoft.WindowsDesktop.App.Runtime"] -IncludeEqual -ExcludeDifferent + + $runtimes["Microsoft.WindowsDesktop.App.Runtime"] = $runtimes["Microsoft.WindowsDesktop.App.Runtime"] | Where-Object { $_ -notin $runtimeFileComparison.InputObject } + + Write-Output $runtimes; +} + function Update-RuntimeFileList($runtimeToken, $runtimeKey) { $depsFilesLists -replace "($runtimeToken = )(.*);", "`$1 {`r`n$(($runtimes[$runtimeKey] | ForEach-Object {' L"'+$_+'"'} | Sort-Object) -join ",`r`n") };" } @@ -16,8 +49,7 @@ function Update-ProductWxsRuntimeFileList($runtimeToken, $runtimeKey) { $productWxs -replace "(define $runtimeToken=)(.*)?>", "`$1$($runtimes[$runtimeKey] -join ';')?>" } -function Update-DotnetFilesComponentGuid() -{ +function Update-DotnetFilesComponentGuid() { $productWxs -replace "Dlls_DotnetFiles_Component"" Guid=""([{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?)""", "Dlls_DotnetFiles_Component"" Guid=""$((New-Guid).ToString().ToUpper())""" } @@ -28,26 +60,12 @@ $depsFilesLists = Get-Content $depsfileslistspath; $productWxs = Get-Content $productwxspath; # Read the deps.json file and convert it to a JSON object -$runtimeFile = Get-Content $depsjsonpath | ConvertFrom-Json; +$runtimeFile = Get-Content $runtimedepsjsonpath | ConvertFrom-Json; +$wpfRuntimeFile = Get-Content $wpfdepsjsonpath | ConvertFrom-Json; $runtimes = @{} -Write-Host "Parsing .NET Runtimes from $depsjsonpath `r`n" - -$runtimeList = ([array]$runtimeFile.targets.PSObject.Properties)[-1].Value.PSObject.Properties | Where-Object { $_.Name -match "runtimepack" }; - -if ($runtimeList.Length -eq 0) { - Write-Host -ForegroundColor Red "No runtimes have been detected" - exit 1 -} - -# Enumerate through array of custom objects and parse the names of the property values into a HashTable -$runtimeList | ForEach-Object { - $runtimes += @{"$($_.Name -replace "runtimepack\.(\S+)\.\S+/\S+",'$1')" = $_.Value.PSObject.Properties.Value | ForEach-Object { - $_.PSObject.Properties.Name - } - } -} +$runtimes = Update-RuntimeHashTable Write-Host "Writing Microsoft.NETCore.App.Runtime files" $depsFilesLists = Update-RuntimeFileList "dotnetRuntimeFiles" "Microsoft.NETCore.App.Runtime" @@ -64,6 +82,4 @@ Write-Host "Updating $depsfileslistspath" Set-Content -Path $depsfileslistspath -Value $depsFilesLists Write-Host "Updating $productwxspath" -Set-Content -Path $productwxspath -Value $productWxs - - +Set-Content -Path $productwxspath -Value $productWxs \ No newline at end of file From 92f61d6ef5993fd557e996fcc11aca46fea13ff8 Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Mon, 30 Jan 2023 19:39:46 +0300 Subject: [PATCH 011/163] [FancyZones] Improve code quality (part 4) (#23638) --- .../FancyZonesLib/DraggingState.cpp | 6 +- .../fancyzones/FancyZonesLib/FancyZones.cpp | 160 +++++---- .../FancyZonesLib/FancyZonesLib.vcxproj | 4 +- .../FancyZonesLib.vcxproj.filters | 12 +- .../FancyZonesLib/HighlightedZones.cpp | 2 +- .../FancyZonesLib/HighlightedZones.h | 2 +- .../fancyzones/FancyZonesLib/WindowDrag.cpp | 245 ++++++++++++++ .../fancyzones/FancyZonesLib/WindowDrag.h | 47 +++ .../FancyZonesLib/WindowMoveHandler.cpp | 317 ------------------ .../FancyZonesLib/WindowMoveHandler.h | 57 ---- .../fancyzones/FancyZonesLib/WorkArea.cpp | 109 ++---- .../fancyzones/FancyZonesLib/WorkArea.h | 17 +- .../UnitTests/WorkArea.Spec.cpp | 92 ----- 13 files changed, 423 insertions(+), 647 deletions(-) create mode 100644 src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp create mode 100644 src/modules/fancyzones/FancyZonesLib/WindowDrag.h delete mode 100644 src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp delete mode 100644 src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h diff --git a/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp b/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp index 03cc789e1e..469f06c10d 100644 --- a/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp +++ b/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp @@ -28,8 +28,8 @@ void DraggingState::Enable() void DraggingState::Disable() { - bool leftShiftPressed = m_leftShiftKeyState.state(); - bool rightShiftPressed = m_rightShiftKeyState.state(); + const bool leftShiftPressed = m_leftShiftKeyState.state(); + const bool rightShiftPressed = m_rightShiftKeyState.state(); if (FancyZonesSettings::settings().shiftDrag) { @@ -80,4 +80,4 @@ bool DraggingState::IsDragging() const noexcept bool DraggingState::IsSelectManyZonesState() const noexcept { return m_ctrlKeyState.state(); -} +} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 940232b38e..9d42d97097 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -1,14 +1,11 @@ #include "pch.h" #include "FancyZones.h" -#include #include #include #include #include -#include #include -#include #include #include @@ -24,20 +21,13 @@ #include #include #include +#include +#include #include #include +#include +#include #include -#include -#include -#include - -#include "on_thread_executor.h" -#include "trace.h" -#include "VirtualDesktop.h" -#include "MonitorWorkAreaHandler.h" -#include "util.h" - -#include enum class DisplayChangeType { @@ -60,7 +50,6 @@ public: FancyZones(HINSTANCE hinstance, std::function disableModuleCallbackFunction) noexcept : SettingsObserver({ SettingId::EditorHotkey, SettingId::PrevTabHotkey, SettingId::NextTabHotkey, SettingId::SpanZonesAcrossMonitors }), m_hinstance(hinstance), - m_windowMoveHandler(), m_draggingState([this]() { PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL); }) @@ -84,36 +73,6 @@ public: IFACEMETHODIMP_(void) Destroy() noexcept; - void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept - { - if (FancyZonesSettings::settings().spanZonesAcrossMonitors) - { - monitor = NULL; - } - - m_draggingState.Enable(); - m_draggingState.UpdateDraggingState(); - m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()), m_draggingState.IsDragging()); - } - - void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept - { - if (FancyZonesSettings::settings().spanZonesAcrossMonitors) - { - monitor = NULL; - } - - m_draggingState.UpdateDraggingState(); - m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()), m_draggingState.IsDragging(), m_draggingState.IsSelectManyZonesState()); - } - - void MoveSizeEnd(HWND window) noexcept - { - m_draggingState.UpdateDraggingState(); - m_windowMoveHandler.MoveSizeEnd(window, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); - m_draggingState.Disable(); - } - IFACEMETHODIMP_(void) HandleWinHookEvent(const WinHookEvent* data) noexcept { @@ -150,6 +109,10 @@ public: IFACEMETHODIMP_(bool) OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept; + void MoveSizeStart(HWND window, HMONITOR monitor); + void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen); + void MoveSizeEnd(); + void WindowCreated(HWND window) noexcept; void ToggleEditor() noexcept; @@ -175,6 +138,7 @@ private: std::pair, ZoneIndexSet> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreaMap) noexcept; void MoveWindowIntoZone(HWND window, std::shared_ptr workArea, const ZoneIndexSet& zoneIndexSet) noexcept; bool MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept; + void RefreshWorkAreaWindows(bool updatePositions); void OnEditorExitEvent() noexcept; void UpdateZoneSets() noexcept; @@ -191,7 +155,7 @@ private: const HINSTANCE m_hinstance{}; HWND m_window{}; - WindowMoveHandler m_windowMoveHandler; + std::unique_ptr m_windowDrag{}; MonitorWorkAreaHandler m_workAreaHandler; DraggingState m_draggingState; @@ -317,6 +281,46 @@ FancyZones::VirtualDesktopChanged() noexcept PostMessage(m_window, WM_PRIV_VD_SWITCH, 0, 0); } +void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor) +{ + m_windowDrag = WindowDrag::Create(window, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); + if (m_windowDrag) + { + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) + { + monitor = NULL; + } + + m_draggingState.Enable(); + m_draggingState.UpdateDraggingState(); + m_windowDrag->MoveSizeStart(monitor, m_draggingState.IsDragging()); + } +} + +void FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) +{ + if (m_windowDrag) + { + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) + { + monitor = NULL; + } + + m_draggingState.UpdateDraggingState(); + m_windowDrag->MoveSizeUpdate(monitor, ptScreen, m_draggingState.IsDragging(), m_draggingState.IsSelectManyZonesState()); + } +} + +void FancyZones::MoveSizeEnd() +{ + if (m_windowDrag) + { + m_windowDrag->MoveSizeEnd(); + m_draggingState.Disable(); + m_windowDrag = nullptr; + } +} + std::pair, ZoneIndexSet> FancyZones::GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreaMap) noexcept { if (monitor) @@ -351,8 +355,9 @@ void FancyZones::MoveWindowIntoZone(HWND window, std::shared_ptr workA if (workArea) { Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); + workArea->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet); } - m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, workArea); + AppZoneHistory::instance().UpdateProcessIdToHandleMap(window, workArea->UniqueId()); } @@ -395,6 +400,25 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primar return false; } +void FancyZones::RefreshWorkAreaWindows(bool updatePositions) +{ + auto activeWorkAreas = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); + for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop()) + { + auto zoneIndexSet = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); + if (zoneIndexSet.size() == 0) + { + continue; + } + + auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + if (monitor && activeWorkAreas.contains(monitor)) + { + activeWorkAreas.at(monitor)->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, updatePositions); + } + } +} + void FancyZones::WindowCreated(HWND window) noexcept { const bool moveToAppLastZone = FancyZonesSettings::settings().appLastZone_moveWindows; @@ -637,13 +661,12 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa auto hwnd = reinterpret_cast(wparam); if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL)) { - MoveSizeStart(hwnd, monitor, ptScreen); + MoveSizeStart(hwnd, monitor); } } else if (message == WM_PRIV_MOVESIZEEND) { - auto hwnd = reinterpret_cast(wparam); - MoveSizeEnd(hwnd); + MoveSizeEnd(); } else if (message == WM_PRIV_LOCATIONCHANGE) { @@ -717,9 +740,7 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept } UpdateWorkAreas(); - - auto activeWorkAreas = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - m_windowMoveHandler.AssignWindowsToZones(activeWorkAreas, FancyZonesSettings::settings().displayChange_moveWindows && changeType != DisplayChangeType::VirtualDesktop); + RefreshWorkAreaWindows(FancyZonesSettings::settings().displayChange_moveWindows && changeType != DisplayChangeType::VirtualDesktop); } void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id) noexcept @@ -820,7 +841,7 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce do { auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), *currMonitorInfo); - if (m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */, workArea)) + if (workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */)) { // unassign from previous work area for (auto& prevWorkArea : m_workAreaHandler.GetAllWorkAreas()) @@ -859,13 +880,13 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce // Single monitor environment, or combined multi-monitor environment. if (FancyZonesSettings::settings().restoreSize) { - bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */, workArea); + bool moved = workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */); if (!moved) { FancyZonesWindowUtils::RestoreWindowOrigin(window); FancyZonesWindowUtils::RestoreWindowSize(window); } - else + else if (workArea) { Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); } @@ -873,11 +894,13 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce } else { - bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, true /* cycle through zones */, workArea); + bool moved = workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, true /* cycle through zones */); + if (moved) { Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); } + return moved; } } @@ -951,7 +974,11 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept { // Moving to another monitor succeeded const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx]; - m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }, workArea); + if (workArea) + { + workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }); + } + Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); return true; } @@ -996,7 +1023,12 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept { // Moving to another monitor succeeded const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx]; - m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }, workArea); + + if (workArea) + { + workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }); + } + Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); return true; } @@ -1030,7 +1062,7 @@ bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle // Check whether Alt is used in the shortcut key combination if (GetAsyncKeyState(VK_MENU) & 0x8000) { - bool result = m_windowMoveHandler.ExtendWindowByDirectionAndPosition(window, vkCode, workArea); + bool result = workArea && workArea->ExtendWindowByDirectionAndPosition(window, vkCode); if (result) { Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); @@ -1039,7 +1071,7 @@ bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle } else { - bool result = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle, workArea); + bool result = workArea && workArea->MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle); if (result) { Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); @@ -1129,11 +1161,7 @@ void FancyZones::UpdateZoneSets() noexcept workArea->UpdateActiveZoneSet(); } - if (FancyZonesSettings::settings().zoneSetChange_moveWindows) - { - auto activeWorkAreas = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - m_windowMoveHandler.AssignWindowsToZones(activeWorkAreas, true); - } + RefreshWorkAreaWindows(FancyZonesSettings::settings().zoneSetChange_moveWindows); } bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index a9969328c6..7209e35a19 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -71,7 +71,7 @@ - + @@ -123,7 +123,7 @@ - + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index 50f82600b4..e3e21c934a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -48,9 +48,6 @@ Header Files - - Header Files - Header Files @@ -165,6 +162,9 @@ Header Files + + Header Files + @@ -191,9 +191,6 @@ Source Files - - Source Files - Source Files @@ -266,6 +263,9 @@ Source Files + + Source Files + diff --git a/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp index 9c6e7d1cca..22a563aef7 100644 --- a/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp @@ -3,7 +3,7 @@ #include -HighlightedZones::HighlightedZones() +HighlightedZones::HighlightedZones() noexcept { } diff --git a/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h index e6a5b22909..b3ba2ca705 100644 --- a/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h +++ b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h @@ -7,7 +7,7 @@ class Layout; class HighlightedZones { public: - HighlightedZones(); + HighlightedZones() noexcept; ~HighlightedZones() = default; const ZoneIndexSet& Zones() const noexcept; diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp new file mode 100644 index 0000000000..fe69251c96 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp @@ -0,0 +1,245 @@ +#include "pch.h" +#include "WindowDrag.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +WindowDrag::WindowDrag(HWND window, const std::unordered_map>& activeWorkAreas) : + m_window(window), + m_activeWorkAreas(activeWorkAreas), + m_snappingMode(false) +{ + m_windowProperties.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window); + m_windowProperties.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window) && + (!FancyZonesWindowUtils::IsPopupWindow(m_window) || FancyZonesSettings::settings().allowSnapPopupWindows); +} + +WindowDrag::~WindowDrag() +{ + ResetWindowTransparency(); +} + +std::unique_ptr WindowDrag::Create(HWND window, const std::unordered_map>& activeWorkAreas) +{ + if (!FancyZonesWindowProcessing::IsProcessable(window) || + !FancyZonesWindowUtils::IsCandidateForZoning(window) || + FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) + { + return nullptr; + } + + if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window)) + { + // Notifies user if unable to drag elevated window + FancyZonesNotifications::WarnIfElevationIsRequired(); + return nullptr; + } + + return std::unique_ptr(new WindowDrag(window, activeWorkAreas)); +} + +bool WindowDrag::MoveSizeStart(HMONITOR monitor, bool isSnapping) +{ + auto iter = m_activeWorkAreas.find(monitor); + if (iter == end(m_activeWorkAreas)) + { + return false; + } + + if (isSnapping) + { + m_currentWorkArea = iter->second; + } + + SwitchSnappingMode(isSnapping); + + return true; +} + +void WindowDrag::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState) +{ + auto iter = m_activeWorkAreas.find(monitor); + if (isSnapping && iter != m_activeWorkAreas.end()) + { + // The drag has moved to a different monitor. + // Change work area + if (iter->second != m_currentWorkArea) + { + m_highlightedZones.Reset(); + + if (m_currentWorkArea) + { + if (!FancyZonesSettings::settings().showZonesOnAllMonitors) + { + m_currentWorkArea->HideZonesOverlay(); + } + else + { + m_currentWorkArea->ShowZonesOverlay({}, m_window); + } + } + + m_currentWorkArea = iter->second; + } + + if (m_currentWorkArea) + { + POINT ptClient = ptScreen; + MapWindowPoints(nullptr, m_currentWorkArea->GetWorkAreaWindow(), &ptClient, 1); + const bool redraw = m_highlightedZones.Update(m_currentWorkArea->GetLayout().get(), ptClient, isSelectManyZonesState); + if (redraw) + { + m_currentWorkArea->ShowZonesOverlay(m_highlightedZones.Zones(), m_window); + } + } + } + + SwitchSnappingMode(isSnapping); +} + +void WindowDrag::MoveSizeEnd() +{ + if (m_snappingMode) + { + const bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window); + const bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window); + + if ((isStandardWindow == false && hasNoVisibleOwner == true && + m_windowProperties.isStandardWindow == true && m_windowProperties.hasNoVisibleOwner == true) || + FancyZonesWindowUtils::IsWindowMaximized(m_window)) + { + // Abort the zoning, this is a Chromium based tab that is merged back with an existing window + // or if the window is maximized by Windows when the cursor hits the screen top border + } + else if (m_currentWorkArea) + { + m_currentWorkArea->MoveWindowIntoZoneByIndexSet(m_window, m_highlightedZones.Zones()); + } + } + else + { + FancyZonesWindowUtils::ResetRoundCornersPreference(m_window); + if (FancyZonesSettings::settings().restoreSize) + { + if (FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) + { + ::RemoveProp(m_window, ZonedWindowProperties::PropertyRestoreSizeID); + } + else if (!FancyZonesWindowUtils::IsWindowMaximized(m_window)) + { + FancyZonesWindowUtils::RestoreWindowSize(m_window); + } + } + } + + SwitchSnappingMode(false); +} + +void WindowDrag::SwitchSnappingMode(bool isSnapping) +{ + if (!m_snappingMode && isSnapping) // turn on + { + m_highlightedZones.Reset(); + SetWindowTransparency(); + + if (FancyZonesSettings::settings().showZonesOnAllMonitors) + { + for (const auto& [_, workArea] : m_activeWorkAreas) + { + if (workArea) + { + workArea->ShowZonesOverlay({}, m_window); + } + } + } + else if (m_currentWorkArea) + { + m_currentWorkArea->ShowZonesOverlay({}, m_window); + } + + if (m_currentWorkArea) + { + m_currentWorkArea->UnsnapWindow(m_window); + FancyZonesWindowProperties::RemoveZoneIndexProperty(m_window); + + const auto& layout = m_currentWorkArea->GetLayout(); + if (layout) + { + auto guidStr = FancyZonesUtils::GuidToString(layout->Id()); + if (guidStr.has_value()) + { + AppZoneHistory::instance().RemoveAppLastZone(m_window, m_currentWorkArea->UniqueId(), guidStr.value()); + } + } + + Trace::WorkArea::MoveOrResizeStarted(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows().get()); + } + } + else if (m_snappingMode && !isSnapping) // turn off + { + ResetWindowTransparency(); + m_highlightedZones.Reset(); + + // Hide all layouts (regardless of settings) + for (auto& [_, workArea] : m_activeWorkAreas) + { + if (workArea) + { + workArea->HideZonesOverlay(); + } + } + + if (m_currentWorkArea) + { + Trace::WorkArea::MoveOrResizeEnd(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows().get()); + } + } + + m_snappingMode = isSnapping; +} + +void WindowDrag::SetWindowTransparency() +{ + if (FancyZonesSettings::settings().makeDraggedWindowTransparent) + { + m_windowProperties.exstyle = GetWindowLong(m_window, GWL_EXSTYLE); + + SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle | WS_EX_LAYERED); + + if (!GetLayeredWindowAttributes(m_window, &m_windowProperties.crKey, &m_windowProperties.alpha, &m_windowProperties.dwFlags)) + { + Logger::error(L"Window transparency: GetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); + return; + } + + if (!SetLayeredWindowAttributes(m_window, 0, (255 * 50) / 100, LWA_ALPHA)) + { + Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); + } + } +} + +void WindowDrag::ResetWindowTransparency() +{ + if (FancyZonesSettings::settings().makeDraggedWindowTransparent) + { + if (!SetLayeredWindowAttributes(m_window, m_windowProperties.crKey, m_windowProperties.alpha, m_windowProperties.dwFlags)) + { + Logger::error(L"Window transparency: SetLayeredWindowAttributes failed"); + } + + if (SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle) == 0) + { + Logger::error(L"Window transparency: SetWindowLong failed, {}", get_last_error_or_default(GetLastError())); + } + } +} diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.h b/src/modules/fancyzones/FancyZonesLib/WindowDrag.h new file mode 100644 index 0000000000..9e083980f3 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +class WorkArea; + +class WindowDrag +{ + WindowDrag(HWND window, const std::unordered_map>& activeWorkAreas); + +public: + static std::unique_ptr Create(HWND window, const std::unordered_map>& activeWorkAreas); + ~WindowDrag(); + + bool MoveSizeStart(HMONITOR monitor, bool isSnapping); + void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState); + void MoveSizeEnd(); + +private: + void SwitchSnappingMode(bool isSnapping); + + void SetWindowTransparency(); + void ResetWindowTransparency(); + + struct WindowProperties + { + // True if from the styles the window looks like a standard window + bool isStandardWindow = false; + // True if the window is a top-level window that does not have a visible owner + bool hasNoVisibleOwner = false; + // Properties to restore after dragging + long exstyle = 0; + COLORREF crKey = RGB(0, 0, 0); + DWORD dwFlags = 0; + BYTE alpha = 0; + }; + + const HWND m_window; + WindowProperties m_windowProperties; // MoveSizeWindowInfo of the window at the moment when dragging started + + const std::unordered_map>& m_activeWorkAreas; // all WorkAreas on current virtual desktop, mapped with monitors + std::shared_ptr m_currentWorkArea; // "Active" WorkArea, where the move/size is happening. Will update as drag moves between monitors. + + bool m_snappingMode{ false }; + + HighlightedZones m_highlightedZones; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp deleted file mode 100644 index 7f314f2d6a..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp +++ /dev/null @@ -1,317 +0,0 @@ -#include "pch.h" -#include "WindowMoveHandler.h" - -#include -#include -#include -#include - -#include "FancyZonesData/AppZoneHistory.h" -#include "Settings.h" -#include "WorkArea.h" -#include -#include -#include - - -WindowMoveHandler::WindowMoveHandler() -{ -} - -void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& /*ptScreen*/, const std::unordered_map>& workAreaMap, bool dragEnabled) noexcept -{ - if (!FancyZonesWindowProcessing::IsProcessable(window)) - { - return; - } - - if (!FancyZonesWindowUtils::IsCandidateForZoning(window) || FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) - { - return; - } - - m_draggedWindowInfo.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(window); - m_draggedWindowInfo.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(window) && (!FancyZonesWindowUtils::IsPopupWindow(window) || FancyZonesSettings::settings().allowSnapPopupWindows); - m_inDragging = true; - - auto iter = workAreaMap.find(monitor); - if (iter == end(workAreaMap)) - { - return; - } - - m_draggedWindow = window; - - if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window)) - { - // Notifies user if unable to drag elevated window - FancyZonesNotifications::WarnIfElevationIsRequired(); - } - - if (dragEnabled) - { - m_draggedWindowWorkArea = iter->second; - SetWindowTransparency(m_draggedWindow); - m_draggedWindowWorkArea->MoveSizeEnter(m_draggedWindow); - if (FancyZonesSettings::settings().showZonesOnAllMonitors) - { - for (const auto& [keyMonitor, workArea] : workAreaMap) - { - // Skip calling ShowZonesOverlay for iter->second (m_draggedWindowWorkArea) since it - // was already called in MoveSizeEnter - const bool moveSizeEnterCalled = workArea == m_draggedWindowWorkArea; - if (workArea && !moveSizeEnterCalled) - { - workArea->ShowZonesOverlay(); - } - } - } - } - else if (m_draggedWindowWorkArea) - { - ResetWindowTransparency(); - m_draggedWindowWorkArea = nullptr; - for (const auto& [keyMonitor, workArea] : workAreaMap) - { - if (workArea) - { - workArea->HideZonesOverlay(); - } - } - } - - auto workArea = workAreaMap.find(monitor); - if (workArea != workAreaMap.end()) - { - workArea->second->UnsnapWindow(window); - } -} - -void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap, bool dragEnabled, bool multipleZones) noexcept -{ - if (!m_inDragging) - { - return; - } - - if (m_draggedWindowWorkArea) - { - // Update the WorkArea already handling move/size - if (!dragEnabled) - { - // Drag got disabled, tell it to cancel and hide all windows - m_draggedWindowWorkArea = nullptr; - ResetWindowTransparency(); - - for (auto [keyMonitor, workArea] : workAreaMap) - { - if (workArea) - { - workArea->HideZonesOverlay(); - } - } - } - else - { - auto iter = workAreaMap.find(monitor); - if (iter != workAreaMap.end()) - { - if (iter->second != m_draggedWindowWorkArea) - { - // The drag has moved to a different monitor. - m_draggedWindowWorkArea->ClearSelectedZones(); - if (!FancyZonesSettings::settings().showZonesOnAllMonitors) - { - m_draggedWindowWorkArea->HideZonesOverlay(); - } - - m_draggedWindowWorkArea = iter->second; - m_draggedWindowWorkArea->MoveSizeEnter(m_draggedWindow); - } - - for (auto [keyMonitor, workArea] : workAreaMap) - { - workArea->MoveSizeUpdate(ptScreen, dragEnabled, multipleZones); - } - } - } - } - else if (dragEnabled) - { - // We'll get here if the user presses/releases shift while dragging. - // Restart the drag on the WorkArea that m_draggedWindow is on - MoveSizeStart(m_draggedWindow, monitor, ptScreen, workAreaMap, dragEnabled); - - // m_dragEnabled could get set to false if we're moving an elevated window. - // In that case do not proceed. - if (dragEnabled) - { - MoveSizeUpdate(monitor, ptScreen, workAreaMap, dragEnabled, multipleZones); - } - } -} - -void WindowMoveHandler::MoveSizeEnd(HWND window, const std::unordered_map>& workAreaMap) noexcept -{ - if (window != m_draggedWindow) - { - return; - } - - if (m_draggedWindowWorkArea) - { - auto workArea = std::move(m_draggedWindowWorkArea); - ResetWindowTransparency(); - - bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(window); - bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(window); - - if ((isStandardWindow == false && hasNoVisibleOwner == true && - m_draggedWindowInfo.isStandardWindow == true && m_draggedWindowInfo.hasNoVisibleOwner == true) || - FancyZonesWindowUtils::IsWindowMaximized(window)) - { - // Abort the zoning, this is a Chromium based tab that is merged back with an existing window - // or if the window is maximized by Windows when the cursor hits the screen top border - } - else - { - workArea->MoveSizeEnd(m_draggedWindow); - } - } - else - { - if (FancyZonesSettings::settings().restoreSize) - { - if (FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) - { - ::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID); - } - else if (!FancyZonesWindowUtils::IsWindowMaximized(window)) - { - FancyZonesWindowUtils::RestoreWindowSize(window); - } - } - - FancyZonesWindowUtils::ResetRoundCornersPreference(window); - - auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - if (monitor) - { - auto workArea = workAreaMap.find(monitor); - if (workArea != workAreaMap.end()) - { - const auto workAreaPtr = workArea->second; - const auto& layout = workAreaPtr->GetLayout(); - if (layout) - { - auto guidStr = FancyZonesUtils::GuidToString(layout->Id()); - if (guidStr.has_value()) - { - AppZoneHistory::instance().RemoveAppLastZone(window, workAreaPtr->UniqueId(), guidStr.value()); - } - } - - workAreaPtr->UnsnapWindow(window); - } - } - - FancyZonesWindowProperties::RemoveZoneIndexProperty(window); - } - - m_inDragging = false; - m_draggedWindow = nullptr; - - // Also, hide all windows (regardless of settings) - for (auto [keyMonitor, workArea] : workAreaMap) - { - if (workArea) - { - workArea->HideZonesOverlay(); - } - } -} - -void WindowMoveHandler::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, std::shared_ptr workArea) noexcept -{ - if (window != m_draggedWindow) - { - workArea->MoveWindowIntoZoneByIndexSet(window, indexSet); - } -} - -bool WindowMoveHandler::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept -{ - return workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, cycle); -} - -bool WindowMoveHandler::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept -{ - return workArea && workArea->MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle); -} - -bool WindowMoveHandler::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode, std::shared_ptr workArea) noexcept -{ - return workArea && workArea->ExtendWindowByDirectionAndPosition(window, vkCode); -} - -void WindowMoveHandler::AssignWindowsToZones(const std::unordered_map>& activeWorkAreas, bool updatePositions) noexcept -{ - for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop()) - { - auto zoneIndexSet = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); - if (zoneIndexSet.size() == 0) - { - continue; - } - - auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - if (monitor && activeWorkAreas.contains(monitor)) - { - activeWorkAreas.at(monitor)->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, updatePositions); - } - } -} - - -void WindowMoveHandler::SetWindowTransparency(HWND window) noexcept -{ - if (FancyZonesSettings::settings().makeDraggedWindowTransparent) - { - m_windowTransparencyProperties.draggedWindowExstyle = GetWindowLong(window, GWL_EXSTYLE); - - SetWindowLong(window, - GWL_EXSTYLE, - m_windowTransparencyProperties.draggedWindowExstyle | WS_EX_LAYERED); - - if (!GetLayeredWindowAttributes(window, &m_windowTransparencyProperties.draggedWindowCrKey, &m_windowTransparencyProperties.draggedWindowInitialAlpha, &m_windowTransparencyProperties.draggedWindowDwFlags)) - { - Logger::error(L"Window transparency: GetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); - return; - } - - m_windowTransparencyProperties.draggedWindow = window; - - if (!SetLayeredWindowAttributes(window, 0, (255 * 50) / 100, LWA_ALPHA)) - { - Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); - } - } -} - -void WindowMoveHandler::ResetWindowTransparency() noexcept -{ - if (FancyZonesSettings::settings().makeDraggedWindowTransparent && m_windowTransparencyProperties.draggedWindow != nullptr) - { - if (!SetLayeredWindowAttributes(m_windowTransparencyProperties.draggedWindow, m_windowTransparencyProperties.draggedWindowCrKey, m_windowTransparencyProperties.draggedWindowInitialAlpha, m_windowTransparencyProperties.draggedWindowDwFlags)) - { - Logger::error(L"Window transparency: SetLayeredWindowAttributes failed"); - } - - if (SetWindowLong(m_windowTransparencyProperties.draggedWindow, GWL_EXSTYLE, m_windowTransparencyProperties.draggedWindowExstyle) == 0) - { - Logger::error(L"Window transparency: SetWindowLong failed, {}", get_last_error_or_default(GetLastError())); - } - - m_windowTransparencyProperties.draggedWindow = nullptr; - } -} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h deleted file mode 100644 index 7c5a8b2ab1..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "FancyZonesWindowProperties.h" -#include "KeyState.h" -#include "SecondaryMouseButtonsHook.h" - -#include - -interface IFancyZonesSettings; -class WorkArea; - -class WindowMoveHandler -{ -public: - WindowMoveHandler(); - - void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap, bool dragEnabled) noexcept; - void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap, bool dragEnabled, bool multipleZones) noexcept; - void MoveSizeEnd(HWND window, const std::unordered_map>& workAreaMap) noexcept; - - void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, std::shared_ptr workArea) noexcept; - bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept; - bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept; - bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode, std::shared_ptr workArea) noexcept; - - void AssignWindowsToZones(const std::unordered_map>& activeWorkAreas, bool updatePositions) noexcept; - -private: - struct WindowTransparencyProperties - { - HWND draggedWindow = nullptr; - long draggedWindowExstyle = 0; - COLORREF draggedWindowCrKey = RGB(0, 0, 0); - DWORD draggedWindowDwFlags = 0; - BYTE draggedWindowInitialAlpha = 0; - }; - - // MoveSize related window properties - struct MoveSizeWindowInfo - { - // True if from the styles the window looks like a standard window - bool isStandardWindow = false; - // True if the window is a top-level window that does not have a visible owner - bool hasNoVisibleOwner = false; - }; - - void SetWindowTransparency(HWND window) noexcept; - void ResetWindowTransparency() noexcept; - - bool m_inDragging{}; // Whether or not a move/size operation is currently active - HWND m_draggedWindow{}; // The window that is being moved/sized - MoveSizeWindowInfo m_draggedWindowInfo; // MoveSizeWindowInfo of the window at the moment when dragging started - std::shared_ptr m_draggedWindowWorkArea; // "Active" WorkArea, where the move/size is happening. Will update as drag moves between monitors. - - WindowTransparencyProperties m_windowTransparencyProperties; - -}; diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index bf9c2252ce..6efeb71bd6 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -127,62 +127,6 @@ WorkArea::~WorkArea() windowPool.FreeZonesOverlayWindow(m_window); } -HRESULT WorkArea::MoveSizeEnter(HWND window) noexcept -{ - m_windowMoveSize = window; - m_highlightedZones.Reset(); - ShowZonesOverlay(); - Trace::WorkArea::MoveOrResizeStarted(m_layout.get(), m_layoutWindows.get()); - return S_OK; -} - -HRESULT WorkArea::MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept -{ - if (!m_layout) - { - return -1; - } - - bool redraw = false; - - if (dragEnabled) - { - POINT ptClient = ptScreen; - MapWindowPoints(nullptr, m_window, &ptClient, 1); - - redraw = m_highlightedZones.Update(m_layout.get(), ptClient, selectManyZones); - } - else if (!m_highlightedZones.Empty()) - { - m_highlightedZones.Reset(); - redraw = true; - } - - if (redraw && m_zonesOverlay) - { - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightedZones.Zones(), Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); - } - - return S_OK; -} - -HRESULT WorkArea::MoveSizeEnd(HWND window) noexcept -{ - if (m_windowMoveSize != window) - { - return E_INVALIDARG; - } - - MoveWindowIntoZoneByIndexSet(window, m_highlightedZones.Zones()); - m_highlightedZones.Reset(); - - Trace::WorkArea::MoveOrResizeEnd(m_layout.get(), m_layoutWindows.get()); - - HideZonesOverlay(); - m_windowMoveSize = nullptr; - return S_OK; -} - void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept { MoveWindowIntoZoneByIndexSet(window, { index }); @@ -482,22 +426,31 @@ ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const noexcept return {}; } -void WorkArea::ShowZonesOverlay() noexcept +void WorkArea::ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow/* = nullptr*/) { - if (m_window && m_layout) + if (m_layout && m_zonesOverlay) { - SetAsTopmostWindow(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightedZones.Zones(), Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + SetWorkAreaWindowAsTopmost(draggedWindow); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), highlight, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); m_zonesOverlay->Show(); } } -void WorkArea::HideZonesOverlay() noexcept +void WorkArea::HideZonesOverlay() { - if (m_window) + if (m_zonesOverlay) { m_zonesOverlay->Hide(); - m_windowMoveSize = nullptr; + } +} + +void WorkArea::FlashZones() +{ + if (m_layout && m_zonesOverlay) + { + SetWorkAreaWindowAsTopmost(nullptr); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + m_zonesOverlay->Flash(); } } @@ -524,25 +477,6 @@ void WorkArea::CycleWindows(HWND window, bool reverse) noexcept } } -void WorkArea::ClearSelectedZones() noexcept -{ - if (!m_highlightedZones.Empty() && m_layout) - { - m_highlightedZones.Reset(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); - } -} - -void WorkArea::FlashZones() noexcept -{ - if (m_window && m_layout) - { - SetAsTopmostWindow(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); - m_zonesOverlay->Flash(); - } -} - #pragma region private bool WorkArea::InitWindow(HINSTANCE hinstance) noexcept @@ -618,21 +552,16 @@ LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept return 0; } -void WorkArea::SetAsTopmostWindow() noexcept +void WorkArea::SetWorkAreaWindowAsTopmost(HWND draggedWindow) noexcept { if (!m_window) { return; } - UINT flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE; - - HWND windowInsertAfter = m_windowMoveSize; - if (windowInsertAfter == nullptr) - { - windowInsertAfter = HWND_TOPMOST; - } + HWND windowInsertAfter = draggedWindow ? draggedWindow : HWND_TOPMOST; + const UINT flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE; SetWindowPos(m_window, windowInsertAfter, 0, 0, 0, 0, flags); } diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.h b/src/modules/fancyzones/FancyZonesLib/WorkArea.h index 27181dd4dd..5c6ddcb276 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.h +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -30,12 +29,10 @@ public: FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; } const std::unique_ptr& GetLayout() const noexcept { return m_layout; } const std::unique_ptr& GetLayoutWindows() const noexcept { return m_layoutWindows; } + const HWND GetWorkAreaWindow() const noexcept { return m_window; } ZoneIndexSet GetWindowZoneIndexes(HWND window) const noexcept; - HRESULT MoveSizeEnter(HWND window) noexcept; - HRESULT MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept; - HRESULT MoveSizeEnd(HWND window) noexcept; void MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept; void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition = true) noexcept; bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) noexcept; @@ -46,10 +43,9 @@ public: void UpdateActiveZoneSet() noexcept; - void ShowZonesOverlay() noexcept; - void HideZonesOverlay() noexcept; - void FlashZones() noexcept; - void ClearSelectedZones() noexcept; + void ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr); + void HideZonesOverlay(); + void FlashZones(); void CycleWindows(HWND window, bool reverse) noexcept; @@ -61,7 +57,7 @@ private: void InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept; void CalculateZoneSet() noexcept; LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept; - void SetAsTopmostWindow() noexcept; + void SetWorkAreaWindowAsTopmost(HWND draggedWindow) noexcept; const FancyZonesUtils::Rect m_workAreaRect{}; const FancyZonesDataTypes::WorkAreaId m_uniqueId; @@ -69,9 +65,6 @@ private: std::unique_ptr m_layout; std::unique_ptr m_layoutWindows; std::unique_ptr m_zonesOverlay; - HighlightedZones m_highlightedZones; - - HWND m_windowMoveSize{}; }; inline std::shared_ptr MakeWorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesDataTypes::WorkAreaId& parentUniqueId, const FancyZonesUtils::Rect& workAreaRect) diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp index 6979112402..242535f26d 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp @@ -204,98 +204,6 @@ namespace FancyZonesUnitTests } public: - TEST_METHOD (MoveSizeEnter) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - constexpr auto expected = S_OK; - const auto actual = workArea->MoveSizeEnter(Mocks::Window()); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeEnterTwice) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - constexpr auto expected = S_OK; - - workArea->MoveSizeEnter(Mocks::Window()); - const auto actual = workArea->MoveSizeEnter(Mocks::Window()); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeUpdate) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - constexpr auto expected = S_OK; - const auto actual = workArea->MoveSizeUpdate(POINT{ 0, 0 }, true, false); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeUpdatePointNegativeCoordinates) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - constexpr auto expected = S_OK; - const auto actual = workArea->MoveSizeUpdate(POINT{ -10, -10 }, true, false); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeUpdatePointBigCoordinates) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - constexpr auto expected = S_OK; - const auto actual = workArea->MoveSizeUpdate(POINT{ LONG_MAX, LONG_MAX }, true, false); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeEnd) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - const auto window = Mocks::Window(); - workArea->MoveSizeEnter(window); - workArea->MoveSizeUpdate({ 20, 20 }, true, true); - - constexpr auto expected = S_OK; - const auto actual = workArea->MoveSizeEnd(window); - Assert::AreEqual(expected, actual); - - const auto& layoutWindows = workArea->GetLayoutWindows(); - const auto actualZoneIndexSet = layoutWindows->GetZoneIndexSetFromWindow(window); - Assert::IsFalse(std::vector{} == actualZoneIndexSet); - } - - TEST_METHOD (MoveSizeEndDifferentWindows) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - const auto window = Mocks::Window(); - workArea->MoveSizeEnter(window); - - constexpr auto expected = E_INVALIDARG; - const auto actual = workArea->MoveSizeEnd(Mocks::Window()); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeEndWindowNotSet) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - constexpr auto expected = E_INVALIDARG; - const auto actual = workArea->MoveSizeEnd(Mocks::Window()); - - Assert::AreEqual(expected, actual); - } - TEST_METHOD (SaveWindowProcessToZoneIndexNullptrWindow) { auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); From fb2b150a1dd394fddcf6663b8590e0faeb179ace Mon Sep 17 00:00:00 2001 From: norwayman22 Date: Mon, 30 Jan 2023 19:51:19 +0100 Subject: [PATCH 012/163] [QuickAccent]Added support for Norwegian letters (#23487) * [QuickAccent]Added support for Norwegian letters * Add language to Settings --- .../poweraccent/PowerAccent.Core/Languages.cs | 15 +++++++++++++++ .../Settings.UI/Strings/en-us/Resources.resw | 3 +++ .../ViewModels/PowerAccentViewModel.cs | 1 + .../Settings.UI/Views/PowerAccentPage.xaml | 1 + 4 files changed, 20 insertions(+) diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 2cfd8e1f0a..2546f366b7 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -29,6 +29,7 @@ namespace PowerAccent.Core MK, MI, NL, + NO, PI, PL, PT, @@ -65,6 +66,7 @@ namespace PowerAccent.Core Language.MK => GetDefaultLetterKeyMK(letter), // Macedonian Language.MI => GetDefaultLetterKeyMI(letter), // Maori Language.NL => GetDefaultLetterKeyNL(letter), // Dutch + Language.NO => GetDefaultLetterKeyNO(letter), // Norwegian Language.PI => GetDefaultLetterKeyPI(letter), // Pinyin Language.PL => GetDefaultLetterKeyPL(letter), // Polish Language.PT => GetDefaultLetterKeyPT(letter), // Portuguese @@ -583,5 +585,18 @@ namespace PowerAccent.Core _ => Array.Empty(), }; } + + // Norwegian + private static string[] GetDefaultLetterKeyNO(LetterKey letter) + { + return letter switch + { + LetterKey.VK_A => new string[] { "å", "æ" }, + LetterKey.VK_E => new string[] { "€" }, + LetterKey.VK_O => new string[] { "ø" }, + LetterKey.VK_S => new string[] { "$" }, + _ => Array.Empty(), + }; + } } } diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 04a8c64b48..74bcd3d3d2 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2752,6 +2752,9 @@ Activate by holding the key for the character you want to add an accent to, then Dutch + + Norwegian + Pinyin diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs index 368f73155a..2596e5952d 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs @@ -41,6 +41,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels "KU", "MK", "MI", + "NO", "PI", "PL", "PT", diff --git a/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml b/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml index c9b640ac30..9d3580467f 100644 --- a/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml +++ b/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml @@ -78,6 +78,7 @@ + From c1c14b4f2e0f68d370fa6b41e16fc21e167ebc46 Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Tue, 31 Jan 2023 00:00:11 +0100 Subject: [PATCH 013/163] [Settings][runner]Quick access system tray launcher (#22408) * Init * Fix running settings * UI design * Left click trigger Wire up colorpicker and pt run * Wire up others * Update FlyoutWindow.xaml.cs * Removed comments * Update FlyoutWindow.xaml * More work * Open Settings page * More UI work * Resolve conflicts * [General] SystemTray Flyout: Add update on tray items' visibility when module gets enabled/disabled Also remove context menu opening on tray icon. * Adding app list * Adding more buttons, resolving conflicts * [General] Flyout: improving opening, closing flyout/settings window. Implementing basic bahaviour on enabling/disabling modules. * [General] FlyoutWindow: proceed with implementation. GPO works. Main functionallity works (launching and enabling apps). * [general] flyout: fix exit button * [general] flyout: implement double click handling * Localization * [Generel] Flyout: Re-implement flyout launching, add workaround: disable flyout hiding in case the user switches on modules on the all apps page + minor changes * [general] flyout: restore the context menu when right clicking on system tray icon * Fix spellchecker * [installer] fixing missing dll files + suppress error on not signed script * Fix spell checker * Fix flyout not focusing when activated * Refresh Settings UI enabled state when flyout changes * fix spellcheck * Remove VCM from the list * [General] flyout: fix settings window opening. Switch to general page only if there is no page opened * [general] flyout: add launching hosts app * Fix CI build * adding check on elevation when launching hosts * Use localization strings that already exist * Remove dll not present in arm64 build * Adding GPO policy check for the launcher page items * fix hosts launching * Add telemetry * Also hide from all apps list when gpo is force enabling * fix spellchecker * Improve focus issues * Fix flickering Bitmap Icons * Fix telemetry error * Fix telemetry call * Fix wrong comment --------- Co-authored-by: Stefan Markovic Co-authored-by: Laszlo Nemeth Co-authored-by: Jaime Bernardo --- .github/actions/spell-check/excludes.txt | 1 + .github/actions/spell-check/expect.txt | 3 + installer/PowerToysSetup/Settings.wxs | 2 +- src/common/interop/interop.cpp | 12 + src/common/interop/shared_constants.h | 5 + .../MeasureToolModuleInterface/dllmain.cpp | 10 + .../MeasureTool/MeasureToolUI/MainWindow.xaml | 2 +- .../ShortcutGuideModuleInterface/dllmain.cpp | 8 + src/runner/main.cpp | 2 +- src/runner/settings_window.cpp | 74 +- src/runner/settings_window.h | 3 +- src/runner/tray_icon.cpp | 43 +- src/runner/tray_icon.h | 2 +- .../Settings.UI.Library/EnabledModules.cs | 20 + .../Settings.UI.Library/GeneralSettings.cs | 5 + .../Events/TrayFlyoutActivatedEvent.cs | 16 + .../Events/TrayFlyoutModuleRunEvent.cs | 18 + src/settings-ui/Settings.UI/App.xaml | 1 + src/settings-ui/Settings.UI/App.xaml.cs | 45 +- .../FlyoutMenuButton/FlyoutMenuButton.cs | 43 + .../FlyoutMenuButton/FlyoutMenuButton.xaml | 104 +++ .../Settings.UI/Flyout/AppsListPage.xaml | 89 ++ .../Settings.UI/Flyout/AppsListPage.xaml.cs | 36 + .../Settings.UI/Flyout/LaunchPage.xaml | 166 ++++ .../Settings.UI/Flyout/LaunchPage.xaml.cs | 140 +++ .../Settings.UI/Flyout/ShellPage.xaml | 14 + .../Settings.UI/Flyout/ShellPage.xaml.cs | 30 + src/settings-ui/Settings.UI/FlyoutWindow.xaml | 32 + .../Settings.UI/FlyoutWindow.xaml.cs | 51 ++ .../Settings.UI/Helpers/IRefreshablePage.cs | 12 + .../Settings.UI/Helpers/NativeMethods.cs | 3 + src/settings-ui/Settings.UI/Helpers/Utils.cs | 8 + .../Settings.UI/MainWindow.xaml.cs | 124 +++ .../Settings.UI/PowerToys.Settings.csproj | 24 + .../Settings.UI/Services/NavigationService.cs | 8 + .../Settings.UI/Strings/en-us/Resources.resw | 49 +- .../Settings.UI/Styles/Button.xaml | 803 +++++++++++++++--- .../Settings.UI/Styles/TextBlock.xaml | 11 +- .../Settings.UI/Themes/Generic.xaml | 5 +- .../ViewModels/AlwaysOnTopViewModel.cs | 35 +- .../Settings.UI/ViewModels/AwakeViewModel.cs | 29 +- .../ViewModels/ColorPickerViewModel.cs | 31 +- .../ViewModels/FancyZonesViewModel.cs | 30 +- .../ViewModels/FileLocksmithViewModel.cs | 17 +- .../ViewModels/Flyout/AllAppsViewModel.cs | 163 ++++ .../ViewModels/Flyout/FlyoutMenuItem.cs | 61 ++ .../ViewModels/Flyout/FlyoutViewModel.cs | 38 + .../ViewModels/Flyout/LauncherViewModel.cs | 166 ++++ .../Settings.UI/ViewModels/HostsViewModel.cs | 11 + .../ViewModels/ImageResizerViewModel.cs | 33 +- .../ViewModels/KeyboardManagerViewModel.cs | 33 +- .../ViewModels/MeasureToolViewModel.cs | 30 +- .../ViewModels/MouseUtilsViewModel.cs | 83 +- .../ViewModels/PowerAccentViewModel.cs | 33 +- .../ViewModels/PowerLauncherViewModel.cs | 35 +- .../ViewModels/PowerOcrViewModel.cs | 27 +- .../ViewModels/PowerRenameViewModel.cs | 12 + .../Settings.UI/ViewModels/ShellViewModel.cs | 2 + .../ViewModels/ShortcutGuideViewModel.cs | 33 +- .../ViewModels/VideoConferenceViewModel.cs | 33 +- .../Settings.UI/Views/AlwaysOnTopPage.xaml.cs | 8 +- .../Settings.UI/Views/AwakePage.xaml.cs | 8 +- .../Settings.UI/Views/ColorPickerPage.xaml.cs | 7 +- .../Settings.UI/Views/FancyZonesPage.xaml.cs | 8 +- .../Views/FileLocksmithPage.xaml.cs | 8 +- .../Settings.UI/Views/HostsPage.xaml | 42 +- .../Settings.UI/Views/HostsPage.xaml.cs | 8 +- .../Settings.UI/Views/ImageResizerPage.xaml | 65 +- .../Views/ImageResizerPage.xaml.cs | 8 +- .../Views/KeyboardManagerPage.xaml.cs | 8 +- .../Settings.UI/Views/MeasureToolPage.xaml.cs | 8 +- .../Settings.UI/Views/MouseUtilsPage.xaml.cs | 8 +- .../Settings.UI/Views/PowerAccentPage.xaml.cs | 8 +- .../Views/PowerLauncherPage.xaml.cs | 8 +- .../Settings.UI/Views/PowerOcrPage.xaml.cs | 8 +- .../Settings.UI/Views/PowerRenamePage.xaml.cs | 8 +- .../Settings.UI/Views/ShellPage.xaml.cs | 114 ++- .../Views/ShortcutGuidePage.xaml.cs | 8 +- .../Settings.UI/Views/VideoConference.xaml.cs | 8 +- 79 files changed, 2881 insertions(+), 426 deletions(-) create mode 100644 src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs create mode 100644 src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs create mode 100644 src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.cs create mode 100644 src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.xaml create mode 100644 src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml create mode 100644 src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml.cs create mode 100644 src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml create mode 100644 src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs create mode 100644 src/settings-ui/Settings.UI/Flyout/ShellPage.xaml create mode 100644 src/settings-ui/Settings.UI/Flyout/ShellPage.xaml.cs create mode 100644 src/settings-ui/Settings.UI/FlyoutWindow.xaml create mode 100644 src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs create mode 100644 src/settings-ui/Settings.UI/Helpers/IRefreshablePage.cs create mode 100644 src/settings-ui/Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs create mode 100644 src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs create mode 100644 src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs create mode 100644 src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index 1c42d9e4a9..eb343d3775 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -79,6 +79,7 @@ ^\Q.pipelines/ESRPSigning_core.json\E$ ^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$ ^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$ +^\Qinstaller/PowerToysSetup/Settings.wxs\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/CorruptJson/Microsoft/PowerToys/settings.json\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.18.2/Microsoft/PowerToys/PowerRename/power-rename-ui-flags\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.19.2/Microsoft/PowerToys/PowerRename/power-rename-ui-flags\E$ diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index d09e6d483e..bfbba4b263 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -201,6 +201,7 @@ CHILDACTIVATE CHILDWINDOW Choibalsan chrdavis +chromaticities Chrzan cht Chukotka @@ -848,6 +849,7 @@ Khakassia Khanty Khovd KILLFOCUS +killrunner Kitts Knownfolders Krai @@ -1446,6 +1448,7 @@ reencoded REFCLSID REFGUID REFIID +Refreshable REGCLS regedit regfile diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs index c476277736..a1072b59f6 100644 --- a/installer/PowerToysSetup/Settings.wxs +++ b/installer/PowerToysSetup/Settings.wxs @@ -4,7 +4,7 @@ - + diff --git a/src/common/interop/interop.cpp b/src/common/interop/interop.cpp index 7d5bfe099f..a7ec3b47dd 100644 --- a/src/common/interop/interop.cpp +++ b/src/common/interop/interop.cpp @@ -187,6 +187,10 @@ public return gcnew String(CommonSharedConstants::FZE_EXIT_EVENT); } + static String ^ FZEToggleEvent() { + return gcnew String(CommonSharedConstants::FANCY_ZONES_EDITOR_TOGGLE_EVENT); + } + static String ^ ColorPickerSendSettingsTelemetryEvent() { return gcnew String(CommonSharedConstants::COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT); } @@ -207,6 +211,14 @@ public return gcnew String(CommonSharedConstants::POWERACCENT_EXIT_EVENT); } + static String ^ ShortcutGuideTriggerEvent() { + return gcnew String(CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT); + } + + static String + ^ MeasureToolTriggerEvent() { + return gcnew String(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT); + } static String ^ GcodePreviewResizeEvent() { return gcnew String(CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT); } diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index a1116a6065..512e5f3039 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -28,6 +28,8 @@ namespace CommonSharedConstants // Path to the event used to show Color Picker const wchar_t SHOW_COLOR_PICKER_SHARED_EVENT[] = L"Local\\ShowColorPickerEvent-8c46be2a-3e05-4186-b56b-4ae986ef2525"; + const wchar_t SHORTCUT_GUIDE_TRIGGER_EVENT[] = L"Local\\ShortcutGuide-TriggerEvent-d4275ad3-2531-4d19-9252-c0becbd9b496"; + const wchar_t SHORTCUT_GUIDE_EXIT_EVENT[] = L"Local\\ShortcutGuide-ExitEvent-35697cdd-a3d2-47d6-a246-34efcc73eac0"; const wchar_t FANCY_ZONES_EDITOR_TOGGLE_EVENT[] = L"Local\\FancyZones-ToggleEditorEvent-1e174338-06a3-472b-874d-073b21c62f14"; @@ -44,6 +46,9 @@ namespace CommonSharedConstants // Path to the event used by PowerOCR const wchar_t SHOW_POWEROCR_SHARED_EVENT[] = L"Local\\PowerOCREvent-dc864e06-e1af-4ecc-9078-f98bee745e3a"; + // Path to the event used by MeasureTool + const wchar_t MEASURE_TOOL_TRIGGER_EVENT[] = L"Local\\MeasureToolEvent-3d46745f-09b3-4671-a577-236be7abd199"; + // Path to the event used by GcodePreviewHandler const wchar_t GCODE_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysGcodePreviewResizeEvent-6ff1f9bd-ccbd-4b24-a79f-40a34fb0317d"; diff --git a/src/modules/MeasureTool/MeasureToolModuleInterface/dllmain.cpp b/src/modules/MeasureTool/MeasureToolModuleInterface/dllmain.cpp index ac80a4a399..4148effdb1 100644 --- a/src/modules/MeasureTool/MeasureToolModuleInterface/dllmain.cpp +++ b/src/modules/MeasureTool/MeasureToolModuleInterface/dllmain.cpp @@ -3,9 +3,11 @@ #include #include #include "trace.h" +#include #include #include #include +#include extern "C" IMAGE_DOS_HEADER __ImageBase; @@ -49,6 +51,9 @@ private: Hotkey m_hotkey; HANDLE m_hProcess; + HANDLE triggerEvent; + EventWaiter triggerEventWaiter; + void parse_hotkey(PowerToysSettings::PowerToyValues& settings) { auto settingsObject = settings.get_raw_json(); @@ -142,6 +147,11 @@ public: { LoggerHelpers::init_logger(L"Measure Tool", L"ModuleInterface", "Measure Tool"); init_settings(); + + triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT); + triggerEventWaiter = EventWaiter(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT, [this](int) { + on_hotkey(0); + }); } ~MeasureTool() diff --git a/src/modules/MeasureTool/MeasureToolUI/MainWindow.xaml b/src/modules/MeasureTool/MeasureToolUI/MainWindow.xaml index d787abb492..973781a777 100644 --- a/src/modules/MeasureTool/MeasureToolUI/MainWindow.xaml +++ b/src/modules/MeasureTool/MeasureToolUI/MainWindow.xaml @@ -5,8 +5,8 @@ xmlns:contract7NotPresent="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,7)" xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:p="using:PowerToys.MeasureToolUI.Properties" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:p="using:PowerToys.MeasureToolUI.Properties" xmlns:winuiex="using:WinUIEx" IsAlwaysOnTop="True" IsMaximizable="False" diff --git a/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp b/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp index a7d7a1cc1b..de6c3a77ea 100644 --- a/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp @@ -10,6 +10,7 @@ #include "../interface/powertoy_module_interface.h" #include "Generated Files/resource.h" #include +#include BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD /*ul_reason_for_call*/, LPVOID /*lpReserved*/) { @@ -35,6 +36,11 @@ public: Logger::warn(L"Failed to create {} event. {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, get_last_error_or_default(GetLastError())); } + triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT); + triggerEventWaiter = EventWaiter(CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT, [this](int) { + OnHotkeyEx(); + }); + InitSettings(); } @@ -191,7 +197,9 @@ private: UINT m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_GLOBAL_WINDOWS_SHORTCUTS; UINT m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_TASKBAR_ICON_SHORTCUTS; + HANDLE triggerEvent; HANDLE exitEvent; + EventWaiter triggerEventWaiter; bool StartProcess(std::wstring args = L"") { diff --git a/src/runner/main.cpp b/src/runner/main.cpp index a5493afced..442eab5698 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -203,7 +203,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow { window = winrt::to_hstring(settingsWindow); } - open_settings_window(window); + open_settings_window(window, false); } if (openOobe) diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 340c6f5a70..53cae3bac3 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -11,12 +11,14 @@ #include "restart_elevated.h" #include "UpdateUtils.h" #include "centralized_kb_hook.h" +#include "Generated files/resource.h" #include #include #include #include #include +#include #include #include #include @@ -175,7 +177,7 @@ void dispatch_received_json(const std::wstring& json_to_parse) const std::wstring settings_string{ get_all_settings().Stringify().c_str() }; { std::unique_lock lock{ ipc_mutex }; - if(current_settings_ipc) + if (current_settings_ipc) current_settings_ipc->send(settings_string); } } @@ -185,7 +187,7 @@ void dispatch_received_json(const std::wstring& json_to_parse) const std::wstring settings_string{ get_all_settings().Stringify().c_str() }; { std::unique_lock lock{ ipc_mutex }; - if(current_settings_ipc) + if (current_settings_ipc) current_settings_ipc->send(settings_string); } } @@ -194,7 +196,7 @@ void dispatch_received_json(const std::wstring& json_to_parse) const std::wstring settings_string{ get_all_settings().Stringify().c_str() }; { std::unique_lock lock{ ipc_mutex }; - if(current_settings_ipc) + if (current_settings_ipc) current_settings_ipc->send(settings_string); } } @@ -205,11 +207,35 @@ void dispatch_received_json(const std::wstring& json_to_parse) { { std::unique_lock lock{ ipc_mutex }; - if(current_settings_ipc) + if (current_settings_ipc) current_settings_ipc->send(result.value()); } } } + else if (name == L"bugreport") + { + std::wstring bug_report_path = get_module_folderpath(); + bug_report_path += L"\\Tools\\PowerToys.BugReportTool.exe"; + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE }; + sei.lpFile = bug_report_path.c_str(); + sei.nShow = SW_HIDE; + if (ShellExecuteExW(&sei)) + { + WaitForSingleObject(sei.hProcess, INFINITE); + CloseHandle(sei.hProcess); + static const std::wstring bugreport_success = GET_RESOURCE_STRING(IDS_BUGREPORT_SUCCESS); + MessageBoxW(nullptr, bugreport_success.c_str(), L"PowerToys", MB_OK); + } + } + else if (name == L"killrunner") + { + const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr); + if (pt_main_window != nullptr) + { + SendMessageW(pt_main_window, WM_CLOSE, 0, 0); + } + } } return; } @@ -290,7 +316,7 @@ BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args, DWORD g_settings_process_id = 0; -void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional settings_window) +void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional settings_window, bool show_flyout = false) { g_isLaunchInProgress = true; @@ -360,11 +386,14 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op // Arg 9: should scoobe window be shown std::wstring settings_showScoobe = show_scoobe_window ? L"true" : L"false"; + // Arg 10: should flyout be shown + std::wstring settings_showFlyout = show_flyout ? L"true" : L"false"; + // create general settings file to initialize the settings file with installation configurations like : // 1. Run on start up. PTSettingsHelper::save_general_settings(save_settings.to_json()); - std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {}", + std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {}", executable_path, powertoys_pipe_name, settings_pipe_name, @@ -373,7 +402,8 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op settings_elevatedStatus, settings_isUserAnAdmin, settings_showOobe, - settings_showScoobe); + settings_showScoobe, + settings_showFlyout); if (settings_window.has_value()) { @@ -520,18 +550,33 @@ void bring_settings_to_front() EnumWindows(callback, 0); } -void open_settings_window(std::optional settings_window) +void open_settings_window(std::optional settings_window, bool show_flyout = false) { if (g_settings_process_id != 0) { - bring_settings_to_front(); + if (show_flyout) + { + if (current_settings_ipc) + { + current_settings_ipc->send(L"{\"ShowYourself\":\"flyout\"}"); + } + } + else + { + // nl instead of showing the window, send message to it (flyout might need to be hidden, main setting window activated) + // bring_settings_to_front(); + if (current_settings_ipc) + { + current_settings_ipc->send(L"{\"ShowYourself\":\"main_page\"}"); + } + } } else { if (!g_isLaunchInProgress) { - std::thread([settings_window]() { - run_settings_window(false, false, settings_window); + std::thread([settings_window, show_flyout]() { + run_settings_window(false, false, settings_window, show_flyout); }).detach(); } } @@ -563,6 +608,13 @@ void open_scoobe_window() }).detach(); } +void open_flyout() +{ + std::thread([]() { + run_settings_window(false, false, std::nullopt, true); + }).detach(); +} + std::string ESettingsWindowNames_to_string(ESettingsWindowNames value) { switch (value) diff --git a/src/runner/settings_window.h b/src/runner/settings_window.h index 7e2f9ed89d..fcbb51b310 100644 --- a/src/runner/settings_window.h +++ b/src/runner/settings_window.h @@ -22,8 +22,9 @@ enum class ESettingsWindowNames std::string ESettingsWindowNames_to_string(ESettingsWindowNames value); ESettingsWindowNames ESettingsWindowNames_from_string(std::string value); -void open_settings_window(std::optional settings_window); +void open_settings_window(std::optional settings_window, bool show_flyout); void close_settings_window(); void open_oobe_window(); void open_scoobe_window(); +void open_flyout(); diff --git a/src/runner/tray_icon.cpp b/src/runner/tray_icon.cpp index 6fe32df61f..1ece986df7 100644 --- a/src/runner/tray_icon.cpp +++ b/src/runner/tray_icon.cpp @@ -30,6 +30,8 @@ namespace HMENU h_menu = nullptr; HMENU h_sub_menu = nullptr; + bool double_click_timer_running = false; + bool double_clicked = false; } // Struct to fill with callback and the data. The window_proc is responsible for cleaning it. @@ -69,7 +71,7 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam) case ID_SETTINGS_MENU_COMMAND: { std::wstring settings_window{ winrt::to_hstring(ESettingsWindowNames_to_string(static_cast(lparam))) }; - open_settings_window(settings_window); + open_settings_window(settings_window, false); } break; case ID_EXIT_MENU_COMMAND: @@ -116,6 +118,15 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam) } } +void click_timer_elapsed() +{ + double_click_timer_running = false; + if (!double_clicked) + { + open_settings_window(std::nullopt, true); + } +} + LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) { switch (message) @@ -168,11 +179,6 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam { switch (lparam) { - case WM_LBUTTONDBLCLK: - { - open_settings_window(std::nullopt); - break; - } case WM_RBUTTONUP: case WM_CONTEXTMENU: { @@ -186,7 +192,6 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam static std::wstring exit_menuitem_label = GET_RESOURCE_STRING(IDS_EXIT_MENU_TEXT); static std::wstring submit_bug_menuitem_label = GET_RESOURCE_STRING(IDS_SUBMIT_BUG_TEXT); static std::wstring documentation_menuitem_label = GET_RESOURCE_STRING(IDS_DOCUMENTATION_MENU_TEXT); - change_menu_item_text(ID_SETTINGS_MENU_COMMAND, settings_menuitem_label.data()); change_menu_item_text(ID_EXIT_MENU_COMMAND, exit_menuitem_label.data()); change_menu_item_text(ID_REPORT_BUG_COMMAND, submit_bug_menuitem_label.data()); @@ -200,6 +205,30 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam GetCursorPos(&mouse_pointer); SetForegroundWindow(window); // Needed for the context menu to disappear. TrackPopupMenu(h_sub_menu, TPM_CENTERALIGN | TPM_BOTTOMALIGN, mouse_pointer.x, mouse_pointer.y, 0, window, nullptr); + break; + } + case WM_LBUTTONUP: + { + // ignore event if this is the second click of a double click + if (!double_click_timer_running) + { + // start timer for detecting single or double click + double_click_timer_running = true; + double_clicked = false; + + UINT doubleClickTime = GetDoubleClickTime(); + std::thread([doubleClickTime]() { + std::this_thread::sleep_for(std::chrono::milliseconds(doubleClickTime)); + click_timer_elapsed(); + }).detach(); + } + break; + } + case WM_LBUTTONDBLCLK: + { + double_clicked = true; + open_settings_window(std::nullopt, false); + break; } break; } diff --git a/src/runner/tray_icon.h b/src/runner/tray_icon.h index e65fa9720f..0d63475d21 100644 --- a/src/runner/tray_icon.h +++ b/src/runner/tray_icon.h @@ -7,7 +7,7 @@ void start_tray_icon(); // Stop the Tray Icon void stop_tray_icon(); // Open the Settings Window -void open_settings_window(std::optional settings_window); +void open_settings_window(std::optional settings_window, bool show_flyout); // Callback type to be called by the tray icon loop typedef void (*main_loop_callback_function)(PVOID); // Calls a callback in _callback diff --git a/src/settings-ui/Settings.UI.Library/EnabledModules.cs b/src/settings-ui/Settings.UI.Library/EnabledModules.cs index de29ae6d35..ea055fb4cd 100644 --- a/src/settings-ui/Settings.UI.Library/EnabledModules.cs +++ b/src/settings-ui/Settings.UI.Library/EnabledModules.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization; @@ -12,6 +13,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library { public class EnabledModules { + private Action notifyEnabledChangedAction; + public EnabledModules() { } @@ -28,6 +31,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); fancyZones = value; + NotifyChange(); } } } @@ -76,6 +80,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); shortcutGuide = value; + NotifyChange(); } } } @@ -139,6 +144,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); powerLauncher = value; + NotifyChange(); } } } @@ -155,6 +161,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); colorPicker = value; + NotifyChange(); } } } @@ -267,6 +274,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); powerOCR = value; + NotifyChange(); } } } @@ -283,6 +291,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); measureTool = value; + NotifyChange(); } } } @@ -299,6 +308,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); hosts = value; + NotifyChange(); } } } @@ -319,6 +329,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + private void NotifyChange() + { + notifyEnabledChangedAction?.Invoke(); + } + public string ToJsonString() { return JsonSerializer.Serialize(this); @@ -333,5 +348,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library }; PowerToysTelemetry.Log.WriteEvent(dataEvent); } + + internal void AddEnabledModuleChangeNotification(Action callBack) + { + notifyEnabledChangedAction = callBack; + } } } diff --git a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs index be57f43c8b..25bd1b2a07 100644 --- a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs +++ b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs @@ -108,5 +108,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library return false; } + + public void AddEnabledModuleChangeNotification(Action callBack) + { + Enabled.AddEnabledModuleChangeNotification(callBack); + } } } diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs new file mode 100644 index 0000000000..8e075aea0c --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events +{ + [EventData] + public class TrayFlyoutActivatedEvent : EventBase, IEvent + { + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs new file mode 100644 index 0000000000..c1813e9adb --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events +{ + [EventData] + public class TrayFlyoutModuleRunEvent : EventBase, IEvent + { + public string ModuleName { get; set; } + + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/settings-ui/Settings.UI/App.xaml b/src/settings-ui/Settings.UI/App.xaml index 3858e4f7d2..b4e581d64d 100644 --- a/src/settings-ui/Settings.UI/App.xaml +++ b/src/settings-ui/Settings.UI/App.xaml @@ -3,6 +3,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:local="using:Microsoft.PowerToys.Settings.UI" xmlns:converters="using:CommunityToolkit.WinUI.UI.Converters" xmlns:labs="using:CommunityToolkit.Labs.WinUI"> diff --git a/src/settings-ui/Settings.UI/App.xaml.cs b/src/settings-ui/Settings.UI/App.xaml.cs index 7f10ecf0b1..d07bc645d3 100644 --- a/src/settings-ui/Settings.UI/App.xaml.cs +++ b/src/settings-ui/Settings.UI/App.xaml.cs @@ -13,6 +13,7 @@ using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; using Microsoft.PowerToys.Settings.UI.Library.Utilities; +using Microsoft.PowerToys.Settings.UI.Views; using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Xaml; using Windows.UI.Popups; @@ -35,12 +36,13 @@ namespace Microsoft.PowerToys.Settings.UI IsUserAdmin, ShowOobeWindow, ShowScoobeWindow, + ShowFlyout, SettingsWindow, } // Quantity of arguments - private const int RequiredArgumentsQty = 9; - private const int RequiredAndOptionalArgumentsQty = 10; + private const int RequiredArgumentsQty = 10; + private const int RequiredAndOptionalArgumentsQty = 11; // Create an instance of the IPC wrapper. private static TwoWayPipeMessageIPCManaged ipcmanager; @@ -53,6 +55,8 @@ namespace Microsoft.PowerToys.Settings.UI public bool ShowOobe { get; set; } + public bool ShowFlyout { get; set; } + public bool ShowScoobe { get; set; } public Type StartupPage { get; set; } = typeof(Views.GeneralPage); @@ -71,15 +75,24 @@ namespace Microsoft.PowerToys.Settings.UI this.InitializeComponent(); } - public static void OpenSettingsWindow(Type type) + public static void OpenSettingsWindow(Type type = null, bool ensurePageIsSelected = false) { if (settingsWindow == null) { settingsWindow = new MainWindow(IsDarkTheme()); + type = typeof(GeneralPage); } settingsWindow.Activate(); - settingsWindow.NavigateToSection(type); + if (type != null) + { + settingsWindow.NavigateToSection(type); + } + + if (ensurePageIsSelected) + { + settingsWindow.EnsurePageIsSelected(); + } } /// @@ -90,6 +103,7 @@ namespace Microsoft.PowerToys.Settings.UI protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { var cmdArgs = Environment.GetCommandLineArgs(); + var isDark = IsDarkTheme(); if (cmdArgs != null && cmdArgs.Length >= RequiredArgumentsQty) @@ -107,6 +121,7 @@ namespace Microsoft.PowerToys.Settings.UI IsUserAnAdmin = cmdArgs[(int)Arguments.IsUserAdmin] == "true"; ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true"; ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true"; + ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true"; if (cmdArgs.Length == RequiredAndOptionalArgumentsQty) { @@ -149,7 +164,7 @@ namespace Microsoft.PowerToys.Settings.UI }); ipcmanager.Start(); - if (!ShowOobe && !ShowScoobe) + if (!ShowOobe && !ShowScoobe && !ShowFlyout) { settingsWindow = new MainWindow(isDark); settingsWindow.Activate(); @@ -176,6 +191,10 @@ namespace Microsoft.PowerToys.Settings.UI scoobeWindow.Activate(); SetOobeWindow(scoobeWindow); } + else if (ShowFlyout) + { + ShellPage.OpenFlyoutCallback(); + } } } else @@ -277,6 +296,7 @@ namespace Microsoft.PowerToys.Settings.UI private static MainWindow settingsWindow; private static OobeWindow oobeWindow; + private static FlyoutWindow flyoutWindow; private static ThemeListener themeListener; public static void ClearSettingsWindow() @@ -294,14 +314,29 @@ namespace Microsoft.PowerToys.Settings.UI return oobeWindow; } + public static FlyoutWindow GetFlyoutWindow() + { + return flyoutWindow; + } + public static void SetOobeWindow(OobeWindow window) { oobeWindow = window; } + public static void SetFlyoutWindow(FlyoutWindow window) + { + flyoutWindow = window; + } + public static void ClearOobeWindow() { oobeWindow = null; } + + public static void ClearFlyoutWindow() + { + flyoutWindow = null; + } } } diff --git a/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.cs b/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.cs new file mode 100644 index 0000000000..17c901cbba --- /dev/null +++ b/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CommunityToolkit.Labs.WinUI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using WinUIEx; + +namespace Microsoft.PowerToys.Settings.UI.Controls +{ + public partial class FlyoutMenuButton : Button + { + /// + /// The backing for the property. + /// + public static readonly DependencyProperty IconProperty = DependencyProperty.Register( + nameof(Icon), + typeof(object), + typeof(FlyoutMenuButton), + new PropertyMetadata(defaultValue: null)); + + /// + /// Gets or sets the icon. + /// + public object Icon + { + get => (object)GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public FlyoutMenuButton() + { + this.DefaultStyleKey = typeof(FlyoutMenuButton); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.xaml b/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.xaml new file mode 100644 index 0000000000..e9b3a6a55f --- /dev/null +++ b/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.xaml @@ -0,0 +1,104 @@ + + + + + + + diff --git a/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml b/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml new file mode 100644 index 0000000000..85a9fa0f10 --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml.cs b/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml.cs new file mode 100644 index 0000000000..6692ff6077 --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.Collections.ObjectModel; +using System.Threading; +using global::Windows.System; +using interop; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; + +namespace Microsoft.PowerToys.Settings.UI.Flyout +{ + public sealed partial class AppsListPage : Page + { + private AllAppsViewModel ViewModel { get; set; } + + public AppsListPage() + { + this.InitializeComponent(); + + var settingsUtils = new SettingsUtils(); + ViewModel = new AllAppsViewModel(SettingsRepository.GetInstance(settingsUtils), Views.ShellPage.SendDefaultIPCMessage); + DataContext = ViewModel; + } + + private void BackButton_Click(object sender, RoutedEventArgs e) + { + Frame.Navigate(typeof(LaunchPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft }); + } + } +} diff --git a/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml new file mode 100644 index 0000000000..05d035275b --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs new file mode 100644 index 0000000000..c921aeb4e0 --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.Collections.ObjectModel; +using System.Threading; +using global::Windows.System; +using interop; +using Microsoft.PowerToys.Settings.UI.Controls; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; +using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.PowerToys.Telemetry; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; + +namespace Microsoft.PowerToys.Settings.UI.Flyout +{ + public sealed partial class LaunchPage : Page + { + private LauncherViewModel ViewModel { get; set; } + + public LaunchPage() + { + this.InitializeComponent(); + var settingsUtils = new SettingsUtils(); + ViewModel = new LauncherViewModel(SettingsRepository.GetInstance(settingsUtils), Views.ShellPage.SendDefaultIPCMessage); + DataContext = ViewModel; + } + + private void ModuleButton_Click(object sender, RoutedEventArgs e) + { + FlyoutMenuButton selectedModuleBtn = sender as FlyoutMenuButton; + bool moduleRun = true; + switch ((string)selectedModuleBtn.Tag) + { + case "ColorPicker": // Launch ColorPicker + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowColorPickerSharedEvent())) + { + eventHandle.Set(); + } + + break; + case "FancyZones": // Launch FancyZones Editor + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.FZEToggleEvent())) + { + eventHandle.Set(); + } + + break; + + case "Hosts": // Launch Hosts + { + bool launchAdmin = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator; + var actionName = "Launch"; + if (!App.IsElevated && launchAdmin) + { + actionName = "LaunchAdministrator"; + } + + Views.ShellPage.SendDefaultIPCMessage("{\"action\":{\"Hosts\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}"); + } + + break; + + case "MeasureTool": // Launch Screen Ruler + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.MeasureToolTriggerEvent())) + { + eventHandle.Set(); + } + + break; + + case "PowerLauncher": // Launch Run + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.PowerLauncherSharedEvent())) + { + eventHandle.Set(); + } + + break; + + case "PowerOCR": // Launch Text Extractor + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowPowerOCRSharedEvent())) + { + eventHandle.Set(); + } + + break; + + case "ShortcutGuide": // Launch Shortcut Guide + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShortcutGuideTriggerEvent())) + { + eventHandle.Set(); + } + + break; + + default: + moduleRun = false; + break; + } + + if (moduleRun) + { + PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutModuleRunEvent() { ModuleName = (string)selectedModuleBtn.Tag }); + } + } + + private void SettingsBtn_Click(object sender, RoutedEventArgs e) + { + App.OpenSettingsWindow(null, true); + } + + private async void DocsBtn_Click(object sender, RoutedEventArgs e) + { + await Launcher.LaunchUriAsync(new Uri("https://aka.ms/PowerToysOverview")); + } + + private void AllAppButton_Click(object sender, RoutedEventArgs e) + { + Frame.Navigate(typeof(AppsListPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight }); + } + + private void QuitButton_Click(object sender, RoutedEventArgs e) + { + ViewModel.KillRunner(); + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + Application.Current.Exit(); + }); + } + + private void ReportBugBtn_Click(object sender, RoutedEventArgs e) + { + ViewModel.StartBugReport(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml b/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml new file mode 100644 index 0000000000..52b5f542ca --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml.cs b/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml.cs new file mode 100644 index 0000000000..fe0aa75f69 --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; + +namespace Microsoft.PowerToys.Settings.UI.Flyout +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class ShellPage : Page + { + public ShellPage() + { + this.InitializeComponent(); + } + + internal void SwitchToLaunchPage() + { + ContentFrame.Navigate(typeof(LaunchPage), null, new SuppressNavigationTransitionInfo()); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + SwitchToLaunchPage(); + } + } +} diff --git a/src/settings-ui/Settings.UI/FlyoutWindow.xaml b/src/settings-ui/Settings.UI/FlyoutWindow.xaml new file mode 100644 index 0000000000..d3c5524d47 --- /dev/null +++ b/src/settings-ui/Settings.UI/FlyoutWindow.xaml @@ -0,0 +1,32 @@ + + + + + + + + diff --git a/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs b/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs new file mode 100644 index 0000000000..aa2ab39849 --- /dev/null +++ b/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; +using Microsoft.PowerToys.Settings.UI.ViewModels.Flyout; +using Microsoft.PowerToys.Telemetry; +using Microsoft.UI; +using Microsoft.UI.Windowing; +using WinUIEx; + +namespace Microsoft.PowerToys.Settings.UI +{ + /// + /// An empty window that can be used on its own or navigated to within a Frame. + /// + public sealed partial class FlyoutWindow : WindowEx + { + private const int WindowWidth = 386; + private const int WindowHeight = 486; + private const int WindowMargin = 12; + + public FlyoutViewModel ViewModel { get; set; } + + public FlyoutWindow() + { + this.InitializeComponent(); + this.Activated += FlyoutWindow_Activated; + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); + WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd); + DisplayArea displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest); + double dpiScale = (float)this.GetDpiForWindow() / 96; + double x = displayArea.WorkArea.Width - (dpiScale * (WindowWidth + WindowMargin)); + double y = displayArea.WorkArea.Height - (dpiScale * (WindowHeight + WindowMargin)); + this.MoveAndResize(x, y, WindowWidth, WindowHeight); + ViewModel = new FlyoutViewModel(); + } + + private void FlyoutWindow_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args) + { + PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutActivatedEvent()); + if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.Deactivated) + { + if (ViewModel.CanHide) + { + FlyoutShellPage.SwitchToLaunchPage(); + this.Hide(); + } + } + } + } +} diff --git a/src/settings-ui/Settings.UI/Helpers/IRefreshablePage.cs b/src/settings-ui/Settings.UI/Helpers/IRefreshablePage.cs new file mode 100644 index 0000000000..d37cb294a1 --- /dev/null +++ b/src/settings-ui/Settings.UI/Helpers/IRefreshablePage.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// An interface so that pages can define refresh method to refresh their view models. +namespace Microsoft.PowerToys.Settings.UI.Helpers +{ + public interface IRefreshablePage + { + void RefreshEnabledState(); + } +} diff --git a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs index d247c03f1c..8a7bdb29e7 100644 --- a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs +++ b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs @@ -51,6 +51,9 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers [DllImport("user32.dll")] public static extern bool AllowSetForegroundWindow(int dwProcessId); + [System.Runtime.InteropServices.DllImport("User32.dll")] + public static extern bool SetForegroundWindow(IntPtr handle); + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr LoadLibrary(string dllToLoad); diff --git a/src/settings-ui/Settings.UI/Helpers/Utils.cs b/src/settings-ui/Settings.UI/Helpers/Utils.cs index d075ccf9c4..53124c7986 100644 --- a/src/settings-ui/Settings.UI/Helpers/Utils.cs +++ b/src/settings-ui/Settings.UI/Helpers/Utils.cs @@ -45,5 +45,13 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers { } } + + public static void BecomeForegroundWindow(IntPtr hWnd) + { + NativeKeyboardHelper.INPUT input = new NativeKeyboardHelper.INPUT { type = NativeKeyboardHelper.INPUTTYPE.INPUT_MOUSE, data = { } }; + NativeKeyboardHelper.INPUT[] inputs = new NativeKeyboardHelper.INPUT[] { input }; + _ = NativeMethods.SendInput(1, inputs, NativeKeyboardHelper.INPUT.Size); + NativeMethods.SetForegroundWindow(hWnd); + } } } diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/MainWindow.xaml.cs index 8723a01110..54cc7430d3 100644 --- a/src/settings-ui/Settings.UI/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/MainWindow.xaml.cs @@ -6,6 +6,7 @@ using System; using ManagedCommon; using Microsoft.PowerLauncher.Telemetry; using Microsoft.PowerToys.Settings.UI.Helpers; +using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Views; using Microsoft.PowerToys.Telemetry; @@ -14,6 +15,7 @@ using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Windows.ApplicationModel.Resources; using Windows.Data.Json; +using WinUIEx; namespace Microsoft.PowerToys.Settings.UI { @@ -73,6 +75,90 @@ namespace Microsoft.PowerToys.Settings.UI App.GetTwoWayIPCManager()?.Send(msg); }); + // open main window + ShellPage.SetOpenMainWindowCallback(() => + { + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + App.OpenSettingsWindow(typeof(GeneralPage))); + }); + + // open main window + ShellPage.SetUpdatingGeneralSettingsCallback((string module, bool isEnabled) => + { + SettingsRepository repository = SettingsRepository.GetInstance(new SettingsUtils()); + GeneralSettings generalSettingsConfig = repository.SettingsConfig; + bool needToUpdate = false; + switch (module) + { + case "AlwaysOnTop": + needToUpdate = generalSettingsConfig.Enabled.AlwaysOnTop != isEnabled; + generalSettingsConfig.Enabled.AlwaysOnTop = isEnabled; break; + case "Awake": + needToUpdate = generalSettingsConfig.Enabled.Awake != isEnabled; + generalSettingsConfig.Enabled.Awake = isEnabled; break; + case "ColorPicker": + needToUpdate = generalSettingsConfig.Enabled.ColorPicker != isEnabled; + generalSettingsConfig.Enabled.ColorPicker = isEnabled; break; + case "FancyZones": + needToUpdate = generalSettingsConfig.Enabled.FancyZones != isEnabled; + generalSettingsConfig.Enabled.FancyZones = isEnabled; break; + case "FileLocksmith": + needToUpdate = generalSettingsConfig.Enabled.FileLocksmith != isEnabled; + generalSettingsConfig.Enabled.FileLocksmith = isEnabled; break; + case "FindMyMouse": + needToUpdate = generalSettingsConfig.Enabled.FindMyMouse != isEnabled; + generalSettingsConfig.Enabled.FindMyMouse = isEnabled; break; + case "Hosts": + needToUpdate = generalSettingsConfig.Enabled.Hosts != isEnabled; + generalSettingsConfig.Enabled.Hosts = isEnabled; break; + case "ImageResizer": + needToUpdate = generalSettingsConfig.Enabled.ImageResizer != isEnabled; + generalSettingsConfig.Enabled.ImageResizer = isEnabled; break; + case "KeyboardManager": + needToUpdate = generalSettingsConfig.Enabled.KeyboardManager != isEnabled; + generalSettingsConfig.Enabled.KeyboardManager = isEnabled; break; + case "MouseHighlighter": + needToUpdate = generalSettingsConfig.Enabled.MouseHighlighter != isEnabled; + generalSettingsConfig.Enabled.MouseHighlighter = isEnabled; break; + case "MousePointerCrosshairs": + needToUpdate = generalSettingsConfig.Enabled.MousePointerCrosshairs != isEnabled; + generalSettingsConfig.Enabled.MousePointerCrosshairs = isEnabled; break; + case "PowerRename": + needToUpdate = generalSettingsConfig.Enabled.PowerRename != isEnabled; + generalSettingsConfig.Enabled.PowerRename = isEnabled; break; + case "PowerLauncher": + needToUpdate = generalSettingsConfig.Enabled.PowerLauncher != isEnabled; + generalSettingsConfig.Enabled.PowerLauncher = isEnabled; break; + case "PowerAccent": + needToUpdate = generalSettingsConfig.Enabled.PowerAccent != isEnabled; + generalSettingsConfig.Enabled.PowerAccent = isEnabled; break; + case "MeasureTool": + needToUpdate = generalSettingsConfig.Enabled.MeasureTool != isEnabled; + generalSettingsConfig.Enabled.MeasureTool = isEnabled; break; + case "ShortcutGuide": + needToUpdate = generalSettingsConfig.Enabled.ShortcutGuide != isEnabled; + generalSettingsConfig.Enabled.ShortcutGuide = isEnabled; break; + case "PowerOCR": + needToUpdate = generalSettingsConfig.Enabled.PowerOCR != isEnabled; + generalSettingsConfig.Enabled.PowerOCR = isEnabled; break; + case "VideoConference": + needToUpdate = generalSettingsConfig.Enabled.VideoConference != isEnabled; + generalSettingsConfig.Enabled.VideoConference = isEnabled; break; + } + + if (needToUpdate) + { + var outgoing = new OutGoingGeneralSettings(generalSettingsConfig); + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + ShellPage.SendDefaultIPCMessage(outgoing.ToString()); + ShellPage.ShellHandler?.SignalGeneralDataUpdate(); + }); + } + + return needToUpdate; + }); + // open oobe ShellPage.SetOpenOobeCallback(() => { @@ -84,6 +170,39 @@ namespace Microsoft.PowerToys.Settings.UI App.GetOobeWindow().Activate(); }); + // open flyout + ShellPage.SetOpenFlyoutCallback(() => + { + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + if (App.GetFlyoutWindow() == null) + { + App.SetFlyoutWindow(new FlyoutWindow()); + } + + FlyoutWindow flyout = App.GetFlyoutWindow(); + flyout.Activate(); + + // https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground + // Need to call SetForegroundWindow to actually gain focus. + Utils.BecomeForegroundWindow(flyout.GetWindowHandle()); + }); + }); + + // disable flyout hiding + ShellPage.SetDisableFlyoutHidingCallback(() => + { + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + if (App.GetFlyoutWindow() == null) + { + App.SetFlyoutWindow(new FlyoutWindow()); + } + + App.GetFlyoutWindow().ViewModel.DisableHiding(); + }); + }); + this.InitializeComponent(); // receive IPC Message @@ -140,5 +259,10 @@ namespace Microsoft.PowerToys.Settings.UI NativeMethods.ShowWindow(hWnd, NativeMethods.SW_HIDE); } } + + internal void EnsurePageIsSelected() + { + ShellPage.EnsurePageIsSelected(); + } } } diff --git a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj index 9c2c9272db..d9b78b6e41 100644 --- a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj +++ b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj @@ -59,6 +59,14 @@ + + + + + + + + @@ -74,6 +82,7 @@ + @@ -92,9 +101,24 @@ + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + MSBuild:Compile + + MSBuild:Compile + + + MSBuild:Compile + Always diff --git a/src/settings-ui/Settings.UI/Services/NavigationService.cs b/src/settings-ui/Settings.UI/Services/NavigationService.cs index 2e3ddea898..be720e2ec6 100644 --- a/src/settings-ui/Settings.UI/Services/NavigationService.cs +++ b/src/settings-ui/Settings.UI/Services/NavigationService.cs @@ -101,5 +101,13 @@ namespace Microsoft.PowerToys.Settings.UI.Services private static void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e) => NavigationFailed?.Invoke(sender, e); private static void Frame_Navigated(object sender, NavigationEventArgs e) => Navigated?.Invoke(sender, e); + + internal static void EnsurePageIsSelected(Type pageType) + { + if (Frame.Content == null) + { + Frame.Navigate(pageType); + } + } } } diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 74bcd3d3d2..7ca657c387 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -895,7 +895,7 @@ Remove Removes a user defined setting group for Image Resizer - + Delete @@ -2693,7 +2693,7 @@ Activate by holding the key for the character you want to add an accent to, then Start selection from the left - + Start selection from the leftmost character for all activation keys, including left and right arrows @@ -2888,4 +2888,47 @@ Activate by holding the key for the character you want to add an accent to, then Preferred language - + + All apps + + + Back + + + Back + + + Bug report + + + Bug report + + + Documentation + + + Documentation + + + FancyZones Editor + Do not localize this string + + + More + + + More + + + Settings + + + Settings + + + Shortcuts + + + Update available + + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/Styles/Button.xaml b/src/settings-ui/Settings.UI/Styles/Button.xaml index 6e60ec3530..dd324e9a92 100644 --- a/src/settings-ui/Settings.UI/Styles/Button.xaml +++ b/src/settings-ui/Settings.UI/Styles/Button.xaml @@ -1,35 +1,15 @@ - + - - - - - - - - + + + + + + + + - - + + diff --git a/src/settings-ui/Settings.UI/Styles/TextBlock.xaml b/src/settings-ui/Settings.UI/Styles/TextBlock.xaml index 806bf88118..2af0b66b0d 100644 --- a/src/settings-ui/Settings.UI/Styles/TextBlock.xaml +++ b/src/settings-ui/Settings.UI/Styles/TextBlock.xaml @@ -18,10 +18,15 @@ 12 - + + 12 + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/Themes/Generic.xaml b/src/settings-ui/Settings.UI/Themes/Generic.xaml index bec1641aaa..45fb5d42e3 100644 --- a/src/settings-ui/Settings.UI/Themes/Generic.xaml +++ b/src/settings-ui/Settings.UI/Themes/Generic.xaml @@ -1,9 +1,8 @@ - + + diff --git a/src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs index 120a93cd12..6dff77c18a 100644 --- a/src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs @@ -41,6 +41,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels GeneralSettingsConfig = settingsRepository.SettingsConfig; + InitializeEnabledValue(); + // To obtain the settings configurations of AlwaysOnTop. if (moduleSettingsRepository == null) { @@ -49,18 +51,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels Settings = moduleSettingsRepository.SettingsConfig; - _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue(); - if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _enabledStateIsGPOConfigured = true; - _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isEnabled = GeneralSettingsConfig.Enabled.AlwaysOnTop; - } - _hotkey = Settings.Properties.Hotkey.Value; _frameEnabled = Settings.Properties.FrameEnabled.Value; _frameThickness = Settings.Properties.FrameThickness.Value; @@ -76,6 +66,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SendConfigMSG = ipcMSGCallBackFunc; } + private void InitializeEnabledValue() + { + _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue(); + if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _enabledStateIsGPOConfigured = true; + _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isEnabled = GeneralSettingsConfig.Enabled.AlwaysOnTop; + } + } + public bool IsEnabled { get => _isEnabled; @@ -274,6 +279,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SettingsUtils.SaveSettings(Settings.ToJsonString(), AlwaysOnTopSettings.ModuleName); } + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + } + private GpoRuleConfigured _enabledGpoRuleConfiguration; private bool _enabledStateIsGPOConfigured; private bool _isEnabled; diff --git a/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs index 609f028f6d..1b122f32b6 100644 --- a/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs @@ -37,6 +37,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels Settings = moduleSettingsRepository.SettingsConfig; + InitializeEnabledValue(); + + _keepDisplayOn = Settings.Properties.KeepDisplayOn; + _mode = Settings.Properties.Mode; + _hours = Settings.Properties.Hours; + _minutes = Settings.Properties.Minutes; + + // set the callback functions value to hangle outgoing IPC message. + SendConfigMSG = ipcMSGCallBackFunc; + } + + private void InitializeEnabledValue() + { _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredAwakeEnabledValue(); if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -48,14 +61,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { _isEnabled = GeneralSettingsConfig.Enabled.Awake; } - - _keepDisplayOn = Settings.Properties.KeepDisplayOn; - _mode = Settings.Properties.Mode; - _hours = Settings.Properties.Hours; - _minutes = Settings.Properties.Minutes; - - // set the callback functions value to hangle outgoing IPC message. - SendConfigMSG = ipcMSGCallBackFunc; } public bool IsEnabled @@ -179,6 +184,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + OnPropertyChanged(nameof(IsTimeConfigurationEnabled)); + OnPropertyChanged(nameof(IsScreenConfigurationPossibleEnabled)); + } + private GpoRuleConfigured _enabledGpoRuleConfiguration; private bool _enabledStateIsGPOConfigured; private bool _isEnabled; diff --git a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs index b193758438..c9c06e4fd9 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs @@ -69,6 +69,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _colorPickerSettings = colorPickerSettingsRepository.SettingsConfig; // used in the unit tests } + InitializeEnabledValue(); + + // set the callback functions value to hangle outgoing IPC message. + SendConfigMSG = ipcMSGCallBackFunc; + + _delayedTimer = new Timer(); + _delayedTimer.Interval = SaveSettingsDelayInMs; + _delayedTimer.Elapsed += DelayedTimer_Tick; + _delayedTimer.AutoReset = false; + + InitializeColorFormats(); + } + + private void InitializeEnabledValue() + { _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredColorPickerEnabledValue(); if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -80,16 +95,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { _isEnabled = GeneralSettingsConfig.Enabled.ColorPicker; } - - // set the callback functions value to hangle outgoing IPC message. - SendConfigMSG = ipcMSGCallBackFunc; - - _delayedTimer = new Timer(); - _delayedTimer.Interval = SaveSettingsDelayInMs; - _delayedTimer.Elapsed += DelayedTimer_Tick; - _delayedTimer.AutoReset = false; - - InitializeColorFormats(); } public bool IsEnabled @@ -360,6 +365,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels JsonSerializer.Serialize(_colorPickerSettings))); } + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + } + protected virtual void Dispose(bool disposing) { if (!disposedValue) diff --git a/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs index ee9330e0c2..b385eb23dc 100644 --- a/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs @@ -116,6 +116,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels string numberColor = Settings.Properties.FancyzonesNumberColor.Value; _zoneNumberColor = !string.IsNullOrEmpty(numberColor) ? numberColor : ConfigDefaults.DefaultFancyzonesNumberColor; + InitializeEnabledValue(); + + _windows11 = Helper.Windows11(); + + // Disable setting on windows 10 + if (!_windows11 && DisableRoundCornersOnWindowSnap) + { + DisableRoundCornersOnWindowSnap = false; + } + } + + private void InitializeEnabledValue() + { _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredFancyZonesEnabledValue(); if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -127,14 +140,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { _isEnabled = GeneralSettingsConfig.Enabled.FancyZones; } - - _windows11 = Helper.Windows11(); - - // Disable setting on windows 10 - if (!_windows11 && DisableRoundCornersOnWindowSnap) - { - DisableRoundCornersOnWindowSnap = false; - } } private GpoRuleConfigured _enabledGpoRuleConfiguration; @@ -883,5 +888,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels OnPropertyChanged(propertyName); SettingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath()); } + + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + OnPropertyChanged(nameof(SnapHotkeysCategoryEnabled)); + OnPropertyChanged(nameof(QuickSwitchEnabled)); + OnPropertyChanged(nameof(WindowSwitchingCategoryEnabled)); + } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs index dc598a3caa..b93e4cefc1 100644 --- a/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs @@ -24,6 +24,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels GeneralSettingsConfig = settingsRepository.SettingsConfig; + InitializeEnabledValue(); + + // set the callback functions value to hangle outgoing IPC message. + SendConfigMSG = ipcMSGCallBackFunc; + } + + private void InitializeEnabledValue() + { _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredFileLocksmithEnabledValue(); if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -35,9 +43,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { _isFileLocksmithEnabled = GeneralSettingsConfig.Enabled.FileLocksmith; } - - // set the callback functions value to hangle outgoing IPC message. - SendConfigMSG = ipcMSGCallBackFunc; } public bool IsFileLocksmithEnabled @@ -77,5 +82,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private GpoRuleConfigured _enabledGpoRuleConfiguration; private bool _enabledStateIsGPOConfigured; private bool _isFileLocksmithEnabled; + + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsFileLocksmithEnabled)); + } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs new file mode 100644 index 0000000000..af05e8b721 --- /dev/null +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using global::PowerToys.GPOWrapper; +using Microsoft.PowerToys.Settings.UI.Flyout; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Helpers; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; +using Windows.ApplicationModel.Resources; + +namespace Microsoft.PowerToys.Settings.UI.ViewModels +{ + public class AllAppsViewModel : Observable + { + public ObservableCollection FlyoutMenuItems { get; set; } + + private ISettingsRepository _settingsRepository; + private GeneralSettings generalSettingsConfig; + + private Func SendConfigMSG { get; } + + public AllAppsViewModel(ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc) + { + _settingsRepository = settingsRepository; + generalSettingsConfig = settingsRepository.SettingsConfig; + generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage); + + FlyoutMenuItems = new ObservableCollection(); + + ResourceLoader resourceLoader = ResourceLoader.GetForViewIndependentUse(); + GpoRuleConfigured gpo; + if ((gpo = GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("AlwaysOnTop/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.AlwaysOnTop, Tag = "AlwaysOnTop", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsAlwaysOnTop.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredAwakeEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("Awake/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.Awake, Tag = "Awake", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsAwake.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredColorPickerEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("ColorPicker/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.ColorPicker, Tag = "ColorPicker", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsColorPicker.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredFancyZonesEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("FancyZones/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.FancyZones, Tag = "FancyZones", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsFancyZones.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredFileLocksmithEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("FileLocksmith/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.FileLocksmith, Tag = "FileLocksmith", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsFileLocksmith.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredFindMyMouseEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("MouseUtils_FindMyMouse/Header"), IsEnabled = generalSettingsConfig.Enabled.FindMyMouse, Tag = "FindMyMouse", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsFindMyMouse.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredHostsFileEditorEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("Hosts/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.Hosts, Tag = "Hosts", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsHosts.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredImageResizerEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("ImageResizer/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.ImageResizer, Tag = "ImageResizer", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsImageResizer.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredKeyboardManagerEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("KeyboardManager/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.KeyboardManager, Tag = "KeyboardManager", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsKeyboardManager.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredMouseHighlighterEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("MouseUtils_MouseHighlighter/Header"), IsEnabled = generalSettingsConfig.Enabled.MouseHighlighter, Tag = "MouseHighlighter", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsMouseHighlighter.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("MouseUtils_MousePointerCrosshairs/Header"), IsEnabled = generalSettingsConfig.Enabled.MousePointerCrosshairs, Tag = "MousePointerCrosshairs", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsMouseCrosshairs.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredPowerRenameEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("PowerRename/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.PowerRename, Tag = "PowerRename", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsPowerRename.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredPowerLauncherEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("PowerLauncher/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.PowerLauncher, Tag = "PowerLauncher", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsPowerToysRun.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredQuickAccentEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("QuickAccent/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.PowerAccent, Tag = "PowerAccent", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsPowerAccent.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredScreenRulerEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("MeasureTool/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.MeasureTool, Tag = "MeasureTool", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsScreenRuler.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredShortcutGuideEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("ShortcutGuide/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.ShortcutGuide, Tag = "ShortcutGuide", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsShortcutGuide.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + if ((gpo = GPOWrapper.GetConfiguredTextExtractorEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("TextExtractor/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.PowerOCR, Tag = "PowerOCR", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsPowerOCR.png", EnabledChangedCallback = EnabledChangedOnUI }); + } + + // set the callback functions value to hangle outgoing IPC message. + SendConfigMSG = ipcMSGCallBackFunc; + } + + private void EnabledChangedOnUI(FlyoutMenuItem flyoutMenuItem) + { + if (Views.ShellPage.UpdateGeneralSettingsCallback(flyoutMenuItem.Tag, flyoutMenuItem.IsEnabled)) + { + Views.ShellPage.DisableFlyoutHidingCallback(); + } + } + + private void ModuleEnabledChangedOnSettingsPage() + { + generalSettingsConfig = _settingsRepository.SettingsConfig; + generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage); + foreach (FlyoutMenuItem item in FlyoutMenuItems) + { + switch (item.Tag) + { + case "AlwaysOnTop": item.IsEnabled = generalSettingsConfig.Enabled.AlwaysOnTop; break; + case "Awake": item.IsEnabled = generalSettingsConfig.Enabled.Awake; break; + case "ColorPicker": item.IsEnabled = generalSettingsConfig.Enabled.ColorPicker; break; + case "FancyZones": item.IsEnabled = generalSettingsConfig.Enabled.FancyZones; break; + case "FileLocksmith": item.IsEnabled = generalSettingsConfig.Enabled.FileLocksmith; break; + case "FindMyMouse": item.IsEnabled = generalSettingsConfig.Enabled.FindMyMouse; break; + case "Hosts": item.IsEnabled = generalSettingsConfig.Enabled.Hosts; break; + case "ImageResizer": item.IsEnabled = generalSettingsConfig.Enabled.ImageResizer; break; + case "KeyboardManager": item.IsEnabled = generalSettingsConfig.Enabled.KeyboardManager; break; + case "MouseHighlighter": item.IsEnabled = generalSettingsConfig.Enabled.MouseHighlighter; break; + case "MousePointerCrosshairs": item.IsEnabled = generalSettingsConfig.Enabled.MousePointerCrosshairs; break; + case "PowerRename": item.IsEnabled = generalSettingsConfig.Enabled.PowerRename; break; + case "PowerLauncher": item.IsEnabled = generalSettingsConfig.Enabled.PowerLauncher; break; + case "PowerAccent": item.IsEnabled = generalSettingsConfig.Enabled.PowerAccent; break; + case "MeasureTool": item.IsEnabled = generalSettingsConfig.Enabled.MeasureTool; break; + case "ShortcutGuide": item.IsEnabled = generalSettingsConfig.Enabled.ShortcutGuide; break; + case "PowerOCR": item.IsEnabled = generalSettingsConfig.Enabled.PowerOCR; break; + case "VideoConference": item.IsEnabled = generalSettingsConfig.Enabled.VideoConference; break; + } + } + } + } +} diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs new file mode 100644 index 0000000000..c8b184b2f1 --- /dev/null +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Microsoft.UI.Xaml; + +namespace Microsoft.PowerToys.Settings.UI.ViewModels +{ + public class FlyoutMenuItem : INotifyPropertyChanged + { + private bool _visible; + private bool _isEnabled; + + public string Label { get; set; } + + public string Icon { get; set; } + + public string ToolTip { get; set; } + + public string Tag { get; set; } + + public bool IsEnabled + { + get => _isEnabled; + set + { + if (_isEnabled != value) + { + _isEnabled = value; + OnPropertyChanged(); + EnabledChangedCallback?.Invoke(this); + } + } + } + + public Action EnabledChangedCallback { get; set; } = null; + + public bool Visible + { + get => _visible; + set + { + if (_visible != value) + { + _visible = value; + OnPropertyChanged(); + } + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + private void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs new file mode 100644 index 0000000000..c781535afb --- /dev/null +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Timers; + +namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout +{ + public class FlyoutViewModel + { + public bool CanHide { get; set; } + + private Timer hideTimer; + + public FlyoutViewModel() + { + CanHide = true; + hideTimer = new Timer(); + hideTimer.Elapsed += HideTimer_Elapsed; + hideTimer.Interval = 1000; + hideTimer.Enabled = false; + } + + private void HideTimer_Elapsed(object sender, ElapsedEventArgs e) + { + CanHide = true; + hideTimer.Stop(); + } + + internal void DisableHiding() + { + CanHide = false; + hideTimer.Stop(); + hideTimer.Start(); + } + } +} diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs new file mode 100644 index 0000000000..66490cea04 --- /dev/null +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.ObjectModel; +using global::PowerToys.GPOWrapper; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Helpers; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; +using Windows.ApplicationModel.Resources; + +namespace Microsoft.PowerToys.Settings.UI.ViewModels +{ + public class LauncherViewModel : Observable + { + public bool IsUpdateAvailable { get; set; } + + public ObservableCollection FlyoutMenuItems { get; set; } + + private GeneralSettings generalSettingsConfig; + private UpdatingSettings updatingSettingsConfig; + private ISettingsRepository _settingsRepository; + + private Func SendIPCMessage { get; } + + public LauncherViewModel(ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc) + { + _settingsRepository = settingsRepository; + generalSettingsConfig = settingsRepository.SettingsConfig; + generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChanged); + + // set the callback functions value to hangle outgoing IPC message. + SendIPCMessage = ipcMSGCallBackFunc; + ResourceLoader resourceLoader = ResourceLoader.GetForViewIndependentUse(); + FlyoutMenuItems = new ObservableCollection(); + if (GPOWrapper.GetConfiguredColorPickerEnabledValue() != GpoRuleConfigured.Disabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() + { + Label = resourceLoader.GetString("ColorPicker/ModuleTitle"), + Tag = "ColorPicker", + Visible = generalSettingsConfig.Enabled.ColorPicker, + ToolTip = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.ToString(), + Icon = "ms-appx:///Assets/FluentIcons/FluentIconsColorPicker.png", + }); + } + + if (GPOWrapper.GetConfiguredFancyZonesEnabledValue() != GpoRuleConfigured.Disabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() + { + Label = resourceLoader.GetString("FZEditorString"), + Tag = "FancyZones", + Visible = generalSettingsConfig.Enabled.FancyZones, + ToolTip = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.ToString(), + Icon = "ms-appx:///Assets/FluentIcons/FluentIconsFancyZones.png", + }); + } + + if (GPOWrapper.GetConfiguredHostsFileEditorEnabledValue() != GpoRuleConfigured.Disabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() + { + Label = resourceLoader.GetString("Hosts/ModuleTitle"), + Tag = "Hosts", + Visible = generalSettingsConfig.Enabled.Hosts, + Icon = "ms-appx:///Assets/FluentIcons/FluentIconsHosts.png", + }); + } + + if (GPOWrapper.GetConfiguredPowerLauncherEnabledValue() != GpoRuleConfigured.Disabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() + { + Label = resourceLoader.GetString("PowerLauncher/ModuleTitle"), + Tag = "PowerLauncher", + Visible = generalSettingsConfig.Enabled.PowerLauncher, + ToolTip = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.OpenPowerLauncher.ToString(), + Icon = "ms-appx:///Assets/FluentIcons/FluentIconsPowerToysRun.png", + }); + } + + if (GPOWrapper.GetConfiguredTextExtractorEnabledValue() != GpoRuleConfigured.Disabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() + { + Label = resourceLoader.GetString("TextExtractor/ModuleTitle"), + Tag = "PowerOCR", + Visible = generalSettingsConfig.Enabled.PowerOCR, + ToolTip = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.ToString(), + Icon = "ms-appx:///Assets/FluentIcons/FluentIconsPowerOcr.png", + }); + } + + if (GPOWrapper.GetConfiguredScreenRulerEnabledValue() != GpoRuleConfigured.Disabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() + { + Label = resourceLoader.GetString("MeasureTool/ModuleTitle"), + Tag = "MeasureTool", + Visible = generalSettingsConfig.Enabled.MeasureTool, + ToolTip = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.ToString(), + Icon = "ms-appx:///Assets/FluentIcons/FluentIconsScreenRuler.png", + }); + } + + if (GPOWrapper.GetConfiguredShortcutGuideEnabledValue() != GpoRuleConfigured.Disabled) + { + FlyoutMenuItems.Add(new FlyoutMenuItem() + { + Label = resourceLoader.GetString("ShortcutGuide/ModuleTitle"), + Tag = "ShortcutGuide", + Visible = generalSettingsConfig.Enabled.ShortcutGuide, + ToolTip = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.OpenShortcutGuide.ToString(), + Icon = "ms-appx:///Assets/FluentIcons/FluentIconsShortcutGuide.png", + }); + } + + if (updatingSettingsConfig == null) + { + updatingSettingsConfig = new UpdatingSettings(); + } + + updatingSettingsConfig = UpdatingSettings.LoadSettings(); + + if (updatingSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToInstall || updatingSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToDownload) + { + IsUpdateAvailable = true; + } + else + { + IsUpdateAvailable = false; + } + } + + private void ModuleEnabledChanged() + { + generalSettingsConfig = _settingsRepository.SettingsConfig; + generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChanged); + foreach (FlyoutMenuItem item in FlyoutMenuItems) + { + switch (item.Tag) + { + case "ColorPicker": item.Visible = generalSettingsConfig.Enabled.ColorPicker; break; + case "FancyZones": item.Visible = generalSettingsConfig.Enabled.FancyZones; break; + case "Hosts": item.Visible = generalSettingsConfig.Enabled.Hosts; break; + case "PowerLauncher": item.Visible = generalSettingsConfig.Enabled.PowerLauncher; break; + case "PowerOCR": item.Visible = generalSettingsConfig.Enabled.PowerOCR; break; + case "MeasureTool": item.Visible = generalSettingsConfig.Enabled.MeasureTool; break; + case "ShortcutGuide": item.Visible = generalSettingsConfig.Enabled.ShortcutGuide; break; + } + } + } + + internal void StartBugReport() + { + SendIPCMessage("{\"bugreport\": 0 }"); + } + + internal void KillRunner() + { + SendIPCMessage("{\"killrunner\": 0 }"); + } + } +} diff --git a/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs index 16b5e00054..bed5dc565b 100644 --- a/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs @@ -109,6 +109,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels Settings = moduleSettingsRepository.SettingsConfig; SendConfigMSG = ipcMSGCallBackFunc; _isElevated = isElevated; + InitializeEnabledValue(); + } + + private void InitializeEnabledValue() + { _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredHostsFileEditorEnabledValue(); if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -139,5 +144,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels OnPropertyChanged(propertyName); SettingsUtils.SaveSettings(Settings.ToJsonString(), HostsSettings.ModuleName); } + + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs index 9e4a764e53..ed9432c0c5 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs @@ -59,17 +59,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; - _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredImageResizerEnabledValue(); - if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _enabledStateIsGPOConfigured = true; - _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isEnabled = GeneralSettingsConfig.Enabled.ImageResizer; - } + InitializeEnabledValue(); _advancedSizes = Settings.Properties.ImageresizerSizes.Value; _jpegQualityLevel = Settings.Properties.ImageresizerJpegQualityLevel.Value; @@ -88,6 +78,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + private void InitializeEnabledValue() + { + _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredImageResizerEnabledValue(); + if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _enabledStateIsGPOConfigured = true; + _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isEnabled = GeneralSettingsConfig.Enabled.ImageResizer; + } + } + private GpoRuleConfigured _enabledGpoRuleConfiguration; private bool _enabledStateIsGPOConfigured; private bool _isEnabled; @@ -420,5 +425,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels return $"{namePrefix} {++newSizeCounter}"; } + + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs index da2e8037da..a12e5cfdfb 100644 --- a/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs @@ -63,17 +63,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels GeneralSettingsConfig = settingsRepository.SettingsConfig; - _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredKeyboardManagerEnabledValue(); - if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _enabledStateIsGPOConfigured = true; - _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isEnabled = GeneralSettingsConfig.Enabled.KeyboardManager; - } + InitializeEnabledValue(); // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; @@ -111,6 +101,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + private void InitializeEnabledValue() + { + _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredKeyboardManagerEnabledValue(); + if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _enabledStateIsGPOConfigured = true; + _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isEnabled = GeneralSettingsConfig.Enabled.KeyboardManager; + } + } + public bool Enabled { get @@ -277,6 +282,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels OnPropertyChanged(nameof(RemapShortcuts)); } + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(Enabled)); + } + public bool LoadProfile() { var success = true; diff --git a/src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs index cd18565128..90af903553 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs @@ -36,6 +36,20 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels GeneralSettingsConfig = settingsRepository.SettingsConfig; + InitializeEnabledValue(); + + if (measureToolSettingsRepository == null) + { + throw new ArgumentNullException(nameof(measureToolSettingsRepository)); + } + + Settings = measureToolSettingsRepository.SettingsConfig; + + SendConfigMSG = ipcMSGCallBackFunc; + } + + private void InitializeEnabledValue() + { _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredScreenRulerEnabledValue(); if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -47,15 +61,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { _isEnabled = GeneralSettingsConfig.Enabled.MeasureTool; } - - if (measureToolSettingsRepository == null) - { - throw new ArgumentNullException(nameof(measureToolSettingsRepository)); - } - - Settings = measureToolSettingsRepository.SettingsConfig; - - SendConfigMSG = ipcMSGCallBackFunc; } public bool IsEnabled @@ -230,6 +235,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SettingsUtils.SaveSettings(Settings.ToJsonString(), MeasureToolSettings.ModuleName); } + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + OnPropertyChanged(nameof(ShowContinuousCaptureWarning)); + } + public bool ShowContinuousCaptureWarning { get => IsEnabled && ContinuousCapture; diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs index 88556b3384..a33755de82 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs @@ -35,41 +35,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels GeneralSettingsConfig = settingsRepository.SettingsConfig; - _findMyMouseEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredFindMyMouseEnabledValue(); - if (_findMyMouseEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _findMyMouseEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _findMyMouseEnabledStateIsGPOConfigured = true; - _isFindMyMouseEnabled = _findMyMouseEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isFindMyMouseEnabled = GeneralSettingsConfig.Enabled.FindMyMouse; - } - - _highlighterEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMouseHighlighterEnabledValue(); - if (_highlighterEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _highlighterEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _highlighterEnabledStateIsGPOConfigured = true; - _isMouseHighlighterEnabled = _highlighterEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isMouseHighlighterEnabled = GeneralSettingsConfig.Enabled.MouseHighlighter; - } - - _mousePointerCrosshairsEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue(); - if (_mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _mousePointerCrosshairsEnabledStateIsGPOConfigured = true; - _isMousePointerCrosshairsEnabled = _mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isMousePointerCrosshairsEnabled = GeneralSettingsConfig.Enabled.MousePointerCrosshairs; - } + InitializeEnabledValues(); // To obtain the find my mouse settings, if the file exists. // If not, to create a file with the default settings and to return the default configurations. @@ -134,6 +100,45 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SendConfigMSG = ipcMSGCallBackFunc; } + private void InitializeEnabledValues() + { + _findMyMouseEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredFindMyMouseEnabledValue(); + if (_findMyMouseEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _findMyMouseEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _findMyMouseEnabledStateIsGPOConfigured = true; + _isFindMyMouseEnabled = _findMyMouseEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isFindMyMouseEnabled = GeneralSettingsConfig.Enabled.FindMyMouse; + } + + _highlighterEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMouseHighlighterEnabledValue(); + if (_highlighterEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _highlighterEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _highlighterEnabledStateIsGPOConfigured = true; + _isMouseHighlighterEnabled = _highlighterEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isMouseHighlighterEnabled = GeneralSettingsConfig.Enabled.MouseHighlighter; + } + + _mousePointerCrosshairsEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue(); + if (_mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _mousePointerCrosshairsEnabledStateIsGPOConfigured = true; + _isMousePointerCrosshairsEnabled = _mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isMousePointerCrosshairsEnabled = GeneralSettingsConfig.Enabled.MousePointerCrosshairs; + } + } + public bool IsFindMyMouseEnabled { get => _isFindMyMouseEnabled; @@ -693,6 +698,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SettingsUtils.SaveSettings(MousePointerCrosshairsSettingsConfig.ToJsonString(), MousePointerCrosshairsSettings.ModuleName); } + public void RefreshEnabledState() + { + InitializeEnabledValues(); + OnPropertyChanged(nameof(IsFindMyMouseEnabled)); + OnPropertyChanged(nameof(IsMouseHighlighterEnabled)); + OnPropertyChanged(nameof(IsMousePointerCrosshairsEnabled)); + } + private Func SendConfigMSG { get; } private GpoRuleConfigured _findMyMouseEnabledGpoRuleConfiguration; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs index 2596e5952d..a46fe408d5 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs @@ -80,17 +80,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); GeneralSettingsConfig = settingsRepository.SettingsConfig; - _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredQuickAccentEnabledValue(); - if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _enabledStateIsGPOConfigured = true; - _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isEnabled = GeneralSettingsConfig.Enabled.PowerAccent; - } + InitializeEnabledValue(); if (_settingsUtils.SettingsExists(PowerAccentSettings.ModuleName)) { @@ -113,6 +103,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SendConfigMSG = ipcMSGCallBackFunc; } + private void InitializeEnabledValue() + { + _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredQuickAccentEnabledValue(); + if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _enabledStateIsGPOConfigured = true; + _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isEnabled = GeneralSettingsConfig.Enabled.PowerAccent; + } + } + public bool IsEnabled { get => _isEnabled; @@ -308,6 +313,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + } + private GpoRuleConfigured _enabledGpoRuleConfiguration; private bool _enabledStateIsGPOConfigured; private bool _isEnabled; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerLauncherViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerLauncherViewModel.cs index 051cfee6eb..fcc08dc904 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerLauncherViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerLauncherViewModel.cs @@ -63,17 +63,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels GeneralSettingsConfig = settingsRepository.SettingsConfig; - _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredPowerLauncherEnabledValue(); - if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _enabledStateIsGPOConfigured = true; - _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isEnabled = GeneralSettingsConfig.Enabled.PowerLauncher; - } + InitializeEnabledValue(); // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; @@ -118,6 +108,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SearchPluginsCommand = new RelayCommand(SearchPlugins); } + private void InitializeEnabledValue() + { + _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredPowerLauncherEnabledValue(); + if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _enabledStateIsGPOConfigured = true; + _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isEnabled = GeneralSettingsConfig.Enabled.PowerLauncher; + } + } + private void OnPluginInfoChange(object sender, PropertyChangedEventArgs e) { if ( @@ -175,6 +180,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(EnablePowerLauncher)); + OnPropertyChanged(nameof(ShowAllPluginsDisabledWarning)); + OnPropertyChanged(nameof(ShowPluginsLoadingMessage)); + } + public bool IsEnabledGpoConfigured { get => _enabledStateIsGPOConfigured; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs index 5b487a059c..7998a3c1e7 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs @@ -95,6 +95,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _powerOcrSettings = powerOcrsettingsRepository.SettingsConfig; + InitializeEnabledValue(); + + // set the callback functions value to hangle outgoing IPC message. + SendConfigMSG = ipcMSGCallBackFunc; + + _delayedTimer = new Timer(); + _delayedTimer.Interval = SaveSettingsDelayInMs; + _delayedTimer.Elapsed += DelayedTimer_Tick; + _delayedTimer.AutoReset = false; + } + + private void InitializeEnabledValue() + { _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredTextExtractorEnabledValue(); if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -106,14 +119,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { _isEnabled = GeneralSettingsConfig.Enabled.PowerOCR; } - - // set the callback functions value to hangle outgoing IPC message. - SendConfigMSG = ipcMSGCallBackFunc; - - _delayedTimer = new Timer(); - _delayedTimer.Interval = SaveSettingsDelayInMs; - _delayedTimer.Elapsed += DelayedTimer_Tick; - _delayedTimer.AutoReset = false; } public bool IsEnabled @@ -237,6 +242,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels JsonSerializer.Serialize(_powerOcrSettings))); } + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + } + protected virtual void Dispose(bool disposing) { if (!disposedValue) diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs index 04252afbd6..039ee3e410 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs @@ -69,6 +69,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _autoComplete = Settings.Properties.MRUEnabled.Value; _powerRenameUseBoostLib = Settings.Properties.UseBoostLib.Value; + InitializeEnabledValue(); + } + + private void InitializeEnabledValue() + { _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredPowerRenameEnabledValue(); if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) { @@ -260,5 +265,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SendConfigMSG(ipcMessage.ToJsonString()); } } + + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + OnPropertyChanged(nameof(GlobalAndMruEnabled)); + } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs index 10e1398850..d21142b5da 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs @@ -5,10 +5,12 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Abstractions; using System.Linq; using System.Threading.Tasks; using System.Windows.Input; using Microsoft.PowerToys.Settings.UI.Helpers; +using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Services; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; diff --git a/src/settings-ui/Settings.UI/ViewModels/ShortcutGuideViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ShortcutGuideViewModel.cs index 43c6091cac..b23800d294 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ShortcutGuideViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ShortcutGuideViewModel.cs @@ -53,17 +53,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels // set the callback functions value to hangle outgoing IPC message. SendConfigMSG = ipcMSGCallBackFunc; - _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredShortcutGuideEnabledValue(); - if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _enabledStateIsGPOConfigured = true; - _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isEnabled = GeneralSettingsConfig.Enabled.ShortcutGuide; - } + InitializeEnabledValue(); _useLegacyPressWinKeyBehavior = Settings.Properties.UseLegacyPressWinKeyBehavior.Value; _pressTimeForGlobalWindowsShortcuts = Settings.Properties.PressTimeForGlobalWindowsShortcuts.Value; @@ -79,6 +69,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + private void InitializeEnabledValue() + { + _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredShortcutGuideEnabledValue(); + if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _enabledStateIsGPOConfigured = true; + _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isEnabled = GeneralSettingsConfig.Enabled.ShortcutGuide; + } + } + private GpoRuleConfigured _enabledGpoRuleConfiguration; private bool _enabledStateIsGPOConfigured; private bool _isEnabled; @@ -267,5 +272,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SendConfigMSG(ipcMessage.ToJsonString()); SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); } + + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs index acc33ea579..c2bd3eb9af 100644 --- a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs @@ -92,17 +92,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _selectedMicrophoneIndex = MicrophoneNames.FindIndex(name => name == Settings.Properties.SelectedMicrophone.Value); } - _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredVideoConferenceMuteEnabledValue(); - if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _enabledStateIsGPOConfigured = true; - _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isEnabled = GeneralSettingsConfig.Enabled.VideoConference; - } + InitializeEnabledValue(); _cameraAndMicrophoneMuteHotkey = Settings.Properties.MuteCameraAndMicrophoneHotkey.Value; _microphoneMuteHotkey = Settings.Properties.MuteMicrophoneHotkey.Value; @@ -163,6 +153,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + private void InitializeEnabledValue() + { + _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredVideoConferenceMuteEnabledValue(); + if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _enabledStateIsGPOConfigured = true; + _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isEnabled = GeneralSettingsConfig.Enabled.VideoConference; + } + } + private GpoRuleConfigured _enabledGpoRuleConfiguration; private bool _enabledStateIsGPOConfigured; private bool _isEnabled; @@ -473,6 +478,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels ModuleName, JsonSerializer.Serialize(Settings))); } + + public void RefreshEnabledState() + { + InitializeEnabledValue(); + OnPropertyChanged(nameof(IsEnabled)); + } } [ComImport] diff --git a/src/settings-ui/Settings.UI/Views/AlwaysOnTopPage.xaml.cs b/src/settings-ui/Settings.UI/Views/AlwaysOnTopPage.xaml.cs index 36d1cb9e25..b38fffc59e 100644 --- a/src/settings-ui/Settings.UI/Views/AlwaysOnTopPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/AlwaysOnTopPage.xaml.cs @@ -2,13 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class AlwaysOnTopPage : Page + public sealed partial class AlwaysOnTopPage : Page, IRefreshablePage { private AlwaysOnTopViewModel ViewModel { get; set; } @@ -19,5 +20,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views DataContext = ViewModel; InitializeComponent(); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs b/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs index cbcdb40d15..ec3abd4806 100644 --- a/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs @@ -2,13 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class AwakePage : Page + public sealed partial class AwakePage : Page, IRefreshablePage { private AwakeViewModel ViewModel { get; set; } @@ -19,5 +20,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views DataContext = ViewModel; InitializeComponent(); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml.cs b/src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml.cs index aff159f9bf..f8b55efdcf 100644 --- a/src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml.cs @@ -14,7 +14,7 @@ using Windows.ApplicationModel.Resources; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class ColorPickerPage : Page + public sealed partial class ColorPickerPage : Page, IRefreshablePage { public ColorPickerViewModel ViewModel { get; set; } @@ -157,5 +157,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views { ColorFormatDialog.IsPrimaryButtonEnabled = ViewModel.SetValidity(ColorFormatDialog.DataContext as ColorFormatModel, ColorFormatDialog.Tag as string); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/FancyZonesPage.xaml.cs b/src/settings-ui/Settings.UI/Views/FancyZonesPage.xaml.cs index 0165b025a2..c224c42683 100644 --- a/src/settings-ui/Settings.UI/Views/FancyZonesPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/FancyZonesPage.xaml.cs @@ -2,13 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class FancyZonesPage : Page + public sealed partial class FancyZonesPage : Page, IRefreshablePage { private FancyZonesViewModel ViewModel { get; set; } @@ -24,5 +25,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views { Helpers.StartProcessHelper.Start(Helpers.StartProcessHelper.ColorsSettings); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/FileLocksmithPage.xaml.cs b/src/settings-ui/Settings.UI/Views/FileLocksmithPage.xaml.cs index 5a191ce9ff..2481580213 100644 --- a/src/settings-ui/Settings.UI/Views/FileLocksmithPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/FileLocksmithPage.xaml.cs @@ -2,13 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class FileLocksmithPage : Page + public sealed partial class FileLocksmithPage : Page, IRefreshablePage { private FileLocksmithViewModel ViewModel { get; set; } @@ -19,5 +20,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views DataContext = ViewModel; InitializeComponent(); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/HostsPage.xaml b/src/settings-ui/Settings.UI/Views/HostsPage.xaml index 88476a47d2..bb54a4b1a3 100644 --- a/src/settings-ui/Settings.UI/Views/HostsPage.xaml +++ b/src/settings-ui/Settings.UI/Views/HostsPage.xaml @@ -9,18 +9,14 @@ xmlns:ui="using:CommunityToolkit.WinUI.UI" mc:Ignorable="d"> - + - + - + - + - - + + - - - + + + @@ -78,9 +58,7 @@ - + diff --git a/src/settings-ui/Settings.UI/Views/HostsPage.xaml.cs b/src/settings-ui/Settings.UI/Views/HostsPage.xaml.cs index 9a621f2d5b..b74f03df44 100644 --- a/src/settings-ui/Settings.UI/Views/HostsPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/HostsPage.xaml.cs @@ -2,13 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class HostsPage : Page + public sealed partial class HostsPage : Page, IRefreshablePage { private HostsViewModel ViewModel { get; } @@ -18,5 +19,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views var settingsUtils = new SettingsUtils(); ViewModel = new HostsViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage, App.IsElevated); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/ImageResizerPage.xaml b/src/settings-ui/Settings.UI/Views/ImageResizerPage.xaml index 01ab223005..53522cf987 100644 --- a/src/settings-ui/Settings.UI/Views/ImageResizerPage.xaml +++ b/src/settings-ui/Settings.UI/Views/ImageResizerPage.xaml @@ -21,18 +21,14 @@ FalseValue="1" TrueValue="0" /> - + - + - - + + public static IPCMessageCallback CheckForUpdatesMsgCallback { get; set; } + /// + /// Gets or sets callback function for opening main window + /// + public static MainOpeningCallback OpenMainWindowCallback { get; set; } + + /// + /// Gets or sets callback function for updating the general settings + /// + public static UpdatingGeneralSettingsCallback UpdateGeneralSettingsCallback { get; set; } + /// /// Gets or sets callback function for opening oobe window /// public static OobeOpeningCallback OpenOobeWindowCallback { get; set; } + /// + /// Gets or sets callback function for opening flyout window + /// + public static FlyoutOpeningCallback OpenFlyoutCallback { get; set; } + + /// + /// Gets or sets callback function for disabling hide of flyout window + /// + public static DisablingFlyoutHidingCallback DisableFlyoutHidingCallback { get; set; } + /// /// Gets view model. /// @@ -82,7 +122,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views DataContext = ViewModel; ShellHandler = this; ViewModel.Initialize(shellFrame, navigationView, KeyboardAccelerators); - shellFrame.Navigate(typeof(GeneralPage)); + + // NL moved navigation to general page to the moment when the window is first activated (to not make flyout window disappear) + // shellFrame.Navigate(typeof(GeneralPage)); + IPCResponseHandleList.Add(ReceiveMessage); } public static int SendDefaultIPCMessage(string msg) @@ -131,6 +174,24 @@ namespace Microsoft.PowerToys.Settings.UI.Views CheckForUpdatesMsgCallback = implementation; } + /// + /// Set main window opening callback function + /// + /// delegate function implementation. + public static void SetOpenMainWindowCallback(MainOpeningCallback implementation) + { + OpenMainWindowCallback = implementation; + } + + /// + /// Set updating the general settings callback function + /// + /// delegate function implementation. + public static void SetUpdatingGeneralSettingsCallback(UpdatingGeneralSettingsCallback implementation) + { + UpdateGeneralSettingsCallback = implementation; + } + /// /// Set oobe opening callback function /// @@ -140,6 +201,24 @@ namespace Microsoft.PowerToys.Settings.UI.Views OpenOobeWindowCallback = implementation; } + /// + /// Set flyout opening callback function + /// + /// delegate function implementation. + public static void SetOpenFlyoutCallback(FlyoutOpeningCallback implementation) + { + OpenFlyoutCallback = implementation; + } + + /// + /// Set disable flyout hiding callback function + /// + /// delegate function implementation. + public static void SetDisableFlyoutHidingCallback(DisablingFlyoutHidingCallback implementation) + { + DisableFlyoutHidingCallback = implementation; + } + public static void SetElevationStatus(bool isElevated) { IsElevated = isElevated; @@ -160,6 +239,16 @@ namespace Microsoft.PowerToys.Settings.UI.Views shellFrame.Navigate(typeof(GeneralPage)); } + // Tell the current page view model to update + public void SignalGeneralDataUpdate() + { + IRefreshablePage currentPage = shellFrame?.Content as IRefreshablePage; + if (currentPage != null) + { + currentPage.RefreshEnabledState(); + } + } + private void OobeButton_Click(object sender, RoutedEventArgs e) { OpenOobeWindowCallback(); @@ -236,5 +325,28 @@ namespace Microsoft.PowerToys.Settings.UI.Views NavigationService.Navigate(pageType); } } + + private void ReceiveMessage(JsonObject json) + { + if (json != null) + { + if (json.ToString().StartsWith("{\"ShowYourself\":")) + { + if (json.ToString().EndsWith("\"flyout\"}")) + { + OpenFlyoutCallback(); + } + else if (json.ToString().EndsWith("\"main_page\"}")) + { + OpenMainWindowCallback(); + } + } + } + } + + internal static void EnsurePageIsSelected() + { + NavigationService.EnsurePageIsSelected(typeof(GeneralPage)); + } } } diff --git a/src/settings-ui/Settings.UI/Views/ShortcutGuidePage.xaml.cs b/src/settings-ui/Settings.UI/Views/ShortcutGuidePage.xaml.cs index 403ee8d6ae..750007595a 100644 --- a/src/settings-ui/Settings.UI/Views/ShortcutGuidePage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/ShortcutGuidePage.xaml.cs @@ -2,13 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class ShortcutGuidePage : Page + public sealed partial class ShortcutGuidePage : Page, IRefreshablePage { private ShortcutGuideViewModel ViewModel { get; set; } @@ -25,5 +26,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views { Helpers.StartProcessHelper.Start(Helpers.StartProcessHelper.ColorsSettings); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/VideoConference.xaml.cs b/src/settings-ui/Settings.UI/Views/VideoConference.xaml.cs index b45ac61aed..1a5d80a035 100644 --- a/src/settings-ui/Settings.UI/Views/VideoConference.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/VideoConference.xaml.cs @@ -4,6 +4,7 @@ using System; using System.Threading.Tasks; +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; @@ -12,7 +13,7 @@ using Windows.Storage.Pickers; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class VideoConferencePage : Page + public sealed partial class VideoConferencePage : Page, IRefreshablePage { private VideoConferenceViewModel ViewModel { get; set; } @@ -43,5 +44,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views DataContext = ViewModel; InitializeComponent(); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } From fcd05f2f5100fad2f9bab98d729e7ed7ec41e024 Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Tue, 31 Jan 2023 12:55:46 +0300 Subject: [PATCH 014/163] [FancyZones] Improve code quality (part 5: work area initialization) (#23671) --- .../fancyzones/FancyZonesLib/FancyZones.cpp | 208 ++++++------- .../FancyZonesLib/FancyZonesLib.vcxproj | 4 +- .../FancyZonesLib.vcxproj.filters | 4 +- .../FancyZonesLib/MonitorWorkAreaHandler.cpp | 141 --------- .../FancyZonesLib/MonitorWorkAreaHandler.h | 93 ------ .../FancyZonesLib/MonitorWorkAreaMap.cpp | 67 ++++ .../FancyZonesLib/MonitorWorkAreaMap.h | 58 ++++ .../fancyzones/FancyZonesLib/WindowDrag.cpp | 23 +- .../fancyzones/FancyZonesLib/WindowDrag.h | 8 +- .../fancyzones/FancyZonesLib/WorkArea.cpp | 133 ++++---- .../fancyzones/FancyZonesLib/WorkArea.h | 63 ++-- .../UnitTests/WorkArea.Spec.cpp | 294 ++++++++---------- 12 files changed, 469 insertions(+), 627 deletions(-) delete mode 100644 src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp delete mode 100644 src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h create mode 100644 src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp create mode 100644 src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 9d42d97097..020b1ec3b0 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -129,17 +129,16 @@ private: bool OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept; bool OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept; bool OnSnapHotkey(DWORD vkCode) noexcept; - bool ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept; + bool ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept; void RegisterVirtualDesktopUpdates() noexcept; void UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept; - std::pair, ZoneIndexSet> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreaMap) noexcept; - void MoveWindowIntoZone(HWND window, std::shared_ptr workArea, const ZoneIndexSet& zoneIndexSet) noexcept; + std::pair GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreas) noexcept; + void MoveWindowIntoZone(HWND window, WorkArea* const workArea, const ZoneIndexSet& zoneIndexSet) noexcept; bool MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept; - void RefreshWorkAreaWindows(bool updatePositions); - + void OnEditorExitEvent() noexcept; void UpdateZoneSets() noexcept; bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept; @@ -156,7 +155,7 @@ private: HWND m_window{}; std::unique_ptr m_windowDrag{}; - MonitorWorkAreaHandler m_workAreaHandler; + MonitorWorkAreaMap m_workAreaHandler; DraggingState m_draggingState; wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on @@ -283,7 +282,7 @@ FancyZones::VirtualDesktopChanged() noexcept void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor) { - m_windowDrag = WindowDrag::Create(window, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); + m_windowDrag = WindowDrag::Create(window, m_workAreaHandler.GetAllWorkAreas()); if (m_windowDrag) { if (FancyZonesSettings::settings().spanZonesAcrossMonitors) @@ -321,70 +320,54 @@ void FancyZones::MoveSizeEnd() } } -std::pair, ZoneIndexSet> FancyZones::GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreaMap) noexcept +std::pair FancyZones::GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreas) noexcept { - if (monitor) + for (const auto& [workAreaMonitor, workArea] : workAreas) { - if (workAreaMap.contains(monitor)) + if (workAreaMonitor == monitor && workArea) { - auto workArea = workAreaMap.at(monitor); - return std::pair, ZoneIndexSet>{ workArea, workArea->GetWindowZoneIndexes(window) }; - } - else - { - Logger::debug(L"No work area for the currently active monitor."); - } - } - else - { - for (const auto& [mon, workArea] : workAreaMap) - { - auto zoneIndexSet = workArea->GetWindowZoneIndexes(window); - if (!zoneIndexSet.empty()) - { - return std::pair, ZoneIndexSet>{ workArea, zoneIndexSet }; - } + return std::pair{ workArea.get(), workArea->GetWindowZoneIndexes(window) }; } } - return std::pair, ZoneIndexSet>{ nullptr, {} }; + Logger::error(L"No work area for the currently active monitor."); + return std::pair{ nullptr, {} }; } -void FancyZones::MoveWindowIntoZone(HWND window, std::shared_ptr workArea, const ZoneIndexSet& zoneIndexSet) noexcept +void FancyZones::MoveWindowIntoZone(HWND window, WorkArea* const workArea, const ZoneIndexSet& zoneIndexSet) noexcept { if (workArea) { Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); workArea->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet); + AppZoneHistory::instance().UpdateProcessIdToHandleMap(window, workArea->UniqueId()); } - - AppZoneHistory::instance().UpdateProcessIdToHandleMap(window, workArea->UniqueId()); } bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept { - auto workAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - if (workAreaMap.empty()) + const auto& workAreas = m_workAreaHandler.GetAllWorkAreas(); + if (workAreas.empty()) { Logger::trace(L"No work area for the current desktop."); return false; } // Search application history on currently active monitor. - std::pair, ZoneIndexSet> appZoneHistoryInfo = GetAppZoneHistoryInfo(window, active, workAreaMap); + auto appZoneHistoryInfo = GetAppZoneHistoryInfo(window, active, workAreas); // No application history on currently active monitor if (appZoneHistoryInfo.second.empty()) { // Search application history on primary monitor. - appZoneHistoryInfo = GetAppZoneHistoryInfo(window, primary, workAreaMap); + appZoneHistoryInfo = GetAppZoneHistoryInfo(window, primary, workAreas); } // No application history on currently active and primary monitors if (appZoneHistoryInfo.second.empty()) { // Search application history on remaining monitors. - appZoneHistoryInfo = GetAppZoneHistoryInfo(window, nullptr, workAreaMap); + appZoneHistoryInfo = GetAppZoneHistoryInfo(window, nullptr, workAreas); } if (!appZoneHistoryInfo.second.empty()) @@ -400,25 +383,6 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primar return false; } -void FancyZones::RefreshWorkAreaWindows(bool updatePositions) -{ - auto activeWorkAreas = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop()) - { - auto zoneIndexSet = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); - if (zoneIndexSet.size() == 0) - { - continue; - } - - auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - if (monitor && activeWorkAreas.contains(monitor)) - { - activeWorkAreas.at(monitor)->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, updatePositions); - } - } -} - void FancyZones::WindowCreated(HWND window) noexcept { const bool moveToAppLastZone = FancyZonesSettings::settings().appLastZone_moveWindows; @@ -457,9 +421,19 @@ void FancyZones::WindowCreated(HWND window) noexcept } bool movedToAppLastZone = false; - if (moveToAppLastZone) + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { - movedToAppLastZone = MoveToAppLastZone(window, active, primary); + if (moveToAppLastZone) + { + movedToAppLastZone = MoveToAppLastZone(window, nullptr, nullptr); + } + } + else + { + if (moveToAppLastZone) + { + movedToAppLastZone = MoveToAppLastZone(window, active, primary); + } } // Open on active monitor if window wasn't zoned @@ -609,7 +583,6 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa { // Changes in taskbar position resulted in different size of work area. // Invalidate cached work-areas so they can be recreated with latest information. - m_workAreaHandler.Clear(); OnDisplayChange(DisplayChangeType::WorkArea); } } @@ -618,7 +591,6 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa case WM_DISPLAYCHANGE: { // Display resolution changed. Invalidate cached work-areas so they can be recreated with latest information. - m_workAreaHandler.Clear(); OnDisplayChange(DisplayChangeType::DisplayChange); } break; @@ -740,42 +712,38 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept } UpdateWorkAreas(); - RefreshWorkAreaWindows(FancyZonesSettings::settings().displayChange_moveWindows && changeType != DisplayChangeType::VirtualDesktop); } void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id) noexcept { - if (m_workAreaHandler.IsNewWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor)) + wil::unique_cotaskmem_string virtualDesktopIdStr; + if (!SUCCEEDED(StringFromCLSID(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), &virtualDesktopIdStr))) { - wil::unique_cotaskmem_string virtualDesktopIdStr; - if (!SUCCEEDED(StringFromCLSID(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), &virtualDesktopIdStr))) - { - Logger::debug(L"Add new work area on virtual desktop {}", virtualDesktopIdStr.get()); - } + Logger::debug(L"Add new work area on virtual desktop {}", virtualDesktopIdStr.get()); + } - FancyZonesDataTypes::WorkAreaId parentId{}; - auto parentArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetPreviousVirtualDesktopId(), monitor); - if (parentArea) - { - parentId = parentArea->UniqueId(); - } + FancyZonesDataTypes::WorkAreaId parentId{}; + auto parentArea = m_workAreaHandler.GetWorkArea(monitor); + if (parentArea) + { + parentId = parentArea->UniqueId(); + } - FancyZonesUtils::Rect rect{}; - if (monitor) - { - rect = MonitorUtils::GetWorkAreaRect(monitor); - } - else - { - rect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>(); - } + FancyZonesUtils::Rect rect{}; + if (monitor) + { + rect = MonitorUtils::GetWorkAreaRect(monitor); + } + else + { + rect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>(); + } - auto workArea = MakeWorkArea(m_hinstance, id, parentId, rect); - if (workArea) - { - m_workAreaHandler.AddWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor, workArea); - AppliedLayouts::instance().SaveData(); - } + auto workArea = WorkArea::Create(m_hinstance, id, parentId, rect); + if (workArea) + { + m_workAreaHandler.AddWorkArea(monitor, std::move(workArea)); + AppliedLayouts::instance().SaveData(); } } @@ -795,6 +763,8 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, void FancyZones::UpdateWorkAreas() noexcept { + m_workAreaHandler.Clear(); + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { FancyZonesDataTypes::WorkAreaId workAreaId; @@ -822,7 +792,7 @@ void FancyZones::CycleWindows(bool reverse) noexcept auto window = GetForegroundWindow(); HMONITOR current = WorkAreaKeyFromWindow(window); - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current); + auto workArea = m_workAreaHandler.GetWorkArea(current); if (workArea) { workArea->CycleWindows(window, reverse); @@ -840,13 +810,13 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current); do { - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), *currMonitorInfo); + auto workArea = m_workAreaHandler.GetWorkArea(*currMonitorInfo); if (workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */)) { // unassign from previous work area - for (auto& prevWorkArea : m_workAreaHandler.GetAllWorkAreas()) + for (auto& [_, prevWorkArea] : m_workAreaHandler.GetAllWorkAreas()) { - if (workArea != prevWorkArea) + if (prevWorkArea && workArea != prevWorkArea.get()) { prevWorkArea->UnsnapWindow(window); } @@ -876,7 +846,7 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce } else { - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current); + auto workArea = m_workAreaHandler.GetWorkArea(current); // Single monitor environment, or combined multi-monitor environment. if (FancyZonesSettings::settings().restoreSize) { @@ -918,7 +888,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept { // Multi monitor environment. // First, try to stay on the same monitor - bool success = ProcessDirectedSnapHotkey(window, vkCode, false, m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current)); + bool success = ProcessDirectedSnapHotkey(window, vkCode, false, m_workAreaHandler.GetWorkArea(current)); if (success) { return true; @@ -937,7 +907,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept } else { - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor); + auto workArea = m_workAreaHandler.GetWorkArea(monitor); if (workArea) { const auto& layout = workArea->GetLayout(); @@ -977,9 +947,9 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept if (workArea) { workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }); + Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); } - Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); return true; } @@ -989,7 +959,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept // Sanity check: the current monitor is valid if (currentMonitorRect.top <= currentMonitorRect.bottom) { - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current); + auto workArea = m_workAreaHandler.GetWorkArea(current); if (workArea) { const auto& layout = workArea->GetLayout(); @@ -1027,9 +997,9 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept if (workArea) { workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }); + Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); } - - Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); + return true; } else @@ -1041,7 +1011,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept else { // Single monitor environment, or combined multi-monitor environment. - return ProcessDirectedSnapHotkey(window, vkCode, true, m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current)); + return ProcessDirectedSnapHotkey(window, vkCode, true, m_workAreaHandler.GetWorkArea(current)); } } @@ -1057,7 +1027,7 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept return (vkCode == VK_LEFT || vkCode == VK_RIGHT) && OnSnapHotkeyBasedOnZoneNumber(window, vkCode); } -bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept +bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept { // Check whether Alt is used in the shortcut key combination if (GetAsyncKeyState(VK_MENU) & 0x8000) @@ -1085,7 +1055,6 @@ void FancyZones::RegisterVirtualDesktopUpdates() noexcept auto guids = VirtualDesktop::instance().GetVirtualDesktopIdsFromRegistry(); if (guids.has_value()) { - m_workAreaHandler.RegisterUpdates(*guids); AppZoneHistory::instance().RemoveDeletedVirtualDesktops(*guids); AppliedLayouts::instance().RemoveDeletedVirtualDesktops(*guids); } @@ -1156,12 +1125,13 @@ void FancyZones::OnEditorExitEvent() noexcept void FancyZones::UpdateZoneSets() noexcept { - for (auto workArea : m_workAreaHandler.GetAllWorkAreas()) + for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) { - workArea->UpdateActiveZoneSet(); + if (workArea) + { + workArea->UpdateActiveZoneSet(); + } } - - RefreshWorkAreaWindows(FancyZonesSettings::settings().zoneSetChange_moveWindows); } bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept @@ -1177,7 +1147,7 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept { HMONITOR monitor = WorkAreaKeyFromWindow(window); - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor); + auto workArea = m_workAreaHandler.GetWorkArea(monitor); if (!workArea) { Logger::error(L"No work area for processing snap hotkey"); @@ -1222,20 +1192,26 @@ void FancyZones::ApplyQuickLayout(int key) noexcept return; } - auto workArea = m_workAreaHandler.GetWorkAreaFromCursor(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value()); - AppliedLayouts::instance().SaveData(); - UpdateZoneSets(); - FlashZones(); + auto workArea = m_workAreaHandler.GetWorkAreaFromCursor(); + if (workArea) + { + AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value()); + AppliedLayouts::instance().SaveData(); + UpdateZoneSets(); + FlashZones(); + } } void FancyZones::FlashZones() noexcept { if (FancyZonesSettings::settings().flashZonesOnQuickSwitch && !m_draggingState.IsDragging()) { - for (auto [monitor, workArea] : m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())) + for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) { - workArea->FlashZones(); + if (workArea) + { + workArea->FlashZones(); + } } } } @@ -1252,10 +1228,10 @@ std::vector FancyZones::GetMonitorsSorted() noexcept std::vector> FancyZones::GetRawMonitorData() noexcept { std::vector> monitorInfo; - const auto& activeWorkAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); + const auto& activeWorkAreaMap = m_workAreaHandler.GetAllWorkAreas(); for (const auto& [monitor, workArea] : activeWorkAreaMap) { - if (workArea->GetLayout() != nullptr) + if (workArea && workArea->GetLayout() != nullptr) { MONITORINFOEX mi; mi.cbSize = sizeof(mi); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index 7209e35a19..2fc91a6f5e 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -59,7 +59,7 @@ - + @@ -113,7 +113,7 @@ - + Create diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index e3e21c934a..76bf858a57 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -54,7 +54,7 @@ Header Files - + Header Files @@ -197,7 +197,7 @@ Source Files - + Source Files diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp deleted file mode 100644 index ec97221794..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "pch.h" -#include "MonitorWorkAreaHandler.h" -#include "VirtualDesktop.h" -#include "WorkArea.h" -#include "util.h" - -#include - -std::shared_ptr MonitorWorkAreaHandler::GetWorkArea(const GUID& desktopId, HMONITOR monitor) -{ - auto desktopIt = workAreaMap.find(desktopId); - if (desktopIt != workAreaMap.end()) - { - auto& perDesktopData = desktopIt->second; - auto monitorIt = perDesktopData.find(monitor); - if (monitorIt != std::end(perDesktopData)) - { - return monitorIt->second; - } - } - return nullptr; -} - -std::shared_ptr MonitorWorkAreaHandler::GetWorkAreaFromCursor(const GUID& desktopId) -{ - auto allMonitorsWorkArea = GetWorkArea(desktopId, NULL); - if (allMonitorsWorkArea) - { - // First, check if there's a work area spanning all monitors (signalled by the NULL monitor handle) - return allMonitorsWorkArea; - } - else - { - // Otherwise, look for the work area based on cursor position - POINT cursorPoint; - if (!GetCursorPos(&cursorPoint)) - { - return nullptr; - } - - return GetWorkArea(desktopId, MonitorFromPoint(cursorPoint, MONITOR_DEFAULTTONULL)); - } -} - -std::shared_ptr MonitorWorkAreaHandler::GetWorkArea(HWND window, const GUID& desktopId) -{ - auto allMonitorsWorkArea = GetWorkArea(desktopId, NULL); - if (allMonitorsWorkArea) - { - // First, check if there's a work area spanning all monitors (signalled by the NULL monitor handle) - return allMonitorsWorkArea; - } - else - { - // Otherwise, look for the work area based on the window's position - HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - return GetWorkArea(desktopId, monitor); - } -} - -const std::unordered_map>& MonitorWorkAreaHandler::GetWorkAreasByDesktopId(const GUID& desktopId) -{ - if (workAreaMap.contains(desktopId)) - { - return workAreaMap[desktopId]; - } - - static const std::unordered_map> empty{}; - return empty; -} - -std::vector> MonitorWorkAreaHandler::GetAllWorkAreas() -{ - std::vector> workAreas{}; - for (const auto& [desktopId, perDesktopData] : workAreaMap) - { - std::transform(std::begin(perDesktopData), - std::end(perDesktopData), - std::back_inserter(workAreas), - [](const auto& item) { return item.second; }); - } - return workAreas; -} - -void MonitorWorkAreaHandler::AddWorkArea(const GUID& desktopId, HMONITOR monitor, std::shared_ptr& workArea) -{ - if (!workAreaMap.contains(desktopId)) - { - workAreaMap[desktopId] = {}; - - auto desktopIdStr = FancyZonesUtils::GuidToString(desktopId); - if (desktopIdStr) - { - Logger::info(L"Add work area on the desktop {}", desktopIdStr.value()); - } - } - auto& perDesktopData = workAreaMap[desktopId]; - perDesktopData[monitor] = std::move(workArea); -} - -bool MonitorWorkAreaHandler::IsNewWorkArea(const GUID& desktopId, HMONITOR monitor) -{ - if (workAreaMap.contains(desktopId)) - { - const auto& perDesktopData = workAreaMap[desktopId]; - if (perDesktopData.contains(monitor)) - { - return false; - } - } - return true; -} - -void MonitorWorkAreaHandler::RegisterUpdates(const std::vector& active) -{ - std::unordered_set activeVirtualDesktops(std::begin(active), std::end(active)); - for (auto desktopIt = std::begin(workAreaMap); desktopIt != std::end(workAreaMap);) - { - auto activeIt = activeVirtualDesktops.find(desktopIt->first); - if (activeIt == std::end(activeVirtualDesktops)) - { - // virtual desktop deleted, remove entry from the map - desktopIt = workAreaMap.erase(desktopIt); - } - else - { - activeVirtualDesktops.erase(desktopIt->first); // virtual desktop already in map, skip it - ++desktopIt; - } - } - // register new virtual desktops, if any - for (const auto& id : activeVirtualDesktops) - { - workAreaMap[id] = {}; - } -} - -void MonitorWorkAreaHandler::Clear() -{ - workAreaMap.clear(); -} diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h deleted file mode 100644 index 191b82d7b3..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include "GuidUtils.h" - -class WorkArea; -struct ZoneColors; -enum struct OverlappingZonesAlgorithm; - -class MonitorWorkAreaHandler -{ -public: - /** - * Get work area based on virtual desktop id and monitor handle. - * - * @param[in] desktopId Virtual desktop identifier. - * @param[in] monitor Monitor handle. - * - * @returns Object representing single work area, interface to all actions available on work area - * (e.g. moving windows through zone layout specified for that work area). - */ - std::shared_ptr GetWorkArea(const GUID& desktopId, HMONITOR monitor); - - /** - * Get work area based on virtual desktop id and the current cursor position. - * - * @param[in] desktopId Virtual desktop identifier. - * - * @returns Object representing single work area, interface to all actions available on work area - * (e.g. moving windows through zone layout specified for that work area). - */ - std::shared_ptr GetWorkAreaFromCursor(const GUID& desktopId); - - /** - * Get work area on which specified window is located. - * - * @param[in] window Window handle. - * @param[in] desktopId GUID current desktop id - * - * @returns Object representing single work area, interface to all actions available on work area - * (e.g. moving windows through zone layout specified for that work area). - */ - std::shared_ptr GetWorkArea(HWND window, const GUID& desktopId); - - /** - * Get map of all work areas on single virtual desktop. Key in the map is monitor handle, while value - * represents single work area. - * - * @param[in] desktopId Virtual desktop identifier. - * - * @returns Map containing pairs of monitor and work area for that monitor (within same virtual desktop). - */ - const std::unordered_map>& GetWorkAreasByDesktopId(const GUID& desktopId); - - /** - * @returns All registered work areas. - */ - std::vector> GetAllWorkAreas(); - - /** - * Register new work area. - * - * @param[in] desktopId Virtual desktop identifier. - * @param[in] monitor Monitor handle. - * @param[in] workAra Object representing single work area. - */ - void AddWorkArea(const GUID& desktopId, HMONITOR monitor, std::shared_ptr& workArea); - - /** - * Check if work area is already registered. - * - * @param[in] desktopId Virtual desktop identifier. - * @param[in] monitor Monitor handle. - * - * @returns Boolean indicating whether work area defined by virtual desktop id and monitor is already registered. - */ - bool IsNewWorkArea(const GUID& desktopId, HMONITOR monitor); - - /** - * Register changes in current virtual desktop layout. - * - * @param[in] active Array of currently active virtual desktop identifiers. - */ - void RegisterUpdates(const std::vector& active); - - /** - * Clear all persisted work area related data. - */ - void Clear(); - -private: - // Work area is uniquely defined by monitor and virtual desktop id. - std::unordered_map>> workAreaMap; -}; diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp new file mode 100644 index 0000000000..089c28c905 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp @@ -0,0 +1,67 @@ +#include "pch.h" +#include "MonitorWorkAreaMap.h" + +#include + +WorkArea* const MonitorWorkAreaMap::GetWorkArea(HMONITOR monitor) const +{ + auto iter = m_workAreaMap.find(monitor); + if (iter != m_workAreaMap.end()) + { + return iter->second.get(); + } + + return nullptr; +} + +WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromCursor() const +{ + const auto allMonitorsWorkArea = GetWorkArea(nullptr); + if (allMonitorsWorkArea) + { + // First, check if there's a work area spanning all monitors (signalled by the NULL monitor handle) + return allMonitorsWorkArea; + } + else + { + // Otherwise, look for the work area based on cursor position + POINT cursorPoint; + if (!GetCursorPos(&cursorPoint)) + { + return nullptr; + } + + return GetWorkArea(MonitorFromPoint(cursorPoint, MONITOR_DEFAULTTONULL)); + } +} + +WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromWindow(HWND window) const +{ + const auto allMonitorsWorkArea = GetWorkArea(nullptr); + if (allMonitorsWorkArea) + { + // First, check if there's a work area spanning all monitors (signalled by the NULL monitor handle) + return allMonitorsWorkArea; + } + else + { + // Otherwise, look for the work area based on the window's position + HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + return GetWorkArea(monitor); + } +} + +const std::unordered_map>& MonitorWorkAreaMap::GetAllWorkAreas() const noexcept +{ + return m_workAreaMap; +} + +void MonitorWorkAreaMap::AddWorkArea(HMONITOR monitor, std::unique_ptr workArea) +{ + m_workAreaMap.insert({ monitor, std::move(workArea) }); +} + +void MonitorWorkAreaMap::Clear() noexcept +{ + m_workAreaMap.clear(); +} diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h new file mode 100644 index 0000000000..10cb2dd0d6 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h @@ -0,0 +1,58 @@ +#pragma once + +#include "GuidUtils.h" + +class WorkArea; + +class MonitorWorkAreaMap +{ +public: + /** + * Get work area based on virtual desktop id and monitor handle. + * + * @param[in] monitor Monitor handle. + * + * @returns Object representing single work area, interface to all actions available on work area + * (e.g. moving windows through zone layout specified for that work area). + */ + WorkArea* const GetWorkArea(HMONITOR monitor) const; + + /** + * Get work area based on virtual desktop id and the current cursor position. + * + * @returns Object representing single work area, interface to all actions available on work area + * (e.g. moving windows through zone layout specified for that work area). + */ + WorkArea* const GetWorkAreaFromCursor() const; + + /** + * Get work area on which specified window is located. + * + * @param[in] window Window handle. + * + * @returns Object representing single work area, interface to all actions available on work area + * (e.g. moving windows through zone layout specified for that work area). + */ + WorkArea* const GetWorkAreaFromWindow(HWND window) const; + + /** + * @returns All registered work areas. + */ + const std::unordered_map>& GetAllWorkAreas() const noexcept; + + /** + * Register new work area. + * + * @param[in] monitor Monitor handle. + * @param[in] workAra Object representing single work area. + */ + void AddWorkArea(HMONITOR monitor, std::unique_ptr workArea); + + /** + * Clear all persisted work area related data. + */ + void Clear() noexcept; + +private: + std::unordered_map> m_workAreaMap; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp index fe69251c96..c5c18d9fae 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp @@ -13,9 +13,10 @@ #include -WindowDrag::WindowDrag(HWND window, const std::unordered_map>& activeWorkAreas) : +WindowDrag::WindowDrag(HWND window, const std::unordered_map>& activeWorkAreas) : m_window(window), m_activeWorkAreas(activeWorkAreas), + m_currentWorkArea(nullptr), m_snappingMode(false) { m_windowProperties.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window); @@ -28,7 +29,7 @@ WindowDrag::~WindowDrag() ResetWindowTransparency(); } -std::unique_ptr WindowDrag::Create(HWND window, const std::unordered_map>& activeWorkAreas) +std::unique_ptr WindowDrag::Create(HWND window, const std::unordered_map>& activeWorkAreas) { if (!FancyZonesWindowProcessing::IsProcessable(window) || !FancyZonesWindowUtils::IsCandidateForZoning(window) || @@ -57,7 +58,7 @@ bool WindowDrag::MoveSizeStart(HMONITOR monitor, bool isSnapping) if (isSnapping) { - m_currentWorkArea = iter->second; + m_currentWorkArea = iter->second.get(); } SwitchSnappingMode(isSnapping); @@ -72,7 +73,7 @@ void WindowDrag::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool is { // The drag has moved to a different monitor. // Change work area - if (iter->second != m_currentWorkArea) + if (iter->second.get() != m_currentWorkArea) { m_highlightedZones.Reset(); @@ -88,7 +89,7 @@ void WindowDrag::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool is } } - m_currentWorkArea = iter->second; + m_currentWorkArea = iter->second.get(); } if (m_currentWorkArea) @@ -169,18 +170,6 @@ void WindowDrag::SwitchSnappingMode(bool isSnapping) if (m_currentWorkArea) { m_currentWorkArea->UnsnapWindow(m_window); - FancyZonesWindowProperties::RemoveZoneIndexProperty(m_window); - - const auto& layout = m_currentWorkArea->GetLayout(); - if (layout) - { - auto guidStr = FancyZonesUtils::GuidToString(layout->Id()); - if (guidStr.has_value()) - { - AppZoneHistory::instance().RemoveAppLastZone(m_window, m_currentWorkArea->UniqueId(), guidStr.value()); - } - } - Trace::WorkArea::MoveOrResizeStarted(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows().get()); } } diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.h b/src/modules/fancyzones/FancyZonesLib/WindowDrag.h index 9e083980f3..f45dc313f8 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowDrag.h +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.h @@ -6,10 +6,10 @@ class WorkArea; class WindowDrag { - WindowDrag(HWND window, const std::unordered_map>& activeWorkAreas); + WindowDrag(HWND window, const std::unordered_map>& activeWorkAreas); public: - static std::unique_ptr Create(HWND window, const std::unordered_map>& activeWorkAreas); + static std::unique_ptr Create(HWND window, const std::unordered_map>& activeWorkAreas); ~WindowDrag(); bool MoveSizeStart(HMONITOR monitor, bool isSnapping); @@ -38,8 +38,8 @@ private: const HWND m_window; WindowProperties m_windowProperties; // MoveSizeWindowInfo of the window at the moment when dragging started - const std::unordered_map>& m_activeWorkAreas; // all WorkAreas on current virtual desktop, mapped with monitors - std::shared_ptr m_currentWorkArea; // "Active" WorkArea, where the move/size is happening. Will update as drag moves between monitors. + const std::unordered_map>& m_activeWorkAreas; // all WorkAreas on current virtual desktop, mapped with monitors + WorkArea* m_currentWorkArea; // "Active" WorkArea, where the move/size is happening. Will update as drag moves between monitors. bool m_snappingMode{ false }; diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index 6efeb71bd6..ab71fbc796 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -127,12 +127,12 @@ WorkArea::~WorkArea() windowPool.FreeZonesOverlayWindow(m_window); } -void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept +void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) { MoveWindowIntoZoneByIndexSet(window, { index }); } -void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition /* = true*/) noexcept +void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition /* = true*/) { if (!m_layout || !m_layoutWindows || m_layout->Zones().empty() || indexSet.empty()) { @@ -143,18 +143,15 @@ void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& ind if (updatePosition) { - auto rect = m_layout->GetCombinedZonesRect(indexSet); - auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); + const auto rect = m_layout->GetCombinedZonesRect(indexSet); + const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); } - m_layoutWindows->Assign(window, indexSet); - FancyZonesWindowProperties::StampZoneIndexProperty(window, indexSet); - - SaveWindowProcessToZoneIndex(window); + SnapWindow(window, indexSet); } -bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) noexcept +bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) { if (!m_layout || !m_layoutWindows || m_layout->Zones().empty()) { @@ -162,7 +159,7 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, } auto zoneIndexes = m_layoutWindows->GetZoneIndexSetFromWindow(window); - auto numZones = m_layout->Zones().size(); + const auto numZones = m_layout->Zones().size(); // The window was not assigned to any zone here if (zoneIndexes.size() == 0) @@ -171,7 +168,7 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, } else { - ZoneIndex oldId = zoneIndexes[0]; + const ZoneIndex oldId = zoneIndexes[0]; // We reached the edge if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == static_cast(numZones) - 1)) @@ -197,15 +194,10 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, } } - if (!FancyZonesWindowUtils::HasVisibleOwner(window)) - { - SaveWindowProcessToZoneIndex(window); - } - return true; } -bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) noexcept +bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) { if (!m_layout || !m_layoutWindows || m_layout->Zones().empty()) { @@ -216,7 +208,7 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCod std::vector usedZoneIndices(zones.size(), false); auto windowZones = m_layoutWindows->GetZoneIndexSetFromWindow(window); - for (ZoneIndex id : windowZones) + for (const ZoneIndex id : windowZones) { usedZoneIndices[id] = true; } @@ -250,7 +242,6 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCod if (result < zoneRects.size()) { MoveWindowIntoZoneByIndex(window, freeZoneIndices[result]); - SaveWindowProcessToZoneIndex(window); Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get()); return true; } @@ -266,7 +257,6 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCod if (result < zoneRects.size()) { MoveWindowIntoZoneByIndex(window, result); - SaveWindowProcessToZoneIndex(window); Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get()); return true; } @@ -275,7 +265,7 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCod return false; } -bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexcept +bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) { if (!m_layout || !m_layoutWindows || m_layout->Zones().empty()) { @@ -307,7 +297,7 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noe } else { - for (ZoneIndex idx : appliedZones) + for (const ZoneIndex idx : appliedZones) { usedZoneIndices[idx] = true; } @@ -327,7 +317,7 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noe } } - auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects); + const auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects); if (result < zoneRects.size()) { ZoneIndex targetZone = freeZoneIndices[result]; @@ -357,14 +347,13 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noe resultIndexSet = m_layout->GetCombinedZoneRange(extendModeData->windowInitialIndexSet[window], { targetZone }); } - auto rect = m_layout->GetCombinedZonesRect(resultIndexSet); - auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); + const auto rect = m_layout->GetCombinedZonesRect(resultIndexSet); + const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); m_layoutWindows->Extend(window, resultIndexSet); - FancyZonesWindowProperties::StampZoneIndexProperty(window, resultIndexSet); - SaveWindowProcessToZoneIndex(window); + SnapWindow(window, resultIndexSet); return true; } @@ -372,39 +361,43 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noe return false; } -void WorkArea::SaveWindowProcessToZoneIndex(HWND window) noexcept +void WorkArea::SnapWindow(HWND window, const ZoneIndexSet& zones) { - if (m_layout && m_layoutWindows) + if (!m_layoutWindows || !m_layout) { - auto zoneIndexSet = m_layoutWindows->GetZoneIndexSetFromWindow(window); - if (zoneIndexSet.size()) - { - auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id()); - if (guidStr.has_value()) - { - AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidStr.value(), zoneIndexSet); - } - } + return; } + + m_layoutWindows->Assign(window, zones); + + auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id()); + if (guidStr.has_value()) + { + AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidStr.value(), zones); + } + + FancyZonesWindowProperties::StampZoneIndexProperty(window, zones); } -bool WorkArea::UnsnapWindow(HWND window) noexcept +void WorkArea::UnsnapWindow(HWND window) { - if (!m_layoutWindows) + if (!m_layoutWindows || !m_layout) { - return false; + return; } - if (!m_layoutWindows->GetZoneIndexSetFromWindow(window).empty()) + m_layoutWindows->Dismiss(window); + + auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id()); + if (guidStr.has_value()) { - m_layoutWindows->Dismiss(window); - return true; + AppZoneHistory::instance().RemoveAppLastZone(window, m_uniqueId, guidStr.value()); } - return false; + FancyZonesWindowProperties::RemoveZoneIndexProperty(window); } -ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const noexcept +ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const { if (m_layout) { @@ -454,9 +447,9 @@ void WorkArea::FlashZones() } } -void WorkArea::UpdateActiveZoneSet() noexcept +void WorkArea::UpdateActiveZoneSet() { - bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); + const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); if (!isLayoutAlreadyApplied) { AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId); @@ -469,7 +462,7 @@ void WorkArea::UpdateActiveZoneSet() noexcept } } -void WorkArea::CycleWindows(HWND window, bool reverse) noexcept +void WorkArea::CycleWindows(HWND window, bool reverse) { if (m_layoutWindows) { @@ -479,7 +472,7 @@ void WorkArea::CycleWindows(HWND window, bool reverse) noexcept #pragma region private -bool WorkArea::InitWindow(HINSTANCE hinstance) noexcept +bool WorkArea::InitWindow(HINSTANCE hinstance) { m_window = windowPool.NewZonesOverlayWindow(m_workAreaRect, hinstance, this); if (!m_window) @@ -492,11 +485,11 @@ bool WorkArea::InitWindow(HINSTANCE hinstance) noexcept return true; } -void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept +void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) { - Logger::info(L"Initialize layout on {}", m_uniqueId.toString()); + Logger::info(L"Initialize layout on {}, work area rect = {}x{}", m_uniqueId.toString(), m_workAreaRect.width(), m_workAreaRect.height()); - bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); + const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); if (!isLayoutAlreadyApplied) { if (parentUniqueId.virtualDesktopId != GUID_NULL) @@ -512,7 +505,37 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) CalculateZoneSet(); } -void WorkArea::CalculateZoneSet() noexcept +void WorkArea::InitSnappedWindows() +{ + static bool updatePositionOnceOnStartFlag = true; + Logger::info(L"Init work area windows, update positions = {}", updatePositionOnceOnStartFlag); + + for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop()) + { + auto zoneIndexSet = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); + if (zoneIndexSet.size() == 0) + { + continue; + } + + if (!m_uniqueId.monitorId.monitor) // one work area across monitors + { + MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, updatePositionOnceOnStartFlag); + } + else + { + const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + if (monitor && m_uniqueId.monitorId.monitor == monitor) + { + MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, updatePositionOnceOnStartFlag); + } + } + } + + updatePositionOnceOnStartFlag = false; +} + +void WorkArea::CalculateZoneSet() { const auto appliedLayout = AppliedLayouts::instance().GetDeviceLayout(m_uniqueId); if (!appliedLayout.has_value()) @@ -561,7 +584,7 @@ void WorkArea::SetWorkAreaWindowAsTopmost(HWND draggedWindow) noexcept HWND windowInsertAfter = draggedWindow ? draggedWindow : HWND_TOPMOST; - const UINT flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE; + constexpr UINT flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE; SetWindowPos(m_window, windowInsertAfter, 0, 0, 0, 0, flags); } diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.h b/src/modules/fancyzones/FancyZonesLib/WorkArea.h index 5c6ddcb276..a273b31675 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.h +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.h @@ -3,17 +3,27 @@ #include #include #include -#include class ZonesOverlay; class WorkArea { -public: WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesUtils::Rect& workAreaRect); - ~WorkArea(); public: + ~WorkArea(); + + static std::unique_ptr Create(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesDataTypes::WorkAreaId& parentUniqueId, const FancyZonesUtils::Rect& workAreaRect) + { + auto self = std::unique_ptr(new WorkArea(hinstance, uniqueId, workAreaRect)); + if (!self->Init(hinstance, parentUniqueId)) + { + return nullptr; + } + + return self; + } + inline bool Init([[maybe_unused]] HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& parentUniqueId) { #ifndef UNIT_TESTS @@ -23,42 +33,48 @@ public: } #endif InitLayout(parentUniqueId); + InitSnappedWindows(); + return true; } - + FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; } const std::unique_ptr& GetLayout() const noexcept { return m_layout; } const std::unique_ptr& GetLayoutWindows() const noexcept { return m_layoutWindows; } const HWND GetWorkAreaWindow() const noexcept { return m_window; } - ZoneIndexSet GetWindowZoneIndexes(HWND window) const noexcept; + ZoneIndexSet GetWindowZoneIndexes(HWND window) const; - void MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept; - void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition = true) noexcept; - bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) noexcept; - bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) noexcept; - bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexcept; - void SaveWindowProcessToZoneIndex(HWND window) noexcept; - bool UnsnapWindow(HWND window) noexcept; + void MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index); + void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition = true); + bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle); + bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle); + bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode); - void UpdateActiveZoneSet() noexcept; + void SnapWindow(HWND window, const ZoneIndexSet& zones); + void UnsnapWindow(HWND window); + + void UpdateActiveZoneSet(); void ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr); void HideZonesOverlay(); void FlashZones(); - void CycleWindows(HWND window, bool reverse) noexcept; + void CycleWindows(HWND window, bool reverse); protected: static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept; private: - bool InitWindow(HINSTANCE hinstance) noexcept; - void InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept; - void CalculateZoneSet() noexcept; - LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept; + bool InitWindow(HINSTANCE hinstance); + void InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId); + void InitSnappedWindows(); + + void CalculateZoneSet(); void SetWorkAreaWindowAsTopmost(HWND draggedWindow) noexcept; + LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept; + const FancyZonesUtils::Rect m_workAreaRect{}; const FancyZonesDataTypes::WorkAreaId m_uniqueId; HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area. @@ -66,14 +82,3 @@ private: std::unique_ptr m_layoutWindows; std::unique_ptr m_zonesOverlay; }; - -inline std::shared_ptr MakeWorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesDataTypes::WorkAreaId& parentUniqueId, const FancyZonesUtils::Rect& workAreaRect) -{ - auto self = std::make_shared(hinstance, uniqueId, workAreaRect); - if (!self->Init(hinstance, parentUniqueId)) - { - return nullptr; - } - - return self; -} diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp index 242535f26d..bbfd3cb159 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "Util.h" @@ -20,7 +21,7 @@ namespace FancyZonesUnitTests TEST_CLASS (WorkAreaCreationUnitTests) { - FancyZonesDataTypes::WorkAreaId m_uniqueId; + FancyZonesDataTypes::WorkAreaId m_workAreaId; FancyZonesDataTypes::WorkAreaId m_emptyUniqueId; FancyZonesUtils::Rect m_workAreaRect{ RECT(0,0,1920,1080) }; @@ -29,10 +30,10 @@ namespace FancyZonesUnitTests TEST_METHOD_INITIALIZE(Init) noexcept { - m_uniqueId.monitorId.deviceId.id = L"DELA026"; - m_uniqueId.monitorId.deviceId.instanceId = L"5&10a58c63&0&UID16777488"; - m_uniqueId.monitorId.serialNumber = L"serial-number"; - m_uniqueId.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(); + m_workAreaId.monitorId.deviceId.id = L"DELA026"; + m_workAreaId.monitorId.deviceId.instanceId = L"5&10a58c63&0&UID16777488"; + m_workAreaId.monitorId.serialNumber = L"serial-number"; + m_workAreaId.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(); AppZoneHistory::instance().LoadData(); AppliedLayouts::instance().LoadData(); @@ -51,9 +52,9 @@ namespace FancyZonesUnitTests { const auto defaultLayout = DefaultLayouts::instance().GetDefaultLayout(); - auto workArea = MakeWorkArea({}, m_uniqueId, m_emptyUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create({}, m_workAreaId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); - Assert::IsTrue(m_uniqueId == workArea->UniqueId()); + Assert::IsTrue(m_workAreaId == workArea->UniqueId()); const auto& layout = workArea->GetLayout(); Assert::IsNotNull(layout.get()); @@ -66,9 +67,9 @@ namespace FancyZonesUnitTests { const auto defaultLayout = DefaultLayouts::instance().GetDefaultLayout(); - auto workArea = MakeWorkArea({}, m_uniqueId, m_emptyUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create({}, m_workAreaId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); - Assert::IsTrue(m_uniqueId == workArea->UniqueId()); + Assert::IsTrue(m_workAreaId == workArea->UniqueId()); const auto& layout = workArea->GetLayout(); Assert::IsNotNull(layout.get()); @@ -96,15 +97,15 @@ namespace FancyZonesUnitTests .sensitivityRadius = 20, }; - auto parentWorkArea = MakeWorkArea(m_hInst, parentUniqueId, m_emptyUniqueId, m_workAreaRect); + auto parentWorkArea = WorkArea::Create(m_hInst, parentUniqueId, m_emptyUniqueId, m_workAreaRect); AppliedLayouts::instance().ApplyLayout(parentUniqueId, layout); - auto actualWorkArea = MakeWorkArea(m_hInst, m_uniqueId, parentUniqueId, m_workAreaRect); + auto actualWorkArea = WorkArea::Create(m_hInst, m_workAreaId, parentUniqueId, m_workAreaRect); Assert::IsNotNull(actualWorkArea->GetLayout().get()); Assert::IsNotNull(actualWorkArea->GetLayoutWindows().get()); - Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(m_uniqueId)); - const auto& actualLayout = AppliedLayouts::instance().GetAppliedLayoutMap().at(m_uniqueId); + Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(m_workAreaId)); + const auto& actualLayout = AppliedLayouts::instance().GetAppliedLayoutMap().at(m_workAreaId); Assert::AreEqual(static_cast(layout.type), static_cast(actualLayout.type)); Assert::AreEqual(FancyZonesUtils::GuidToString(layout.uuid).value(), FancyZonesUtils::GuidToString(actualLayout.uuid).value()); @@ -132,9 +133,9 @@ namespace FancyZonesUnitTests DefaultLayouts::instance().LoadData(); // test - auto workArea = MakeWorkArea({}, m_uniqueId, m_emptyUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create({}, m_workAreaId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); - Assert::IsTrue(m_uniqueId == workArea->UniqueId()); + Assert::IsTrue(m_workAreaId == workArea->UniqueId()); Assert::IsNotNull(workArea->GetLayout().get()); @@ -165,9 +166,9 @@ namespace FancyZonesUnitTests DefaultLayouts::instance().LoadData(); // test - auto workArea = MakeWorkArea({}, m_uniqueId, m_emptyUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create({}, m_workAreaId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); - Assert::IsTrue(m_uniqueId == workArea->UniqueId()); + Assert::IsTrue(m_workAreaId == workArea->UniqueId()); Assert::IsNotNull(workArea->GetLayout().get()); @@ -178,134 +179,10 @@ namespace FancyZonesUnitTests } }; - TEST_CLASS (WorkAreaUnitTests) - { - FancyZonesDataTypes::WorkAreaId m_uniqueId; - FancyZonesDataTypes::WorkAreaId m_parentUniqueId; // default empty - FancyZonesUtils::Rect m_workAreaRect{ RECT(0, 0, 1920, 1080) }; - - HINSTANCE m_hInst{}; - - TEST_METHOD_INITIALIZE(Init) noexcept - { - m_uniqueId.monitorId.deviceId.id = L"DELA026"; - m_uniqueId.monitorId.deviceId.instanceId = L"5&10a58c63&0&UID16777488"; - m_uniqueId.monitorId.serialNumber = L"serial-number"; - m_uniqueId.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(); - - AppZoneHistory::instance().LoadData(); - AppliedLayouts::instance().LoadData(); - } - - TEST_METHOD_CLEANUP(CleanUp) noexcept - { - std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); - std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); - } - - public: - TEST_METHOD (SaveWindowProcessToZoneIndexNullptrWindow) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - workArea->SaveWindowProcessToZoneIndex(nullptr); - - const auto actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory(); - Assert::IsTrue(actualAppZoneHistory.empty()); - } - - TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAdded) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - auto window = Mocks::WindowCreate(m_hInst); - workArea->GetLayout()->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); - - workArea->SaveWindowProcessToZoneIndex(window); - - const auto actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory(); - Assert::IsTrue(actualAppZoneHistory.empty()); - } - - TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAddedWithFilledAppZoneHistory) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - const auto window = Mocks::WindowCreate(m_hInst); - const auto processPath = get_process_path(window); - const auto deviceId = workArea->UniqueId(); - const auto& layoutId = workArea->GetLayout()->Id(); - - // fill app zone history map - Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, deviceId, Helpers::GuidToString(layoutId), { 0 })); - Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); - const auto& appHistoryArray1 = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); - Assert::AreEqual((size_t)1, appHistoryArray1.size()); - Assert::IsTrue(std::vector{ 0 } == appHistoryArray1.at(0).zoneIndexSet); - - // add zone without window - workArea->GetLayout()->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); - - workArea->SaveWindowProcessToZoneIndex(window); - Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); - const auto& appHistoryArray2 = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); - Assert::AreEqual((size_t)1, appHistoryArray2.size()); - Assert::IsTrue(std::vector{ 0 } == appHistoryArray2.at(0).zoneIndexSet); - } - - TEST_METHOD (SaveWindowProcessToZoneIndexWindowAdded) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - auto window = Mocks::WindowCreate(m_hInst); - const auto processPath = get_process_path(window); - const auto deviceId = workArea->UniqueId(); - const auto& layoutId = workArea->GetLayout()->Id(); - - workArea->MoveWindowIntoZoneByIndex(window, 0); - - //fill app zone history map - Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, deviceId, Helpers::GuidToString(layoutId), { 2 })); - Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); - const auto& appHistoryArray = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); - Assert::AreEqual((size_t)1, appHistoryArray.size()); - Assert::IsTrue(std::vector{ 2 } == appHistoryArray.at(0).zoneIndexSet); - - workArea->SaveWindowProcessToZoneIndex(window); - - const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory(); - Assert::AreEqual((size_t)1, actualAppZoneHistory.size()); - const auto& expected = workArea->GetLayoutWindows()->GetZoneIndexSetFromWindow(window); - const auto& actual = appHistoryArray.at(0).zoneIndexSet; - Assert::IsTrue(expected == actual); - } - - TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt) - { - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); - - auto window = Mocks::WindowCreate(m_hInst); - - const int originalWidth = 450; - const int originalHeight = 550; - - SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW); - SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX); - - workArea->GetLayout()->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true); - - RECT inZoneRect; - GetWindowRect(window, &inZoneRect); - Assert::AreEqual(originalWidth, (int)inZoneRect.right - (int)inZoneRect.left); - Assert::AreEqual(originalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top); - } - }; - TEST_CLASS (WorkAreaMoveWindowUnitTests) { const std::wstring m_virtualDesktopIdStr = L"{A998CA86-F08D-4BCA-AED8-77F5C8FC9925}"; - const FancyZonesDataTypes::WorkAreaId m_uniqueId{ + const FancyZonesDataTypes::WorkAreaId m_workAreaId{ .monitorId = { .monitor = Mocks::Monitor(), .deviceId = { @@ -338,10 +215,10 @@ namespace FancyZonesUnitTests layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(0)); json::JsonObject workAreaId{}; - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_uniqueId.monitorId.deviceId.id)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_uniqueId.monitorId.deviceId.instanceId)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_uniqueId.monitorId.serialNumber)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_uniqueId.monitorId.deviceId.number)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_workAreaId.monitorId.deviceId.id)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_workAreaId.monitorId.deviceId.instanceId)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_workAreaId.monitorId.serialNumber)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_workAreaId.monitorId.deviceId.number)); workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(m_virtualDesktopIdStr)); json::JsonObject obj{}; @@ -372,10 +249,10 @@ namespace FancyZonesUnitTests layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(20)); json::JsonObject workAreaId{}; - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_uniqueId.monitorId.deviceId.id)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_uniqueId.monitorId.deviceId.instanceId)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_uniqueId.monitorId.serialNumber)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_uniqueId.monitorId.deviceId.number)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_workAreaId.monitorId.deviceId.id)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_workAreaId.monitorId.deviceId.instanceId)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_workAreaId.monitorId.serialNumber)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_workAreaId.monitorId.deviceId.number)); workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(m_virtualDesktopIdStr)); json::JsonObject obj{}; @@ -407,7 +284,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -424,7 +301,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -441,7 +318,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -458,7 +335,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -475,7 +352,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); const auto& layoutWindows = workArea->GetLayoutWindows(); @@ -494,7 +371,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -512,7 +389,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -530,7 +407,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -547,7 +424,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -564,7 +441,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -581,7 +458,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -599,7 +476,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -617,7 +494,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -635,7 +512,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -653,7 +530,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); const auto& layoutWindows = workArea->GetLayoutWindows(); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -672,7 +549,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -690,7 +567,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -708,7 +585,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId, m_parentUniqueId, m_workAreaRect); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -721,5 +598,86 @@ namespace FancyZonesUnitTests const auto& layoutWindows = workArea->GetLayoutWindows(); Assert::IsTrue(ZoneIndexSet{ 0, 2 } == layoutWindows->GetZoneIndexSetFromWindow(window)); } + + TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + constexpr int originalWidth = 450; + constexpr int originalHeight = 550; + + SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW); + SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX); + + workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true); + + RECT inZoneRect; + GetWindowRect(window, &inZoneRect); + + Assert::AreEqual(originalWidth, (int)inZoneRect.right - (int)inZoneRect.left); + Assert::AreEqual(originalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top); + } + + TEST_METHOD (SnapWindowPropertyTest) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + const ZoneIndexSet expected = { 1, 2 }; + workArea->SnapWindow(window, expected); + + const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); + Assert::AreEqual(expected.size(), actual.size()); + for (int i = 0; i < expected.size(); i++) + { + Assert::AreEqual(expected.at(i), actual.at(i)); + } + } + + TEST_METHOD (SnapAppZoneHistoryTest) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + const ZoneIndexSet expected = { 1, 2 }; + workArea->SnapWindow(window, expected); + + const auto processPath = get_process_path(window); + const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId); + + Assert::IsTrue(history.has_value()); + Assert::AreEqual(expected.size(), history->zoneIndexSet.size()); + for (int i = 0; i < expected.size(); i++) + { + Assert::AreEqual(expected.at(i), history->zoneIndexSet.at(i)); + } + } + + TEST_METHOD (UnsnapPropertyTest) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + workArea->SnapWindow(window, { 1, 2 }); + workArea->UnsnapWindow(window); + + const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); + Assert::IsTrue(actual.empty()); + } + + TEST_METHOD (UnsnapAppZoneHistoryTest) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + workArea->SnapWindow(window, { 1, 2 }); + workArea->UnsnapWindow(window); + + const auto processPath = get_process_path(window); + const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId); + + Assert::IsFalse(history.has_value()); + } }; } From fa37287bdc67874105736ea65431fb06ec413713 Mon Sep 17 00:00:00 2001 From: Jacob P <81419612+CanePlayz@users.noreply.github.com> Date: Wed, 1 Feb 2023 16:30:42 +0100 Subject: [PATCH 015/163] Fixed typos in Readme (#23703) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3a5b008836..da49432be0 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline ### Via GitHub with EXE [Recommended] -Go to [Microsoft PowerToys GitHub releases page][github-release-link], click on `Assets` at the bottom to show the files available in the release. Please use the appropriate the PowerToys installer that matches your machine's architecture. For most, it is `x64`. +Go to [Microsoft PowerToys GitHub releases page][github-release-link], click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture. For most, it is `x64`. - **For x64 processors (most common):** [PowerToysSetup-0.66.0-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.66.0/PowerToysSetup-0.66.0-x64.exe) - **For ARM64 processors:** [PowerToysSetup-0.66.0-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.66.0/PowerToysSetup-0.66.0-arm64.exe) @@ -54,7 +54,7 @@ winget install Microsoft.PowerToys -s winget ### Other install methods -There are [community driven install methods](./doc/unofficialInstallMethods.md) such as Chocolatey and Scoop. If these are your preferred install solutions, this will have the install instructions. +There are [community driven install methods](./doc/unofficialInstallMethods.md) such as Chocolatey and Scoop. If these are your preferred install solutions, you can find the install instructions there. ## Contributing From f38c83805050cd7fbb9c00b3b93ceb7183894427 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Wed, 1 Feb 2023 22:51:55 +0100 Subject: [PATCH 016/163] 0.67 changelog (#23688) Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> Co-authored-by: Clint Rutkas --- README.md | 111 +++++++++++++++++++++--------------------------------- 1 file changed, 42 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index da49432be0..e75ccb56cc 100644 --- a/README.md +++ b/README.md @@ -36,16 +36,16 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline Go to [Microsoft PowerToys GitHub releases page][github-release-link], click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture. For most, it is `x64`. - - **For x64 processors (most common):** [PowerToysSetup-0.66.0-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.66.0/PowerToysSetup-0.66.0-x64.exe) - - **For ARM64 processors:** [PowerToysSetup-0.66.0-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.66.0/PowerToysSetup-0.66.0-arm64.exe) + - **For x64 processors (most common):** [PowerToysSetup-0.67.0-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.67.0/PowerToysSetup-0.67.0-x64.exe) + - **For ARM64 processors:** [PowerToysSetup-0.67.0-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.67.0/PowerToysSetup-0.67.0-arm64.exe) This is our preferred method. ### Via Microsoft Store -Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. 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. +Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. 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 is available for both Windows 11 and Windows 10. -### Via WinGet (Preview) +### Via WinGet Download PowerToys from [WinGet][winget-link]. To install PowerToys, run the following command from the command line / PowerShell: ```powershell @@ -72,102 +72,75 @@ 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.66 - December 2022 Update +### 0.67 - January 2023 Update -In this release, we focused on stability and improvements. +In this release, we focused on releasing new features and improvements. **Highlights** -- PowerToy utilities now ship with self-contained .NET 7, meaning it's not necessary to install .NET as part of the installer and it's easier to keep up to date. -- It's possible to pick which of the installed OCR languages is used by Text Extractor by selecting it in the right-click context menu. -- Added a setting to sort the order of the accented characters by usage frequency in Quick Accent. +- Added an option for PowerToys Run to tab through results instead of context buttons. Thanks [@maws6502](https://github.com/maws6502)! +- All PowerToys registry entries are moved from machine scope (HKLM) to user scope (HKCU). +- Quick access system tray launcher. Thanks [@niels9001](https://github.com/niels9001)! -### General +### Awake -- Reduced resource consumption caused by logging. A thread for each logger was being created even for disabled utilities. -- The .NET 7 dependency is now shipped self-contained within the utilities, using deep links to reduce storage space usage. - -### Color Picker - -- Fixed an issue where the custom color formats were not working when picking colors without using the editor. -- Fixed a crash when using duplicated names for color formats. -- Added two decimal formats, to distinguish between RGB and BGR. -- Fixed color name localization, which was not working correctly on 0.65. +- Disable instead of hiding "Keep screen on" option. ### FancyZones -- Fixed an editor crash caused by deleting a zone while trying to move it. -- Reduce the time it takes the tooltip for layout shortcut setting to appear in the editor. +- Refactored and improved code quality. -### File Locksmith +### File explorer add-ons -- Fixed an issue causing File Locksmith to hang when looking for open handles in some machines. +- Fixed escaping HTML-sensitive characters when previewing developer files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -### Hosts File Editor +### Image Resizer -- Added a warning when duplicated entries are detected. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! +- Improved code quality around a silent crash that was being reported to Microsoft servers. ### PowerToys Run -- Support drag and dropping for file results. Thanks [@daniel-richter](https://github.com/daniel-richter)! +- Add option to tab through results only. Thanks [@maws6502](https://github.com/maws6502)! +- System plugin - Updated Recycle Bin command to allow opening the Recycle Bin. Thanks [@htcfreek](https://github.com/htcfreek)! +- System plugin - Improved Recycle Bin command to not block PT Run while the deletion is running. Thanks [@htcfreek](https://github.com/htcfreek)! +- System plugin - Small other changes to improve the usability of the Recycle Bin command. Thanks [@htcfreek](https://github.com/htcfreek)! +- WindowWalker plugin - Show all open windows with action keyword. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! ### Quick Accent -- Added support for dark theme. Thanks [@niels9001](https://github.com/niels9001)! -- Increased default input delay to improve out of the box experience. -- Fixed a bug causing the first character to not be selected when opening the overlay. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -- Fixed the positioning of the overlay when showing near the horizontal edges of the screen. -- Added additional Pinyin characters. Thanks [@char-46](https://github.com/char-46)! -- Added Macedonian characters. Thanks [@ad-mca-mk](https://github.com/ad-mca-mk)! -- Added a setting to sort characters by usage frequency. -- Added a setting to always start selection in the first character, even when using the arrow keys as the activation method. +- Added dashes characters. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! +- Added Estonian characters. Thanks [@jovark](https://github.com/jovark)! +- Added Hebrew characters. Thanks [@Evyatar-E](https://github.com/Evyatar-E)! +- Added diacritical marks. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! +- Added Norwegian characters. Thanks [@norwayman22](https://github.com/norwayman22)! ### Settings -- Fixed an error that hid the option to keep the display on when using the "Indefinitely Awake" mode. -- Fixed an accessibility issue causing the navigation bar to not work with narrator in scan mode. -- Fixed an accessibility issue where the name for the shortcut control was not being read correctly. -- Tweaked the Color Picker custom color format UI. Thanks [@niels9001](https://github.com/niels9001)! -- Improved the shortcut control visibility and accessibility. Thanks [@niels9001](https://github.com/niels9001)! -- Fixed an issue causing the Settings to not be saved correctly on scenarios where the admin user would be different then the user running PowerToys. -- Added a setting to pick which language should be used by default when using Text Extractor. - -### Text Extractor - -- Improve behavior for CJK languages by not adding spaces for some characters that don't need them. Thanks [@AO2233](https://github.com/AO2233)! -- OCR language can now be picked in the right-click context menu. - -### Video Conference Mute - -- Reduced resource consumption by not starting the File Watchers when the utility is disabled. +- Fixed URL click crash on the "What's New" screen. +- Added quick access system tray launcher. Thanks [@niels9001](https://github.com/niels9001)! ### Documentation -- Updated the development setup documentation. -- Improved the Markdown documentation lists numbering in many docs. Thanks [@sanidhyas3s](https://github.com/sanidhyas3s)! +- Added PowerToys [disk usage footprint document](doc\devdocs\disk-usage-footprint.md). +- Fixed some grammar issues on main readme / Wiki. Thanks [@CanePlayz](https://github.com/CanePlayz)! ### Development +- Verify notice.md file and used NuGet packages are synced. - Turned on C++ code analysis and incrementally fixing warnings. -- C++ code analysis no longer runs on release CI to speed up building release candidates. It still runs on GitHub CI and when building locally to maintain code quality. -- Cleaned up "to-do" comments referring to disposing memory on C#. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -- Added a fabric bot rule for localization issues. -- Fixed a CI build error after a .NET tools update. -- Update the Windows App SDK dependency version to 1.2. -- When building for arm64, the arm64 build tools are now preferred when building on an arm64 device. Thanks [@snickler](https://github.com/snickler)! -- Updated the C# test framework and removed unused Newtonsoft.Json package references. -- Updated StyleCop and fixed/enabled more warnings. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -- Fixed a language typo in the code. Thanks [@eltociear](https://github.com/eltociear)! -- Improved code quality around some silent crashes that were being reported to Microsoft servers. -- Moved the GPO asset files to source instead of docs in the repo. -- Upgraded the unit test NuGet packages. +- Automatically add list of .NET Runtime deps to Installer during build. Thanks [@snickler](https://github.com/snickler)! +- Move all installer registry entries to HKCU. +- Refactor installer - extract module related stuff from Product.wxs to per-module .wxs file. +- Enhance ARM64 build configuration verification. Thanks [@snickler](https://github.com/snickler)! +- Helped identify a WebView2 issue affecting PowerToys File explorer add-ons, which has been fixed upstream and released as an update through the usual Windows Update channels. -#### What is being planned for version 0.67 +#### What is being planned for version 0.68 -For [v0.67][github-next-release-work], we'll work on below: +For [v0.68][github-next-release-work], we'll work on below: - Allow installing without UAC. -- Add a flyout menu to the system tray icon for quick access. +- New utility: [PowerToys Peek](https://github.com/microsoft/PowerToys/issues/80) +- New utility: [Paste as plain text](https://github.com/microsoft/PowerToys/issues/1684) - Stability / bug fixes ## PowerToys Community @@ -195,5 +168,5 @@ The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has [usingPowerToys-docs-link]: https://aka.ms/powertoys-docs -[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F40 -[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F39 +[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F41 +[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F40 From b09ce1bb575f5c36d3ca95ed995e81a1f4bc1d8a Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Thu, 2 Feb 2023 19:25:04 +0300 Subject: [PATCH 017/163] [FancyZones] Fix crashing on win + arrows usage (#23757) --- src/modules/fancyzones/FancyZonesLib/FancyZones.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 020b1ec3b0..4659f7b34f 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -896,7 +896,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept // If that didn't work, extract zones from all other monitors and target one of them std::vector zoneRects; - std::vector>> zoneRectsInfo; + std::vector> zoneRectsInfo; RECT currentMonitorRect{ .top = 0, .bottom = -1 }; for (const auto& [monitor, monitorRect] : allMonitors) From 82c5c000929d3907c9dbdb30bc59ccc58ff9e884 Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Fri, 3 Feb 2023 18:08:51 +0300 Subject: [PATCH 018/163] [FancyZones][Hotfix]Opacity reset fix (#23818) --- src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp | 12 ++++++++++-- src/modules/fancyzones/FancyZonesLib/WindowDrag.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp index c5c18d9fae..25de2dbb1a 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp @@ -213,22 +213,30 @@ void WindowDrag::SetWindowTransparency() if (!SetLayeredWindowAttributes(m_window, 0, (255 * 50) / 100, LWA_ALPHA)) { Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); + return; } + + m_windowProperties.transparencySet = true; } } void WindowDrag::ResetWindowTransparency() { - if (FancyZonesSettings::settings().makeDraggedWindowTransparent) + if (FancyZonesSettings::settings().makeDraggedWindowTransparent && m_windowProperties.transparencySet) { + bool reset = true; if (!SetLayeredWindowAttributes(m_window, m_windowProperties.crKey, m_windowProperties.alpha, m_windowProperties.dwFlags)) { - Logger::error(L"Window transparency: SetLayeredWindowAttributes failed"); + Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); + reset = false; } if (SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle) == 0) { Logger::error(L"Window transparency: SetWindowLong failed, {}", get_last_error_or_default(GetLastError())); + reset = false; } + + m_windowProperties.transparencySet = !reset; } } diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.h b/src/modules/fancyzones/FancyZonesLib/WindowDrag.h index f45dc313f8..0956af9841 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowDrag.h +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.h @@ -33,6 +33,7 @@ private: COLORREF crKey = RGB(0, 0, 0); DWORD dwFlags = 0; BYTE alpha = 0; + bool transparencySet{false}; }; const HWND m_window; From 2e047e04de3d188e0179e1edfdc0bde58895901b Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Fri, 3 Feb 2023 16:09:39 +0100 Subject: [PATCH 019/163] [Installer][Hotfix]Revert HKCU to HKLM (#23754) * Revert HKCU to HKLM * Update comments --- installer/PowerToysSetup/Core.wxs | 12 ++--- installer/PowerToysSetup/FileLocksmith.wxs | 6 +-- installer/PowerToysSetup/ImageResizer.wxs | 63 ++++++++++------------ installer/PowerToysSetup/PowerRename.wxs | 4 +- installer/PowerToysSetup/Run.wxs | 2 +- 5 files changed, 40 insertions(+), 47 deletions(-) diff --git a/installer/PowerToysSetup/Core.wxs b/installer/PowerToysSetup/Core.wxs index 601500ef97..549da15b0f 100644 --- a/installer/PowerToysSetup/Core.wxs +++ b/installer/PowerToysSetup/Core.wxs @@ -14,7 +14,7 @@ - + @@ -22,7 +22,7 @@ - + @@ -89,12 +89,12 @@ - - + + - - + + diff --git a/installer/PowerToysSetup/FileLocksmith.wxs b/installer/PowerToysSetup/FileLocksmith.wxs index c2ee5f2571..a0e34f7051 100644 --- a/installer/PowerToysSetup/FileLocksmith.wxs +++ b/installer/PowerToysSetup/FileLocksmith.wxs @@ -17,16 +17,16 @@ - + - + - + diff --git a/installer/PowerToysSetup/ImageResizer.wxs b/installer/PowerToysSetup/ImageResizer.wxs index 88a1fea3e7..22e6b44d94 100644 --- a/installer/PowerToysSetup/ImageResizer.wxs +++ b/installer/PowerToysSetup/ImageResizer.wxs @@ -17,73 +17,66 @@ - + - - - - - - - - - - - - - - - - diff --git a/installer/PowerToysSetup/PowerRename.wxs b/installer/PowerToysSetup/PowerRename.wxs index 0386d7aaff..6239033894 100644 --- a/installer/PowerToysSetup/PowerRename.wxs +++ b/installer/PowerToysSetup/PowerRename.wxs @@ -19,13 +19,13 @@ - + - + diff --git a/installer/PowerToysSetup/Run.wxs b/installer/PowerToysSetup/Run.wxs index e8df425b90..6b42d9f6b4 100644 --- a/installer/PowerToysSetup/Run.wxs +++ b/installer/PowerToysSetup/Run.wxs @@ -36,7 +36,7 @@ - + From fc8ee435e6fd5110a773cf9eb4c3795f992ccc57 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Fri, 3 Feb 2023 15:10:14 +0000 Subject: [PATCH 020/163] [Flyout][Hotfix]Appear next to the tray icon position (#23772) * [Flyout]Appear next to the tray icon position * fix spellchecker --- src/runner/settings_window.cpp | 64 ++++++++++++------- src/runner/settings_window.h | 2 +- src/runner/tray_icon.cpp | 7 +- src/runner/tray_icon.h | 2 +- src/settings-ui/Settings.UI/App.xaml.cs | 34 ++++++++-- .../Settings.UI/FlyoutWindow.xaml.cs | 60 ++++++++++++++--- .../Settings.UI/MainWindow.xaml.cs | 7 +- .../Settings.UI/Views/ShellPage.xaml.cs | 25 ++++++-- 8 files changed, 153 insertions(+), 48 deletions(-) diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 53cae3bac3..5b65693083 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -316,7 +316,7 @@ BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args, DWORD g_settings_process_id = 0; -void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional settings_window, bool show_flyout = false) +void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional settings_window, bool show_flyout = false, const std::optional& flyout_position = std::nullopt) { g_isLaunchInProgress = true; @@ -389,21 +389,31 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op // Arg 10: should flyout be shown std::wstring settings_showFlyout = show_flyout ? L"true" : L"false"; + // Arg 11: contains if there's a settings window argument. If true, will add one extra argument with the value to the call. + std::wstring settings_containsSettingsWindow = settings_window.has_value() ? L"true" : L"false"; + + // Arg 12: contains if there's flyout coordinates. If true, will add two extra arguments to the call containing the x and y coordinates. + std::wstring settings_containsFlyoutPosition = flyout_position.has_value() ? L"true" : L"false"; + + // Args 13, .... : Optional arguments depending on the options presented before. All by the same value. + // create general settings file to initialize the settings file with installation configurations like : // 1. Run on start up. PTSettingsHelper::save_general_settings(save_settings.to_json()); - std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {}", - executable_path, - powertoys_pipe_name, - settings_pipe_name, - std::to_wstring(powertoys_pid), - settings_theme, - settings_elevatedStatus, - settings_isUserAnAdmin, - settings_showOobe, - settings_showScoobe, - settings_showFlyout); + std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {} {} {}", + executable_path, + powertoys_pipe_name, + settings_pipe_name, + std::to_wstring(powertoys_pid), + settings_theme, + settings_elevatedStatus, + settings_isUserAnAdmin, + settings_showOobe, + settings_showScoobe, + settings_showFlyout, + settings_containsSettingsWindow, + settings_containsFlyoutPosition); if (settings_window.has_value()) { @@ -411,6 +421,14 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op executable_args.append(settings_window.value()); } + if (flyout_position) + { + executable_args.append(L" "); + executable_args.append(std::to_wstring(flyout_position.value().x)); + executable_args.append(L" "); + executable_args.append(std::to_wstring(flyout_position.value().y)); + } + BOOL process_created = false; // Commented out to fix #22659 @@ -550,7 +568,7 @@ void bring_settings_to_front() EnumWindows(callback, 0); } -void open_settings_window(std::optional settings_window, bool show_flyout = false) +void open_settings_window(std::optional settings_window, bool show_flyout = false, const std::optional& flyout_position) { if (g_settings_process_id != 0) { @@ -558,7 +576,14 @@ void open_settings_window(std::optional settings_window, bool show { if (current_settings_ipc) { - current_settings_ipc->send(L"{\"ShowYourself\":\"flyout\"}"); + if (!flyout_position.has_value()) + { + current_settings_ipc->send(L"{\"ShowYourself\":\"flyout\"}"); + } + else + { + current_settings_ipc->send(fmt::format(L"{{\"ShowYourself\":\"flyout\", \"x_position\":{}, \"y_position\":{} }}", std::to_wstring(flyout_position.value().x), std::to_wstring(flyout_position.value().y))); + } } } else @@ -575,8 +600,8 @@ void open_settings_window(std::optional settings_window, bool show { if (!g_isLaunchInProgress) { - std::thread([settings_window, show_flyout]() { - run_settings_window(false, false, settings_window, show_flyout); + std::thread([settings_window, show_flyout, flyout_position]() { + run_settings_window(false, false, settings_window, show_flyout, flyout_position); }).detach(); } } @@ -608,13 +633,6 @@ void open_scoobe_window() }).detach(); } -void open_flyout() -{ - std::thread([]() { - run_settings_window(false, false, std::nullopt, true); - }).detach(); -} - std::string ESettingsWindowNames_to_string(ESettingsWindowNames value) { switch (value) diff --git a/src/runner/settings_window.h b/src/runner/settings_window.h index fcbb51b310..e03a7c0ef0 100644 --- a/src/runner/settings_window.h +++ b/src/runner/settings_window.h @@ -22,7 +22,7 @@ enum class ESettingsWindowNames std::string ESettingsWindowNames_to_string(ESettingsWindowNames value); ESettingsWindowNames ESettingsWindowNames_from_string(std::string value); -void open_settings_window(std::optional settings_window, bool show_flyout); +void open_settings_window(std::optional settings_window, bool show_flyout, const std::optional& flyout_position); void close_settings_window(); void open_oobe_window(); diff --git a/src/runner/tray_icon.cpp b/src/runner/tray_icon.cpp index 1ece986df7..d29527bc72 100644 --- a/src/runner/tray_icon.cpp +++ b/src/runner/tray_icon.cpp @@ -32,6 +32,8 @@ namespace HMENU h_sub_menu = nullptr; bool double_click_timer_running = false; bool double_clicked = false; + POINT tray_icon_click_point; + } // Struct to fill with callback and the data. The window_proc is responsible for cleaning it. @@ -123,7 +125,7 @@ void click_timer_elapsed() double_click_timer_running = false; if (!double_clicked) { - open_settings_window(std::nullopt, true); + open_settings_window(std::nullopt, true, tray_icon_click_point); } } @@ -212,6 +214,9 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam // ignore event if this is the second click of a double click if (!double_click_timer_running) { + // save the cursor position for sending where to show the popup. + GetCursorPos(&tray_icon_click_point); + // start timer for detecting single or double click double_click_timer_running = true; double_clicked = false; diff --git a/src/runner/tray_icon.h b/src/runner/tray_icon.h index 0d63475d21..812f1f1c20 100644 --- a/src/runner/tray_icon.h +++ b/src/runner/tray_icon.h @@ -7,7 +7,7 @@ void start_tray_icon(); // Stop the Tray Icon void stop_tray_icon(); // Open the Settings Window -void open_settings_window(std::optional settings_window, bool show_flyout); +void open_settings_window(std::optional settings_window, bool show_flyout, const std::optional& flyout_position = std::nullopt); // Callback type to be called by the tray icon loop typedef void (*main_loop_callback_function)(PVOID); // Calls a callback in _callback diff --git a/src/settings-ui/Settings.UI/App.xaml.cs b/src/settings-ui/Settings.UI/App.xaml.cs index d07bc645d3..41486f5d17 100644 --- a/src/settings-ui/Settings.UI/App.xaml.cs +++ b/src/settings-ui/Settings.UI/App.xaml.cs @@ -37,12 +37,12 @@ namespace Microsoft.PowerToys.Settings.UI ShowOobeWindow, ShowScoobeWindow, ShowFlyout, - SettingsWindow, + ContainsSettingsWindow, + ContainsFlyoutPosition, } // Quantity of arguments - private const int RequiredArgumentsQty = 10; - private const int RequiredAndOptionalArgumentsQty = 11; + private const int RequiredArgumentsQty = 12; // Create an instance of the IPC wrapper. private static TwoWayPipeMessageIPCManaged ipcmanager; @@ -122,11 +122,16 @@ namespace Microsoft.PowerToys.Settings.UI ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true"; ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true"; ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true"; + bool containsSettingsWindow = cmdArgs[(int)Arguments.ContainsSettingsWindow] == "true"; + bool containsFlyoutPosition = cmdArgs[(int)Arguments.ContainsFlyoutPosition] == "true"; - if (cmdArgs.Length == RequiredAndOptionalArgumentsQty) + // To keep track of variable arguments + int currentArgumentIndex = RequiredArgumentsQty; + + if (containsSettingsWindow) { // open specific window - switch (cmdArgs[(int)Arguments.SettingsWindow]) + switch (cmdArgs[currentArgumentIndex]) { case "Overview": StartupPage = typeof(Views.GeneralPage); break; case "AlwaysOnTop": StartupPage = typeof(Views.AlwaysOnTopPage); break; @@ -148,6 +153,17 @@ namespace Microsoft.PowerToys.Settings.UI case "Hosts": StartupPage = typeof(Views.HostsPage); break; default: Debug.Assert(false, "Unexpected SettingsWindow argument value"); break; } + + currentArgumentIndex++; + } + + int flyout_x = 0; + int flyout_y = 0; + if (containsFlyoutPosition) + { + // get the flyout position arguments + int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x); + int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y); } RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () => @@ -193,7 +209,13 @@ namespace Microsoft.PowerToys.Settings.UI } else if (ShowFlyout) { - ShellPage.OpenFlyoutCallback(); + POINT? p = null; + if (containsFlyoutPosition) + { + p = new POINT(flyout_x, flyout_y); + } + + ShellPage.OpenFlyoutCallback(p); } } } diff --git a/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs b/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs index aa2ab39849..c816561034 100644 --- a/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; using Microsoft.PowerToys.Settings.UI.ViewModels.Flyout; using Microsoft.PowerToys.Telemetry; using Microsoft.UI; using Microsoft.UI.Windowing; +using Windows.Graphics; using WinUIEx; namespace Microsoft.PowerToys.Settings.UI @@ -21,23 +23,65 @@ namespace Microsoft.PowerToys.Settings.UI public FlyoutViewModel ViewModel { get; set; } - public FlyoutWindow() + public POINT? FlyoutAppearPosition { get; set; } + + public FlyoutWindow(POINT? initialPosition) { this.InitializeComponent(); this.Activated += FlyoutWindow_Activated; - var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); - WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd); - DisplayArea displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest); - double dpiScale = (float)this.GetDpiForWindow() / 96; - double x = displayArea.WorkArea.Width - (dpiScale * (WindowWidth + WindowMargin)); - double y = displayArea.WorkArea.Height - (dpiScale * (WindowHeight + WindowMargin)); - this.MoveAndResize(x, y, WindowWidth, WindowHeight); + FlyoutAppearPosition = initialPosition; ViewModel = new FlyoutViewModel(); } private void FlyoutWindow_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args) { PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutActivatedEvent()); + if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.CodeActivated) + { + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); + WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd); + if (!FlyoutAppearPosition.HasValue) + { + DisplayArea displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest); + double dpiScale = (float)this.GetDpiForWindow() / 96; + double x = displayArea.WorkArea.Width - (dpiScale * (WindowWidth + WindowMargin)); + double y = displayArea.WorkArea.Height - (dpiScale * (WindowHeight + WindowMargin)); + this.MoveAndResize(x, y, WindowWidth, WindowHeight); + } + else + { + DisplayArea displayArea = DisplayArea.GetFromPoint(new PointInt32(FlyoutAppearPosition.Value.X, FlyoutAppearPosition.Value.Y), DisplayAreaFallback.Nearest); + + // Move the window to the correct screen as a little blob, so we can get the accurate dpi for the screen to calculate the best position to show it. + this.MoveAndResize(FlyoutAppearPosition.Value.X, FlyoutAppearPosition.Value.Y, 1, 1); + double dpiScale = (float)this.GetDpiForWindow() / 96; + + // Position the window so that it's inside the display are closest to the point. + POINT newPosition = new POINT(FlyoutAppearPosition.Value.X - (int)(dpiScale * WindowWidth / 2), FlyoutAppearPosition.Value.Y - (int)(dpiScale * WindowHeight / 2)); + if (newPosition.X < displayArea.WorkArea.X) + { + newPosition.X = displayArea.WorkArea.X; + } + + if (newPosition.Y < displayArea.WorkArea.Y) + { + newPosition.Y = displayArea.WorkArea.Y; + } + + if (newPosition.X + (dpiScale * WindowWidth) > displayArea.WorkArea.X + displayArea.WorkArea.Width) + { + newPosition.X = (int)(displayArea.WorkArea.X + displayArea.WorkArea.Width - (dpiScale * WindowWidth)); + } + + if (newPosition.Y + (dpiScale * WindowHeight) > displayArea.WorkArea.Y + displayArea.WorkArea.Height) + { + newPosition.Y = (int)(displayArea.WorkArea.Y + displayArea.WorkArea.Height - (dpiScale * WindowHeight)); + } + + this.MoveAndResize(newPosition.X, newPosition.Y, WindowWidth, WindowHeight); + } + } + if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.Deactivated) { if (ViewModel.CanHide) diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/MainWindow.xaml.cs index 54cc7430d3..4fef64531c 100644 --- a/src/settings-ui/Settings.UI/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/MainWindow.xaml.cs @@ -171,16 +171,17 @@ namespace Microsoft.PowerToys.Settings.UI }); // open flyout - ShellPage.SetOpenFlyoutCallback(() => + ShellPage.SetOpenFlyoutCallback((POINT? p) => { this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => { if (App.GetFlyoutWindow() == null) { - App.SetFlyoutWindow(new FlyoutWindow()); + App.SetFlyoutWindow(new FlyoutWindow(p)); } FlyoutWindow flyout = App.GetFlyoutWindow(); + flyout.FlyoutAppearPosition = p; flyout.Activate(); // https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground @@ -196,7 +197,7 @@ namespace Microsoft.PowerToys.Settings.UI { if (App.GetFlyoutWindow() == null) { - App.SetFlyoutWindow(new FlyoutWindow()); + App.SetFlyoutWindow(new FlyoutWindow(null)); } App.GetFlyoutWindow().ViewModel.DisableHiding(); diff --git a/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs b/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs index 96bf5698d5..fdaf5dec21 100644 --- a/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs @@ -45,7 +45,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views /// /// Declaration for the opening flyout window callback function. /// - public delegate void FlyoutOpeningCallback(); + public delegate void FlyoutOpeningCallback(POINT? point); /// - public static void DrawPreviewPlaceholders( + public static void DrawPreviewScreenPlaceholders( Graphics previewGraphics, IEnumerable screenBounds) { // we can exclude the activated screen because we've already draw @@ -183,7 +120,7 @@ internal static class DrawingHelper } /// - /// Draws screen captures from the specified desktop handle onto the target device context. + /// Draws a screen capture from the specified desktop handle onto the target device context. /// public static void DrawPreviewScreen( HDC sourceHdc, @@ -193,7 +130,7 @@ internal static class DrawingHelper { var source = sourceBounds.ToRectangle(); var target = targetBounds.ToRectangle(); - _ = Gdi32.StretchBlt( + var result = Gdi32.StretchBlt( targetHdc, target.X, target.Y, @@ -204,7 +141,12 @@ internal static class DrawingHelper source.Y, source.Width, source.Height, - MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY); + Gdi32.ROP_CODE.SRCCOPY); + if (!result) + { + throw new InvalidOperationException( + $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); + } } /// @@ -220,7 +162,7 @@ internal static class DrawingHelper { var source = sourceBounds[i].ToRectangle(); var target = targetBounds[i].ToRectangle(); - _ = Gdi32.StretchBlt( + var result = Gdi32.StretchBlt( targetHdc, target.X, target.Y, @@ -231,7 +173,12 @@ internal static class DrawingHelper source.Y, source.Width, source.Height, - MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY); + Gdi32.ROP_CODE.SRCCOPY); + if (!result) + { + throw new InvalidOperationException( + $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); + } } } } diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs new file mode 100644 index 0000000000..768a6f8ddd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Layout; + +namespace MouseJumpUI.Helpers; + +internal static class LayoutHelper +{ + public static LayoutInfo CalculateLayoutInfo( + LayoutConfig layoutConfig) + { + if (layoutConfig is null) + { + throw new ArgumentNullException(nameof(layoutConfig)); + } + + var builder = new LayoutInfo.Builder + { + LayoutConfig = layoutConfig, + }; + + builder.ActivatedScreenBounds = layoutConfig.Screens[layoutConfig.ActivatedScreenIndex].Bounds; + + // work out the maximum *constrained* form size + // * can't be bigger than the activated screen + // * can't be bigger than the max form size + var maxFormSize = builder.ActivatedScreenBounds.Size + .Intersect(layoutConfig.MaximumFormSize); + + // the drawing area for screen images is inside the + // form border and inside the preview border + var maxDrawingSize = maxFormSize + .Shrink(layoutConfig.FormPadding) + .Shrink(layoutConfig.PreviewPadding); + + // scale the virtual screen to fit inside the drawing bounds + var scalingRatio = layoutConfig.VirtualScreenBounds.Size + .ScaleToFitRatio(maxDrawingSize); + + // position the drawing bounds inside the preview border + var drawingBounds = layoutConfig.VirtualScreenBounds.Size + .ScaleToFit(maxDrawingSize) + .PlaceAt(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top); + + // now we know the size of the drawing area we can work out the preview size + builder.PreviewBounds = drawingBounds.Enlarge(layoutConfig.PreviewPadding); + + // ... and the form size + // * center the form to the activated position, but nudge it back + // inside the visible area of the activated screen if it falls outside + builder.FormBounds = builder.PreviewBounds + .Enlarge(layoutConfig.FormPadding) + .Center(layoutConfig.ActivatedLocation) + .Clamp(builder.ActivatedScreenBounds); + + // now calculate the positions of each of the screen images on the preview + builder.ScreenBounds = layoutConfig.Screens + .Select( + screen => screen.Bounds + .Offset(layoutConfig.VirtualScreenBounds.Location.ToSize().Negate()) + .Scale(scalingRatio) + .Offset(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top)) + .ToList(); + + return builder.Build(); + } + + /// + /// Resize and position the specified form. + /// + public static void PositionForm( + Form form, RectangleInfo formBounds) + { + // note - do this in two steps rather than "this.Bounds = formBounds" as there + // appears to be an issue in WinForms with dpi scaling even when using PerMonitorV2, + // where the form scaling uses either the *primary* screen scaling or the *previous* + // screen's scaling when the form is moved to a different screen. i've got no idea + // *why*, but the exact sequence of calls below seems to be a workaround... + // see https://github.com/mikeclayton/FancyMouse/issues/2 + var bounds = formBounds.ToRectangle(); + form.Location = bounds.Location; + _ = form.PointToScreen(Point.Empty); + form.Size = bounds.Size; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs index d96f2eabd9..78eb9a496d 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs @@ -2,9 +2,12 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Drawing; +using System.ComponentModel; +using System.Runtime.InteropServices; using System.Windows.Forms; -using MouseJumpUI.Drawing.Models; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.NativeMethods; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.Helpers; @@ -26,13 +29,33 @@ internal static class MouseHelper .Offset(desktopBounds.Location); } + /// + /// Get the current position of the cursor. + /// + public static PointInfo GetCursorPosition() + { + var lpPoint = new LPPOINT(new POINT(0, 0)); + var result = User32.GetCursorPos(lpPoint); + if (!result) + { + throw new Win32Exception( + Marshal.GetLastWin32Error()); + } + + var point = lpPoint.ToStructure(); + lpPoint.Free(); + + return new PointInfo( + point.x, point.y); + } + /// /// Moves the cursor to the specified location. /// /// /// See https://github.com/mikeclayton/FancyMouse/pull/3 /// - public static void JumpCursor(PointInfo location) + public static void SetCursorPosition(PointInfo location) { // set the new cursor position *twice* - the cursor sometimes end up in // the wrong place if we try to cross the dead space between non-aligned @@ -51,8 +74,18 @@ internal static class MouseHelper // setting the position a second time seems to fix this and moves the // cursor to the expected location (b) var point = location.ToPoint(); - Cursor.Position = point; - Cursor.Position = point; + for (var i = 0; i < 2; i++) + { + var result = User32.SetCursorPos(point.X, point.Y); + if (!result) + { + throw new Win32Exception( + Marshal.GetLastWin32Error()); + } + } + + // temporary workaround for issue #1273 + MouseHelper.SimulateMouseMovementEvent(location); } /// @@ -62,26 +95,43 @@ internal static class MouseHelper /// See https://github.com/microsoft/PowerToys/issues/24523 /// https://github.com/microsoft/PowerToys/pull/24527 /// - public static void SimulateMouseMovementEvent(Point location) + public static void SimulateMouseMovementEvent(PointInfo location) { - var mouseMoveInput = new NativeMethods.INPUT + var inputs = new User32.INPUT[] { - type = NativeMethods.INPUTTYPE.INPUT_MOUSE, - data = new NativeMethods.InputUnion - { - mi = new NativeMethods.MOUSEINPUT - { - dx = NativeMethods.CalculateAbsoluteCoordinateX(location.X), - dy = NativeMethods.CalculateAbsoluteCoordinateY(location.Y), - mouseData = 0, - dwFlags = (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_MOVE - | (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_ABSOLUTE, - time = 0, - dwExtraInfo = 0, - }, - }, + new( + type: User32.INPUT_TYPE.INPUT_MOUSE, + data: new User32.INPUT.DUMMYUNIONNAME( + mi: new User32.MOUSEINPUT( + dx: (int)MouseHelper.CalculateAbsoluteCoordinateX(location.X), + dy: (int)MouseHelper.CalculateAbsoluteCoordinateY(location.Y), + mouseData: 0, + dwFlags: User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE, + time: 0, + dwExtraInfo: ULONG_PTR.Null))), }; - var inputs = new NativeMethods.INPUT[] { mouseMoveInput }; - _ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size); + var result = User32.SendInput( + (uint)inputs.Length, + new User32.LPINPUT(inputs), + User32.INPUT.Size * inputs.Length); + if (result != inputs.Length) + { + throw new Win32Exception( + Marshal.GetLastWin32Error()); + } + } + + private static decimal CalculateAbsoluteCoordinateX(decimal x) + { + // If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535. + // see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput + return (x * 65535) / User32.GetSystemMetrics(User32.SYSTEM_METRICS_INDEX.SM_CXSCREEN); + } + + internal static decimal CalculateAbsoluteCoordinateY(decimal y) + { + // If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535. + // see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput + return (y * 65535) / User32.GetSystemMetrics(User32.SYSTEM_METRICS_INDEX.SM_CYSCREEN); } } diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs deleted file mode 100644 index bdc27b3c95..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -namespace MouseJumpUI.Helpers; - -// Win32 functions required for temporary workaround for issue #1273 -internal static class NativeMethods -{ - [DllImport("user32.dll")] - internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); - - [DllImport("user32.dll")] - internal static extern int GetSystemMetrics(SystemMetric smIndex); - - [StructLayout(LayoutKind.Sequential)] - public struct INPUT - { - internal INPUTTYPE type; - internal InputUnion data; - - internal static int Size - { - get { return Marshal.SizeOf(typeof(INPUT)); } - } - } - - [StructLayout(LayoutKind.Explicit)] - internal struct InputUnion - { - [FieldOffset(0)] - internal MOUSEINPUT mi; - [FieldOffset(0)] - internal KEYBDINPUT ki; - [FieldOffset(0)] - internal HARDWAREINPUT hi; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct MOUSEINPUT - { - internal int dx; - internal int dy; - internal int mouseData; - internal uint dwFlags; - internal uint time; - internal UIntPtr dwExtraInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct KEYBDINPUT - { - internal short wVk; - internal short wScan; - internal uint dwFlags; - internal int time; - internal UIntPtr dwExtraInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HARDWAREINPUT - { - internal int uMsg; - internal short wParamL; - internal short wParamH; - } - - internal enum INPUTTYPE : uint - { - INPUT_MOUSE = 0, - INPUT_KEYBOARD = 1, - INPUT_HARDWARE = 2, - } - - internal enum MOUSE_INPUT_FLAGS : uint - { - MOUSEEVENTF_MOVE = 0x0001, - MOUSEEVENTF_LEFTDOWN = 0x0002, - MOUSEEVENTF_LEFTUP = 0x0004, - MOUSEEVENTF_RIGHTDOWN = 0x0008, - MOUSEEVENTF_RIGHTUP = 0x0010, - MOUSEEVENTF_MIDDLEDOWN = 0x0020, - MOUSEEVENTF_MIDDLEUP = 0x0040, - MOUSEEVENTF_XDOWN = 0x0080, - MOUSEEVENTF_XUP = 0x0100, - MOUSEEVENTF_WHEEL = 0x0800, - MOUSEEVENTF_HWHEEL = 0x1000, - MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000, - MOUSEEVENTF_VIRTUALDESK = 0x4000, - MOUSEEVENTF_ABSOLUTE = 0x8000, - } - - internal enum SystemMetric - { - SM_CXSCREEN = 0, - SM_CYSCREEN = 1, - } - - internal static int CalculateAbsoluteCoordinateX(int x) - { - return (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN); - } - - internal static int CalculateAbsoluteCoordinateY(int y) - { - return (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN); - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs new file mode 100644 index 0000000000..4a61e6a6b3 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Screen; +using MouseJumpUI.NativeMethods; +using static MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.User32; + +namespace MouseJumpUI.Helpers; + +internal static class ScreenHelper +{ + /// + /// Duplicates functionality available in System.Windows.Forms.SystemInformation + /// to reduce the dependency on WinForms + /// + public static RectangleInfo GetVirtualScreen() + { + return new( + User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_XVIRTUALSCREEN), + User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_YVIRTUALSCREEN), + User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXVIRTUALSCREEN), + User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYVIRTUALSCREEN)); + } + + public static IEnumerable GetAllScreens() + { + // enumerate the monitors attached to the system + var hMonitors = new List(); + var result = User32.EnumDisplayMonitors( + HDC.Null, + LPCRECT.Null, + (unnamedParam1, unnamedParam2, unnamedParam3, unnamedParam4) => + { + hMonitors.Add(unnamedParam1); + return true; + }, + LPARAM.Null); + if (!result) + { + throw new Win32Exception( + $"{nameof(User32.EnumDisplayMonitors)} failed with return code {result.Value}"); + } + + // get detailed info about each monitor + foreach (var hMonitor in hMonitors) + { + var monitorInfoPtr = new LPMONITORINFO( + new MONITORINFO((uint)MONITORINFO.Size, RECT.Empty, RECT.Empty, 0)); + result = User32.GetMonitorInfoW(hMonitor, monitorInfoPtr); + if (!result) + { + throw new Win32Exception( + $"{nameof(User32.GetMonitorInfoW)} failed with return code {result.Value}"); + } + + var monitorInfo = monitorInfoPtr.ToStructure(); + monitorInfoPtr.Free(); + + yield return new ScreenInfo( + handle: hMonitor, + primary: monitorInfo.dwFlags.HasFlag(User32.MONITOR_INFO_FLAGS.MONITORINFOF_PRIMARY), + displayArea: new RectangleInfo( + monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.top, + monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top), + workingArea: new RectangleInfo( + monitorInfo.rcWork.left, + monitorInfo.rcWork.top, + monitorInfo.rcWork.right - monitorInfo.rcWork.left, + monitorInfo.rcWork.bottom - monitorInfo.rcWork.top)); + } + } + + public static HMONITOR MonitorFromPoint( + PointInfo pt) + { + var hMonitor = User32.MonitorFromPoint( + new((int)pt.X, (int)pt.Y), + User32.MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + if (hMonitor.IsNull) + { + throw new InvalidOperationException($"no monitor found for point {pt}"); + } + + return hMonitor; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs index b068989062..7dbc1c6dd4 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs @@ -9,20 +9,28 @@ using System.Drawing.Imaging; using System.Linq; using System.Windows.Forms; using ManagedCommon; -using MouseJumpUI.Drawing.Models; +using Microsoft.PowerToys.Settings.UI.Library; using MouseJumpUI.Helpers; -using MouseJumpUI.NativeMethods.Core; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Layout; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI; internal partial class MainForm : Form { - public MainForm() + public MainForm(MouseJumpSettings settings) { this.InitializeComponent(); + this.Settings = settings ?? throw new ArgumentNullException(nameof(settings)); this.ShowThumbnail(); } + public MouseJumpSettings Settings + { + get; + } + private void MainForm_Load(object sender, EventArgs e) { } @@ -32,6 +40,65 @@ internal partial class MainForm : Form if (e.KeyCode == Keys.Escape) { this.OnDeactivate(EventArgs.Empty); + return; + } + + // map screens to their screen number in "System > Display" + var screens = ScreenHelper.GetAllScreens() + .Select((screen, index) => new { Screen = screen, Index = index, Number = index + 1 }) + .ToList(); + + var currentLocation = MouseHelper.GetCursorPosition(); + var currentScreenHandle = ScreenHelper.MonitorFromPoint(currentLocation); + var currentScreen = screens + .Single(item => item.Screen.Handle == currentScreenHandle.Value); + var targetScreenNumber = default(int?); + + if (((e.KeyCode >= Keys.D1) && (e.KeyCode <= Keys.D9)) + || ((e.KeyCode >= Keys.NumPad1) && (e.KeyCode <= Keys.NumPad9))) + { + // number keys 1-9 or numpad keys 1-9 - move to the numbered screen + var screenNumber = e.KeyCode - Keys.D0; + if (screenNumber <= screens.Count) + { + targetScreenNumber = screenNumber; + } + } + else if (e.KeyCode == Keys.P) + { + // "P" - move to the primary screen + targetScreenNumber = screens.Single(item => item.Screen.Primary).Number; + } + else if (e.KeyCode == Keys.Left) + { + // move to the previous screen + targetScreenNumber = currentScreen.Number == 1 + ? screens.Count + : currentScreen.Number - 1; + } + else if (e.KeyCode == Keys.Right) + { + // move to the next screen + targetScreenNumber = currentScreen.Number == screens.Count + ? 1 + : currentScreen.Number + 1; + } + else if (e.KeyCode == Keys.Home) + { + // move to the first screen + targetScreenNumber = 1; + } + else if (e.KeyCode == Keys.End) + { + // move to the last screen + targetScreenNumber = screens.Count; + } + + if (targetScreenNumber.HasValue) + { + MouseHelper.SetCursorPosition( + screens[targetScreenNumber.Value - 1].Screen.Bounds.Midpoint); + this.OnDeactivate(EventArgs.Empty); } } @@ -59,15 +126,13 @@ internal partial class MainForm : Form if (mouseEventArgs.Button == MouseButtons.Left) { // plain click - move mouse pointer + var virtualScreen = ScreenHelper.GetVirtualScreen(); var scaledLocation = MouseHelper.GetJumpLocation( new PointInfo(mouseEventArgs.X, mouseEventArgs.Y), new SizeInfo(this.Thumbnail.Size), - new RectangleInfo(SystemInformation.VirtualScreen)); + virtualScreen); Logger.LogInfo($"scaled location = {scaledLocation}"); - MouseHelper.JumpCursor(scaledLocation); - - // Simulate mouse input for handlers that won't just catch the Cursor change - MouseHelper.SimulateMouseMovementEvent(scaledLocation.ToPoint()); + MouseHelper.SetCursorPosition(scaledLocation); Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); } @@ -76,57 +141,93 @@ internal partial class MainForm : Form public void ShowThumbnail() { - var screens = Screen.AllScreens; - foreach (var i in Enumerable.Range(0, screens.Length)) + var stopwatch = Stopwatch.StartNew(); + var layoutInfo = MainForm.GetLayoutInfo(this); + LayoutHelper.PositionForm(this, layoutInfo.FormBounds); + MainForm.RenderPreview(this, layoutInfo); + stopwatch.Stop(); + + // we have to activate the form to make sure the deactivate event fires + Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); + this.Activate(); + } + + private static LayoutInfo GetLayoutInfo(MainForm form) + { + // map screens to their screen number in "System > Display" + var screens = ScreenHelper.GetAllScreens() + .Select((screen, index) => new { Screen = screen, Index = index, Number = index + 1 }) + .ToList(); + foreach (var screen in screens) { - var screen = screens[i]; Logger.LogInfo(string.Join( '\n', - $"screen[{i}] = \"{screen.DeviceName}\"", - $"\tprimary = {screen.Primary}", - $"\tbounds = {screen.Bounds}", - $"\tworking area = {screen.WorkingArea}")); + $"screen[{screen.Number}]", + $"\tprimary = {screen.Screen.Primary}", + $"\tdisplay area = {screen.Screen.DisplayArea}", + $"\tworking area = {screen.Screen.WorkingArea}")); } // collect together some values that we need for calculating layout - var activatedLocation = Cursor.Position; + var activatedLocation = MouseHelper.GetCursorPosition(); + var activatedScreenHandle = ScreenHelper.MonitorFromPoint(activatedLocation); + var activatedScreenIndex = screens + .Single(item => item.Screen.Handle == activatedScreenHandle.Value) + .Index; + var layoutConfig = new LayoutConfig( - virtualScreen: SystemInformation.VirtualScreen, - screenBounds: Screen.AllScreens.Select(screen => screen.Bounds), + virtualScreenBounds: ScreenHelper.GetVirtualScreen(), + screens: screens.Select(item => item.Screen).ToList(), activatedLocation: activatedLocation, - activatedScreen: Array.IndexOf(Screen.AllScreens, Screen.FromPoint(activatedLocation)), - maximumFormSize: new Size(1600, 1200), - formPadding: this.panel1.Padding, - previewPadding: new Padding(0)); + activatedScreenIndex: activatedScreenIndex, + activatedScreenNumber: activatedScreenIndex + 1, + maximumFormSize: new( + form.Settings.Properties.ThumbnailSize.Width, + form.Settings.Properties.ThumbnailSize.Height), + formPadding: new( + form.panel1.Padding.Left, + form.panel1.Padding.Top, + form.panel1.Padding.Right, + form.panel1.Padding.Bottom), + previewPadding: new(0)); Logger.LogInfo(string.Join( '\n', $"Layout config", $"-------------", - $"virtual screen = {layoutConfig.VirtualScreen}", - $"activated location = {layoutConfig.ActivatedLocation}", - $"activated screen = {layoutConfig.ActivatedScreen}", - $"maximum form size = {layoutConfig.MaximumFormSize}", - $"form padding = {layoutConfig.FormPadding}", - $"preview padding = {layoutConfig.PreviewPadding}")); + $"virtual screen = {layoutConfig.VirtualScreenBounds}", + $"activated location = {layoutConfig.ActivatedLocation}", + $"activated screen index = {layoutConfig.ActivatedScreenIndex}", + $"activated screen number = {layoutConfig.ActivatedScreenNumber}", + $"maximum form size = {layoutConfig.MaximumFormSize}", + $"form padding = {layoutConfig.FormPadding}", + $"preview padding = {layoutConfig.PreviewPadding}")); // calculate the layout coordinates for everything - var layoutInfo = DrawingHelper.CalculateLayoutInfo(layoutConfig); + var layoutInfo = LayoutHelper.CalculateLayoutInfo(layoutConfig); Logger.LogInfo(string.Join( '\n', $"Layout info", $"-----------", $"form bounds = {layoutInfo.FormBounds}", $"preview bounds = {layoutInfo.PreviewBounds}", - $"activated screen = {layoutInfo.ActivatedScreen}")); + $"activated screen = {layoutInfo.ActivatedScreenBounds}")); - DrawingHelper.PositionForm(this, layoutInfo.FormBounds); + return layoutInfo; + } + + private static void RenderPreview( + MainForm form, LayoutInfo layoutInfo) + { + var layoutConfig = layoutInfo.LayoutConfig; + + var stopwatch = Stopwatch.StartNew(); // initialize the preview image var preview = new Bitmap( (int)layoutInfo.PreviewBounds.Width, (int)layoutInfo.PreviewBounds.Height, PixelFormat.Format32bppArgb); - this.Thumbnail.Image = preview; + form.Thumbnail.Image = preview; using var previewGraphics = Graphics.FromImage(preview); @@ -137,49 +238,51 @@ internal partial class MainForm : Form var previewHdc = HDC.Null; try { + // sort the source and target screen areas, putting the activated screen first + // (we need to capture and draw the activated screen before we show the form + // because otherwise we'll capture the form as part of the screenshot!) + var sourceScreens = layoutConfig.Screens + .Where((_, idx) => idx == layoutConfig.ActivatedScreenIndex) + .Union(layoutConfig.Screens.Where((_, idx) => idx != layoutConfig.ActivatedScreenIndex)) + .Select(screen => screen.Bounds) + .ToList(); + var targetScreens = layoutInfo.ScreenBounds + .Where((_, idx) => idx == layoutConfig.ActivatedScreenIndex) + .Union(layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreenIndex)) + .ToList(); + DrawingHelper.EnsureDesktopDeviceContext(ref desktopHwnd, ref desktopHdc); - - // we have to capture the screen where we're going to show the form first - // as the form will obscure the screen as soon as it's visible - var activatedStopwatch = Stopwatch.StartNew(); DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); - DrawingHelper.DrawPreviewScreen( - desktopHdc, - previewHdc, - layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen], - layoutInfo.ScreenBounds[layoutConfig.ActivatedScreen]); - activatedStopwatch.Stop(); - // show the placeholder images if it looks like it might take a while - // to capture the remaining screenshot images - if (activatedStopwatch.ElapsedMilliseconds > 250) + var placeholdersDrawn = false; + for (var i = 0; i < sourceScreens.Count; i++) { - var activatedArea = layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen].Area; - var totalArea = layoutConfig.ScreenBounds.Sum(screen => screen.Area); - if ((activatedArea / totalArea) < 0.5M) + DrawingHelper.DrawPreviewScreen( + desktopHdc, previewHdc, sourceScreens[i], targetScreens[i]); + + // show the placeholder images and show the form if it looks like it might take + // a while to capture the remaining screenshot images (but only if there are any) + if ((i < (sourceScreens.Count - 1)) && (stopwatch.ElapsedMilliseconds > 250)) { - // we need to release the device context handle before we can draw the placeholders + // we need to release the device context handle before we draw the placeholders // using the Graphics object otherwise we'll get an error from GDI saying // "Object is currently in use elsewhere" DrawingHelper.FreePreviewDeviceContext(previewGraphics, ref previewHdc); - DrawingHelper.DrawPreviewPlaceholders( - previewGraphics, - layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen)); - MainForm.ShowPreview(this); - } - } - // draw the remaining screen captures (if any) on the preview image - var sourceScreens = layoutConfig.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen).ToList(); - if (sourceScreens.Any()) - { - DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); - DrawingHelper.DrawPreviewScreens( - desktopHdc, - previewHdc, - sourceScreens, - layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen).ToList()); - MainForm.ShowPreview(this); + if (!placeholdersDrawn) + { + // draw placeholders for any undrawn screens + DrawingHelper.DrawPreviewScreenPlaceholders( + previewGraphics, + targetScreens.Where((_, idx) => idx > i)); + placeholdersDrawn = true; + } + + MainForm.RefreshPreview(form); + + // we've still got more screens to draw so open the device context again + DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); + } } } finally @@ -188,13 +291,11 @@ internal partial class MainForm : Form DrawingHelper.FreePreviewDeviceContext(previewGraphics, ref previewHdc); } - // we have to activate the form to make sure the deactivate event fires - MainForm.ShowPreview(this); - Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); - this.Activate(); + MainForm.RefreshPreview(form); + stopwatch.Stop(); } - private static void ShowPreview(MainForm form) + private static void RefreshPreview(MainForm form) { if (!form.Visible) { diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs similarity index 88% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs index 2d7a5791b9..9437bf0531 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs @@ -4,7 +4,7 @@ using System.Windows.Forms; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Drawing; /// /// Immutable version of a System.Windows.Forms.Padding object with some extra utility methods. @@ -16,11 +16,6 @@ public sealed class PaddingInfo { } - public PaddingInfo(Padding padding) - : this(padding.Left, padding.Top, padding.Right, padding.Bottom) - { - } - public PaddingInfo(decimal left, decimal top, decimal right, decimal bottom) { this.Left = left; diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs similarity index 89% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs index 1e8481df4c..40e452a378 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs @@ -4,7 +4,7 @@ using System.Drawing; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Drawing; /// /// Immutable version of a System.Drawing.Point object with some extra utility methods. @@ -32,7 +32,10 @@ public sealed class PointInfo get; } - public SizeInfo Size => new((int)this.X, (int)this.Y); + public SizeInfo ToSize() + { + return new((int)this.X, (int)this.Y); + } public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor); diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs similarity index 86% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs index ebfaeb0b9e..af46b85470 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs @@ -5,7 +5,7 @@ using System; using System.Drawing; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Drawing; /// /// Immutable version of a System.Drawing.Rectangle object with some extra utility methods. @@ -69,6 +69,14 @@ public sealed class RectangleInfo public decimal Area => this.Width * this.Height; + /// + /// Adapted from https://github.com/dotnet/runtime + /// See https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs + /// + public bool Contains(RectangleInfo rect) => + (this.X <= rect.X) && (rect.X + rect.Width <= this.X + this.Width) && + (this.Y <= rect.Y) && (rect.Y + rect.Height <= this.Y + this.Height); + public RectangleInfo Enlarge(PaddingInfo padding) => new( this.X + padding.Left, this.Y + padding.Top, diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs similarity index 98% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs index 4167c89df5..a91febc23c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs @@ -5,7 +5,7 @@ using System; using System.Drawing; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Drawing; /// /// Immutable version of a System.Drawing.Size object with some extra utility methods. diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs similarity index 51% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs index bc80d991d4..540a980a7c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Drawing; using System.Linq; -using System.Windows.Forms; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Screen; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Layout; /// /// Represents a collection of values needed for calculating the MainForm layout. @@ -17,29 +17,31 @@ namespace MouseJumpUI.Drawing.Models; public sealed class LayoutConfig { public LayoutConfig( - Rectangle virtualScreen, - IEnumerable screenBounds, - Point activatedLocation, - int activatedScreen, - Size maximumFormSize, - Padding formPadding, - Padding previewPadding) + RectangleInfo virtualScreenBounds, + List screens, + PointInfo activatedLocation, + int activatedScreenIndex, + int activatedScreenNumber, + SizeInfo maximumFormSize, + PaddingInfo formPadding, + PaddingInfo previewPadding) { // make sure the virtual screen entirely contains all of the individual screen bounds - ArgumentNullException.ThrowIfNull(screenBounds); - if (screenBounds.Any(screen => !virtualScreen.Contains(screen))) + ArgumentNullException.ThrowIfNull(virtualScreenBounds); + ArgumentNullException.ThrowIfNull(screens); + if (screens.Any(screen => !virtualScreenBounds.Contains(screen.Bounds))) { - throw new ArgumentException($"'{nameof(virtualScreen)}' must contain all of the screens in '{nameof(screenBounds)}'", nameof(virtualScreen)); + throw new ArgumentException($"'{nameof(virtualScreenBounds)}' must contain all of the screens in '{nameof(screens)}'", nameof(virtualScreenBounds)); } - this.VirtualScreen = new RectangleInfo(virtualScreen); - this.ScreenBounds = new( - screenBounds.Select(screen => new RectangleInfo(screen)).ToList()); - this.ActivatedLocation = new(activatedLocation); - this.ActivatedScreen = activatedScreen; - this.MaximumFormSize = new(maximumFormSize); - this.FormPadding = new(formPadding); - this.PreviewPadding = new(previewPadding); + this.VirtualScreenBounds = virtualScreenBounds; + this.Screens = new(screens.ToList()); + this.ActivatedLocation = activatedLocation; + this.ActivatedScreenIndex = activatedScreenIndex; + this.ActivatedScreenNumber = activatedScreenNumber; + this.MaximumFormSize = maximumFormSize; + this.FormPadding = formPadding; + this.PreviewPadding = previewPadding; } /// @@ -49,15 +51,15 @@ public sealed class LayoutConfig /// The Virtual Screen is the bounding rectangle of all the monitors. /// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen /// - public RectangleInfo VirtualScreen + public RectangleInfo VirtualScreenBounds { get; } /// - /// Gets the bounds of all of the screens connected to the system. + /// Gets a collection containing the individual screens connected to the system. /// - public ReadOnlyCollection ScreenBounds + public ReadOnlyCollection Screens { get; } @@ -67,8 +69,8 @@ public sealed class LayoutConfig /// /// /// The preview form will be centered on this location unless there are any - /// constraints such as the being too close to edge of a screen, in which case - /// the form will be displayed as close as possible to this location. + /// constraints such as being too close to edge of a screen, in which case + /// the form will be displayed centered as close as possible to this location. /// public PointInfo ActivatedLocation { @@ -77,8 +79,19 @@ public sealed class LayoutConfig /// /// Gets the index of the screen the cursor was on when the form was activated. + /// The value is an index into the ScreenBounds array and is 0-indexed as a result. /// - public int ActivatedScreen + public int ActivatedScreenIndex + { + get; + } + + /// + /// Gets the screen number the cursor was on when the form was activated. + /// The value matches the screen numbering scheme in the "Display Settings" dialog + /// and is 1-indexed as a result. + /// + public int ActivatedScreenNumber { get; } diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs similarity index 85% rename from src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs rename to src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs index 30eeed17c6..8a2cf74154 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs @@ -6,8 +6,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using MouseJumpUI.Models.Drawing; -namespace MouseJumpUI.Drawing.Models; +namespace MouseJumpUI.Models.Layout; public sealed class LayoutInfo { @@ -42,7 +43,7 @@ public sealed class LayoutInfo set; } - public RectangleInfo? ActivatedScreen + public RectangleInfo? ActivatedScreenBounds { get; set; @@ -55,7 +56,7 @@ public sealed class LayoutInfo formBounds: this.FormBounds ?? throw new InvalidOperationException(), previewBounds: this.PreviewBounds ?? throw new InvalidOperationException(), screenBounds: this.ScreenBounds ?? throw new InvalidOperationException(), - activatedScreen: this.ActivatedScreen ?? throw new InvalidOperationException()); + activatedScreenBounds: this.ActivatedScreenBounds ?? throw new InvalidOperationException()); } } @@ -64,7 +65,7 @@ public sealed class LayoutInfo RectangleInfo formBounds, RectangleInfo previewBounds, IEnumerable screenBounds, - RectangleInfo activatedScreen) + RectangleInfo activatedScreenBounds) { this.LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig)); this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds)); @@ -72,7 +73,7 @@ public sealed class LayoutInfo this.ScreenBounds = new( (screenBounds ?? throw new ArgumentNullException(nameof(screenBounds))) .ToList()); - this.ActivatedScreen = activatedScreen ?? throw new ArgumentNullException(nameof(activatedScreen)); + this.ActivatedScreenBounds = activatedScreenBounds ?? throw new ArgumentNullException(nameof(activatedScreenBounds)); } /// @@ -104,7 +105,7 @@ public sealed class LayoutInfo get; } - public RectangleInfo ActivatedScreen + public RectangleInfo ActivatedScreenBounds { get; } diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs new file mode 100644 index 0000000000..d7791fb063 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using MouseJumpUI.Models.Drawing; + +namespace MouseJumpUI.Models.Screen; + +/// +/// Immutable version of a System.Windows.Forms.Screen object so we don't need to +/// take a dependency on WinForms just for screen info. +/// +public sealed class ScreenInfo +{ + internal ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea) + { + this.Handle = handle; + this.Primary = primary; + this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea)); + this.WorkingArea = workingArea ?? throw new ArgumentNullException(nameof(workingArea)); + } + + public int Handle + { + get; + } + + public bool Primary + { + get; + } + + public RectangleInfo DisplayArea + { + get; + } + + public RectangleInfo Bounds => + this.DisplayArea; + + public RectangleInfo WorkingArea + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj index 1300f083ab..8d15866ace 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj +++ b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj @@ -63,5 +63,6 @@ + \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs index 5c2c500820..851cb77a46 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs @@ -2,35 +2,43 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.NativeMethods.Core; +namespace MouseJumpUI.NativeMethods; -/// -/// A Boolean variable (should be TRUE or FALSE). -/// This type is declared in WinDef.h as follows: -/// typedef int BOOL; -/// -/// -/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types -/// -internal readonly struct BOOL +internal static partial class Core { - public readonly int Value; - - public BOOL(int value) + /// + /// A Boolean variable (should be TRUE or FALSE). + /// This type is declared in WinDef.h as follows: + /// typedef int BOOL; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct BOOL { - this.Value = value; + public readonly int Value; + + public BOOL(int value) + { + this.Value = value; + } + + public BOOL(bool value) + { + this.Value = value ? 1 : 0; + } + + public static implicit operator bool(BOOL value) => value.Value != 0; + + public static implicit operator BOOL(bool value) => new(value); + + public static implicit operator int(BOOL value) => value.Value; + + public static implicit operator BOOL(int value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } } - - public BOOL(bool value) - { - this.Value = value ? 1 : 0; - } - - public static implicit operator bool(BOOL value) => value.Value != 0; - - public static implicit operator BOOL(bool value) => new(value); - - public static implicit operator int(BOOL value) => value.Value; - - public static implicit operator BOOL(int value) => new(value); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs new file mode 100644 index 0000000000..a44da78727 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// The CRECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect + /// + [SuppressMessage("Naming Rules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Name and value taken from Win32Api")] + internal readonly struct CRECT + { + public static readonly CRECT Empty = new(0, 0, 0, 0); + + public readonly LONG left; + public readonly LONG top; + public readonly LONG right; + public readonly LONG bottom; + + public CRECT( + LONG left, LONG top, LONG right, LONG bottom) + { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public static int Size => + Marshal.SizeOf(typeof(CRECT)); + + public override string ToString() + { + return $"left={this.left},top={this.top},right={this.right},bottom={this.bottom}"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs new file mode 100644 index 0000000000..9009b63c86 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A 32-bit unsigned integer. The range is 0 through 4294967295 decimal. + /// This type is declared in IntSafe.h as follows: + /// typedef unsigned long DWORD; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct DWORD + { + public readonly uint Value; + + public DWORD(uint value) + { + this.Value = value; + } + + public static implicit operator uint(DWORD value) => value.Value; + + public static implicit operator DWORD(uint value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs new file mode 100644 index 0000000000..1df2624ed5 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A handle to an object. + /// This type is declared in WinNT.h as follows: + /// typedef PVOID HANDLE; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HANDLE + { + public static readonly HANDLE Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HANDLE(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HANDLE.Null.Value; + + public static implicit operator IntPtr(HANDLE value) => value.Value; + + public static implicit operator HANDLE(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs index 4ebfce933b..b02a811c06 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs @@ -4,26 +4,34 @@ using System; -namespace MouseJumpUI.NativeMethods.Core; +namespace MouseJumpUI.NativeMethods; -/// -/// A handle to a device context (DC). -/// This type is declared in WinDef.h as follows: -/// typedef HANDLE HDC; -/// -/// -/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types -/// -internal readonly struct HDC +internal static partial class Core { - public static readonly HDC Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public HDC(IntPtr value) + /// + /// A handle to a device context (DC). + /// This type is declared in WinDef.h as follows: + /// typedef HANDLE HDC; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HDC { - this.Value = value; - } + public static readonly HDC Null = new(IntPtr.Zero); - public bool IsNull => this.Value == HDC.Null.Value; + public readonly IntPtr Value; + + public HDC(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HDC.Null.Value; + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs new file mode 100644 index 0000000000..047c654f6f --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A handle to a display monitor. + /// This type is declared in WinDef.h as follows: + /// if(WINVER >= 0x0500) typedef HANDLE HMONITOR; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HMONITOR + { + public static readonly HMONITOR Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HMONITOR(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HMONITOR.Null.Value; + + public static implicit operator int(HMONITOR value) => value.Value.ToInt32(); + + public static implicit operator HMONITOR(int value) => new(value); + + public static implicit operator IntPtr(HMONITOR value) => value.Value; + + public static implicit operator HMONITOR(IntPtr value) => new(value); + + public static implicit operator HANDLE(HMONITOR value) => new(value.Value); + + public static implicit operator HMONITOR(HANDLE value) => new(value.Value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs index 2478e0c8bf..e12f2846f3 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs @@ -4,26 +4,34 @@ using System; -namespace MouseJumpUI.NativeMethods.Core; +namespace MouseJumpUI.NativeMethods; -/// -/// A handle to a window. -/// This type is declared in WinDef.h as follows: -/// typedef HANDLE HWND; -/// -/// -/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types -/// -internal readonly struct HWND +internal static partial class Core { - public static readonly HWND Null = new(IntPtr.Zero); - - public readonly IntPtr Value; - - public HWND(IntPtr value) + /// + /// A handle to a window. + /// This type is declared in WinDef.h as follows: + /// typedef HANDLE HWND; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HWND { - this.Value = value; - } + public static readonly HWND Null = new(IntPtr.Zero); - public bool IsNull => this.Value == HWND.Null.Value; + public readonly IntPtr Value; + + public HWND(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HWND.Null.Value; + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs new file mode 100644 index 0000000000..2d2dd82283 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A 32-bit signed integer.The range is -2147483648 through 2147483647 decimal. + /// This type is declared in WinNT.h as follows: + /// typedef long LONG; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct LONG + { + public readonly int Value; + + public LONG(int value) + { + this.Value = value; + } + + public static implicit operator int(LONG value) => value.Value; + + public static implicit operator LONG(int value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.cs new file mode 100644 index 0000000000..6a227f5e21 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A message parameter. + /// This type is declared in WinDef.h as follows: + /// typedef LONG_PTR LPARAM; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct LPARAM + { + public static readonly LPARAM Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPARAM(IntPtr value) + { + this.Value = value; + } + + public static implicit operator IntPtr(LPARAM value) => value.Value; + + public static implicit operator LPARAM(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs new file mode 100644 index 0000000000..d19abd9c39 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + internal readonly struct LPCRECT + { + public static readonly LPCRECT Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPCRECT(IntPtr value) + { + this.Value = value; + } + + public LPCRECT(CRECT value) + { + this.Value = LPCRECT.ToPtr(value); + } + + private static IntPtr ToPtr(CRECT value) + { + var ptr = Marshal.AllocHGlobal(CRECT.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public static implicit operator IntPtr(LPCRECT value) => value.Value; + + public static implicit operator LPCRECT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs new file mode 100644 index 0000000000..db3423decd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + internal readonly struct LPPOINT + { + public static readonly LPPOINT Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPPOINT(IntPtr value) + { + this.Value = value; + } + + public LPPOINT(POINT value) + { + this.Value = LPPOINT.ToPtr(value); + } + + private static IntPtr ToPtr(POINT value) + { + var ptr = Marshal.AllocHGlobal(POINT.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public POINT ToStructure() + { + return Marshal.PtrToStructure(this.Value); + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public static implicit operator IntPtr(LPPOINT value) => value.Value; + + public static implicit operator LPPOINT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs new file mode 100644 index 0000000000..087e22abe3 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + internal readonly struct LPRECT + { + public static readonly LPRECT Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPRECT(IntPtr value) + { + this.Value = value; + } + + public LPRECT(RECT value) + { + this.Value = LPRECT.ToPtr(value); + } + + private static IntPtr ToPtr(RECT value) + { + var ptr = Marshal.AllocHGlobal(RECT.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public static implicit operator IntPtr(LPRECT value) => value.Value; + + public static implicit operator LPRECT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs new file mode 100644 index 0000000000..d4513d6740 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// The POINT structure defines the x- and y-coordinates of a point. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-point + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Names match Win32 api")] + internal readonly struct POINT + { + /// + /// Specifies the x-coordinate of the point. + /// + public readonly LONG x; + + /// + /// Specifies the y-coordinate of the point. + /// + public readonly LONG y; + + public POINT( + LONG x, + LONG y) + { + this.x = x; + this.y = y; + } + + public static int Size => + Marshal.SizeOf(typeof(POINT)); + + public override string ToString() + { + return $"x={this.x},y={this.y}"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs new file mode 100644 index 0000000000..ff107f6ad8 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect + /// + [SuppressMessage("Naming Rules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Name and value taken from Win32Api")] + internal readonly struct RECT + { + public static readonly RECT Empty = new(0, 0, 0, 0); + + public readonly LONG left; + public readonly LONG top; + public readonly LONG right; + public readonly LONG bottom; + + public RECT( + LONG left, LONG top, LONG right, LONG bottom) + { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public static int Size => + Marshal.SizeOf(typeof(RECT)); + + public override string ToString() + { + return $"left={this.left},top={this.top},right={this.right},bottom={this.bottom}"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.cs new file mode 100644 index 0000000000..e135212ec9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// An unsigned INT. The range is 0 through 4294967295 decimal. + /// This type is declared in WinDef.h as follows: + /// typedef unsigned int UINT; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct UINT + { + public readonly uint Value; + + public UINT(uint value) + { + this.Value = value; + } + + public static implicit operator uint(UINT value) => value.Value; + + public static implicit operator UINT(uint value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs new file mode 100644 index 0000000000..76d99d88be --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// An unsigned LONG_PTR. + /// This type is declared in BaseTsd.h as follows: + /// C++ + /// #if defined(_WIN64) + /// typedef unsigned __int64 ULONG_PTR; + /// #else + /// typedef unsigned long ULONG_PTR; + /// #endif + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct ULONG_PTR + { + public static readonly ULONG_PTR Null = new(UIntPtr.Zero); + + public readonly UIntPtr Value; + + public ULONG_PTR(UIntPtr value) + { + this.Value = value; + } + + public static implicit operator UIntPtr(ULONG_PTR value) => value.Value; + + public static implicit operator ULONG_PTR(UIntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs new file mode 100644 index 0000000000..642d0b0514 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A 16-bit unsigned integer.The range is 0 through 65535 decimal. + /// This type is declared in WinDef.h as follows: + /// typedef unsigned short WORD; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct WORD + { + public readonly ushort Value; + + public WORD(ushort value) + { + this.Value = value; + } + + public static implicit operator ulong(WORD value) => value.Value; + + public static implicit operator WORD(ushort value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs similarity index 97% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs index f91c6e05f8..994eeb7165 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs @@ -14,7 +14,7 @@ internal static partial class Gdi32 /// /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt /// - public enum ROP_CODE : uint + internal enum ROP_CODE : uint { BLACKNESS = 0x00000042, CAPTUREBLT = 0x40000000, diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs index 71214b2204..575e092982 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs @@ -12,7 +12,7 @@ internal static partial class Gdi32 /// /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode /// - public enum STRETCH_BLT_MODE : int + internal enum STRETCH_BLT_MODE : int { BLACKONWHITE = 1, COLORONCOLOR = 3, diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs similarity index 90% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs index 085d03295f..410af308d4 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -21,7 +21,7 @@ internal static partial class Gdi32 /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode /// [LibraryImport(Libraries.Gdi32)] - public static partial int SetStretchBltMode( + internal static partial int SetStretchBltMode( HDC hdc, STRETCH_BLT_MODE mode); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs index 2e7524358e..345391d666 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -23,7 +23,7 @@ internal static partial class Gdi32 /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-stretchblt /// [LibraryImport(Libraries.Gdi32)] - public static partial BOOL StretchBlt( + internal static partial BOOL StretchBlt( HDC hdcDest, int xDest, int yDest, diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs new file mode 100644 index 0000000000..6d5a0687bb --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The EnumDisplayMonitors function enumerates display monitors (including invisible + /// pseudo-monitors associated with the mirroring drivers) that intersect a region formed + /// by the intersection of a specified clipping rectangle and the visible region of a + /// device context. EnumDisplayMonitors calls an application-defined MonitorEnumProc + /// callback function once for each monitor that is enumerated. Note that + /// GetSystemMetrics (SM_CMONITORS) counts only the display monitors. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors + /// + [LibraryImport(Libraries.User32)] + internal static partial BOOL EnumDisplayMonitors( + HDC hdc, + LPCRECT lprcClip, + MONITORENUMPROC lpfnEnum, + LPARAM dwData); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs new file mode 100644 index 0000000000..ad55845cf9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The GetMonitorInfo function retrieves information about a display monitor. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors + /// + [LibraryImport(Libraries.User32)] + internal static partial BOOL GetMonitorInfoW( + HMONITOR hMonitor, + LPMONITORINFO lpmi); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs index 4263aa0b7d..ea0b79db9b 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -26,6 +26,6 @@ internal static partial class User32 /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdc /// [LibraryImport(Libraries.User32)] - public static partial HDC GetWindowDC( + internal static partial HDC GetWindowDC( HWND hWnd); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs new file mode 100644 index 0000000000..f44a2c7972 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput + /// + internal readonly struct LPMONITORINFO + { + public static readonly LPMONITORINFO Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPMONITORINFO(IntPtr value) + { + this.Value = value; + } + + public LPMONITORINFO(MONITORINFO value) + { + this.Value = LPMONITORINFO.ToPtr(value); + } + + public MONITORINFO ToStructure() + { + return Marshal.PtrToStructure(this.Value); + } + + private static IntPtr ToPtr(MONITORINFO value) + { + var ptr = Marshal.AllocHGlobal(MONITORINFO.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs new file mode 100644 index 0000000000..0e366c8163 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// A MonitorEnumProc function is an application-defined callback function that is called by the EnumDisplayMonitors function. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-monitorenumproc + /// + internal delegate BOOL MONITORENUMPROC( + HMONITOR unnamedParam1, + HDC unnamedParam2, + LPRECT unnamedParam3, + LPARAM unnamedParam4); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs new file mode 100644 index 0000000000..24fb270938 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct MONITORINFO + { + public readonly DWORD cbSize; + public readonly RECT rcMonitor; + public readonly RECT rcWork; + public readonly MONITOR_INFO_FLAGS dwFlags; + + public MONITORINFO(DWORD cbSize, RECT rcMonitor, RECT rcWork, MONITOR_INFO_FLAGS dwFlags) + { + this.cbSize = cbSize; + this.rcMonitor = rcMonitor; + this.rcWork = rcWork; + this.dwFlags = dwFlags; + } + + public static int Size => + Marshal.SizeOf(typeof(INPUT)); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs new file mode 100644 index 0000000000..5d5fc3b035 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + /// + /// Determines the function's return value if the point is not contained within any display monitor. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint + /// + internal enum MONITOR_FROM_FLAGS : uint + { + MONITOR_DEFAULTTONULL = 0x00000000, + MONITOR_DEFAULTTOPRIMARY = 0x00000001, + MONITOR_DEFAULTTONEAREST = 0x00000002, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs new file mode 100644 index 0000000000..f20e6a2f58 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + /// + /// A set of flags that represent attributes of the display monitor. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo + /// + internal enum MONITOR_INFO_FLAGS : uint + { + MONITORINFOF_PRIMARY = 1, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs new file mode 100644 index 0000000000..93878544e5 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The MonitorFromPoint function retrieves a handle to the display monitor that contains a specified point. + /// + /// + /// If the point is contained by a display monitor, the return value is an HMONITOR handle to that display monitor. + /// If the point is not contained by a display monitor, the return value depends on the value of dwFlags. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint + /// + [LibraryImport(Libraries.User32)] + internal static partial HMONITOR MonitorFromPoint( + POINT pt, + MONITOR_FROM_FLAGS dwFlags); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs similarity index 92% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs index f745c13c27..a6f6caa355 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -22,7 +22,7 @@ internal static partial class User32 /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-releasedc /// [LibraryImport(Libraries.User32)] - public static partial int ReleaseDC( + internal static partial int ReleaseDC( HWND hWnd, HDC hDC); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs new file mode 100644 index 0000000000..254b60eecd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Contains information about a simulated message generated by an input device + /// other than a keyboard or mouse. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-hardwareinput + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct HARDWAREINPUT + { + public readonly DWORD uMsg; + public readonly WORD wParamL; + public readonly WORD wParamH; + + public HARDWAREINPUT( + DWORD uMsg, + WORD wParamL, + WORD wParamH) + { + this.uMsg = uMsg; + this.wParamL = wParamL; + this.wParamH = wParamH; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs new file mode 100644 index 0000000000..1a4bcf8546 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct INPUT + { + public readonly INPUT_TYPE type; + public readonly DUMMYUNIONNAME data; + + public INPUT(INPUT_TYPE type, DUMMYUNIONNAME data) + { + this.type = type; + this.data = data; + } + + public static int Size => + Marshal.SizeOf(typeof(INPUT)); + + [StructLayout(LayoutKind.Explicit)] + public readonly struct DUMMYUNIONNAME + { + [FieldOffset(0)] + public readonly MOUSEINPUT mi; + [FieldOffset(0)] + public readonly KEYBDINPUT ki; + [FieldOffset(0)] + public readonly HARDWAREINPUT hi; + + public DUMMYUNIONNAME(MOUSEINPUT mi) + { + this.mi = mi; + } + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs new file mode 100644 index 0000000000..89e8f60218 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + /// + /// The type of the input event. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input + /// + internal enum INPUT_TYPE : uint + { + INPUT_MOUSE = 0, + INPUT_KEYBOARD = 1, + INPUT_HARDWARE = 2, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs new file mode 100644 index 0000000000..a5c9bc782b --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Contains information about a simulated keyboard event. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct KEYBDINPUT + { + public readonly WORD wVk; + public readonly WORD wScan; + public readonly DWORD dwFlags; + public readonly DWORD time; + public readonly ULONG_PTR dwExtraInfo; + + public KEYBDINPUT( + WORD wVk, + WORD wScan, + DWORD dwFlags, + DWORD time, + ULONG_PTR dwExtraInfo) + { + this.wVk = wVk; + this.wScan = wScan; + this.dwFlags = dwFlags; + this.time = time; + this.dwExtraInfo = dwExtraInfo; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs new file mode 100644 index 0000000000..ff689a5931 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput + /// + internal readonly struct LPINPUT + { + public static readonly LPINPUT Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPINPUT(IntPtr value) + { + this.Value = value; + } + + public LPINPUT(INPUT[] values) + { + this.Value = LPINPUT.ToPtr(values); + } + + public INPUT ToStructure() + { + return Marshal.PtrToStructure(this.Value); + } + + public IEnumerable ToStructure(int count) + { + var ptr = this.Value; + var size = INPUT.Size; + for (var i = 0; i < count; i++) + { + yield return Marshal.PtrToStructure(this.Value); + ptr += size; + } + } + + private static IntPtr ToPtr(INPUT[] values) + { + var mem = Marshal.AllocHGlobal(INPUT.Size * values.Length); + var ptr = mem; + var size = INPUT.Size; + foreach (var value in values) + { + Marshal.StructureToPtr(value, ptr, true); + ptr += size; + } + + return mem; + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs new file mode 100644 index 0000000000..80f9bbe2fc --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Contains information about a simulated mouse event. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct MOUSEINPUT + { + public readonly int dx; + public readonly int dy; + public readonly DWORD mouseData; + public readonly MOUSE_EVENT_FLAGS dwFlags; + public readonly DWORD time; + public readonly ULONG_PTR dwExtraInfo; + + public MOUSEINPUT( + int dx, + int dy, + DWORD mouseData, + MOUSE_EVENT_FLAGS dwFlags, + DWORD time, + ULONG_PTR dwExtraInfo) + { + this.dx = dx; + this.dy = dy; + this.mouseData = mouseData; + this.dwFlags = dwFlags; + this.time = time; + this.dwExtraInfo = dwExtraInfo; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs new file mode 100644 index 0000000000..b6f3cba549 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput + /// + [Flags] + internal enum MOUSE_EVENT_FLAGS : uint + { + MOUSEEVENTF_MOVE = 0x0001, + MOUSEEVENTF_LEFTDOWN = 0x0002, + MOUSEEVENTF_LEFTUP = 0x0004, + MOUSEEVENTF_RIGHTDOWN = 0x0008, + MOUSEEVENTF_RIGHTUP = 0x0010, + MOUSEEVENTF_MIDDLEDOWN = 0x0020, + MOUSEEVENTF_MIDDLEUP = 0x0040, + MOUSEEVENTF_XDOWN = 0x0080, + MOUSEEVENTF_XUP = 0x0100, + MOUSEEVENTF_WHEEL = 0x0800, + MOUSEEVENTF_HWHEEL = 0x1000, + MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000, + MOUSEEVENTF_VIRTUALDESK = 0x4000, + MOUSEEVENTF_ABSOLUTE = 0x8000, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs new file mode 100644 index 0000000000..b7bfba63bf --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Synthesizes keystrokes, mouse motions, and button clicks. + /// + /// + /// The function returns the number of events that it successfully inserted into the keyboard or mouse input stream. + /// If the function returns zero, the input was already blocked by another thread. + /// To get extended error information, call GetLastError. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput + /// + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial UINT SendInput( + UINT cInputs, + LPINPUT pInputs, + int cbSize); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs new file mode 100644 index 0000000000..f6cbfa75bc --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Retrieves the position of the mouse cursor, in screen coordinates. + /// + /// + /// Returns nonzero if successful or zero otherwise. + /// To get extended error information, call GetLastError. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos + /// + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial BOOL GetCursorPos( + LPPOINT lpPoint); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs similarity index 88% rename from src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs rename to src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs index bb5ed16514..4d16b95e15 100644 --- a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; -using MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI.NativeMethods; @@ -20,5 +20,5 @@ internal static partial class User32 /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdesktopwindow /// [LibraryImport(Libraries.User32)] - public static partial HWND GetDesktopWindow(); + internal static partial HWND GetDesktopWindow(); } diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs new file mode 100644 index 0000000000..7791c22963 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Retrieves the specified system metric or system configuration setting. + /// + /// Note that all dimensions retrieved by GetSystemMetrics are in pixels. + /// + /// + /// If the function succeeds, the return value is the requested system metric or configuration setting. + /// If the function fails, the return value is 0. GetLastError does not provide extended error information. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics + /// + [LibraryImport(Libraries.User32)] + internal static partial int GetSystemMetrics( + SYSTEM_METRICS_INDEX smIndex); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs new file mode 100644 index 0000000000..b2ca6b3b68 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + internal enum SYSTEM_METRICS_INDEX : uint + { + SM_ARRANGE = 56, + SM_CLEANBOOT = 67, + SM_CMONITORS = 80, + SM_CMOUSEBUTTONS = 43, + SM_CONVERTIBLESLATEMODE = 0x2003, + SM_CXBORDER = 5, + SM_CXCURSOR = 13, + SM_CXDLGFRAME = 7, + SM_CXDOUBLECLK = 36, + SM_CXDRAG = 68, + SM_CXEDGE = 45, + SM_CXFIXEDFRAME = SM_CXDLGFRAME, + SM_CXFOCUSBORDER = 83, + SM_CXFRAME = 32, + SM_CXFULLSCREEN = 16, + SM_CXHSCROLL = 21, + SM_CXHTHUMB = 10, + SM_CXICON = 11, + SM_CXICONSPACING = 38, + SM_CXMAXIMIZED = 61, + SM_CXMAXTRACK = 59, + SM_CXMENUCHECK = 71, + SM_CXMENUSIZE = 54, + SM_CXMIN = 28, + SM_CXMINIMIZED = 57, + SM_CXMINSPACING = 47, + SM_CXMINTRACK = 34, + SM_CXPADDEDBORDER = 92, + SM_CXSCREEN = 0, + SM_CXSIZE = 30, + SM_CXSIZEFRAME = SM_CXFRAME, + SM_CXSMICON = 49, + SM_CXSMSIZE = 52, + SM_CXVIRTUALSCREEN = 78, + SM_CXVSCROLL = 2, + SM_CYBORDER = 6, + SM_CYCAPTION = 4, + SM_CYCURSOR = 14, + SM_CYDLGFRAME = 8, + SM_CYDOUBLECLK = 37, + SM_CYDRAG = 69, + SM_CYEDGE = 46, + SM_CYFIXEDFRAME = SM_CYDLGFRAME, + SM_CYFOCUSBORDER = 84, + SM_CYFRAME = 33, + SM_CYFULLSCREEN = 17, + SM_CYHSCROLL = 3, + SM_CYICON = 12, + SM_CYICONSPACING = 39, + SM_CYKANJIWINDOW = 18, + SM_CYMAXIMIZED = 62, + SM_CYMAXTRACK = 60, + SM_CYMENU = 15, + SM_CYMENUCHECK = 72, + SM_CYMENUSIZE = 55, + SM_CYMIN = 29, + SM_CYMINIMIZED = 58, + SM_CYMINSPACING = 48, + SM_CYMINTRACK = 35, + SM_CYSCREEN = 1, + SM_CYSIZE = 31, + SM_CYSIZEFRAME = SM_CYFRAME, + SM_CYSMCAPTION = 51, + SM_CYSMICON = 50, + SM_CYSMSIZE = 53, + SM_CYVIRTUALSCREEN = 79, + SM_CYVSCROLL = 20, + SM_CYVTHUMB = 9, + SM_DBCSENABLED = 42, + SM_DEBUG = 22, + SM_DIGITIZER = 94, + SM_IMMENABLED = 82, + SM_MAXIMUMTOUCHES = 95, + SM_MEDIACENTER = 87, + SM_MENUDROPALIGNMENT = 40, + SM_MIDEASTENABLED = 74, + SM_MOUSEPRESENT = 19, + SM_MOUSEHORIZONTALWHEELPRESENT = 91, + SM_MOUSEWHEELPRESENT = 75, + SM_NETWORK = 63, + SM_PENWINDOWS = 41, + SM_REMOTECONTROL = 0x2001, + SM_REMOTESESSION = 0x1000, + SM_SAMEDISPLAYFORMA = 81, + SM_SECURE = 44, + SM_SERVERR2 = 89, + SM_SHOWSOUNDS = 70, + SM_SHUTTINGDOWN = 0x2000, + SM_SLOWMACHINE = 73, + SM_STARTER = 88, + SM_SWAPBUTTON = 23, + SM_SYSTEMDOCKED = 0x2004, + SM_TABLETPC = 86, + SM_XVIRTUALSCREEN = 76, + SM_YVIRTUALSCREEN = 77, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs new file mode 100644 index 0000000000..b548148925 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Moves the cursor to the specified screen coordinates. If the new coordinates are not within + /// the screen rectangle set by the most recent ClipCursor function call, the system automatically + /// adjusts the coordinates so that the cursor stays within the rectangle. + /// + /// + /// Returns nonzero if successful or zero otherwise. + /// To get extended error information, call GetLastError. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos + /// + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial BOOL SetCursorPos( + int X, + int Y); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs deleted file mode 100644 index 7613b8825c..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class Gdi32 -{ - public static int SetStretchBltMode(HDC hdc, NativeMethods.Gdi32.STRETCH_BLT_MODE mode) - { - var result = NativeMethods.Gdi32.SetStretchBltMode(hdc, mode); - - return result == 0 - ? throw new InvalidOperationException( - $"{nameof(Gdi32.SetStretchBltMode)} returned {result}") - : result; - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs deleted file mode 100644 index 115edafee9..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class Gdi32 -{ - public static BOOL StretchBlt( - HDC hdcDest, - int xDest, - int yDest, - int wDest, - int hDest, - HDC hdcSrc, - int xSrc, - int ySrc, - int wSrc, - int hSrc, - NativeMethods.Gdi32.ROP_CODE rop) - { - var result = NativeMethods.Gdi32.StretchBlt( - hdcDest, - xDest, - yDest, - wDest, - hDest, - hdcSrc, - xSrc, - ySrc, - wSrc, - hSrc, - rop); - - return result - ? result - : throw new InvalidOperationException( - $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs deleted file mode 100644 index 8df89f7e39..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class User32 -{ - public static HWND GetDesktopWindow() - { - return NativeMethods.User32.GetDesktopWindow(); - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs deleted file mode 100644 index 6379e3f73a..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class User32 -{ - public static HDC GetWindowDC(HWND hWnd) - { - var hdc = NativeMethods.User32.GetWindowDC(hWnd); - - return hdc.IsNull - ? throw new InvalidOperationException( - $"{nameof(User32.GetWindowDC)} returned null") - : hdc; - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs deleted file mode 100644 index 44a8318b85..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using MouseJumpUI.NativeMethods.Core; - -namespace MouseJumpUI.NativeWrappers; - -internal static partial class User32 -{ - public static int ReleaseDC(HWND hWnd, HDC hDC) - { - var result = NativeMethods.User32.ReleaseDC(hWnd, hDC); - - return result == 0 - ? throw new InvalidOperationException( - $"{nameof(User32.ReleaseDC)} returned {result}") - : result; - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/Program.cs b/src/modules/MouseUtils/MouseJumpUI/Program.cs index f5932c13c0..7d3c385730 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Program.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Program.cs @@ -3,15 +3,18 @@ // See the LICENSE file in the project root for more information. using System; +using System.IO; +using System.Text.Json; using System.Windows.Forms; using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library; namespace MouseJumpUI; internal static class Program { /// - /// The main entry point for the application. + /// The main entry point for the application. /// [STAThread] private static void Main() @@ -35,6 +38,34 @@ internal static class Program return; } - Application.Run(new MainForm()); + var settings = Program.ReadSettings(); + var mainForm = new MainForm(settings); + + Application.Run(mainForm); + } + + private static MouseJumpSettings ReadSettings() + { + var settingsUtils = new SettingsUtils(); + var settingsPath = settingsUtils.GetSettingsFilePath(MouseJumpSettings.ModuleName); + if (!File.Exists(settingsPath)) + { + var scaffoldSettings = new MouseJumpSettings(); + settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), MouseJumpSettings.ModuleName); + } + + var settings = new MouseJumpSettings(); + try + { + settings = settingsUtils.GetSettings(MouseJumpSettings.ModuleName); + } + catch (Exception ex) + { + var errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}"; + Logger.LogInfo(errorMessage); + Logger.LogDebug(errorMessage); + } + + return settings; } } diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs b/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs index 1d5a9ad480..524039b06e 100644 --- a/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs +++ b/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs @@ -11,9 +11,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("activation_shortcut")] public HotkeySettings ActivationShortcut { get; set; } + [JsonPropertyName("thumbnail_size")] + public MouseJumpThumbnailSize ThumbnailSize { get; set; } + public MouseJumpProperties() { ActivationShortcut = new HotkeySettings(true, false, false, true, 0x44); + ThumbnailSize = new MouseJumpThumbnailSize(); } } } diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs b/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs new file mode 100644 index 0000000000..58af7d3683 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class MouseJumpThumbnailSize : INotifyPropertyChanged + { + private int _width; + private int _height; + + [JsonPropertyName("width")] + public int Width + { + get + { + return _width; + } + + set + { + var newWidth = Math.Max(0, value); + if (newWidth != _width) + { + _width = newWidth; + OnPropertyChanged(); + } + } + } + + [JsonPropertyName("height")] + public int Height + { + get + { + return _height; + } + + set + { + var newHeight = Math.Max(0, value); + if (newHeight != _height) + { + _height = newHeight; + OnPropertyChanged(); + } + } + } + + public MouseJumpThumbnailSize() + { + Width = 1600; + Height = 1200; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 25761aee7c..5eb65309d4 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -3158,4 +3158,21 @@ Activate by holding the key for the character you want to add an accent to, then Using this shortcut may prevent non-text paste actions (e.g. images, files) or built-in paste plain text actions in other applications from functioning. + + Thumbnail Size + + + Constrain thumbnail image size to a maximum of + + + pixels + + + Maximum height (px) + px = pixels + + + Maximum width (px) + px = pixels + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs index 91c866449c..e781e92a7e 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.ComponentModel; using System.Runtime.CompilerServices; using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; @@ -86,6 +87,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } MouseJumpSettingsConfig = mouseJumpSettingsRepository.SettingsConfig; + MouseJumpSettingsConfig.Properties.ThumbnailSize.PropertyChanged += MouseJumpThumbnailSizePropertyChanged; if (mousePointerCrosshairsSettingsRepository == null) { @@ -599,6 +601,29 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public MouseJumpThumbnailSize MouseJumpThumbnailSize + { + get + { + return MouseJumpSettingsConfig.Properties.ThumbnailSize; + } + + set + { + if ((MouseJumpSettingsConfig.Properties.ThumbnailSize.Width != value?.Width) + && (MouseJumpSettingsConfig.Properties.ThumbnailSize.Height != value?.Height)) + { + MouseJumpSettingsConfig.Properties.ThumbnailSize = value; + NotifyMouseJumpPropertyChanged(); + } + } + } + + public void MouseJumpThumbnailSizePropertyChanged(object sender, PropertyChangedEventArgs e) + { + NotifyMouseJumpPropertyChanged(nameof(MouseJumpThumbnailSize)); + } + public void NotifyMouseJumpPropertyChanged([CallerMemberName] string propertyName = null) { OnPropertyChanged(propertyName); diff --git a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml index 09c60be62e..054b31b59a 100644 --- a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml +++ b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml @@ -229,6 +229,81 @@ MinWidth="{StaticResource SettingActionControlMinWidth}" HotkeySettings="{x:Bind Path=ViewModel.MouseJumpActivationShortcut, Mode=TwoWay}" /> + + + + + + + + + + + + + + From c1f9db01b6135216fe1b0561a147d9e63b526590 Mon Sep 17 00:00:00 2001 From: Michael Clayton Date: Wed, 26 Apr 2023 09:05:31 +0100 Subject: [PATCH 158/163] [Contributors] - adding @mikeclayton per @crutkas request (#25718) --- COMMUNITY.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/COMMUNITY.md b/COMMUNITY.md index 5c9590ef9a..8fce8b92f4 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -37,6 +37,9 @@ Helping keep our spelling correct :) Color Picker is from Martin. +### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com) +Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility. + ### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/) Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well. From 4b1aadafc2a8db6ffeb954d57d7d12b4289d7e24 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Wed, 26 Apr 2023 13:10:18 +0200 Subject: [PATCH 159/163] [runner]Don't save settings on Settings app start (#25647) --- src/runner/settings_window.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 99dc0a4ae6..9dfb269aae 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -370,8 +370,6 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op settings_theme = L"dark"; } - GeneralSettings save_settings = get_general_settings(); - // Arg 6: elevated status bool isElevated{ get_general_settings().isElevated }; std::wstring settings_elevatedStatus = isElevated ? L"true" : L"false"; @@ -397,10 +395,6 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op // Args 13, .... : Optional arguments depending on the options presented before. All by the same value. - // create general settings file to initialize the settings file with installation configurations like : - // 1. Run on start up. - PTSettingsHelper::save_general_settings(save_settings.to_json()); - std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {} {} {}", executable_path, powertoys_pipe_name, From 4da66f483a131f862e8b60c0281744f5aeb2e5a1 Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Wed, 26 Apr 2023 13:11:16 +0200 Subject: [PATCH 160/163] [Settings]A11y fixes, upgrade community toolkit labs dependency (#25579) * Upgrading package * Hiding icon and adding blocking tab focus --- Directory.Packages.props | 2 +- NOTICE.md | 2 +- src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 422fd0e96c..6a0575cbc9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ - + diff --git a/NOTICE.md b/NOTICE.md index 4245f4b462..54aa0e9e95 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -281,7 +281,7 @@ SOFTWARE. ## NuGet Packages used by PowerToys -- CommunityToolkit.Labs.WinUI.SettingsControls 0.0.17 +- CommunityToolkit.Labs.WinUI.SettingsControls 0.0.18 - CommunityToolkit.Mvvm 8.0.0 - CommunityToolkit.WinUI.UI 7.1.2 - CommunityToolkit.WinUI.UI.Controls 7.1.2 diff --git a/src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml b/src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml index b12562892b..5846d9ee9d 100644 --- a/src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml +++ b/src/settings-ui/Settings.UI/Views/ColorPickerPage.xaml @@ -22,7 +22,10 @@ - + @@ -103,6 +107,7 @@ Click="EditButton_Click" Description="{x:Bind Example, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="{x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" + IsActionIconVisible="False" IsClickEnabled="True"> 42 From 12781d7a69743acb945c8ad64da60acde44fee1c Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Wed, 26 Apr 2023 13:15:13 +0200 Subject: [PATCH 161/163] [updater] Do not start PowerToys from PowerToys.Update.exe (#25690) * [updater] Do not start PowerToys from PowerToys.Update.exe PowerToys is already being started from the installer which is run by the updater * Revert version.props --- src/Update/PowerToys.Update.cpp | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/Update/PowerToys.Update.cpp b/src/Update/PowerToys.Update.cpp index 31e4eb8143..d19647623d 100644 --- a/src/Update/PowerToys.Update.cpp +++ b/src/Update/PowerToys.Update.cpp @@ -110,7 +110,7 @@ bool InstallNewVersionStage1(fs::path installer) { // Detect if PT was running const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr); - const bool launch_powertoys = pt_main_window != nullptr; + if (pt_main_window != nullptr) { SendMessageW(pt_main_window, WM_CLOSE, 0, 0); @@ -119,10 +119,7 @@ bool InstallNewVersionStage1(fs::path installer) std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2 }; arguments += L" \""; arguments += installer.c_str(); - arguments += L"\" \""; - arguments += get_module_folderpath(); - arguments += L"\" "; - arguments += launch_powertoys ? UPDATE_STAGE2_RESTART_PT : UPDATE_STAGE2_DONT_START_PT; + arguments += L"\""; SHELLEXECUTEINFOW sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC }; sei.lpFile = copy_in_temp->c_str(); @@ -137,7 +134,7 @@ bool InstallNewVersionStage1(fs::path installer) } } -bool InstallNewVersionStage2(std::wstring installer_path, std::wstring_view install_path, bool launch_powertoys) +bool InstallNewVersionStage2(std::wstring installer_path) { std::transform(begin(installer_path), end(installer_path), begin(installer_path), ::towlower); @@ -181,18 +178,6 @@ bool InstallNewVersionStage2(std::wstring installer_path, std::wstring_view inst state.state = UpdateState::upToDate; }); - if (launch_powertoys) - { - std::wstring new_pt_path{ install_path }; - new_pt_path += L"\\PowerToys.exe"; - SHELLEXECUTEINFOW sei{ sizeof(sei) }; - sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC }; - sei.lpFile = new_pt_path.c_str(); - sei.nShow = SW_SHOWNORMAL; - sei.lpParameters = UPDATE_REPORT_SUCCESS; - return ShellExecuteExW(&sei) == TRUE; - } - return true; } @@ -230,7 +215,7 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) else if (action == UPDATE_NOW_LAUNCH_STAGE2) { using namespace std::string_view_literals; - const bool failed = !InstallNewVersionStage2(args[2], args[3], args[4] == std::wstring_view{ UPDATE_STAGE2_RESTART_PT }); + const bool failed = !InstallNewVersionStage2(args[2]); if (failed) { UpdateState::store([&](UpdateState& state) { From df5791717e2474b977414b86a8a6c9a45a8911e5 Mon Sep 17 00:00:00 2001 From: Randy Date: Wed, 26 Apr 2023 07:17:34 -0700 Subject: [PATCH 162/163] [Registry Preview] Adds "open Registry Editor at this Key" to the Command Bar (#25554) * Adds JumpToKey Adds a new command button that allows the Registry Editor to open to whatever key is selected in the TreeView Also adds some separators in the commandbar for logical grouping of opperations. Also had to pick up the ERRORIMAGE constant from another CR so I don't forget to use it! * Correcting typos in comments --- .github/actions/spell-check/expect.txt | 1 + .../RegistryPreviewUI/MainWindow.Events.cs | 23 ++++++++++++++++ .../RegistryPreviewUI/MainWindow.Utilities.cs | 26 +++++++++++++++++++ .../RegistryPreviewUI/MainWindow.xaml | 19 ++++++++++++-- .../RegistryPreviewUI/MainWindow.xaml.cs | 1 + .../Strings/en-US/Resources.resw | 3 +++ 6 files changed, 71 insertions(+), 2 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 5f5c563b8d..40eaa8397a 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -598,6 +598,7 @@ ERASEBKGND EREOF EResize ERole +ERRORIMAGE ERRORLEVEL ERRORTITLE ESettings diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs index 90eb9f2c24..9fbfbd1931 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs @@ -219,6 +219,26 @@ namespace RegistryPreview OpenRegistryEditor(string.Empty); } + /// + /// Opens the Registry Editor and tries to set "last used"; UAC is handled by the request to open + /// + private void RegistryJumpToKeyButton_Click(object sender, RoutedEventArgs e) + { + // Get the selected Key, if there is one + TreeViewNode currentNode = treeView.SelectedNode; + if (currentNode != null) + { + // since there is a valid node, get the FullPath of the key that was selected + string key = ((RegistryKey)currentNode.Content).FullPath; + + // it's impossible to directly open a key via command-line option, so we must override the last remember key + Microsoft.Win32.Registry.SetValue(@"HKEY_Current_User\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit", "LastKey", key); + } + + // pass in an empty string as we have no file to open + OpenRegistryEditor(string.Empty); + } + /// /// Merges the currently saved file into the Registry Editor; UAC is handled by the request to open /// @@ -308,6 +328,9 @@ namespace RegistryPreview treeViewNode = treeView.SelectedNode; } + // Update the toolbar button for the tree + registryJumpToKeyButton.IsEnabled = CheckTreeForValidKey(); + // Grab the object that has Registry data in it from the currently selected treeView node RegistryKey registryKey = (RegistryKey)treeViewNode.Content; diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs index 06a57ad0bb..7c7f08e073 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs @@ -153,6 +153,9 @@ namespace RegistryPreview } } + // Update the toolbar button for the trees + registryJumpToKeyButton.IsEnabled = CheckTreeForValidKey(); + // enable the UI ChangeCursor(gridPreview, false); } @@ -484,6 +487,9 @@ namespace RegistryPreview refreshButton.IsEnabled = enableRefresh; editButton.IsEnabled = enableEdit; writeButton.IsEnabled = enableWrite; + + // Now check the tree and see if anything is in there + registryJumpToKeyButton.IsEnabled = CheckTreeForValidKey(); } /// @@ -899,5 +905,25 @@ namespace RegistryPreview registryValue.ToolTipText = value; } + + /// + /// Turns the Open Key button in the command bar on/off, depending on if a key is selected + /// + private bool CheckTreeForValidKey() + { + if (treeView == null) + { + return false; + } + + // See if a key is available + TreeViewNode treeViewNode = treeView.SelectedNode; + if (treeViewNode != null && ((RegistryKey)treeViewNode.Content).Image != ERRORIMAGE) + { + return true; + } + + return false; + } } } diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml index b3125844bb..b59d229bbb 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml @@ -4,9 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="using:RegistryPreview" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="using:CommunityToolkit.WinUI.UI" xmlns:winuiex="using:WinUIEx" Closed="Window_Closed" mc:Ignorable="d"> @@ -73,6 +71,7 @@ + + + + + + + + + + + Open Registry Editor... + + Open Key... + Save file as... From 633bf2e8b2a2560c2e0e200294057974d8c99041 Mon Sep 17 00:00:00 2001 From: Randy Date: Wed, 26 Apr 2023 08:14:41 -0700 Subject: [PATCH 163/163] [Registry Preview] Better parsing for Keys and Values (#25537) * Better parsing for keys * Minor source fixes Adds a local comment that didn't get saved on a new proc; also adds ERRORIMAGE to the spellcheck expected list. * Better value parsing * Adding support for REG_NONE * Thank you, File Merging tool! * Encore, file merging tool! * Added check for HKEY_ root Note: HKCU shortcuts don't seem to import successfully in the Editor. * Update MainWindow.Utilities.cs Better checking for the root of a Key. --- .../RegistryPreviewUI/MainWindow.Utilities.cs | 144 +++++++++++++++++- .../Strings/en-US/Resources.resw | 9 ++ 2 files changed, 146 insertions(+), 7 deletions(-) diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs index 7c7f08e073..3e89cf8069 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs @@ -237,18 +237,24 @@ namespace RegistryPreview // remove the - as we won't need it but it will get special treatment in the UI registryLine = registryLine.Remove(1, 1); + string imageName = DELETEDKEYIMAGE; + CheckKeyLineForBrackets(ref registryLine, ref imageName); + // this is a key, so remove the first [ and last ] registryLine = StripFirstAndLast(registryLine); // do not track the result of this node, since it should have no children - AddTextToTree(registryLine, DELETEDKEYIMAGE); + AddTextToTree(registryLine, imageName); } else if (registryLine.StartsWith("[", StringComparison.InvariantCulture)) { + string imageName = KEYIMAGE; + CheckKeyLineForBrackets(ref registryLine, ref imageName); + // this is a key, so remove the first [ and last ] registryLine = StripFirstAndLast(registryLine); - treeViewNode = AddTextToTree(registryLine, KEYIMAGE); + treeViewNode = AddTextToTree(registryLine, imageName); } else if (registryLine.StartsWith("\"", StringComparison.InvariantCulture) && registryLine.EndsWith("=-", StringComparison.InvariantCulture)) { @@ -293,7 +299,16 @@ namespace RegistryPreview // Create a new listview item that will be used to display the value registryValue = new RegistryValue(name, "REG_SZ", string.Empty); - // if the first and last character is a " then this is a string value; get rid of the first and last " + // if the first character is a " then this is a string value, so find the last most " which will avoid comments + if (value.StartsWith("\"", StringComparison.InvariantCulture)) + { + int last = value.LastIndexOf("\"", StringComparison.InvariantCulture); + if (last >= 0) + { + value = value.Substring(0, last + 1); + } + } + if (value.StartsWith("\"", StringComparison.InvariantCulture) && value.EndsWith("\"", StringComparison.InvariantCulture)) { value = StripFirstAndLast(value); @@ -329,6 +344,32 @@ namespace RegistryPreview registryValue.Type = "REG_MULTI_SZ"; value = value.Replace("hex(7):", string.Empty); } + else if (value.StartsWith("hex(0):", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_NONE"; + value = value.Replace("hex(0):", string.Empty); + } + + // special casing for various key types + switch (registryValue.Type) + { + case "REG_SZ": + case "ERROR": + + // no special handling for these two + break; + default: + // check to see if a continuation marker is the first character + if (value == @"\") + { + // pad the value, so the parsing below is triggered + value = @",\"; + } + + value = ScanAndRemoveComments(value); + + break; + } // Parse for the case where a \ is added immediately after hex is declared switch (registryValue.Type) @@ -366,6 +407,7 @@ namespace RegistryPreview } registryLine = registryLines[index]; + registryLine = ScanAndRemoveComments(registryLine); registryLine = registryLine.TrimStart(); value += registryLine; } @@ -373,10 +415,25 @@ namespace RegistryPreview // Clean out any escaped characters in the value, only for the preview value = StripEscapedCharacters(value); - // update the ListViewItem with this information - if (registryValue.Type != "ERROR") + // update the ListViewItem with the loaded value, based off REG value type + switch (registryValue.Type) { - registryValue.Value = value; + case "ERROR": + // do nothing + break; + case "REG_BINARY": + case "REG_NONE": + if (value.Length <= 0) + { + value = resourceLoader.GetString("ZeroLength"); + } + + registryValue.Value = value; + + break; + default: + registryValue.Value = value; + break; } // update the ToolTip @@ -400,7 +457,9 @@ namespace RegistryPreview // check to see if anything got parsed! if (treeView.RootNodes.Count <= 0) { - ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText")); + AddTextToTree(resourceLoader.GetString("NoNodesFoundInFile"), ERRORIMAGE); + + // ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText")); } ChangeCursor(gridPreview, false); @@ -500,6 +559,7 @@ namespace RegistryPreview private TreeViewNode AddTextToTree(string keys, string image) { string[] individualKeys = keys.Split('\\'); + string fullPath = keys; TreeViewNode returnNewNode = null, newNode = null, previousNode = null; @@ -874,6 +934,9 @@ namespace RegistryPreview case KEYIMAGE: value = resourceLoader.GetString("ToolTipAddedKey"); break; + case ERRORIMAGE: + value = resourceLoader.GetString("ToolTipErrorKey"); + break; } return value; @@ -906,6 +969,73 @@ namespace RegistryPreview registryValue.ToolTipText = value; } + /// + /// Checks a Key line for the closing bracket and treat it as an error if it cannot be found + /// + private void CheckKeyLineForBrackets(ref string registryLine, ref string imageName) + { + // following the current behavior of the registry editor, find the last ] and treat everything else as ignorable + int lastBracket = registryLine.LastIndexOf(']'); + if (lastBracket == -1) + { + // since we don't have a last bracket yet, add an extra space and continue processing + registryLine += " "; + imageName = ERRORIMAGE; + } + else + { + // having found the last ] and there is text after it, drop the rest of the string on the floor + if (lastBracket < registryLine.Length - 1) + { + registryLine = registryLine.Substring(0, lastBracket + 1); + } + + if (CheckForKnownGoodBranches(registryLine) == false) + { + imageName = ERRORIMAGE; + } + } + } + + /// + /// Takes a binary registry value, sees if it has a ; and dumps the rest of the line - this does not work for REG_SZ values + /// + private string ScanAndRemoveComments(string value) + { + // scan for comments and remove them + int indexOf = value.IndexOf(";", StringComparison.InvariantCulture); + if (indexOf > -1) + { + // presume that there is nothing following the start of the comment + value = value.Remove(indexOf, value.Length - indexOf); + } + + return value; + } + + /// + /// Make sure the root of a full path start with one of the five "hard coded" roots. Throw an error for the branch if it doesn't. + /// + private bool CheckForKnownGoodBranches(string key) + { + if ((key.StartsWith("[HKEY_CLASSES_ROOT]", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith("[HKEY_CURRENT_USER]", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith("[HKEY_USERS]", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith("[HKEY_LOCAL_MACHINE]", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith("[HKEY_CURRENT_CONFIG]", StringComparison.InvariantCultureIgnoreCase) == false) + && + (key.StartsWith(@"[HKEY_CLASSES_ROOT\", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith(@"[HKEY_CURRENT_USER\", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith(@"[HKEY_USERS\", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith(@"[HKEY_LOCAL_MACHINE\", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith(@"[HKEY_CURRENT_CONFIG\", StringComparison.InvariantCultureIgnoreCase) == false)) + { + return false; + } + + return true; + } + /// /// Turns the Open Key button in the command bar on/off, depending on if a key is selected /// diff --git a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw index de20d4c51d..d0b843f39f 100644 --- a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw +++ b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw @@ -153,6 +153,9 @@ Name + + No valid Keys were found + OK @@ -198,6 +201,9 @@ Value will be deleted + + Key couldn't be parsed + Value has a syntax error @@ -234,4 +240,7 @@ Registry Preview + + (zero-length binary value) + \ No newline at end of file /// Declaration for the disabling hide of flyout window callback function. @@ -330,13 +330,28 @@ namespace Microsoft.PowerToys.Settings.UI.Views { if (json != null) { - if (json.ToString().StartsWith("{\"ShowYourself\":")) + IJsonValue whatToShowJson; + if (json.TryGetValue("ShowYourself", out whatToShowJson)) { - if (json.ToString().EndsWith("\"flyout\"}")) + if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("flyout")) { - OpenFlyoutCallback(); + POINT? p = null; + + IJsonValue flyoutPointX; + IJsonValue flyoutPointY; + if (json.TryGetValue("x_position", out flyoutPointX) && json.TryGetValue("y_position", out flyoutPointY)) + { + if (flyoutPointX.ValueType == JsonValueType.Number && flyoutPointY.ValueType == JsonValueType.Number) + { + int flyout_x = (int)flyoutPointX.GetNumber(); + int flyout_y = (int)flyoutPointY.GetNumber(); + p = new POINT(flyout_x, flyout_y); + } + } + + OpenFlyoutCallback(p); } - else if (json.ToString().EndsWith("\"main_page\"}")) + else if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("main_page")) { OpenMainWindowCallback(); } From a3edac62f188da77ae741b66bbd63a3d4b187f58 Mon Sep 17 00:00:00 2001 From: Jaskaran Singh <81610058+Jaskaran320@users.noreply.github.com> Date: Sun, 5 Feb 2023 11:15:44 +0530 Subject: [PATCH 021/163] Fixed typo in the devdocs ReadMe (#23855) --- doc/devdocs/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/devdocs/readme.md b/doc/devdocs/readme.md index cfc328aeb6..cadde70700 100644 --- a/doc/devdocs/readme.md +++ b/doc/devdocs/readme.md @@ -40,7 +40,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and you 1. Windows 10 April 2018 Update (version 1803) or newer 1. Visual Studio Community/Professional/Enterprise 2022 17.4 or newer 1. Git clone PowerToys repository -1. Open started the `PowerToys.sln` +1. Open the `PowerToys.sln` file. 1. If you see a dialog that says `install extra components` in the solution explorer pane, click `install` ### Get Submodules to compile From c6528cc4ed87c3599784f6f252958e63239e3e77 Mon Sep 17 00:00:00 2001 From: Shrayas Rajagopal Date: Sun, 5 Feb 2023 11:26:49 +0530 Subject: [PATCH 022/163] Fix link to disk usage footprint document (#23798) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e75ccb56cc..5224650e17 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ In this release, we focused on releasing new features and improvements. ### Documentation -- Added PowerToys [disk usage footprint document](doc\devdocs\disk-usage-footprint.md). +- Added PowerToys [disk usage footprint document](/doc/devdocs/disk-usage-footprint.md). - Fixed some grammar issues on main readme / Wiki. Thanks [@CanePlayz](https://github.com/CanePlayz)! ### Development From 7dafb45ab4316e44c085f08d19f61cd8c39e461d Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Mon, 6 Feb 2023 12:40:45 +0100 Subject: [PATCH 023/163] [PTRun]Fix dispatcher crash on Adobe Reader (#23789) * improve thumbnail handling and fix dispatcher crash * spell check * fix spellcheck 2 * restore old code * add adobe thumbnail check * clean spell-check * fix reading of default reg value * switch to real time evaluation * fix spelling * add comment * improve comments * Add additional logs and exception handler * result cache, code improvements, comment improvements * reset log marker to report when switching back to Adobe * spell checker * Improve log text * fix bug where detection was incorrect * spell check * fix return on exception --- .github/actions/spell-check/expect.txt | 1 + .../Wox.Infrastructure/Image/ImageLoader.cs | 9 +- .../Image/WindowsThumbnailProvider.cs | 97 ++++++++++++++++++- 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index bfbba4b263..3a50dce6b3 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1479,6 +1479,7 @@ rescap resgen resheader resizers +RESIZETOFIT resmimetype RESOURCEID resourcemanager diff --git a/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs b/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs index 3cc5832c5c..ef30ab672d 100644 --- a/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs +++ b/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs @@ -180,10 +180,17 @@ namespace Wox.Infrastructure.Image image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.ThumbnailOnly); } } + else if (extension == ".pdf" && WindowsThumbnailProvider.DoesPdfUseAcrobatAsProvider()) + { + // The PDF thumbnail provider from Adobe Reader and Acrobat Pro lets crash PT Run with an Dispatcher exception. (https://github.com/microsoft/PowerToys/issues/18166) + // To not run into the crash, we only request the icon of PDF files if the PDF thumbnail handler is set to Adobe Reader/Acrobat Pro. + type = ImageType.File; + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); + } else { type = ImageType.File; - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.None); + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.RESIZETOFIT); } } else diff --git a/src/modules/launcher/Wox.Infrastructure/Image/WindowsThumbnailProvider.cs b/src/modules/launcher/Wox.Infrastructure/Image/WindowsThumbnailProvider.cs index 267b062c09..8200a6409e 100644 --- a/src/modules/launcher/Wox.Infrastructure/Image/WindowsThumbnailProvider.cs +++ b/src/modules/launcher/Wox.Infrastructure/Image/WindowsThumbnailProvider.cs @@ -4,17 +4,20 @@ using System; using System.IO.Abstractions; +using System.Reflection; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Media.Imaging; +using Microsoft.Win32; +using Wox.Plugin.Logger; namespace Wox.Infrastructure.Image { [Flags] public enum ThumbnailOptions { - None = 0x00, + RESIZETOFIT = 0x00, BiggerSizeOk = 0x01, InMemoryOnly = 0x02, IconOnly = 0x04, @@ -125,5 +128,97 @@ namespace Wox.Infrastructure.Image throw new InvalidComObjectException($"Error while extracting thumbnail for {fileName}", Marshal.GetExceptionForHR((int)hr)); } + + private static bool logReportedAdobeReaderDetected; // Keep track if Adobe Reader detection has been logged yet. + private static bool logReportedErrorInDetectingAdobeReader; // Keep track if we reported an exception while trying to detect Adobe Reader yet. + private static bool adobeReaderDetectionLastResult; // The last result when Adobe Reader detection has read the registry. + private static DateTime adobeReaderDetectionLastTime; // The last time when Adobe Reader detection has read the registry. + + // We have to evaluate this in real time to not crash, if the user switches to Adobe Reader/Acrobat Pro after starting PT Run. + // Adobe registers its thumbnail handler always in "HKCR\Acrobat.Document.*\shellex\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}". + public static bool DoesPdfUseAcrobatAsProvider() + { + // If the last run is not more than five seconds ago use its result. + // Doing this we minimize the amount of Registry queries, if more than one new PDF file is shown in the results. + if ((DateTime.Now - adobeReaderDetectionLastTime).TotalSeconds < 5) + { + return adobeReaderDetectionLastResult; + } + + // If cache condition is false, then query the registry. + try + { + // First detect if there is a provider other than Adobe. For example PowerToys. + // Generic GUIDs used by Explorer to identify the configured provider types: {E357FCCD-A995-4576-B01F-234630154E96} = File thumbnail, {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1} = Image thumbnail. + RegistryKey key1 = Registry.ClassesRoot.OpenSubKey(".pdf\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}", false); + string value1 = (string)key1?.GetValue(string.Empty); + key1?.Close(); + RegistryKey key2 = Registry.ClassesRoot.OpenSubKey(".pdf\\shellex\\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}", false); + string value2 = (string)key2?.GetValue(string.Empty); + key2?.Close(); + if (!string.IsNullOrEmpty(value1) || !string.IsNullOrEmpty(value2)) + { + // A provider other than Adobe is used. (For example the PowerToys PDF Thumbnail provider.) + logReportedAdobeReaderDetected = false; // Reset log marker to report when Adobe is reused. (Example: Adobe -> Test PowerToys. -> Back to Adobe.) + adobeReaderDetectionLastResult = false; + adobeReaderDetectionLastTime = DateTime.Now; + return false; + } + + // Then detect if Adobe is the default PDF application. + // The global config can be found under "HKCR\.pdf", but the "UserChoice" key under HKCU has precedence. + RegistryKey pdfKeyUser = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.pdf\\UserChoice", false); + string pdfAppUser = (string)pdfKeyUser?.GetValue("ProgId"); + pdfKeyUser?.Close(); + RegistryKey pdfKeyGlobal = Registry.ClassesRoot.OpenSubKey(".pdf", false); + string pdfAppGlobal = (string)pdfKeyGlobal?.GetValue(string.Empty); + pdfKeyGlobal?.Close(); + string pdfApp = !string.IsNullOrEmpty(pdfAppUser) ? pdfAppUser : pdfAppGlobal; // User choice has precedence. + if (string.IsNullOrEmpty(pdfApp) || !pdfApp.StartsWith("Acrobat.Document.", StringComparison.OrdinalIgnoreCase)) + { + // Adobe is not used as PDF application. + logReportedAdobeReaderDetected = false; // Reset log marker to report when Adobe is reused. (Example: Adobe -> Test PowerToys. -> Back to Adobe.) + adobeReaderDetectionLastResult = false; + adobeReaderDetectionLastTime = DateTime.Now; + return false; + } + + // Detect if the thumbnail handler from Adobe is disabled. + RegistryKey adobeAppKey = Registry.ClassesRoot.OpenSubKey(pdfApp + "\\shellex\\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}", false); + string adobeAppProvider = (string)adobeAppKey?.GetValue(string.Empty); + adobeAppKey?.Close(); + if (string.IsNullOrEmpty(adobeAppProvider)) + { + // No Adobe handler. + logReportedAdobeReaderDetected = false; // Reset log marker to report when Adobe is reused. (Example: Adobe -> Test PowerToys. -> Back to Adobe.) + adobeReaderDetectionLastResult = false; + adobeReaderDetectionLastTime = DateTime.Now; + return false; + } + + // Thumbnail handler from Adobe is enabled and used. + if (!logReportedAdobeReaderDetected) + { + logReportedAdobeReaderDetected = true; + Log.Info("Adobe Reader / Adobe Acrobat Pro has been detected as the PDF thumbnail provider.", MethodBase.GetCurrentMethod().DeclaringType); + } + + adobeReaderDetectionLastResult = true; + adobeReaderDetectionLastTime = DateTime.Now; + return true; + } + catch (System.Exception ex) + { + if (!logReportedErrorInDetectingAdobeReader) + { + logReportedErrorInDetectingAdobeReader = true; + Log.Exception("Got an exception while trying to detect Adobe Reader / Adobe Acrobat Pro as PDF thumbnail provider. To prevent PT Run from a Dispatcher crash, we report that Adobe Reader / Adobe Acrobat Pro is used and show only the PDF icon in the results.", ex, MethodBase.GetCurrentMethod().DeclaringType); + } + + // If we fail to detect it, we return that Adobe is used. Otherwise we could run into the Dispatcher crash. + // (This only results in showing the icon instead of a thumbnail. It has no other functional impact.) + return true; + } + } } } From 840b8d73b12898d070e80cbabc92a161bda65119 Mon Sep 17 00:00:00 2001 From: sosssego Date: Mon, 6 Feb 2023 13:33:26 +0000 Subject: [PATCH 024/163] [Settings][Hosts]Correct Hosts image in settings (#23830) --- src/settings-ui/Settings.UI/Views/HostsPage.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings-ui/Settings.UI/Views/HostsPage.xaml b/src/settings-ui/Settings.UI/Views/HostsPage.xaml index bb54a4b1a3..90736fa153 100644 --- a/src/settings-ui/Settings.UI/Views/HostsPage.xaml +++ b/src/settings-ui/Settings.UI/Views/HostsPage.xaml @@ -9,7 +9,7 @@ xmlns:ui="using:CommunityToolkit.WinUI.UI" mc:Ignorable="d"> - + Date: Mon, 6 Feb 2023 17:15:19 +0100 Subject: [PATCH 025/163] [FancyZones] Fix window resize on layout change (#23829) --- .../fancyzones/FancyZonesLib/FancyZones.cpp | 53 +++++++++---------- .../FancyZonesLib/LayoutAssignedWindows.cpp | 5 ++ .../FancyZonesLib/LayoutAssignedWindows.h | 1 + .../fancyzones/FancyZonesLib/WorkArea.cpp | 47 +++++++++++----- .../fancyzones/FancyZonesLib/WorkArea.h | 3 +- 5 files changed, 66 insertions(+), 43 deletions(-) diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 4659f7b34f..b45e28ac59 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -118,13 +118,13 @@ public: LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; void OnDisplayChange(DisplayChangeType changeType) noexcept; - void AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id) noexcept; + void AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id, bool updateWindowsPositions) noexcept; protected: static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; private: - void UpdateWorkAreas() noexcept; + void UpdateWorkAreas(bool updateWindowPositions) noexcept; void CycleWindows(bool reverse) noexcept; bool OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept; bool OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept; @@ -139,8 +139,7 @@ private: void MoveWindowIntoZone(HWND window, WorkArea* const workArea, const ZoneIndexSet& zoneIndexSet) noexcept; bool MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept; - void OnEditorExitEvent() noexcept; - void UpdateZoneSets() noexcept; + void UpdateActiveLayouts() noexcept; bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept; void ApplyQuickLayout(int key) noexcept; void FlashZones() noexcept; @@ -618,15 +617,8 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa } else if (message == WM_PRIV_EDITOR) { - if (lparam == static_cast(EditorExitKind::Exit)) - { - OnEditorExitEvent(); - } - - { - // Clean up the event either way - m_terminateEditorEvent.release(); - } + // Clean up the event either way + m_terminateEditorEvent.release(); } else if (message == WM_PRIV_MOVESIZESTART) { @@ -667,7 +659,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa else if (message == WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE) { AppliedLayouts::instance().LoadData(); - UpdateZoneSets(); + UpdateActiveLayouts(); } else if (message == WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE) { @@ -711,10 +703,12 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept } } - UpdateWorkAreas(); + bool updateWindowsPositionsOnResolutionChange = FancyZonesSettings::settings().displayChange_moveWindows && changeType == DisplayChangeType::DisplayChange; + bool updateWindowsPositionsOnStart = FancyZonesSettings::settings().zoneSetChange_moveWindows && changeType == DisplayChangeType::Initialization; + UpdateWorkAreas(updateWindowsPositionsOnResolutionChange || updateWindowsPositionsOnStart); } -void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id) noexcept +void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id, bool updateWindowsPositions) noexcept { wil::unique_cotaskmem_string virtualDesktopIdStr; if (!SUCCEEDED(StringFromCLSID(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), &virtualDesktopIdStr))) @@ -742,8 +736,12 @@ void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAr auto workArea = WorkArea::Create(m_hinstance, id, parentId, rect); if (workArea) { + if (updateWindowsPositions) + { + workArea->UpdateWindowPositions(); + } + m_workAreaHandler.AddWorkArea(monitor, std::move(workArea)); - AppliedLayouts::instance().SaveData(); } } @@ -761,7 +759,7 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, DefWindowProc(window, message, wparam, lparam); } -void FancyZones::UpdateWorkAreas() noexcept +void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept { m_workAreaHandler.Clear(); @@ -771,7 +769,7 @@ void FancyZones::UpdateWorkAreas() noexcept workAreaId.virtualDesktopId = VirtualDesktop::instance().GetCurrentVirtualDesktopId(); workAreaId.monitorId = { .deviceId = { .id = ZonedWindowProperties::MultiMonitorName, .instanceId = ZonedWindowProperties::MultiMonitorInstance } }; - AddWorkArea(nullptr, workAreaId); + AddWorkArea(nullptr, workAreaId, updateWindowPositions); } else { @@ -782,7 +780,7 @@ void FancyZones::UpdateWorkAreas() noexcept workAreaId.virtualDesktopId = VirtualDesktop::instance().GetCurrentVirtualDesktopId(); workAreaId.monitorId = monitor; - AddWorkArea(monitor.monitor, workAreaId); + AddWorkArea(monitor.monitor, workAreaId, updateWindowPositions); } } } @@ -1117,19 +1115,18 @@ void FancyZones::SettingsUpdate(SettingId id) } } -void FancyZones::OnEditorExitEvent() noexcept -{ - // Collect information about changes in zone layout after editor exited. - UpdateZoneSets(); -} - -void FancyZones::UpdateZoneSets() noexcept +void FancyZones::UpdateActiveLayouts() noexcept { for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) { if (workArea) { workArea->UpdateActiveZoneSet(); + + if (FancyZonesSettings::settings().zoneSetChange_moveWindows) + { + workArea->UpdateWindowPositions(); + } } } } @@ -1197,7 +1194,7 @@ void FancyZones::ApplyQuickLayout(int key) noexcept { AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value()); AppliedLayouts::instance().SaveData(); - UpdateZoneSets(); + UpdateActiveLayouts(); FlashZones(); } } diff --git a/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp b/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp index 45ab955d76..75cc6b5fa4 100644 --- a/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp +++ b/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp @@ -69,6 +69,11 @@ void LayoutAssignedWindows::Dismiss(HWND window) FancyZonesWindowProperties::SetTabSortKeyWithinZone(window, std::nullopt); } +std::map LayoutAssignedWindows::SnappedWindows() const noexcept +{ + return m_windowIndexSet; +} + ZoneIndexSet LayoutAssignedWindows::GetZoneIndexSetFromWindow(HWND window) const noexcept { auto it = m_windowIndexSet.find(window); diff --git a/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h b/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h index ae7c3d4fd2..64377e36b1 100644 --- a/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h +++ b/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h @@ -19,6 +19,7 @@ public : void Extend(HWND window, const ZoneIndexSet& zones); void Dismiss(HWND window); + std::map SnappedWindows() const noexcept; ZoneIndexSet GetZoneIndexSetFromWindow(HWND window) const noexcept; bool IsZoneEmpty(ZoneIndex zoneIndex) const noexcept; diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index ab71fbc796..d418adffb7 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -144,8 +144,11 @@ void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& ind if (updatePosition) { const auto rect = m_layout->GetCombinedZonesRect(indexSet); - const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); - FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); + if (rect.bottom - rect.top > 0 && rect.right - rect.left > 0) + { + const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); + FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); + } } SnapWindow(window, indexSet); @@ -351,9 +354,7 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); - m_layoutWindows->Extend(window, resultIndexSet); - - SnapWindow(window, resultIndexSet); + SnapWindow(window, resultIndexSet, true); return true; } @@ -361,15 +362,22 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) return false; } -void WorkArea::SnapWindow(HWND window, const ZoneIndexSet& zones) +void WorkArea::SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend) { if (!m_layoutWindows || !m_layout) { return; } - m_layoutWindows->Assign(window, zones); - + if (extend) + { + m_layoutWindows->Extend(window, zones); + } + else + { + m_layoutWindows->Assign(window, zones); + } + auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id()); if (guidStr.has_value()) { @@ -462,6 +470,20 @@ void WorkArea::UpdateActiveZoneSet() } } +void WorkArea::UpdateWindowPositions() +{ + if (!m_layoutWindows) + { + return; + } + + const auto& snappedWindows = m_layoutWindows->SnappedWindows(); + for (const auto& [window, zones] : snappedWindows) + { + MoveWindowIntoZoneByIndexSet(window, zones, true); + } +} + void WorkArea::CycleWindows(HWND window, bool reverse) { if (m_layoutWindows) @@ -507,8 +529,7 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) void WorkArea::InitSnappedWindows() { - static bool updatePositionOnceOnStartFlag = true; - Logger::info(L"Init work area windows, update positions = {}", updatePositionOnceOnStartFlag); + Logger::info(L"Init work area windows"); for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop()) { @@ -520,19 +541,17 @@ void WorkArea::InitSnappedWindows() if (!m_uniqueId.monitorId.monitor) // one work area across monitors { - MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, updatePositionOnceOnStartFlag); + MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, false); } else { const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); if (monitor && m_uniqueId.monitorId.monitor == monitor) { - MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, updatePositionOnceOnStartFlag); + MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, false); } } } - - updatePositionOnceOnStartFlag = false; } void WorkArea::CalculateZoneSet() diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.h b/src/modules/fancyzones/FancyZonesLib/WorkArea.h index a273b31675..ae0f4f1a5b 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.h +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.h @@ -51,10 +51,11 @@ public: bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle); bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode); - void SnapWindow(HWND window, const ZoneIndexSet& zones); + void SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend = false); void UnsnapWindow(HWND window); void UpdateActiveZoneSet(); + void UpdateWindowPositions(); void ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr); void HideZonesOverlay(); From a6e039629099dbfbb8ebe8182927f6461b1cb3d0 Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Tue, 7 Feb 2023 12:14:24 +0100 Subject: [PATCH 026/163] [FancyZones] Remove app from app-zone-history when dragging starts (#23926) --- src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp index 25de2dbb1a..c281f88c51 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp @@ -56,13 +56,15 @@ bool WindowDrag::MoveSizeStart(HMONITOR monitor, bool isSnapping) return false; } - if (isSnapping) - { - m_currentWorkArea = iter->second.get(); - } + m_currentWorkArea = iter->second.get(); SwitchSnappingMode(isSnapping); + if (m_currentWorkArea) + { + m_currentWorkArea->UnsnapWindow(m_window); + } + return true; } @@ -169,7 +171,6 @@ void WindowDrag::SwitchSnappingMode(bool isSnapping) if (m_currentWorkArea) { - m_currentWorkArea->UnsnapWindow(m_window); Trace::WorkArea::MoveOrResizeStarted(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows().get()); } } From c180150d5413c58f8ab7a4eed551dd0ab71ec52e Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Tue, 7 Feb 2023 13:02:22 +0100 Subject: [PATCH 027/163] [FancyZones] Keep the same layouts on the new virtual desktops. (#23927) --- .../fancyzones/FancyZonesLib/FancyZones.cpp | 10 ++------- .../FancyZonesLib/MonitorWorkAreaMap.cpp | 22 +++++++++++++++++++ .../FancyZonesLib/MonitorWorkAreaMap.h | 9 ++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index b45e28ac59..a8eb8c018f 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -716,13 +716,6 @@ void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAr Logger::debug(L"Add new work area on virtual desktop {}", virtualDesktopIdStr.get()); } - FancyZonesDataTypes::WorkAreaId parentId{}; - auto parentArea = m_workAreaHandler.GetWorkArea(monitor); - if (parentArea) - { - parentId = parentArea->UniqueId(); - } - FancyZonesUtils::Rect rect{}; if (monitor) { @@ -733,7 +726,7 @@ void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAr rect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>(); } - auto workArea = WorkArea::Create(m_hinstance, id, parentId, rect); + auto workArea = WorkArea::Create(m_hinstance, id, m_workAreaHandler.GetParent(monitor), rect); if (workArea) { if (updateWindowsPositions) @@ -761,6 +754,7 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept { + m_workAreaHandler.SaveParentIds(); m_workAreaHandler.Clear(); if (FancyZonesSettings::settings().spanZonesAcrossMonitors) diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp index 089c28c905..486e2739ea 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp +++ b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp @@ -61,6 +61,28 @@ void MonitorWorkAreaMap::AddWorkArea(HMONITOR monitor, std::unique_ptr m_workAreaMap.insert({ monitor, std::move(workArea) }); } +FancyZonesDataTypes::WorkAreaId MonitorWorkAreaMap::GetParent(HMONITOR monitor) const +{ + if (m_workAreaParents.contains(monitor)) + { + return m_workAreaParents.at(monitor); + } + + return FancyZonesDataTypes::WorkAreaId{}; +} + +void MonitorWorkAreaMap::SaveParentIds() +{ + m_workAreaParents.clear(); + for (const auto& [monitor, workArea] : m_workAreaMap) + { + if (workArea) + { + m_workAreaParents.insert({ monitor, workArea->UniqueId() }); + } + } +} + void MonitorWorkAreaMap::Clear() noexcept { m_workAreaMap.clear(); diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h index 10cb2dd0d6..45fabda709 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h +++ b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h @@ -1,6 +1,7 @@ #pragma once #include "GuidUtils.h" +#include class WorkArea; @@ -48,6 +49,13 @@ public: */ void AddWorkArea(HMONITOR monitor, std::unique_ptr workArea); + FancyZonesDataTypes::WorkAreaId GetParent(HMONITOR monitor) const; + + /** + * Saving current work area IDs as parents for later use. + */ + void SaveParentIds(); + /** * Clear all persisted work area related data. */ @@ -55,4 +63,5 @@ public: private: std::unordered_map> m_workAreaMap; + std::unordered_map m_workAreaParents{}; }; From 53f0b00328a9688f36e5b1eeae311c488ae3cae5 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Tue, 7 Feb 2023 21:11:31 +0100 Subject: [PATCH 028/163] [Thumbnail providers] Cover errors and edge cases (#23947) * [Thumbnail providers] Cover errors and edge cases * Add TODO comment * Fix tests --- .../GcodeThumbnailProvider.cs | 8 ++- .../GcodeThumbnailProvider/Program.cs | 7 ++- .../GcodeThumbnailProvider.cpp | 16 ++++-- .../GcodeThumbnailProvider.h | 2 +- .../PdfThumbnailProvider.cs | 53 +++++++++---------- .../PdfThumbnailProvider/Program.cs | 7 ++- .../PdfThumbnailProvider.cpp | 14 +++-- .../PdfThumbnailProvider.h | 2 +- .../StlThumbnailProvider/Program.cs | 7 ++- .../StlThumbnailProvider.cs | 15 ++---- .../StlThumbnailProvider.cpp | 15 ++++-- .../StlThumbnailProvider.h | 2 +- .../SvgThumbnailProvider/Program.cs | 7 ++- .../SvgThumbnailProvider.cpp | 14 +++-- .../SvgThumbnailProvider.h | 2 +- .../PdfThumbnailProviderTests.cs | 6 +-- 16 files changed, 104 insertions(+), 73 deletions(-) diff --git a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs index 09a3c98c27..cc30703e2b 100644 --- a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs +++ b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs @@ -159,12 +159,10 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Gcode using (var reader = new StreamReader(this.Stream)) { - using (Bitmap thumbnail = GetThumbnail(reader, cx)) + Bitmap thumbnail = GetThumbnail(reader, cx); + if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) { - if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) - { - return (Bitmap)thumbnail.Clone(); - } + return thumbnail; } } diff --git a/src/modules/previewpane/GcodeThumbnailProvider/Program.cs b/src/modules/previewpane/GcodeThumbnailProvider/Program.cs index 8426ca7755..e1b920feb6 100644 --- a/src/modules/previewpane/GcodeThumbnailProvider/Program.cs +++ b/src/modules/previewpane/GcodeThumbnailProvider/Program.cs @@ -26,8 +26,11 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Gcode _thumbnailProvider = new GcodeThumbnailProvider(filePath); Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); - filePath = filePath.Replace(".gcode", ".bmp"); - thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + if (thumbnail != null) + { + filePath = filePath.Replace(".gcode", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } } else { diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp index 7f16288d46..64e2a8067d 100644 --- a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp @@ -159,11 +159,19 @@ IFACEMETHODIMP GcodeThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS WaitForSingleObject(m_process, INFINITE); std::filesystem::remove(fileName); + std::wstring fileNameBmp = filePath + guid + L".bmp"; - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; - - std::filesystem::remove(fileNameBmp); + if (std::filesystem::exists(fileNameBmp)) + { + *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + std::filesystem::remove(fileNameBmp); + } + else + { + Logger::info(L"Bmp file not generated."); + return E_FAIL; + } } catch (std::exception& e) { diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h index 8662de66e6..91dabda4ad 100644 --- a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h @@ -19,7 +19,7 @@ public: // IInitializeWithStream IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); - // IPreviewHandler + // IThumbnailProvider IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); GcodeThumbnailProvider(); diff --git a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs index 0852369f96..e82607b299 100644 --- a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs +++ b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs @@ -1,14 +1,8 @@ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Drawing; -using System.IO; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using Common.ComInterlop; -using Common.Utilities; using Windows.Data.Pdf; +using Windows.Storage; using Windows.Storage.Streams; namespace Microsoft.PowerToys.ThumbnailHandler.Pdf @@ -21,7 +15,6 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf public PdfThumbnailProvider(string filePath) { FilePath = filePath; - Stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); } /// @@ -29,11 +22,6 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf /// public string FilePath { get; private set; } - /// - /// Gets the stream object to access file. - /// - public Stream Stream { get; private set; } - /// /// The maximum dimension (width or height) thumbnail we will generate. /// @@ -45,6 +33,16 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf /// Maximum thumbnail size, in pixels. /// Generated bitmap public Bitmap GetThumbnail(uint cx) + { + return DoGetThumbnail(cx).Result; + } + + /// + /// Generate thumbnail bitmap for provided Pdf file/stream. + /// + /// Maximum thumbnail size, in pixels. + /// Generated bitmap + private async Task DoGetThumbnail(uint cx) { if (cx == 0 || cx > MaxThumbnailSize) { @@ -57,26 +55,27 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf return null; } - using var memStream = new MemoryStream(); - - this.Stream.CopyTo(memStream); - memStream.Position = 0; - - // AsRandomAccessStream() extension method from System.Runtime.WindowsRuntime - var pdf = PdfDocument.LoadFromStreamAsync(memStream.AsRandomAccessStream()).GetAwaiter().GetResult(); - - if (pdf.PageCount > 0) + Bitmap thumbnail = null; + try { - using var page = pdf.GetPage(0); + var file = await StorageFile.GetFileFromPathAsync(FilePath); + var pdf = await PdfDocument.LoadFromFileAsync(file); - var image = PageToImage(page, cx); + if (pdf.PageCount > 0) + { + using var page = pdf.GetPage(0); - using Bitmap thumbnail = new Bitmap(image); + var image = PageToImage(page, cx); - return (Bitmap)thumbnail.Clone(); + thumbnail = new Bitmap(image); + } + } + catch (Exception) + { + // TODO: add logger } - return null; + return thumbnail; } /// diff --git a/src/modules/previewpane/PdfThumbnailProvider/Program.cs b/src/modules/previewpane/PdfThumbnailProvider/Program.cs index 59cf1acb99..5415a333ad 100644 --- a/src/modules/previewpane/PdfThumbnailProvider/Program.cs +++ b/src/modules/previewpane/PdfThumbnailProvider/Program.cs @@ -26,8 +26,11 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf _thumbnailProvider = new PdfThumbnailProvider(filePath); Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); - filePath = filePath.Replace(".pdf", ".bmp"); - thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + if (thumbnail != null) + { + filePath = filePath.Replace(".pdf", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } } else { diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp index 312512a454..d74fd9bfab 100644 --- a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp @@ -158,10 +158,18 @@ IFACEMETHODIMP PdfThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A std::filesystem::remove(fileName); std::wstring fileNameBmp = filePath + guid + L".bmp"; - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + if (std::filesystem::exists(fileNameBmp)) + { + *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + std::filesystem::remove(fileNameBmp); + } + else + { + Logger::info(L"Bmp file not generated."); + return E_FAIL; + } - std::filesystem::remove(fileNameBmp); } catch (std::exception& e) { diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h index 9ae9b43445..c63d6d37bd 100644 --- a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h @@ -19,7 +19,7 @@ public: // IInitializeWithStream IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); - // IPreviewHandler + // IThumbnailProvider IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); PdfThumbnailProvider(); diff --git a/src/modules/previewpane/StlThumbnailProvider/Program.cs b/src/modules/previewpane/StlThumbnailProvider/Program.cs index 77e904ba1e..ce22471f67 100644 --- a/src/modules/previewpane/StlThumbnailProvider/Program.cs +++ b/src/modules/previewpane/StlThumbnailProvider/Program.cs @@ -25,8 +25,11 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Stl _thumbnailProvider = new StlThumbnailProvider(filePath); Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); - filePath = filePath.Replace(".stl", ".bmp"); - thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + if (thumbnail != null) + { + filePath = filePath.Replace(".stl", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } } else { diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs index c7d4ebdd14..aeecf4599a 100644 --- a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs +++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs @@ -130,19 +130,10 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Stl return null; } - using (var memStream = new MemoryStream()) + Bitmap thumbnail = GetThumbnail(this.Stream, cx); + if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) { - this.Stream.CopyTo(memStream); - - memStream.Position = 0; - - using (Bitmap thumbnail = GetThumbnail(memStream, cx)) - { - if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) - { - return (Bitmap)thumbnail.Clone(); - } - } + return thumbnail; } return null; diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp index eada9c4b06..b01d331ce7 100644 --- a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp @@ -158,10 +158,17 @@ IFACEMETHODIMP StlThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A std::filesystem::remove(fileName); std::wstring fileNameBmp = filePath + guid + L".bmp"; - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; - - std::filesystem::remove(fileNameBmp); + if (std::filesystem::exists(fileNameBmp)) + { + *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + std::filesystem::remove(fileNameBmp); + } + else + { + Logger::info(L"Bmp file not generated."); + return E_FAIL; + } } catch (std::exception& e) { diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h index ea97d69c82..fe7c4c5119 100644 --- a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h @@ -19,7 +19,7 @@ public: // IInitializeWithStream IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); - // IPreviewHandler + // IThumbnailProvider IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); StlThumbnailProvider(); diff --git a/src/modules/previewpane/SvgThumbnailProvider/Program.cs b/src/modules/previewpane/SvgThumbnailProvider/Program.cs index f81a8f46ce..71172f0ac1 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/Program.cs +++ b/src/modules/previewpane/SvgThumbnailProvider/Program.cs @@ -26,8 +26,11 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg _thumbnailProvider = new SvgThumbnailProvider(filePath); Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); - filePath = filePath.Replace(".svg", ".bmp"); - thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + if (thumbnail != null ) + { + filePath = filePath.Replace(".svg", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } } else { diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp index 0f42b32f3b..7661095e32 100644 --- a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp @@ -158,10 +158,18 @@ IFACEMETHODIMP SvgThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A std::filesystem::remove(fileName); std::wstring fileNameBmp = filePath + guid + L".bmp"; - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; - std::filesystem::remove(fileNameBmp); + if (std::filesystem::exists(fileNameBmp)) + { + *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + std::filesystem::remove(fileNameBmp); + } + else + { + Logger::info(L"Bmp file not generated."); + return E_FAIL; + } } catch (std::exception& e) { diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h index 3c8a7f1b9c..b247dfa390 100644 --- a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h @@ -19,7 +19,7 @@ public: // IInitializeWithStream IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); - // IPreviewHandler + // IThumbnailProvider IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); SvgThumbnailProvider(); diff --git a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs index a406c89ff5..635317e51e 100644 --- a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs +++ b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs @@ -22,7 +22,7 @@ namespace PdfThumbnailProviderUnitTests public void GetThumbnailValidStreamPDF() { // Act - var filePath = "HelperFiles/sample.pdf"; + var filePath = System.IO.Path.GetFullPath("HelperFiles/sample.pdf"); PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); @@ -35,7 +35,7 @@ namespace PdfThumbnailProviderUnitTests public void GetThumbnailInValidSizePDF() { // Act - var filePath = "HelperFiles/sample.pdf"; + var filePath = System.IO.Path.GetFullPath("HelperFiles/sample.pdf"); PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); @@ -48,7 +48,7 @@ namespace PdfThumbnailProviderUnitTests public void GetThumbnailToBigPDF() { // Act - var filePath = "HelperFiles/sample.pdf"; + var filePath = System.IO.Path.GetFullPath("HelperFiles/sample.pdf"); PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); From 956eb9812518f0b3d71e7e39946cd0bd1263400a Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 10:59:34 +0000 Subject: [PATCH 029/163] [Analyzers][CPP] Turn on rule 26497 (#23119) * Turn on warning 26497 This function function-name could be marked constexpr if compile-time evaluation is desired. * C++20 has constexpr swap * as constexpr is not only for compile time, make all functions that can be constexpr constexpr * constexpr functions are implicity inline --- CppRuleSet.ruleset | 2 +- src/common/notifications/notifications.cpp | 2 +- src/common/utils/exec.h | 3 +- .../ShortcutGuide/shortcut_guide.cpp | 4 +-- .../fancyzones/FancyZonesLib/MonitorUtils.cpp | 2 +- src/modules/fancyzones/FancyZonesLib/util.cpp | 26 ----------------- src/modules/fancyzones/FancyZonesLib/util.h | 29 +++++++++++++++++-- .../KeyDropDownControl.cpp | 6 +++- .../XamlBridge.cpp | 2 +- .../keyboardmanager/common/Shortcut.cpp | 4 +-- .../VideoConferenceShared/Logging.h | 4 +-- tools/WebcamReportTool/DirectShowUtils.h | 3 +- tools/WebcamReportTool/main.cpp | 3 +- 13 files changed, 48 insertions(+), 42 deletions(-) diff --git a/CppRuleSet.ruleset b/CppRuleSet.ruleset index 1a59aeeeb3..ad63dc5a1a 100644 --- a/CppRuleSet.ruleset +++ b/CppRuleSet.ruleset @@ -84,7 +84,7 @@ - + diff --git a/src/common/notifications/notifications.cpp b/src/common/notifications/notifications.cpp index 7f03af0633..f6d5dbb11b 100644 --- a/src/common/notifications/notifications.cpp +++ b/src/common/notifications/notifications.cpp @@ -206,7 +206,7 @@ void notifications::show_toast(std::wstring message, std::wstring title, toast_p show_toast_with_activations(std::move(message), std::move(title), {}, {}, std::move(params)); } -inline void xml_escape(std::wstring data) +constexpr inline void xml_escape(std::wstring data) { std::wstring buffer; buffer.reserve(data.size()); diff --git a/src/common/utils/exec.h b/src/common/utils/exec.h index feeda08b89..3c6094e98d 100644 --- a/src/common/utils/exec.h +++ b/src/common/utils/exec.h @@ -4,8 +4,9 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable: 26471) +#pragma warning(disable : 26471 26497) #include #pragma warning(pop) diff --git a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp index 8cff1c4e47..e150cf0c08 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp @@ -115,12 +115,12 @@ namespace return true; } - bool isWin(int key) + constexpr bool isWin(int key) { return key == VK_LWIN || key == VK_RWIN; } - bool isKeyDown(LowlevelKeyboardEvent event) + constexpr bool isKeyDown(LowlevelKeyboardEvent event) { return event.wParam == WM_KEYDOWN || event.wParam == WM_SYSKEYDOWN; } diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp index 9f8281804b..4926c287d3 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp @@ -207,7 +207,7 @@ namespace MonitorUtils return { .id = str.substr(0, dividerPos), .instanceId = str.substr(dividerPos + 1) }; } - inline bool not_digit(wchar_t ch) + constexpr inline bool not_digit(wchar_t ch) { return '0' <= ch && ch <= '9'; } diff --git a/src/modules/fancyzones/FancyZonesLib/util.cpp b/src/modules/fancyzones/FancyZonesLib/util.cpp index 151e5d9220..4b4ccc40cb 100644 --- a/src/modules/fancyzones/FancyZonesLib/util.cpp +++ b/src/modules/fancyzones/FancyZonesLib/util.cpp @@ -248,32 +248,6 @@ namespace FancyZonesUtils return closestIdx; } - RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept - { - LONG deltaX = 0, deltaY = 0; - switch (vkCode) - { - case VK_UP: - deltaY = workAreaRect.bottom - workAreaRect.top; - break; - case VK_DOWN: - deltaY = workAreaRect.top - workAreaRect.bottom; - break; - case VK_LEFT: - deltaX = workAreaRect.right - workAreaRect.left; - break; - case VK_RIGHT: - deltaX = workAreaRect.left - workAreaRect.right; - } - - windowRect.left += deltaX; - windowRect.right += deltaX; - windowRect.top += deltaY; - windowRect.bottom += deltaY; - - return windowRect; - } - void SwallowKey(const WORD key) noexcept { INPUT inputKey[1] = {}; diff --git a/src/modules/fancyzones/FancyZonesLib/util.h b/src/modules/fancyzones/FancyZonesLib/util.h index 8a6e28a313..41d1a9b4d8 100644 --- a/src/modules/fancyzones/FancyZonesLib/util.h +++ b/src/modules/fancyzones/FancyZonesLib/util.h @@ -93,7 +93,7 @@ namespace FancyZonesUtils } } - inline BYTE OpacitySettingToAlpha(int opacity) + constexpr inline BYTE OpacitySettingToAlpha(int opacity) { return static_cast(opacity * 2.55); } @@ -168,6 +168,32 @@ namespace FancyZonesUtils return result; } + constexpr RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept + { + LONG deltaX = 0, deltaY = 0; + switch (vkCode) + { + case VK_UP: + deltaY = workAreaRect.bottom - workAreaRect.top; + break; + case VK_DOWN: + deltaY = workAreaRect.top - workAreaRect.bottom; + break; + case VK_LEFT: + deltaX = workAreaRect.right - workAreaRect.left; + break; + case VK_RIGHT: + deltaX = workAreaRect.left - workAreaRect.right; + } + + windowRect.left += deltaX; + windowRect.right += deltaX; + windowRect.top += deltaY; + windowRect.bottom += deltaY; + + return windowRect; + } + UINT GetDpiForMonitor(HMONITOR monitor) noexcept; void OrderMonitors(std::vector>& monitorInfo); @@ -175,7 +201,6 @@ namespace FancyZonesUtils std::optional GuidFromString(const std::wstring& str) noexcept; std::optional GuidToString(const GUID& guid) noexcept; - RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept; size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector& zoneRects) noexcept; void SwallowKey(const WORD key) noexcept; diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp index fad1aa4a26..44e109ee19 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp @@ -421,8 +421,12 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl } } +// Disable 26497 this function should be evaluated at compile time +#pragma warning(push) +#pragma warning(disable : 26497) // Get number of selected keys. Do not count -1 and 0 values as they stand for Not selected and None int KeyDropDownControl::GetNumberOfSelectedKeys(std::vector keyCodes) { return (int)std::count_if(keyCodes.begin(), keyCodes.end(), [](int32_t a) { return a != -1 && a != 0; }); -} \ No newline at end of file +} +#pragma warning(pop) \ No newline at end of file diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp index c7ec4a9816..637161b527 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp @@ -165,7 +165,7 @@ int XamlBridge::MessageLoop() static const WPARAM invalidKey = (WPARAM)-1; -WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason reason) +constexpr WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason reason) { auto key = invalidKey; if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Last || reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First) diff --git a/src/modules/keyboardmanager/common/Shortcut.cpp b/src/modules/keyboardmanager/common/Shortcut.cpp index c06cdb531a..331cf10365 100644 --- a/src/modules/keyboardmanager/common/Shortcut.cpp +++ b/src/modules/keyboardmanager/common/Shortcut.cpp @@ -592,13 +592,13 @@ bool Shortcut::CheckModifiersKeyboardState(KeyboardManagerInput::InputInterface& } // Helper method for checking if a key is in a range for cleaner code -bool in_range(DWORD key, DWORD a, DWORD b) +constexpr bool in_range(DWORD key, DWORD a, DWORD b) { return (key >= a && key <= b); } // Helper method for checking if a key is equal to a value for cleaner code -bool equals(DWORD key, DWORD a) +constexpr bool equals(DWORD key, DWORD a) { return (key == a); } diff --git a/src/modules/videoconference/VideoConferenceShared/Logging.h b/src/modules/videoconference/VideoConferenceShared/Logging.h index 633656f695..e585759229 100644 --- a/src/modules/videoconference/VideoConferenceShared/Logging.h +++ b/src/modules/videoconference/VideoConferenceShared/Logging.h @@ -41,12 +41,12 @@ std::string toMediaTypeString(GUID subtype); #define LOG(str) LogToFile(str, false); #endif -inline bool failed(HRESULT hr) +constexpr inline bool failed(HRESULT hr) { return hr != S_OK; } -inline bool failed(bool val) +constexpr inline bool failed(bool val) { return val == false; } diff --git a/tools/WebcamReportTool/DirectShowUtils.h b/tools/WebcamReportTool/DirectShowUtils.h index 71a091648d..bb7b632fba 100644 --- a/tools/WebcamReportTool/DirectShowUtils.h +++ b/tools/WebcamReportTool/DirectShowUtils.h @@ -6,8 +6,9 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable: 26471) +#pragma warning(disable : 26471 26497) #include #pragma warning(push) diff --git a/tools/WebcamReportTool/main.cpp b/tools/WebcamReportTool/main.cpp index 3a7501ce23..233b046c8c 100644 --- a/tools/WebcamReportTool/main.cpp +++ b/tools/WebcamReportTool/main.cpp @@ -6,8 +6,9 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable: 26471) +#pragma warning(disable : 26471 26497) #include #pragma warning(push) From a743e496c5d3ce98e38447a7b0f9b74065edd248 Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 11:00:19 +0000 Subject: [PATCH 030/163] [Analyzers][CPP]Changes to fix warning 26493 on src/common (#23467) * Changes to fix warning 26493 on src/common It will be multi PR change. This does not turn on the rule but fix the code under src/commom * int cast --- src/common/GPOWrapper/GPOWrapper.cpp | 54 +++++++++---------- src/common/SettingsAPI/settings_objects.cpp | 2 +- src/common/SettingsAPI/settings_objects.h | 2 +- src/common/Themes/icon_helpers.cpp | 6 +-- src/common/Themes/theme_helpers.cpp | 5 +- src/common/Themes/theme_listener.cpp | 2 +- src/common/Themes/windows_colors.cpp | 2 +- src/common/interop/keyboard_layout.cpp | 2 +- .../interop/two_way_pipe_message_ipc.cpp | 20 +++---- src/common/utils/HDropIterator.h | 8 +-- src/common/utils/HttpClient.h | 2 +- src/common/utils/UnhandledExceptionHandler.h | 4 +- src/common/utils/elevation.h | 4 +- src/common/utils/gpo.h | 6 +-- src/common/utils/process_path.h | 4 +- src/common/utils/registry.h | 2 +- src/common/utils/timeutil.h | 2 +- src/common/utils/winapi_error.h | 4 +- src/common/utils/window.h | 4 +- 19 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index 513ba8a5c1..08948db0d1 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -6,110 +6,110 @@ namespace winrt::PowerToys::GPOWrapper::implementation { GpoRuleConfigured GPOWrapper::GetConfiguredAlwaysOnTopEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredAlwaysOnTopEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredAlwaysOnTopEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredAwakeEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredAwakeEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredAwakeEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredColorPickerEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredColorPickerEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredColorPickerEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredFancyZonesEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredFancyZonesEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredFancyZonesEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredFileLocksmithEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredFileLocksmithEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredFileLocksmithEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredSvgPreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredSvgPreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredSvgPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredMarkdownPreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredMarkdownPreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredMarkdownPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredMonacoPreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredMonacoPreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredMonacoPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredPdfPreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredPdfPreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredPdfPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredGcodePreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredGcodePreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredGcodePreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredSvgThumbnailsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredPdfThumbnailsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredPdfThumbnailsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredPdfThumbnailsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredGcodeThumbnailsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredStlThumbnailsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredStlThumbnailsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredStlThumbnailsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredHostsFileEditorEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredHostsFileEditorEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredHostsFileEditorEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredImageResizerEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredImageResizerEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredImageResizerEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredKeyboardManagerEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredKeyboardManagerEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredKeyboardManagerEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredFindMyMouseEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredFindMyMouseEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredFindMyMouseEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredMouseHighlighterEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredMouseHighlighterEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredMouseHighlighterEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredMousePointerCrosshairsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredMousePointerCrosshairsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredMousePointerCrosshairsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredPowerRenameEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredPowerRenameEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredPowerRenameEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredPowerLauncherEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredPowerLauncherEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredPowerLauncherEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredQuickAccentEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredQuickAccentEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredQuickAccentEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredScreenRulerEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredScreenRulerEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredScreenRulerEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredShortcutGuideEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredShortcutGuideEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredShortcutGuideEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredTextExtractorEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredTextExtractorEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredTextExtractorEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredVideoConferenceMuteEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue()); } } diff --git a/src/common/SettingsAPI/settings_objects.cpp b/src/common/SettingsAPI/settings_objects.cpp index f7bd573845..6f3aa4c792 100644 --- a/src/common/SettingsAPI/settings_objects.cpp +++ b/src/common/SettingsAPI/settings_objects.cpp @@ -247,7 +247,7 @@ namespace PowerToysSettings bool Settings::serialize_to_buffer(wchar_t* buffer, int* buffer_size) { auto result = m_json.Stringify(); - const int result_len = (int)result.size() + 1; + const int result_len = static_cast(result.size() + 1); if (buffer == nullptr || *buffer_size < result_len) { diff --git a/src/common/SettingsAPI/settings_objects.h b/src/common/SettingsAPI/settings_objects.h index cee95d7a56..1c84ac19b7 100644 --- a/src/common/SettingsAPI/settings_objects.h +++ b/src/common/SettingsAPI/settings_objects.h @@ -220,7 +220,7 @@ namespace PowerToysSettings std::array key_states{}; // Zero-initialize std::array output; const UINT wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer) - auto output_bytes = ToUnicodeEx(key_code, scan_code, key_states.data(), output.data(), (int)output.size() - 1, wFlags, layout); + auto output_bytes = ToUnicodeEx(key_code, scan_code, key_states.data(), output.data(), static_cast(output.size()) - 1, wFlags, layout); if (output_bytes <= 0) { // If ToUnicodeEx fails (e.g. for F1-F12 keys) use GetKeyNameTextW diff --git a/src/common/Themes/icon_helpers.cpp b/src/common/Themes/icon_helpers.cpp index e5387e85a9..c426a0c747 100644 --- a/src/common/Themes/icon_helpers.cpp +++ b/src/common/Themes/icon_helpers.cpp @@ -14,7 +14,7 @@ HRESULT GetIconIndexFromPath(_In_ PCWSTR path, _Out_ int* index) if (!PathIsRelative(path)) { DWORD attrib = GetFileAttributes(path); - HIMAGELIST himl = (HIMAGELIST)SHGetFileInfo(path, attrib, &shFileInfo, sizeof(shFileInfo), (SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES)); + auto himl =SHGetFileInfo(path, attrib, &shFileInfo, sizeof(shFileInfo), (SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES)); if (himl) { *index = shFileInfo.iIcon; @@ -61,14 +61,14 @@ HBITMAP CreateBitmapFromIcon(_In_ HICON hIcon, _In_opt_ UINT width, _In_opt_ UIN if (hBitmap != NULL) { // Select bitmap into DC - HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap); + HBITMAP hBitmapOld = static_cast(SelectObject(hDC, hBitmap)); if (hBitmapOld != NULL) { // Draw icon into DC if (DrawIconEx(hDC, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL)) { // Restore original bitmap in DC - hBitmapResult = (HBITMAP)SelectObject(hDC, hBitmapOld); + hBitmapResult = static_cast(SelectObject(hDC, hBitmapOld)); hBitmapOld = NULL; hBitmap = NULL; } diff --git a/src/common/Themes/theme_helpers.cpp b/src/common/Themes/theme_helpers.cpp index 280eda9ded..c00bd92f76 100644 --- a/src/common/Themes/theme_helpers.cpp +++ b/src/common/Themes/theme_helpers.cpp @@ -28,10 +28,7 @@ AppTheme ThemeHelpers::GetAppTheme() } // convert bytes written to our buffer to an int, assuming little-endian - auto i = int(buffer[3] << 24 | - buffer[2] << 16 | - buffer[1] << 8 | - buffer[0]); + auto i = static_cast(buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]); return AppTheme(i); } diff --git a/src/common/Themes/theme_listener.cpp b/src/common/Themes/theme_listener.cpp index fb81a42e4c..3a31b0db3c 100644 --- a/src/common/Themes/theme_listener.cpp +++ b/src/common/Themes/theme_listener.cpp @@ -8,7 +8,7 @@ #pragma warning(disable : 4702) DWORD WINAPI _checkTheme(LPVOID lpParam) { - auto listener = (ThemeListener*)lpParam; + auto listener = static_cast(lpParam); listener->CheckTheme(); return 0; } diff --git a/src/common/Themes/windows_colors.cpp b/src/common/Themes/windows_colors.cpp index a30329c181..e5a8eec732 100644 --- a/src/common/Themes/windows_colors.cpp +++ b/src/common/Themes/windows_colors.cpp @@ -11,7 +11,7 @@ DWORD WindowsColors::rgb_color(DWORD abgr_color) } DWORD WindowsColors::rgb_color(winrt::Windows::UI::Color color) { - return ((DWORD)color.R << 16) | ((DWORD)color.G << 8) | ((DWORD)color.B); + return static_cast((color.R << 16) | (color.G << 8) | (color.B)); } WindowsColors::Color WindowsColors::get_button_face_color() { diff --git a/src/common/interop/keyboard_layout.cpp b/src/common/interop/keyboard_layout.cpp index 441059bbb0..87562d4bf3 100644 --- a/src/common/interop/keyboard_layout.cpp +++ b/src/common/interop/keyboard_layout.cpp @@ -56,7 +56,7 @@ bool mapKeycodeToUnicode(const int vCode, HKL layout, const BYTE* keyState, std: const UINT scanCode = MapVirtualKeyExW(vCode, MAPVK_VK_TO_VSC, layout); // Get the unicode representation from the virtual key code and scan code pair const UINT wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer) - const int result = ToUnicodeEx(vCode, scanCode, keyState, outBuffer.data(), (int)outBuffer.size(), wFlags, layout); + const int result = ToUnicodeEx(vCode, scanCode, keyState, outBuffer.data(), static_cast(outBuffer.size()), wFlags, layout); return result != 0; } diff --git a/src/common/interop/two_way_pipe_message_ipc.cpp b/src/common/interop/two_way_pipe_message_ipc.cpp index 69215665c7..7dd1dedf36 100644 --- a/src/common/interop/two_way_pipe_message_ipc.cpp +++ b/src/common/interop/two_way_pipe_message_ipc.cpp @@ -184,9 +184,9 @@ BOOL TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::GetLogonSID(HANDLE hToken, if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto Cleanup; - ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), + ptg = static_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, - dwLength); + dwLength)); if (ptg == NULL) goto Cleanup; @@ -213,14 +213,14 @@ BOOL TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::GetLogonSID(HANDLE hToken, // Found the logon SID; make a copy of it. dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid); - *ppsid = (PSID)HeapAlloc(GetProcessHeap(), + *ppsid = static_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, - dwLength); + dwLength)); if (*ppsid == NULL) goto Cleanup; if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid)) { - HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid); + HeapFree(GetProcessHeap(), 0, static_cast(*ppsid)); goto Cleanup; } break; @@ -233,7 +233,7 @@ Cleanup: // Free the buffer for the token groups. if (ptg != NULL) - HeapFree(GetProcessHeap(), 0, (LPVOID)ptg); + HeapFree(GetProcessHeap(), 0, static_cast(ptg)); return bSuccess; } @@ -241,7 +241,7 @@ Cleanup: VOID TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::FreeLogonSID(PSID* ppsid) { // From https://learn.microsoft.com/previous-versions/aa446670(v=vs.85) - HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid); + HeapFree(GetProcessHeap(), 0, static_cast(*ppsid)); } int TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::change_pipe_security_allow_restricted_token(HANDLE handle, HANDLE token) @@ -279,7 +279,7 @@ int TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::change_pipe_security_allow_r ea.grfInheritance = NO_INHERITANCE; ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; ea.Trustee.TrusteeType = TRUSTEE_IS_USER; - ea.Trustee.ptstrName = (LPTSTR)user_restricted; + ea.Trustee.ptstrName = static_cast(user_restricted); if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl)) { @@ -302,9 +302,9 @@ int TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::change_pipe_security_allow_r error = 0; Lclean_dacl: - LocalFree((HLOCAL)new_dacl); + LocalFree(static_cast(new_dacl)); Lclean_sd: - LocalFree((HLOCAL)sd); + LocalFree(static_cast(sd)); Lclean_sid: FreeLogonSID(&user_restricted); Ldone: diff --git a/src/common/utils/HDropIterator.h b/src/common/utils/HDropIterator.h index 3b52d65bab..11ffab5863 100644 --- a/src/common/utils/HDropIterator.h +++ b/src/common/utils/HDropIterator.h @@ -19,7 +19,7 @@ public: if (SUCCEEDED(pDataObject->GetData(&formatetc, &m_medium))) { - _listCount = DragQueryFile((HDROP)m_medium.hGlobal, 0xFFFFFFFF, NULL, 0); + _listCount = DragQueryFile(static_cast(m_medium.hGlobal), 0xFFFFFFFF, NULL, 0); } else { @@ -52,10 +52,10 @@ public: LPTSTR CurrentItem() const { - UINT cch = DragQueryFile((HDROP)m_medium.hGlobal, _current, NULL, 0) + 1; - LPTSTR pszPath = (LPTSTR)malloc(sizeof(TCHAR) * cch); + UINT cch = DragQueryFile(static_cast(m_medium.hGlobal), _current, NULL, 0) + 1; + LPTSTR pszPath = static_cast(malloc(sizeof(TCHAR) * cch)); - DragQueryFile((HDROP)m_medium.hGlobal, _current, pszPath, cch); + DragQueryFile(static_cast(m_medium.hGlobal), _current, pszPath, cch); return pszPath; } diff --git a/src/common/utils/HttpClient.h b/src/common/utils/HttpClient.h index 86b201a5d1..8726368fbb 100644 --- a/src/common/utils/HttpClient.h +++ b/src/common/utils/HttpClient.h @@ -57,7 +57,7 @@ namespace http totalBytesRead += buffer.Length(); if (progressUpdateCallback) { - float percentage = (float)totalBytesRead / totalBytes; + float percentage = static_cast(totalBytesRead) / totalBytes; progressUpdateCallback(percentage); } diff --git a/src/common/utils/UnhandledExceptionHandler.h b/src/common/utils/UnhandledExceptionHandler.h index a3a69b2932..bbe2e81f24 100644 --- a/src/common/utils/UnhandledExceptionHandler.h +++ b/src/common/utils/UnhandledExceptionHandler.h @@ -93,7 +93,7 @@ inline std::wstring GetModuleName(HANDLE process, const STACKFRAME64& stack) return std::wstring(); } - if (!GetModuleFileNameW((HINSTANCE)moduleBase, modulePath, MAX_PATH)) + if (!GetModuleFileNameW(reinterpret_cast(moduleBase), modulePath, MAX_PATH)) { Logger::error(L"Failed to get a module path. {}", get_last_error_or_default(GetLastError())); return std::wstring(); @@ -105,7 +105,7 @@ inline std::wstring GetModuleName(HANDLE process, const STACKFRAME64& stack) inline std::wstring GetName(HANDLE process, const STACKFRAME64& stack) { - static IMAGEHLP_SYMBOL64* pSymbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(TCHAR)); + static IMAGEHLP_SYMBOL64* pSymbol = static_cast(malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(TCHAR))); if (!pSymbol) { return std::wstring(); diff --git a/src/common/utils/elevation.h b/src/common/utils/elevation.h index 2615d4b8ad..bad925c4e9 100644 --- a/src/common/utils/elevation.h +++ b/src/common/utils/elevation.h @@ -198,7 +198,7 @@ inline bool drop_elevated_privileges() TOKEN_MANDATORY_LABEL label = { 0 }; label.Label.Attributes = SE_GROUP_INTEGRITY; label.Label.Sid = medium_sid; - DWORD size = (DWORD)sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid); + DWORD size = static_cast(sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid)); BOOL result = SetTokenInformation(token, TokenIntegrityLevel, &label, size); LocalFree(medium_sid); @@ -464,7 +464,7 @@ inline bool check_user_is_admin() } // Allocate the buffer. - pGroupInfo = (PTOKEN_GROUPS)GlobalAlloc(GPTR, dwSize); + pGroupInfo = static_cast(GlobalAlloc(GPTR, dwSize)); // Call GetTokenInformation again to get the group information. if (!GetTokenInformation(hToken, TokenGroups, pGroupInfo, dwSize, &dwSize)) diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index c77c036c51..6c02e2b159 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -50,7 +50,7 @@ namespace powertoys_gpo { inline gpo_rule_configured_t getConfiguredValue(const std::wstring& registry_value_name) { HKEY key{}; - DWORD value = (DWORD) -2; + DWORD value = 0xFFFFFFFE; DWORD valueSize = sizeof(value); bool machine_key_found = true; @@ -62,7 +62,7 @@ namespace powertoys_gpo { if(machine_key_found) { // If the path was found in the machine, we need to check if the value for the policy exists. - auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, (LPBYTE)&value, &valueSize); + auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast(&value), &valueSize); RegCloseKey(key); @@ -82,7 +82,7 @@ namespace powertoys_gpo { } return gpo_rule_configured_unavailable; } - auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, (LPBYTE)&value, &valueSize); + auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast(&value), &valueSize); RegCloseKey(key); if (res != ERROR_SUCCESS) { diff --git a/src/common/utils/process_path.h b/src/common/utils/process_path.h index 382a1d2ea5..56129a9bb7 100644 --- a/src/common/utils/process_path.h +++ b/src/common/utils/process_path.h @@ -15,7 +15,7 @@ inline std::wstring get_process_path(DWORD pid) noexcept { name.resize(MAX_PATH); DWORD name_length = static_cast(name.length()); - if (QueryFullProcessImageNameW(process, 0, (LPWSTR)name.data(), &name_length) == 0) + if (QueryFullProcessImageNameW(process, 0, name.data(), &name_length) == 0) { name_length = 0; } @@ -118,5 +118,5 @@ inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool remo { PathRemoveFileSpecW(buffer); } - return { buffer, (UINT)lstrlenW(buffer) }; + return { buffer, static_cast(lstrlenW(buffer))}; } diff --git a/src/common/utils/registry.h b/src/common/utils/registry.h index d254457999..fbb44a23f5 100644 --- a/src/common/utils/registry.h +++ b/src/common/utils/registry.h @@ -241,7 +241,7 @@ namespace registry switch (type) { case REG_DWORD: - return *(DWORD*)buffer; + return *reinterpret_cast(buffer); case REG_SZ: { if (!valueSize) diff --git a/src/common/utils/timeutil.h b/src/common/utils/timeutil.h index 8242cef9d0..b82e7981bd 100644 --- a/src/common/utils/timeutil.h +++ b/src/common/utils/timeutil.h @@ -60,7 +60,7 @@ namespace timeutil inline int64_t in_days(const std::time_t to, const std::time_t from) { - return static_cast(std::difftime(to, from) / (3600 * (int64_t)24)); + return static_cast(std::difftime(to, from) / (3600 * 24LL)); } } } diff --git a/src/common/utils/winapi_error.h b/src/common/utils/winapi_error.h index b4cc01aa83..dec9f93146 100644 --- a/src/common/utils/winapi_error.h +++ b/src/common/utils/winapi_error.h @@ -35,7 +35,7 @@ inline void show_last_error_message(const wchar_t* functionName, DWORD dw, const { return; } - LPWSTR lpDisplayBuf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (system_message->size() + lstrlenW(functionName) + 40) * sizeof(WCHAR)); + LPWSTR lpDisplayBuf = static_cast(LocalAlloc(LMEM_ZEROINIT, (system_message->size() + lstrlenW(functionName) + 40) * sizeof(WCHAR))); if (lpDisplayBuf != NULL) { StringCchPrintfW(lpDisplayBuf, @@ -44,7 +44,7 @@ inline void show_last_error_message(const wchar_t* functionName, DWORD dw, const functionName, system_message->c_str(), dw); - MessageBoxW(NULL, (LPCTSTR)lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR); + MessageBoxW(NULL, lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR); LocalFree(lpDisplayBuf); } } diff --git a/src/common/utils/window.h b/src/common/utils/window.h index 36e4b671c3..cfcf20534a 100644 --- a/src/common/utils/window.h +++ b/src/common/utils/window.h @@ -60,7 +60,7 @@ template inline T GetWindowCreateParam(LPARAM lparam) { static_assert(sizeof(T) <= sizeof(void*)); - T data{ (T)(reinterpret_cast(lparam)->lpCreateParams) }; + T data{ static_cast (reinterpret_cast(lparam)->lpCreateParams) }; return data; } @@ -74,5 +74,5 @@ inline void StoreWindowParam(HWND window, T data) template inline T GetWindowParam(HWND window) { - return (T)GetWindowLongPtrW(window, GWLP_USERDATA); + return reinterpret_cast (GetWindowLongPtrW(window, GWLP_USERDATA)); } From fb531139c8b81b780c6995b02755bbd87a88db32 Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 11:00:45 +0000 Subject: [PATCH 031/163] [Analyzers][CPP]Changes to fix warning 26493 on src/modules/ (A to F) (#23482) starting with letter A to Letter F --- .../FileLocksmithExt/ExplorerCommand.cpp | 2 +- .../alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp | 4 ++-- .../alwaysontop/AlwaysOnTop/FrameDrawer.cpp | 16 ++++++++-------- src/modules/alwaysontop/AlwaysOnTop/Settings.cpp | 2 +- .../FancyZonesData/CustomLayouts.cpp | 2 +- .../FancyZonesLib/FancyZonesWindowProperties.cpp | 2 +- .../fancyzones/FancyZonesLib/GenericKeyHook.h | 2 +- .../FancyZonesLib/LayoutConfigurator.cpp | 4 ++-- .../fancyzones/FancyZonesLib/MonitorUtils.cpp | 2 +- .../fancyzones/FancyZonesLib/Settings.cpp | 4 ++-- .../fancyzones/FancyZonesLib/WindowUtils.cpp | 2 +- .../fancyzones/FancyZonesLib/ZonesOverlay.cpp | 4 ++-- .../UnitTests/AppZoneHistoryTests.Spec.cpp | 2 +- .../FancyZonesTests/UnitTests/Util.cpp | 8 ++++---- .../FancyZonesTests/UnitTests/WorkArea.Spec.cpp | 2 +- .../FancyZonesTests/UnitTests/Zone.Spec.cpp | 2 +- 16 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp index 6db892d2fb..e61d50da9e 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp @@ -247,7 +247,7 @@ inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool remo { PathRemoveFileSpecW(buffer); } - return { buffer, (UINT)lstrlenW(buffer) }; + return { buffer, static_cast(lstrlenW(buffer)) }; } HRESULT ExplorerCommand::LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer) diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp index 9f9db964f5..b61b75ea8d 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp @@ -23,7 +23,7 @@ namespace NonLocalizable bool isExcluded(HWND window) { auto processPath = get_process_path(window); - CharUpperBuffW(processPath.data(), (DWORD)processPath.length()); + CharUpperBuffW(processPath.data(), static_cast(processPath.length())); return find_app_name_in_path(processPath, AlwaysOnTopSettings::settings().excludedApps); } @@ -381,7 +381,7 @@ bool AlwaysOnTop::IsPinned(HWND window) const noexcept bool AlwaysOnTop::PinTopmostWindow(HWND window) const noexcept { - if (!SetProp(window, NonLocalizable::WINDOW_IS_PINNED_PROP, (HANDLE)1)) + if (!SetProp(window, NonLocalizable::WINDOW_IS_PINNED_PROP, reinterpret_cast(1))) { Logger::error(L"SetProp failed, {}", get_last_error_or_default(GetLastError())); } diff --git a/src/modules/alwaysontop/AlwaysOnTop/FrameDrawer.cpp b/src/modules/alwaysontop/AlwaysOnTop/FrameDrawer.cpp index 9613769f2f..3978f3d105 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/FrameDrawer.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/FrameDrawer.cpp @@ -188,10 +188,10 @@ D2D1_ROUNDED_RECT FrameDrawer::ConvertRect(RECT rect, int thickness, float radiu float halfThickness = thickness / 2.0f; // 1 is needed to eliminate the gap between border and window - auto d2d1Rect = D2D1::RectF((float)rect.left + halfThickness + 1, - (float)rect.top + halfThickness + 1, - (float)rect.right - halfThickness - 1, - (float)rect.bottom - halfThickness - 1); + auto d2d1Rect = D2D1::RectF(static_cast(rect.left) + halfThickness + 1, + static_cast(rect.top) + halfThickness + 1, + static_cast(rect.right) - halfThickness - 1, + static_cast(rect.bottom) - halfThickness - 1); return D2D1::RoundedRect(d2d1Rect, radius, radius); } @@ -200,10 +200,10 @@ D2D1_RECT_F FrameDrawer::ConvertRect(RECT rect, int thickness) float halfThickness = thickness / 2.0f; // 1 is needed to eliminate the gap between border and window - return D2D1::RectF((float)rect.left + halfThickness + 1, - (float)rect.top + halfThickness + 1, - (float)rect.right - halfThickness - 1, - (float)rect.bottom - halfThickness - 1); + return D2D1::RectF(static_cast(rect.left) + halfThickness + 1, + static_cast(rect.top) + halfThickness + 1, + static_cast(rect.right) - halfThickness - 1, + static_cast(rect.bottom) - halfThickness - 1); } void FrameDrawer::Render() diff --git a/src/modules/alwaysontop/AlwaysOnTop/Settings.cpp b/src/modules/alwaysontop/AlwaysOnTop/Settings.cpp index f91ce253a8..251fa1f6ba 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/Settings.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/Settings.cpp @@ -169,7 +169,7 @@ void AlwaysOnTopSettings::LoadSettings() std::wstring apps = std::move(*jsonVal); std::vector excludedApps; auto excludedUppercase = apps; - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length())); std::wstring_view view(excludedUppercase); view = left_trim(trim(view)); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp index a64e2f4991..4040273cc7 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp @@ -242,7 +242,7 @@ std::optional CustomLayouts::GetLayout(const GUID& id) const noexcep { auto layoutInfo = std::get(customLayout.info); layout.sensitivityRadius = layoutInfo.sensitivityRadius; - layout.zoneCount = (int)layoutInfo.zones.size(); + layout.zoneCount = static_cast(layoutInfo.zones.size()); } return layout; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProperties.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProperties.cpp index 2cec6f78f4..dd3298cd8a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProperties.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProperties.cpp @@ -85,7 +85,7 @@ ZoneIndexSet FancyZonesWindowProperties::RetrieveZoneIndexProperty(HWND window) void FancyZonesWindowProperties::StampMovedOnOpeningProperty(HWND window) { - ::SetPropW(window, ZonedWindowProperties::PropertyMovedOnOpening, (HANDLE)1); + ::SetPropW(window, ZonedWindowProperties::PropertyMovedOnOpening, reinterpret_cast(1)); } bool FancyZonesWindowProperties::RetrieveMovedOnOpeningProperty(HWND window) diff --git a/src/modules/fancyzones/FancyZonesLib/GenericKeyHook.h b/src/modules/fancyzones/FancyZonesLib/GenericKeyHook.h index 76716ca407..a826e2280f 100644 --- a/src/modules/fancyzones/FancyZonesLib/GenericKeyHook.h +++ b/src/modules/fancyzones/FancyZonesLib/GenericKeyHook.h @@ -48,7 +48,7 @@ private: { if (wParam == WM_KEYDOWN || wParam == WM_KEYUP) { - PKBDLLHOOKSTRUCT kbdHookStruct = (PKBDLLHOOKSTRUCT)lParam; + PKBDLLHOOKSTRUCT kbdHookStruct = reinterpret_cast(lParam); if (((kbdHookStruct->vkCode == keys) || ...)) { callback(wParam == WM_KEYDOWN); diff --git a/src/modules/fancyzones/FancyZonesLib/LayoutConfigurator.cpp b/src/modules/fancyzones/FancyZonesLib/LayoutConfigurator.cpp index 1f1402ff15..9585e358c9 100644 --- a/src/modules/fancyzones/FancyZonesLib/LayoutConfigurator.cpp +++ b/src/modules/fancyzones/FancyZonesLib/LayoutConfigurator.cpp @@ -197,8 +197,8 @@ ZonesMap LayoutConfigurator::Focus(FancyZonesUtils::Rect workArea, int zoneCount long left{ 100 }; long top{ 100 }; - long right{ left + long(workArea.width() * 0.4) }; - long bottom{ top + long(workArea.height() * 0.4) }; + long right{ left + static_cast(workArea.width() * 0.4) }; + long bottom{ top + static_cast(workArea.height() * 0.4) }; RECT focusZoneRect{ left, top, right, bottom }; diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp index 4926c287d3..6e46babd2a 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp @@ -88,7 +88,7 @@ namespace MonitorUtils // on a particular host computer. IWbemLocator* pLocator = 0; - hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator); + hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast(&pLocator)); if (FAILED(hres)) { Logger::error(L"Failed to create IWbemLocator object. {}", get_last_error_or_default(hres)); diff --git a/src/modules/fancyzones/FancyZonesLib/Settings.cpp b/src/modules/fancyzones/FancyZonesLib/Settings.cpp index 844b4dbc06..4f7e9cf463 100644 --- a/src/modules/fancyzones/FancyZonesLib/Settings.cpp +++ b/src/modules/fancyzones/FancyZonesLib/Settings.cpp @@ -212,7 +212,7 @@ void FancyZonesSettings::LoadSettings() std::wstring apps = std::move(*val); std::vector excludedApps; auto excludedUppercase = apps; - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length())); std::wstring_view view(excludedUppercase); view = left_trim(trim(view)); @@ -236,7 +236,7 @@ void FancyZonesSettings::LoadSettings() if (auto val = values.get_int_value(NonLocalizable::OverlappingZonesAlgorithmID)) { // Avoid undefined behavior - if (*val >= 0 || *val < (int)OverlappingZonesAlgorithm::EnumElements) + if (*val >= 0 || *val < static_cast(OverlappingZonesAlgorithm::EnumElements)) { auto algorithm = (OverlappingZonesAlgorithm)*val; if (m_settings.overlappingZonesAlgorithm != algorithm) diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp index 41f079f6b6..c9252ba5df 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp @@ -226,7 +226,7 @@ bool FancyZonesWindowUtils::IsCandidateForZoning(HWND window) } std::wstring processPath = get_process_path_waiting_uwp(window); - CharUpperBuffW(const_cast(processPath).data(), (DWORD)processPath.length()); + CharUpperBuffW(const_cast(processPath).data(), static_cast(processPath.length())); if (IsExcludedByUser(processPath)) { return false; diff --git a/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp b/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp index b5908eade7..ada45b5d5c 100644 --- a/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp +++ b/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp @@ -70,7 +70,7 @@ D2D1_COLOR_F ZonesOverlay::ConvertColor(COLORREF color) D2D1_RECT_F ZonesOverlay::ConvertRect(RECT rect) { - return D2D1::RectF((float)rect.left + 0.5f, (float)rect.top + 0.5f, (float)rect.right - 0.5f, (float)rect.bottom - 0.5f); + return D2D1::RectF(rect.left + 0.5f, rect.top + 0.5f, rect.right - 0.5f, rect.bottom - 0.5f); } ZonesOverlay::ZonesOverlay(HWND window) @@ -174,7 +174,7 @@ ZonesOverlay::RenderResult ZonesOverlay::Render() { textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); - m_renderTarget->DrawTextW(idStr.c_str(), (UINT32)idStr.size(), textFormat, drawableRect.rect, textBrush); + m_renderTarget->DrawTextW(idStr.c_str(), static_cast(idStr.size()), textFormat, drawableRect.rect, textBrush); } if (textBrush) diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp index 0071514185..f1365f2079 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp @@ -16,7 +16,7 @@ namespace FancyZonesUnitTests TEST_METHOD_INITIALIZE(Init) { - m_hInst = (HINSTANCE)GetModuleHandleW(nullptr); + m_hInst = static_cast(GetModuleHandleW(nullptr)); AppZoneHistory::instance().LoadData(); } diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/Util.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/Util.cpp index f85597c0c6..6f638085e7 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/Util.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/Util.cpp @@ -70,7 +70,7 @@ BOOL RegisterDLLWindowClass(LPCWSTR szClassName, Mocks::HwndCreator* creator) wc.lpszMenuName = NULL; wc.cbClsExtra = 0; wc.cbWndExtra = 0; - wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND; + wc.hbrBackground = reinterpret_cast(COLOR_BACKGROUND); auto regRes = RegisterClassEx(&wc); return regRes; @@ -83,9 +83,9 @@ DWORD WINAPI ThreadProc(LPVOID lpParam) if (!creator) return static_cast(-1); - if (RegisterDLLWindowClass((LPCWSTR)creator->getWindowClassName().c_str(), creator) != 0) + if (RegisterDLLWindowClass(creator->getWindowClassName().c_str(), creator) != 0) { - auto hWnd = CreateWindowEx(0, (LPCWSTR)creator->getWindowClassName().c_str(), (LPCWSTR)creator->getTitle().c_str(), WS_EX_APPWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, nullptr, nullptr, creator->getHInstance(), NULL); + auto hWnd = CreateWindowEx(0, creator->getWindowClassName().c_str(), creator->getTitle().c_str(), WS_EX_APPWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, nullptr, nullptr, creator->getHInstance(), NULL); SetWindowPos(hWnd, HWND_TOPMOST, 10, 10, 100, 100, SWP_SHOWWINDOW); creator->setHwnd(hWnd); creator->setCondition(true); @@ -130,7 +130,7 @@ namespace Mocks m_conditionFlag = false; std::unique_lock lock(m_mutex); - m_thread = CreateThread(0, NULL, ThreadProc, (LPVOID)this, NULL, NULL); + m_thread = CreateThread(0, NULL, ThreadProc, reinterpret_cast(this), NULL, NULL); m_conditionVar.wait(lock, [this] { return m_conditionFlag; }); return m_hWnd; diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp index bbfd3cb159..f37f6c8614 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp @@ -382,7 +382,7 @@ namespace FancyZonesUnitTests Assert::AreEqual((size_t)1, actualAppZoneHistory.size()); const auto& layoutWindows = workArea->GetLayoutWindows(); - Assert::IsTrue(ZoneIndexSet{ static_cast(workArea->GetLayout()->Zones().size()) - 1 } == layoutWindows->GetZoneIndexSetFromWindow(window)); + Assert::IsTrue(ZoneIndexSet{ static_cast(workArea->GetLayout()->Zones().size() - 1) } == layoutWindows->GetZoneIndexSetFromWindow(window)); } TEST_METHOD (MoveAppliedWindowByIndexNoCycle) diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/Zone.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/Zone.Spec.cpp index f0ee4ffd01..987371875f 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/Zone.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/Zone.Spec.cpp @@ -16,7 +16,7 @@ namespace FancyZonesUnitTests TEST_METHOD_INITIALIZE(Init) { - m_hInst = (HINSTANCE)GetModuleHandleW(nullptr); + m_hInst = static_cast(GetModuleHandleW(nullptr)); } public: From 0c5b528c54998c335824a6158688dae921d3e058 Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 11:01:13 +0000 Subject: [PATCH 032/163] [Analyzers][CPP]Changes to fix warning 26493 on src/modules/ (H to K) (#23484) * Changes to fix warning 26493 on src/modules/ starting with letter H to Letter K * change from PR comments --- .../Hosts/HostsModuleInterface/dllmain.cpp | 2 +- .../ImageResizerContextMenu/dllmain.cpp | 4 +- .../imageresizer/dll/ContextMenuHandler.cpp | 8 +- .../BufferValidationHelpers.cpp | 14 ++-- .../EditKeyboardWindow.cpp | 8 +- .../EditShortcutsWindow.cpp | 10 +-- .../KeyDropDownControl.cpp | 2 +- .../ShortcutControl.cpp | 2 +- .../SingleKeyRemapControl.cpp | 2 +- .../XamlBridge.cpp | 6 +- .../KeyboardManagerEditorLibrary/XamlBridge.h | 2 +- .../KeyboardEventHandlers.cpp | 78 +++++++++---------- .../OSLevelShortcutRemappingTests.cpp | 2 +- .../keyboardmanager/common/Helpers.cpp | 22 +++--- .../common/KeyboardEventHandlers.cpp | 2 +- .../common/MappingConfiguration.cpp | 2 +- .../keyboardmanager/common/Shortcut.cpp | 10 +-- 17 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/modules/Hosts/HostsModuleInterface/dllmain.cpp b/src/modules/Hosts/HostsModuleInterface/dllmain.cpp index 8daa522b5d..aeb05ba2e6 100644 --- a/src/modules/Hosts/HostsModuleInterface/dllmain.cpp +++ b/src/modules/Hosts/HostsModuleInterface/dllmain.cpp @@ -57,7 +57,7 @@ private: void bring_process_to_front() { auto enum_windows = [](HWND hwnd, LPARAM param) -> BOOL { - HANDLE process_handle = (HANDLE)param; + HANDLE process_handle = reinterpret_cast(param); DWORD window_process_id = 0; GetWindowThreadProcessId(hwnd, &window_process_id); diff --git a/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp b/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp index 4b940e3206..26bf622520 100644 --- a/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp +++ b/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp @@ -227,7 +227,7 @@ private: auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L""); } - else if (UuidToString(&temp_uuid, (RPC_WSTR*)&uuid_chars) != RPC_S_OK) + else if (UuidToString(&temp_uuid, reinterpret_cast(& uuid_chars)) != RPC_S_OK) { auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L""); @@ -236,7 +236,7 @@ private: if (uuid_chars != nullptr) { pipe_name += std::wstring(uuid_chars); - RpcStringFree((RPC_WSTR*)&uuid_chars); + RpcStringFree(reinterpret_cast(&uuid_chars)); uuid_chars = nullptr; } create_pipe_thread = std::thread(&ImageResizerContextMenuCommand::StartNamedPipeServerAndSendData, this, pipe_name); diff --git a/src/modules/imageresizer/dll/ContextMenuHandler.cpp b/src/modules/imageresizer/dll/ContextMenuHandler.cpp index 6440b9677b..fddf2b4497 100644 --- a/src/modules/imageresizer/dll/ContextMenuHandler.cpp +++ b/src/modules/imageresizer/dll/ContextMenuHandler.cpp @@ -130,7 +130,7 @@ HRESULT CContextMenuHandler::QueryContextMenu(_In_ HMENU hmenu, UINT indexMenu, mii.fType = MFT_STRING; mii.dwTypeData = (PWSTR)strResizePictures; mii.fState = MFS_ENABLED; - HICON hIcon = (HICON)LoadImage(g_hInst_imageResizer, MAKEINTRESOURCE(IDI_RESIZE_PICTURES), IMAGE_ICON, 16, 16, 0); + HICON hIcon = static_cast(LoadImage(g_hInst_imageResizer, MAKEINTRESOURCE(IDI_RESIZE_PICTURES), IMAGE_ICON, 16, 16, 0)); if (hIcon) { mii.fMask |= MIIM_BITMAP; @@ -185,7 +185,7 @@ HRESULT CContextMenuHandler::GetCommandString(UINT_PTR idCmd, UINT uType, _In_ U { if (uType == GCS_VERBW) { - wcscpy_s((LPWSTR)pszName, cchMax, RESIZE_PICTURES_VERBW); + wcscpy_s(reinterpret_cast(pszName), cchMax, RESIZE_PICTURES_VERBW); } } else @@ -211,7 +211,7 @@ HRESULT CContextMenuHandler::InvokeCommand(_In_ CMINVOKECOMMANDINFO* pici) } else if (fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW)) { - if (wcscmp(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, RESIZE_PICTURES_VERBW) == 0) + if (wcscmp((reinterpret_cast(pici))->lpVerbW, RESIZE_PICTURES_VERBW) == 0) { hr = ResizePictures(pici, nullptr); } @@ -230,7 +230,7 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte // Set the application path based on the location of the dll std::wstring path = get_module_folderpath(g_hInst_imageResizer); path = path + L"\\PowerToys.ImageResizer.exe"; - LPTSTR lpApplicationName = (LPTSTR)path.c_str(); + LPTSTR lpApplicationName = &path[0]; // Create an anonymous pipe to stream filenames SECURITY_ATTRIBUTES sa; HANDLE hReadPipe; diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp index 3de686b6e0..79ffcf0152 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp @@ -29,9 +29,9 @@ namespace BufferValidationHelpers if (selectedKeyCode != -1) { // Check if the value being set is the same as the other column - if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0) + if (remapBuffer[rowIndex].first[std::abs(colIndex - 1)].index() == 0) { - DWORD otherColumnKeyCode = std::get(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]); + DWORD otherColumnKeyCode = std::get(remapBuffer[rowIndex].first[std::abs(colIndex - 1)]); if (otherColumnKeyCode == selectedKeyCode || IsKeyRemappingToItsCombinedKey(selectedKeyCode, otherColumnKeyCode)) { errorType = ShortcutErrorType::MapToSameKey; @@ -189,7 +189,7 @@ namespace BufferValidationHelpers // If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. // If it is a hybrid control, this can be done even on the first key bool isClear = true; - for (int i = dropDownIndex + 1; i < (int)dropDownCount; i++) + for (int i = dropDownIndex + 1; i < static_cast(dropDownCount); i++) { if (selectedCodes[i] != -1) { @@ -244,9 +244,9 @@ namespace BufferValidationHelpers if (tempShortcut.index() == 1) { // If shortcut to shortcut - if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 1) + if (remapBuffer[rowIndex].first[std::abs(colIndex - 1)].index() == 1) { - auto& shortcut = std::get(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]); + auto& shortcut = std::get(remapBuffer[rowIndex].first[std::abs(colIndex - 1)]); if (shortcut == std::get(tempShortcut) && EditorHelpers::IsValidShortcut(shortcut) && EditorHelpers::IsValidShortcut(std::get(tempShortcut))) { errorType = ShortcutErrorType::MapToSameShortcut; @@ -258,9 +258,9 @@ namespace BufferValidationHelpers else { // If key to key - if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0) + if (remapBuffer[rowIndex].first[std::abs(colIndex - 1)].index() == 0) { - DWORD otherColumnKeyCode = std::get(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]); + DWORD otherColumnKeyCode = std::get(remapBuffer[rowIndex].first[std::abs(colIndex - 1)]); DWORD shortcutKeyCode = std::get(tempShortcut); if ((otherColumnKeyCode == shortcutKeyCode || IsKeyRemappingToItsCombinedKey(otherColumnKeyCode, shortcutKeyCode)) && otherColumnKeyCode != NULL && shortcutKeyCode != NULL) { diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp index 1230a25f74..2c4f2c681c 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp @@ -137,14 +137,14 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan windowClass.lpfnWndProc = EditKeyboardWindowProc; windowClass.hInstance = hInst; windowClass.lpszClassName = szWindowClass; - windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW); - windowClass.hIcon = (HICON)LoadImageW( + windowClass.hbrBackground = reinterpret_cast(COLOR_WINDOW); + windowClass.hIcon = static_cast(LoadImageW( windowClass.hInstance, MAKEINTRESOURCE(IDS_KEYBOARDMANAGER_ICON), IMAGE_ICON, 48, 48, - LR_DEFAULTCOLOR); + LR_DEFAULTCOLOR)); if (RegisterClassEx(&windowClass) == NULL) { @@ -433,7 +433,7 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar // To avoid UI elements overlapping on making the window smaller enforce a minimum window size case WM_GETMINMAXINFO: { - LPMINMAXINFO mmi = (LPMINMAXINFO)lParam; + LPMINMAXINFO mmi = reinterpret_cast(lParam); float minWidth = EditorConstants::MinimumEditKeyboardWindowWidth; float minHeight = EditorConstants::MinimumEditKeyboardWindowHeight; DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp index 0350cc590a..d312e0091a 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp @@ -75,14 +75,14 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa windowClass.lpfnWndProc = EditShortcutsWindowProc; windowClass.hInstance = hInst; windowClass.lpszClassName = szWindowClass; - windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW); - windowClass.hIcon = (HICON)LoadImageW( + windowClass.hbrBackground = reinterpret_cast(COLOR_WINDOW); + windowClass.hIcon = static_cast(LoadImageW( windowClass.hInstance, MAKEINTRESOURCE(IDS_KEYBOARDMANAGER_ICON), IMAGE_ICON, 48, 48, - LR_DEFAULTCOLOR); + LR_DEFAULTCOLOR)); if (RegisterClassEx(&windowClass) == NULL) { MessageBox(NULL, GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORTITLE).c_str(), NULL); @@ -199,7 +199,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa StackPanel tableHeader = StackPanel(); tableHeader.Orientation(Orientation::Horizontal); tableHeader.Margin({ 10, 0, 0, 10 }); - auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, EditorConstants::ShortcutOriginColumnWidth + (double)EditorConstants::ShortcutArrowColumnWidth); + auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, EditorConstants::ShortcutOriginColumnWidth + static_cast(EditorConstants::ShortcutArrowColumnWidth)); tableHeader.Children().Append(originalShortcutContainer.as()); auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, EditorConstants::ShortcutTargetColumnWidth); tableHeader.Children().Append(newShortcutHeaderContainer.as()); @@ -386,7 +386,7 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa // To avoid UI elements overlapping on making the window smaller enforce a minimum window size case WM_GETMINMAXINFO: { - LPMINMAXINFO mmi = (LPMINMAXINFO)lParam; + LPMINMAXINFO mmi = reinterpret_cast(lParam); float minWidth = EditorConstants::MinimumEditShortcutsWindowWidth; float minHeight = EditorConstants::MinimumEditShortcutsWindowHeight; DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp index 44e109ee19..c847ad2a1b 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp @@ -326,7 +326,7 @@ void KeyDropDownControl::AddDropDown(StackPanel& table, StackPanel row, Variable keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->SetSelectionHandler(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow); // Update accessible name - SetAccessibleNameForComboBox(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox(), (int)keyDropDownControlObjects.size()); + SetAccessibleNameForComboBox(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox(), static_cast(keyDropDownControlObjects.size())); } // Function to get the list of key codes from the shortcut combo box stack panel diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp index f41e767f6c..f45c3d1952 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp @@ -254,7 +254,7 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vectorGetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size()); + UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->GetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, static_cast(keyboardRemapControlObjects.size())); // Set the shortcut text if the two vectors are not empty (i.e. default args) if (EditorHelpers::IsValidShortcut(originalKeys) && !(newKeys.index() == 0 && std::get(newKeys) == NULL) && !(newKeys.index() == 1 && !EditorHelpers::IsValidShortcut(std::get(newKeys)))) diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/SingleKeyRemapControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/SingleKeyRemapControl.cpp index a58f8b8c36..b83e71906b 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/SingleKeyRemapControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/SingleKeyRemapControl.cpp @@ -206,7 +206,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve } // Set accessible names - UpdateAccessibleNames(keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl(), keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl(), deleteRemapKeys, (int)keyboardRemapControlObjects.size()); + UpdateAccessibleNames(keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl(), keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl(), deleteRemapKeys, static_cast(keyboardRemapControlObjects.size())); } // Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp index 637161b527..fe68461559 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp @@ -141,7 +141,7 @@ bool XamlBridge::NavigateFocus(MSG* msg) } // Function to run the message loop for the xaml island window -int XamlBridge::MessageLoop() +WPARAM XamlBridge::MessageLoop() { MSG msg = {}; HRESULT hr = S_OK; @@ -160,10 +160,10 @@ int XamlBridge::MessageLoop() } Logger::trace("XamlBridge::MessageLoop() stopped"); - return (int)msg.wParam; + return msg.wParam; } -static const WPARAM invalidKey = (WPARAM)-1; +static const WPARAM invalidKey = 0xFFFFFFFFFFFFFFFF; constexpr WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason reason) { diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.h index bb01e4961b..20453bd082 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.h @@ -5,7 +5,7 @@ class XamlBridge { public: // Function to run the message loop for the xaml island window - int MessageLoop(); + WPARAM MessageLoop(); // Constructor XamlBridge(HWND parent) : diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp index f2ee2447f3..be2a8cc02b 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp @@ -66,11 +66,11 @@ namespace KeyboardEventHandlers { if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) { - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(target), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); } else { - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(target), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); } } else @@ -79,7 +79,7 @@ namespace KeyboardEventHandlers Shortcut targetShortcut = std::get(it->second); if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(targetShortcut.GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); i++; Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); // Dummy key is not required here since SetModifierKeyEvents will only add key-up events for the modifiers here, and the action key key-up is already sent before it @@ -88,7 +88,7 @@ namespace KeyboardEventHandlers { // Dummy key is not required here since SetModifierKeyEvents will only add key-down events for the modifiers here, and the action key key-down is already sent after it Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(targetShortcut.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); i++; } } @@ -236,13 +236,13 @@ namespace KeyboardEventHandlers memset(keyEventList, 0, sizeof(keyEventList)); int i = 0; Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } else { // Dummy key, key up for all the original shortcut modifier keys and key down for all the new shortcut keys but common keys in each are not repeated - key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + (dest_size) - (2 * (size_t)commonKeys); + key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + (dest_size) - (2 * static_cast(commonKeys)); keyEventList = new INPUT[key_count](); memset(keyEventList, 0, sizeof(keyEventList)); @@ -255,7 +255,7 @@ namespace KeyboardEventHandlers // Set new shortcut key down state Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } @@ -295,14 +295,14 @@ namespace KeyboardEventHandlers // Set target key down state if (std::get(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } // Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL) { - ResetIfModifierKeyForLowerLevelKeyHandlers(ii, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), data->lParam->vkCode); + ResetIfModifierKeyForLowerLevelKeyHandlers(ii,static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), data->lParam->vkCode); } } @@ -313,7 +313,7 @@ namespace KeyboardEventHandlers state.SetActivatedApp(*activatedApp); } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; @@ -350,7 +350,7 @@ namespace KeyboardEventHandlers else { // release all new shortcut keys except the common modifiers and add all original shortcut modifiers except the common ones, and dummy key - key_count = (dest_size - 1) + (src_size - 2) - (2 * (size_t)commonKeys) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE; + key_count = (dest_size - 1) + (src_size - 2) - (2 * static_cast(commonKeys)) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE; } // If the target shortcut's action key is pressed, then it should be released @@ -368,7 +368,7 @@ namespace KeyboardEventHandlers int i = 0; if (isActionKeyPressed) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode); @@ -407,7 +407,7 @@ namespace KeyboardEventHandlers int i = 0; if (std::get(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED && isTargetKeyPressed) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } @@ -432,7 +432,7 @@ namespace KeyboardEventHandlers // key count can be 0 if both shortcuts have same modifiers and the action key is not held down. delete will throw an error if keyEventList is empty if (key_count > 0) { - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; } return 1; @@ -457,14 +457,14 @@ namespace KeyboardEventHandlers memset(keyEventList, 0, sizeof(keyEventList)); if (remapToShortcut) { - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else { - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; } @@ -478,7 +478,7 @@ namespace KeyboardEventHandlers { keyEventList = new INPUT[key_count](); memset(keyEventList, 0, sizeof(keyEventList)); - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else if (std::get(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) { @@ -497,7 +497,7 @@ namespace KeyboardEventHandlers { keyEventList = new INPUT[key_count](); memset(keyEventList, 0, sizeof(keyEventList)); - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD,static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else { @@ -512,7 +512,7 @@ namespace KeyboardEventHandlers // Release new key state int i = 0; - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; // Set original shortcut key down state except the action key @@ -534,7 +534,7 @@ namespace KeyboardEventHandlers } } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; } @@ -586,23 +586,23 @@ namespace KeyboardEventHandlers if (newRemapping.RemapToKey()) { DWORD to = std::get<0>(newRemapping.targetShortcut); - bool isLastKeyStillPressed = ii.GetVirtualKeyState((WORD)from.actionKey); + bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast(from.actionKey)); key_count = static_cast(from.Size()) - 1 + 1 + (isLastKeyStillPressed ? 1 : 0); keyEventList = new INPUT[key_count](); memset(keyEventList, 0, sizeof(keyEventList)); int i = 0; Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); - if (ii.GetVirtualKeyState((WORD)from.actionKey)) + if (ii.GetVirtualKeyState(static_cast(from.actionKey))) { // If the action key from the last shortcut is still being pressed, release it. - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)from.actionKey, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)to, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(to), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); }else { Shortcut to = std::get(newRemapping.targetShortcut); - bool isLastKeyStillPressed = ii.GetVirtualKeyState((WORD)from.actionKey); + bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast(from.actionKey)); size_t temp_key_count_calculation = static_cast(from.Size()) - 1; temp_key_count_calculation += static_cast(to.Size()) - 1; @@ -612,14 +612,14 @@ namespace KeyboardEventHandlers int i = 0; Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to); - if (ii.GetVirtualKeyState((WORD)from.actionKey)) + if (ii.GetVirtualKeyState(static_cast(from.actionKey))) { // If the action key from the last shortcut is still being pressed, release it. - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)from.actionKey, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from); - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)to.actionKey, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); newRemapping.isShortcutInvoked = true; } @@ -636,7 +636,7 @@ namespace KeyboardEventHandlers else { // Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated - key_count = (dest_size) + (src_size - 1) - (2 * (size_t)commonKeys); + key_count = (dest_size) + (src_size - 1) - (2 * static_cast(commonKeys)); // If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set bool isActionKeyPressed = false; @@ -653,7 +653,7 @@ namespace KeyboardEventHandlers int i = 0; if (isActionKeyPressed) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); @@ -664,12 +664,12 @@ namespace KeyboardEventHandlers // key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again if (isActionKeyPressed) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(data->lParam->vkCode), 0, 0); i++; // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu @@ -686,7 +686,7 @@ namespace KeyboardEventHandlers state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; } @@ -730,7 +730,7 @@ namespace KeyboardEventHandlers if (isRemapToDisable && isOriginalActionKeyPressed) { // Set original action key - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD,static_cast(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } else @@ -739,7 +739,7 @@ namespace KeyboardEventHandlers } // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(data->lParam->vkCode), 0, 0); i++; // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu @@ -755,7 +755,7 @@ namespace KeyboardEventHandlers state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; } @@ -858,8 +858,8 @@ namespace KeyboardEventHandlers memset(keyEventList, 0, sizeof(keyEventList)); // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)key, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(key), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; } } diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp index 236fec31d2..c99ce09851 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp @@ -38,7 +38,7 @@ namespace RemappingLogicTests } else { - return (intptr_t)1; + return 1LL; } }); } diff --git a/src/modules/keyboardmanager/common/Helpers.cpp b/src/modules/keyboardmanager/common/Helpers.cpp index a85e585d49..2b9e9670eb 100644 --- a/src/modules/keyboardmanager/common/Helpers.cpp +++ b/src/modules/keyboardmanager/common/Helpers.cpp @@ -103,15 +103,15 @@ namespace Helpers // Set wScan to the value from MapVirtualKey as some applications may use the scan code for handling input, for instance, Windows Terminal ignores non-character input which has scancode set to 0. // MapVirtualKey returns 0 if the key code does not correspond to a physical key (such as unassigned/reserved keys). More details at https://github.com/microsoft/PowerToys/pull/7143#issue-498877747 - keyEventArray[index].ki.wScan = (WORD)MapVirtualKey(keyCode, MAPVK_VK_TO_VSC); + keyEventArray[index].ki.wScan = static_cast(MapVirtualKey(keyCode, MAPVK_VK_TO_VSC)); } // Function to set the dummy key events used for remapping shortcuts, required to ensure releasing a modifier doesn't trigger another action (For example, Win->Start Menu or Alt->Menu bar) void SetDummyKeyEvent(LPINPUT keyEventArray, int& index, ULONG_PTR extraInfo) { - SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, 0, extraInfo); + SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(KeyboardManagerConstants::DUMMY_KEY), 0, extraInfo); index++; - SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, KEYEVENTF_KEYUP, extraInfo); + SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(KeyboardManagerConstants::DUMMY_KEY), KEYEVENTF_KEYUP, extraInfo); index++; } @@ -184,22 +184,22 @@ namespace Helpers // If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckWinKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), 0, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetWinKey(winKeyInvoked)), 0, extraInfoFlag); index++; } if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), 0, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetCtrlKey()), 0, extraInfoFlag); index++; } if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), 0, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetAltKey()), 0, extraInfoFlag); index++; } if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), 0, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetShiftKey()), 0, extraInfoFlag); index++; } } @@ -210,22 +210,22 @@ namespace Helpers // If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey() || shortcutToBeSent.CheckShiftKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), KEYEVENTF_KEYUP, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetShiftKey()), KEYEVENTF_KEYUP, extraInfoFlag); index++; } if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey() || shortcutToBeSent.CheckAltKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), KEYEVENTF_KEYUP, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetAltKey()), KEYEVENTF_KEYUP, extraInfoFlag); index++; } if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey() || shortcutToBeSent.CheckCtrlKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), KEYEVENTF_KEYUP, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetCtrlKey()), KEYEVENTF_KEYUP, extraInfoFlag); index++; } if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked) || shortcutToBeSent.CheckWinKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), KEYEVENTF_KEYUP, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetWinKey(winKeyInvoked)), KEYEVENTF_KEYUP, extraInfoFlag); index++; } } diff --git a/src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp index 262ced5c7e..97487106ca 100644 --- a/src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp +++ b/src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp @@ -18,7 +18,7 @@ namespace KeyboardEventHandlers // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); Helpers::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); - ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; } } diff --git a/src/modules/keyboardmanager/common/MappingConfiguration.cpp b/src/modules/keyboardmanager/common/MappingConfiguration.cpp index ff5827afc0..d2727be708 100644 --- a/src/modules/keyboardmanager/common/MappingConfiguration.cpp +++ b/src/modules/keyboardmanager/common/MappingConfiguration.cpp @@ -296,7 +296,7 @@ bool MappingConfiguration::SaveSettingsToFile() for (const auto& it : singleKeyReMap) { json::JsonObject keys; - keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring((unsigned int)it.first))); + keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring(static_cast(it.first)))); // For key to key remapping if (it.second.index() == 0) diff --git a/src/modules/keyboardmanager/common/Shortcut.cpp b/src/modules/keyboardmanager/common/Shortcut.cpp index 331cf10365..acc7edde14 100644 --- a/src/modules/keyboardmanager/common/Shortcut.cpp +++ b/src/modules/keyboardmanager/common/Shortcut.cpp @@ -425,23 +425,23 @@ winrt::hstring Shortcut::ToHstringVK() const winrt::hstring output; if (winKey != ModifierKey::Disabled) { - output = output + winrt::to_hstring((unsigned int)GetWinKey(ModifierKey::Both)) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetWinKey(ModifierKey::Both))) + winrt::to_hstring(L";"); } if (ctrlKey != ModifierKey::Disabled) { - output = output + winrt::to_hstring((unsigned int)GetCtrlKey()) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetCtrlKey())) + winrt::to_hstring(L";"); } if (altKey != ModifierKey::Disabled) { - output = output + winrt::to_hstring((unsigned int)GetAltKey()) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetAltKey())) + winrt::to_hstring(L";"); } if (shiftKey != ModifierKey::Disabled) { - output = output + winrt::to_hstring((unsigned int)GetShiftKey()) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetShiftKey())) + winrt::to_hstring(L";"); } if (actionKey != NULL) { - output = output + winrt::to_hstring((unsigned int)GetActionKey()) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetActionKey())) + winrt::to_hstring(L";"); } if (!output.empty()) From b13f74c0893279c4c09aad8f1717a4b4282c62ab Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 11:01:35 +0000 Subject: [PATCH 033/163] [Analyzers][CPP]Changes to fix warning 26493 on src/modules/ (L to M) (#23486) starting with letter L to Letter M --- .../MouseUtils/FindMyMouse/FindMyMouse.cpp | 32 ++++----- .../MouseUtils/FindMyMouse/dllmain.cpp | 65 ++++++++++++++++--- .../MouseHighlighter/MouseHighlighter.cpp | 12 ++-- .../MouseUtils/MouseHighlighter/dllmain.cpp | 42 ++++++++++-- .../InclusiveCrosshairs.cpp | 36 +++++----- .../MousePointerCrosshairs/dllmain.cpp | 42 ++++++++++-- .../launcher/Microsoft.Launcher/dllmain.cpp | 4 +- 7 files changed, 172 insertions(+), 61 deletions(-) diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp index d03e91c684..4c84691095 100644 --- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp +++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp @@ -161,7 +161,7 @@ bool SuperSonar::Initialize(HINSTANCE hinst) wc.hInstance = hinst; wc.hIcon = LoadIcon(hinst, IDI_APPLICATION); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wc.hbrBackground = static_cast(GetStockObject(NULL_BRUSH)); wc.lpszClassName = className; if (!RegisterClassW(&wc)) @@ -196,14 +196,14 @@ LRESULT SuperSonar::s_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM SuperSonar* self; if (message == WM_NCCREATE) { - auto info = (LPCREATESTRUCT)lParam; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)info->lpCreateParams); - self = (SuperSonar*)info->lpCreateParams; + auto info = reinterpret_cast(lParam); + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(info->lpCreateParams)); + self = static_cast(info->lpCreateParams); self->m_hwnd = hwnd; } else { - self = (SuperSonar*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + self = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); } if (self) { @@ -230,7 +230,7 @@ LRESULT SuperSonar::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) n break; case WM_INPUT: - OnSonarInput(wParam, (HRAWINPUT)lParam); + OnSonarInput(wParam, reinterpret_cast(lParam)); break; case WM_TIMER: @@ -272,7 +272,7 @@ void SuperSonar::OnSonarInput(WPARAM flags, HRAWINPUT hInput) RAWINPUT input; UINT size = sizeof(input); auto result = GetRawInputData(hInput, RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER)); - if ((int)result < sizeof(RAWINPUTHEADER)) + if (result < sizeof(RAWINPUTHEADER)) { return; } @@ -397,7 +397,7 @@ void SuperSonar::DetectShake() { currentX += movement.diff.x; currentY += movement.diff.y; - distanceTravelled += sqrt((double)movement.diff.x * movement.diff.x + (double)movement.diff.y * movement.diff.y); // Pythagorean theorem + distanceTravelled += sqrt(static_cast(movement.diff.x) * movement.diff.x + static_cast(movement.diff.y) * movement.diff.y); // Pythagorean theorem minX = min(currentX, minX); maxX = max(currentX, maxX); minY = min(currentY, minY); @@ -410,8 +410,8 @@ void SuperSonar::DetectShake() } // Size of the rectangle the pointer moved in. - double rectangleWidth = (double)maxX - minX; - double rectangleHeight = (double)maxY - minY; + double rectangleWidth = static_cast(maxX) - minX; + double rectangleHeight = static_cast(maxY) - minY; double diagonal = sqrt(rectangleWidth * rectangleWidth + rectangleHeight * rectangleHeight); if (diagonal > 0 && distanceTravelled / diagonal > ShakeFactor) @@ -592,7 +592,7 @@ bool SuperSonar::IsForegroundAppExcluded() if (HWND foregroundApp{ GetForegroundWindow() }) { auto processPath = get_process_path(foregroundApp); - CharUpperBuffW(processPath.data(), (DWORD)processPath.length()); + CharUpperBuffW(processPath.data(), static_cast(processPath.length())); return find_app_name_in_path(processPath, m_excludedApps); } else @@ -613,7 +613,7 @@ struct CompositionSpotlight : SuperSonar void AfterMoveSonar() { - m_spotlight.Offset({ (float)m_sonarPos.x, (float)m_sonarPos.y, 0.0f }); + m_spotlight.Offset({ static_cast(m_sonarPos.x), static_cast(m_sonarPos.y), 0.0f }); } LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept @@ -924,10 +924,10 @@ struct GdiSpotlight : GdiSonar auto spotlight = CreateRoundRectRgn( this->m_sonarPos.x - radius, this->m_sonarPos.y - radius, this->m_sonarPos.x + radius, this->m_sonarPos.y + radius, radius * 2, radius * 2); - FillRgn(ps.hdc, spotlight, (HBRUSH)GetStockObject(WHITE_BRUSH)); + FillRgn(ps.hdc, spotlight, static_cast(GetStockObject(WHITE_BRUSH))); Sleep(1000 / 60); ExtSelectClipRgn(ps.hdc, spotlight, RGN_DIFF); - FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)GetStockObject(BLACK_BRUSH)); + FillRect(ps.hdc, &ps.rcPaint, static_cast(GetStockObject(BLACK_BRUSH))); DeleteObject(spotlight); EndPaint(this->m_hwnd, &ps); @@ -959,7 +959,7 @@ struct GdiCrosshairs : GdiSonar auto radius = CurrentSonarRadius(); RECT rc; - HBRUSH white = (HBRUSH)GetStockObject(WHITE_BRUSH); + HBRUSH white = static_cast(GetStockObject(WHITE_BRUSH)); rc.left = m_sonarPos.x - radius; rc.top = ps.rcPaint.top; @@ -973,7 +973,7 @@ struct GdiCrosshairs : GdiSonar rc.bottom = m_sonarPos.y + radius; FillRect(ps.hdc, &rc, white); - HBRUSH black = (HBRUSH)GetStockObject(BLACK_BRUSH); + HBRUSH black = static_cast(GetStockObject(BLACK_BRUSH)); // Top left rc.left = ps.rcPaint.left; diff --git a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp index 27d809d07d..0241a8922b 100644 --- a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp +++ b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp @@ -185,11 +185,16 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Activation Method auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_METHOD); - UINT value = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); - if (value < (int)FindMyMouseActivationMethod::EnumElements) + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value < static_cast(FindMyMouseActivationMethod::EnumElements) && value >= 0) { - findMyMouseSettings.activationMethod = (FindMyMouseActivationMethod)value; + findMyMouseSettings.activationMethod = static_cast(value); } + else + { + throw; + } + } catch (...) { @@ -198,7 +203,7 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) try { auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE); - findMyMouseSettings.doNotActivateOnGameMode = (bool)jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE); + findMyMouseSettings.doNotActivateOnGameMode = jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE); } catch (...) { @@ -246,7 +251,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Overlay Opacity auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_OVERLAY_OPACITY); - findMyMouseSettings.overlayOpacity = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.overlayOpacity = value; + } + else + { + throw; + } } catch (...) { @@ -256,7 +269,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Spotlight Radius auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SPOTLIGHT_RADIUS); - findMyMouseSettings.spotlightRadius = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.spotlightRadius = value; + } + else + { + throw; + } } catch (...) { @@ -266,7 +287,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Animation Duration auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ANIMATION_DURATION_MS); - findMyMouseSettings.animationDurationMs = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.animationDurationMs = value; + } + else + { + throw; + } } catch (...) { @@ -276,7 +305,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Spotlight Initial Zoom auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SPOTLIGHT_INITIAL_ZOOM); - findMyMouseSettings.spotlightInitialZoom = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.spotlightInitialZoom = value; + } + else + { + throw; + } } catch (...) { @@ -289,7 +326,7 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) std::wstring apps = jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE).c_str(); std::vector excludedApps; auto excludedUppercase = apps; - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length())); std::wstring_view view(excludedUppercase); view = left_trim(trim(view)); @@ -311,7 +348,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Shaking Minimum Distance auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHAKING_MINIMUM_DISTANCE); - findMyMouseSettings.shakeMinimumDistance = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.shakeMinimumDistance = value; + } + else + { + throw; + } } catch (...) { diff --git a/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp b/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp index f1107db483..2a10ff648d 100644 --- a/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp +++ b/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp @@ -132,7 +132,7 @@ void Highlighter::AddDrawingPoint(MouseButton button) auto circleGeometry = m_compositor.CreateEllipseGeometry(); circleGeometry.Radius({ m_radius, m_radius }); auto circleShape = m_compositor.CreateSpriteShape(circleGeometry); - circleShape.Offset({ (float)pt.x, (float)pt.y }); + circleShape.Offset({ static_cast(pt.x), static_cast(pt.y )}); if (button == MouseButton::Left) { circleShape.FillBrush(m_compositor.CreateColorBrush(m_leftClickColor)); @@ -166,12 +166,12 @@ void Highlighter::UpdateDrawingPointPosition(MouseButton button) if (button == MouseButton::Left) { - m_leftPointer.Offset({ (float)pt.x, (float)pt.y }); + m_leftPointer.Offset({ static_cast(pt.x), static_cast(pt.y )}); } else { //right - m_rightPointer.Offset({ (float)pt.x, (float)pt.y }); + m_rightPointer.Offset({ static_cast(pt.x), static_cast(pt.y )}); } } void Highlighter::StartDrawingPointFading(MouseButton button) @@ -217,7 +217,7 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa { if (nCode >= 0) { - MSLLHOOKSTRUCT* hookData = (MSLLHOOKSTRUCT*)lParam; + MSLLHOOKSTRUCT* hookData = reinterpret_cast(lParam); switch (wParam) { case WM_LBUTTONDOWN: @@ -293,7 +293,7 @@ void Highlighter::SwitchActivationMode() } void Highlighter::ApplySettings(MouseHighlighterSettings settings) { - m_radius = (float)settings.radius; + m_radius = static_cast(settings.radius); m_fadeDelay_ms = settings.fadeDelayMs; m_fadeDuration_ms = settings.fadeDurationMs; m_leftClickColor = settings.leftButtonColor; @@ -349,7 +349,7 @@ bool Highlighter::MyRegisterClass(HINSTANCE hInstance) wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wc.hbrBackground = static_cast(GetStockObject(NULL_BRUSH)); wc.lpszClassName = m_className; if (!RegisterClassW(&wc)) diff --git a/src/modules/MouseUtils/MouseHighlighter/dllmain.cpp b/src/modules/MouseUtils/MouseHighlighter/dllmain.cpp index 75cf746a02..b3860a227b 100644 --- a/src/modules/MouseUtils/MouseHighlighter/dllmain.cpp +++ b/src/modules/MouseUtils/MouseHighlighter/dllmain.cpp @@ -215,7 +215,15 @@ public: { // Parse Opacity auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_OPACITY); - opacity = (uint8_t)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + opacity = value; + } + else + { + throw; + } } catch (...) { @@ -234,7 +242,7 @@ public: auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_LEFT_BUTTON_CLICK_COLOR); auto leftColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE); uint8_t r, g, b; - if (!checkValidRGB(leftColor,&r,&g,&b)) + if (!checkValidRGB(leftColor, &r, &g, &b)) { Logger::error("Left click color RGB value is invalid. Will use default value"); } @@ -270,7 +278,15 @@ public: { // Parse Radius auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_RADIUS); - highlightSettings.radius = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + highlightSettings.radius = value; + } + else + { + throw; + } } catch (...) { @@ -280,7 +296,15 @@ public: { // Parse Fade Delay auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_FADE_DELAY_MS); - highlightSettings.fadeDelayMs = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + highlightSettings.fadeDelayMs = value; + } + else + { + throw; + } } catch (...) { @@ -290,7 +314,15 @@ public: { // Parse Fade Duration auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_FADE_DURATION_MS); - highlightSettings.fadeDurationMs = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + highlightSettings.fadeDurationMs = value; + } + else + { + throw; + } } catch (...) { diff --git a/src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp b/src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp index 0f84fe9632..6cedfe6d52 100644 --- a/src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp +++ b/src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp @@ -212,25 +212,25 @@ void InclusiveCrosshairs::UpdateCrosshairsPosition() float halfPixelAdjustment = m_crosshairs_thickness % 2 == 1 ? 0.5f : 0.0f; // Position crosshairs components around the mouse pointer. - m_left_crosshairs_border.Offset({ (float)ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, (float)ptCursor.y + halfPixelAdjustment, .0f }); - m_left_crosshairs_border.Size({ (float)ptCursor.x - (float)ptMonitorUpperLeft.x - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, (float)m_crosshairs_thickness + m_crosshairs_border_size * 2 }); - m_left_crosshairs.Offset({ (float)ptCursor.x - m_crosshairs_radius + halfPixelAdjustment * 2, (float)ptCursor.y + halfPixelAdjustment, .0f }); - m_left_crosshairs.Size({ (float)ptCursor.x - (float)ptMonitorUpperLeft.x - m_crosshairs_radius + halfPixelAdjustment * 2, (float)m_crosshairs_thickness }); + m_left_crosshairs_border.Offset({ ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, ptCursor.y + halfPixelAdjustment, .0f }); + m_left_crosshairs_border.Size({ ptCursor.x - ptMonitorUpperLeft.x - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, m_crosshairs_thickness + m_crosshairs_border_size * 2.f }); + m_left_crosshairs.Offset({ ptCursor.x - m_crosshairs_radius + halfPixelAdjustment * 2.f, ptCursor.y + halfPixelAdjustment, .0f }); + m_left_crosshairs.Size({ ptCursor.x - ptMonitorUpperLeft.x - m_crosshairs_radius + halfPixelAdjustment * 2, static_cast(m_crosshairs_thickness) }); - m_right_crosshairs_border.Offset({ (float)ptCursor.x + m_crosshairs_radius - m_crosshairs_border_size, (float)ptCursor.y + halfPixelAdjustment, .0f }); - m_right_crosshairs_border.Size({ (float)ptMonitorBottomRight.x - (float)ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size, (float)m_crosshairs_thickness + m_crosshairs_border_size * 2 }); - m_right_crosshairs.Offset({ (float)ptCursor.x + m_crosshairs_radius, (float)ptCursor.y + halfPixelAdjustment, .0f }); - m_right_crosshairs.Size({ (float)ptMonitorBottomRight.x - (float)ptCursor.x - m_crosshairs_radius, (float)m_crosshairs_thickness }); + m_right_crosshairs_border.Offset({static_cast(ptCursor.x) + m_crosshairs_radius - m_crosshairs_border_size, ptCursor.y + halfPixelAdjustment, .0f }); + m_right_crosshairs_border.Size({ static_cast(ptMonitorBottomRight.x) - ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size, m_crosshairs_thickness + m_crosshairs_border_size * 2.f }); + m_right_crosshairs.Offset({ static_cast(ptCursor.x) + m_crosshairs_radius, ptCursor.y + halfPixelAdjustment, .0f }); + m_right_crosshairs.Size({ static_cast(ptMonitorBottomRight.x) - ptCursor.x - m_crosshairs_radius, static_cast(m_crosshairs_thickness) }); - m_top_crosshairs_border.Offset({ (float)ptCursor.x + halfPixelAdjustment, (float)ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, .0f }); - m_top_crosshairs_border.Size({ (float)m_crosshairs_thickness + m_crosshairs_border_size * 2, (float)ptCursor.y - (float)ptMonitorUpperLeft.y - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2 }); - m_top_crosshairs.Offset({ (float)ptCursor.x + halfPixelAdjustment, (float)ptCursor.y - m_crosshairs_radius + halfPixelAdjustment * 2, .0f }); - m_top_crosshairs.Size({ (float)m_crosshairs_thickness, (float)ptCursor.y - (float)ptMonitorUpperLeft.y - m_crosshairs_radius + halfPixelAdjustment * 2 }); + m_top_crosshairs_border.Offset({ ptCursor.x + halfPixelAdjustment, ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, .0f }); + m_top_crosshairs_border.Size({ m_crosshairs_thickness + m_crosshairs_border_size * 2.f, ptCursor.y - ptMonitorUpperLeft.y - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2 }); + m_top_crosshairs.Offset({ ptCursor.x + halfPixelAdjustment, ptCursor.y - m_crosshairs_radius + halfPixelAdjustment * 2, .0f }); + m_top_crosshairs.Size({ static_cast(m_crosshairs_thickness), ptCursor.y - ptMonitorUpperLeft.y - m_crosshairs_radius + halfPixelAdjustment * 2 }); - m_bottom_crosshairs_border.Offset({ (float)ptCursor.x + halfPixelAdjustment, (float)ptCursor.y + m_crosshairs_radius - m_crosshairs_border_size, .0f }); - m_bottom_crosshairs_border.Size({ (float)m_crosshairs_thickness + m_crosshairs_border_size * 2, (float)ptMonitorBottomRight.y - (float)ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size }); - m_bottom_crosshairs.Offset({ (float)ptCursor.x + halfPixelAdjustment, (float)ptCursor.y + m_crosshairs_radius, .0f }); - m_bottom_crosshairs.Size({ (float)m_crosshairs_thickness, (float)ptMonitorBottomRight.y - (float)ptCursor.y - m_crosshairs_radius }); + m_bottom_crosshairs_border.Offset({ ptCursor.x + halfPixelAdjustment, static_cast(ptCursor.y) + m_crosshairs_radius - m_crosshairs_border_size, .0f }); + m_bottom_crosshairs_border.Size({ m_crosshairs_thickness + m_crosshairs_border_size * 2.f, static_cast(ptMonitorBottomRight.y) - ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size }); + m_bottom_crosshairs.Offset({ ptCursor.x + halfPixelAdjustment, static_cast(ptCursor.y) + m_crosshairs_radius, .0f }); + m_bottom_crosshairs.Size({ static_cast(m_crosshairs_thickness), static_cast(ptMonitorBottomRight.y) - ptCursor.y - m_crosshairs_radius }); } @@ -238,7 +238,7 @@ LRESULT CALLBACK InclusiveCrosshairs::MouseHookProc(int nCode, WPARAM wParam, LP { if (nCode >= 0) { - MSLLHOOKSTRUCT* hookData = (MSLLHOOKSTRUCT*)lParam; + MSLLHOOKSTRUCT* hookData = reinterpret_cast(lParam); if (wParam == WM_MOUSEMOVE) { instance->UpdateCrosshairsPosition(); } @@ -362,7 +362,7 @@ bool InclusiveCrosshairs::MyRegisterClass(HINSTANCE hInstance) wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wc.hbrBackground = static_cast(GetStockObject(NULL_BRUSH)); wc.lpszClassName = m_className; if (!RegisterClassW(&wc)) diff --git a/src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp b/src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp index 81c79c57b6..257d050e5e 100644 --- a/src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp +++ b/src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp @@ -222,7 +222,15 @@ public: { // Parse Opacity auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_OPACITY); - inclusiveCrosshairsSettings.crosshairsOpacity = (uint8_t)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + inclusiveCrosshairsSettings.crosshairsOpacity = value; + } + else + { + throw; + } } catch (...) { @@ -251,7 +259,16 @@ public: { // Parse Radius auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_RADIUS); - inclusiveCrosshairsSettings.crosshairsRadius = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + inclusiveCrosshairsSettings.crosshairsRadius = value; + } + else + { + throw; + } + } catch (...) { @@ -261,7 +278,16 @@ public: { // Parse Thickness auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_THICKNESS); - inclusiveCrosshairsSettings.crosshairsThickness = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + inclusiveCrosshairsSettings.crosshairsThickness = value; + } + else + { + throw; + } + } catch (...) { @@ -290,7 +316,15 @@ public: { // Parse border size auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_BORDER_SIZE); - inclusiveCrosshairsSettings.crosshairsBorderSize = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast (jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + inclusiveCrosshairsSettings.crosshairsBorderSize = value; + } + else + { + throw; + } } catch (...) { diff --git a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp index 386f9b0420..b692dd1daf 100644 --- a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp +++ b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp @@ -349,7 +349,7 @@ public: DWORD windowPid; GetWindowThreadProcessId(nextWindow, &windowPid); - if (windowPid == (DWORD)closePid) + if (windowPid == static_cast(closePid)) ::PostMessage(nextWindow, WM_CLOSE, 0, 0); return true; @@ -401,7 +401,7 @@ void Microsoft_Launcher::parse_hotkey(PowerToysSettings::PowerToyValues& setting try { auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES); - m_use_centralized_keyboard_hook = (bool)jsonPropertiesObject.GetNamedBoolean(JSON_KEY_USE_CENTRALIZED_KEYBOARD_HOOK); + m_use_centralized_keyboard_hook =jsonPropertiesObject.GetNamedBoolean(JSON_KEY_USE_CENTRALIZED_KEYBOARD_HOOK); } catch (...) { From 49b2823056e6af130efd0f5d120666e08a95245a Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 11:15:16 +0000 Subject: [PATCH 034/163] [Analyzers][CPP]Changes to fix warning 26493 on src/modules/ (S to V) (#23625) Modules starting with S to V --- .../ShortcutGuide/ShortcutGuide/d2d_svg.cpp | 6 +- .../ShortcutGuide/ShortcutGuide/d2d_text.cpp | 2 +- .../ShortcutGuide/d2d_window.cpp | 2 +- .../ShortcutGuide/overlay_window.cpp | 100 +++++++++--------- .../ShortcutGuide/shortcut_guide.cpp | 12 +-- .../ShortcutGuide/tasklist_positions.cpp | 8 +- .../ShortcutGuideModuleInterface/dllmain.cpp | 22 +++- .../VideoConferenceModule/Toolbar.cpp | 4 +- .../ImageLoading.cpp | 2 +- .../VideoCaptureProxyFilter.cpp | 2 +- 10 files changed, 88 insertions(+), 72 deletions(-) diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp index 08c5c79fe8..0f01aaf403 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp @@ -24,9 +24,9 @@ D2DSVG& D2DSVG::load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc) svg->GetRoot(root.put()); float tmp; winrt::check_hresult(root->GetAttributeValue(L"width", &tmp)); - svg_width = (int)tmp; + svg_width = static_cast(tmp); winrt::check_hresult(root->GetAttributeValue(L"height", &tmp)); - svg_height = (int)tmp; + svg_height = static_cast(tmp); return *this; } @@ -43,7 +43,7 @@ D2DSVG& D2DSVG::resize(int x, int y, int width, int height, float fill, float ma used_scale = std::min(used_scale, max_scale); } transform = transform * D2D1::Matrix3x2F::Scale(used_scale, used_scale, D2D1::Point2F(width / 2.0f, height / 2.0f)); - transform = transform * D2D1::Matrix3x2F::Translation((float)x, (float)y); + transform = transform * D2D1::Matrix3x2F::Translation(static_cast(x), static_cast(y)); return *this; } diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp index 87afa42930..7f25c4e32c 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp @@ -47,7 +47,7 @@ void D2DText::write(ID2D1DeviceContext5* d2d_dc, D2D1_COLOR_F color, D2D1_RECT_F winrt::com_ptr brush; d2d_dc->CreateSolidColorBrush(color, brush.put()); d2d_dc->DrawText(text.c_str(), - (UINT32)text.length(), + static_cast(text.length()), format.get(), rect, brush.get()); diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp index 095e7dcba8..f4bf7e9cf6 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp @@ -199,7 +199,7 @@ LRESULT __stdcall D2DWindow::d2d_window_proc(HWND window, UINT message, WPARAM w } case WM_MOVE: case WM_SIZE: - self->base_resize((unsigned)lparam & 0xFFFF, (unsigned)lparam >> 16); + self->base_resize(static_cast(lparam) & 0xFFFF, static_cast(lparam) >> 16); [[fallthrough]]; case WM_PAINT: self->base_render(); diff --git a/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp b/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp index ddf5e30680..7ef1e90301 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp @@ -111,10 +111,10 @@ D2DOverlaySVG& D2DOverlaySVG::resize(int x, int y, int width, int height, float { auto scaled_top_left = transform.TransformPoint(thumbnail_top_left); auto scanled_bottom_right = transform.TransformPoint(thumbnail_bottom_right); - thumbnail_scaled_rect.left = (int)scaled_top_left.x; - thumbnail_scaled_rect.top = (int)scaled_top_left.y; - thumbnail_scaled_rect.right = (int)scanled_bottom_right.x; - thumbnail_scaled_rect.bottom = (int)scanled_bottom_right.y; + thumbnail_scaled_rect.left = static_cast(scaled_top_left.x); + thumbnail_scaled_rect.top = static_cast(scaled_top_left.y); + thumbnail_scaled_rect.right = static_cast(scanled_bottom_right.x); + thumbnail_scaled_rect.bottom = static_cast(scanled_bottom_right.y); } return *this; } @@ -156,10 +156,10 @@ ScaleResult D2DOverlaySVG::get_thumbnail_rect_and_scale(int x_offset, int y_offs float scale_v = fill * thumbnail_scaled_rect_heigh / window_cy; float use_scale = std::min(scale_h, scale_v); RECT thumb_rect; - thumb_rect.left = thumbnail_scaled_rect.left + (int)(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset; - thumb_rect.right = thumbnail_scaled_rect.right - (int)(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset; - thumb_rect.top = thumbnail_scaled_rect.top + (int)(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset; - thumb_rect.bottom = thumbnail_scaled_rect.bottom - (int)(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset; + thumb_rect.left = thumbnail_scaled_rect.left + static_cast(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset; + thumb_rect.right = thumbnail_scaled_rect.right - static_cast(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset; + thumb_rect.top = thumbnail_scaled_rect.top + static_cast(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset; + thumb_rect.bottom = thumbnail_scaled_rect.bottom - static_cast(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset; ScaleResult result; result.scale = use_scale; result.rect = thumb_rect; @@ -185,8 +185,8 @@ D2DOverlaySVG& D2DOverlaySVG::toggle_window_group(bool active) D2D1_RECT_F D2DOverlaySVG::get_maximize_label() const { D2D1_RECT_F result; - auto height = (float)(thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top); - auto width = (float)(thumbnail_scaled_rect.right - thumbnail_scaled_rect.left); + auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top; + auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left; if (width >= height) { result.top = thumbnail_scaled_rect.bottom + height * 0.210f; @@ -198,7 +198,7 @@ D2D1_RECT_F D2DOverlaySVG::get_maximize_label() const { result.top = thumbnail_scaled_rect.top + height * 0.323f; result.bottom = thumbnail_scaled_rect.top + height * 0.398f; - result.left = (float)thumbnail_scaled_rect.right; + result.left = static_cast(thumbnail_scaled_rect.right); result.right = thumbnail_scaled_rect.right + width * 1.45f; } return result; @@ -206,8 +206,8 @@ D2D1_RECT_F D2DOverlaySVG::get_maximize_label() const D2D1_RECT_F D2DOverlaySVG::get_minimize_label() const { D2D1_RECT_F result; - auto height = (float)(thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top); - auto width = (float)(thumbnail_scaled_rect.right - thumbnail_scaled_rect.left); + auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top; + auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left; if (width >= height) { result.top = thumbnail_scaled_rect.bottom + height * 0.8f; @@ -219,7 +219,7 @@ D2D1_RECT_F D2DOverlaySVG::get_minimize_label() const { result.top = thumbnail_scaled_rect.top + height * 0.725f; result.bottom = thumbnail_scaled_rect.top + height * 0.800f; - result.left = (float)thumbnail_scaled_rect.right; + result.left =static_cast(thumbnail_scaled_rect.right); result.right = thumbnail_scaled_rect.right + width * 1.45f; } return result; @@ -227,8 +227,8 @@ D2D1_RECT_F D2DOverlaySVG::get_minimize_label() const D2D1_RECT_F D2DOverlaySVG::get_snap_left() const { D2D1_RECT_F result; - auto height = (float)(thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top); - auto width = (float)(thumbnail_scaled_rect.right - thumbnail_scaled_rect.left); + auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top; + auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left; if (width >= height) { result.top = thumbnail_scaled_rect.bottom + height * 0.5f; @@ -240,7 +240,7 @@ D2D1_RECT_F D2DOverlaySVG::get_snap_left() const { result.top = thumbnail_scaled_rect.top + height * 0.523f; result.bottom = thumbnail_scaled_rect.top + height * 0.598f; - result.left = (float)thumbnail_scaled_rect.right; + result.left = static_cast(thumbnail_scaled_rect.right); result.right = thumbnail_scaled_rect.right + width * 0.450f; } return result; @@ -248,8 +248,8 @@ D2D1_RECT_F D2DOverlaySVG::get_snap_left() const D2D1_RECT_F D2DOverlaySVG::get_snap_right() const { D2D1_RECT_F result; - auto height = (float)(thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top); - auto width = (float)(thumbnail_scaled_rect.right - thumbnail_scaled_rect.left); + auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top; + auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left; if (width >= height) { result.top = thumbnail_scaled_rect.bottom + height * 0.5f; @@ -261,7 +261,7 @@ D2D1_RECT_F D2DOverlaySVG::get_snap_right() const { result.top = thumbnail_scaled_rect.top + height * 0.523f; result.bottom = thumbnail_scaled_rect.top + height * 0.598f; - result.left = (float)thumbnail_scaled_rect.right + width; + result.left = static_cast(thumbnail_scaled_rect.right + width); result.right = thumbnail_scaled_rect.right + width * 1.45f; } return result; @@ -390,7 +390,7 @@ void D2DOverlayWindow::show(HWND window, bool snappable) // Check if taskbar is auto-hidden. If so, don't display the number arrows APPBARDATA param = {}; param.cbSize = sizeof(APPBARDATA); - if ((UINT)SHAppBarMessage(ABM_GETSTATE, ¶m) != ABS_AUTOHIDE) + if (static_cast(SHAppBarMessage(ABM_GETSTATE, ¶m)) != ABS_AUTOHIDE) { tasklist_cv_mutex.lock(); tasklist_update = true; @@ -588,12 +588,12 @@ void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_ dy = -1; arrow.toggle_element(L"bottom", true); } - double arrow_ratio = (double)arrow.height() / arrow.width(); + double arrow_ratio = static_cast(arrow.height()) / arrow.width(); if (dy != 0) { // assume button is 25% wider than taller, +10% to make room for each of the arrows that are hidden - auto render_arrow_width = (int)(button.height * 1.25f * 1.2f); - auto render_arrow_height = (int)(render_arrow_width * arrow_ratio); + auto render_arrow_width = static_cast(button.height * 1.25f * 1.2f); + auto render_arrow_height = static_cast(render_arrow_width * arrow_ratio); arrow.resize((button.x + (button.width - render_arrow_width) / 2) + x_offset, (dy == -1 ? button.y - render_arrow_height : 0) + y_offset, render_arrow_width, @@ -605,8 +605,8 @@ void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_ else { // same as above - make room for the hidden arrow - auto render_arrow_height = (int)(button.height * 1.2f); - auto render_arrow_width = (int)(render_arrow_height / arrow_ratio); + auto render_arrow_height = static_cast(button.height * 1.2f); + auto render_arrow_width = static_cast(render_arrow_height / arrow_ratio); arrow.resize((dx == -1 ? button.x - render_arrow_width : 0) + x_offset, (button.y + (button.height - render_arrow_height) / 2) + y_offset, render_arrow_width, @@ -627,7 +627,7 @@ bool D2DOverlayWindow::show_thumbnail(const RECT& rect, double alpha) thumb_properties.dwFlags = DWM_TNP_SOURCECLIENTAREAONLY | DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY; thumb_properties.fSourceClientAreaOnly = FALSE; thumb_properties.fVisible = TRUE; - thumb_properties.opacity = (BYTE)(255 * alpha); + thumb_properties.opacity = static_cast(255 * alpha); thumb_properties.rcDestination = rect; if (DwmUpdateThumbnailProperties(thumbnail, &thumb_properties) != S_OK) { @@ -655,10 +655,10 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) d2d_device_context->Clear(); int taskbar_icon_shortcuts_x_offset = 0, taskbar_icon_shortcuts_y_offset = 0; - float current_background_anim_value = (float)background_animation.value(Animation::AnimFunctions::LINEAR); - float current_global_windows_shortcuts_anim_value = (float)global_windows_shortcuts_animation.value(Animation::AnimFunctions::LINEAR); - float pos_global_windows_shortcuts_anim_value = 1 - (float)global_windows_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); - float pos_taskbar_icon_shortcuts_anim_value = 1 - (float)taskbar_icon_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); + double current_background_anim_value = background_animation.value(Animation::AnimFunctions::LINEAR); + double current_global_windows_shortcuts_anim_value = global_windows_shortcuts_animation.value(Animation::AnimFunctions::LINEAR); + double pos_global_windows_shortcuts_anim_value = 1 - global_windows_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); + double pos_taskbar_icon_shortcuts_anim_value = 1 - taskbar_icon_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); // Draw background SetLayeredWindowAttributes(hwnd, 0, static_cast(255 * current_background_anim_value), LWA_ALPHA); @@ -667,8 +667,8 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) D2D1_COLOR_F brushColor = light_mode ? D2D1::ColorF(1.0f, 1.0f, 1.0f, brush_opacity) : D2D1::ColorF(0, 0, 0, brush_opacity); winrt::check_hresult(d2d_device_context->CreateSolidColorBrush(brushColor, brush.put())); D2D1_RECT_F background_rect = {}; - background_rect.bottom = (float)window_height; - background_rect.right = (float)window_width; + background_rect.bottom = static_cast(window_height); + background_rect.right = static_cast(window_width); d2d_device_context->SetTransform(D2D1::Matrix3x2F::Identity()); d2d_device_context->FillRectangle(background_rect, brush.get()); @@ -680,26 +680,26 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) if (tasklist_buttons[0].x <= window_rect.left) { // taskbar on left - taskbar_icon_shortcuts_x_offset = (int)(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale()); + taskbar_icon_shortcuts_x_offset = static_cast(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale()); } if (tasklist_buttons[0].x >= window_rect.right) { // taskbar on right - taskbar_icon_shortcuts_x_offset = (int)(pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale()); + taskbar_icon_shortcuts_x_offset = static_cast(pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale()); } if (tasklist_buttons[0].y <= window_rect.top) { // taskbar on top - taskbar_icon_shortcuts_y_offset = (int)(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); + taskbar_icon_shortcuts_y_offset = static_cast(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); } if (tasklist_buttons[0].y >= window_rect.bottom) { // taskbar on bottom - taskbar_icon_shortcuts_y_offset = (int)(pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); + taskbar_icon_shortcuts_y_offset = static_cast(pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); } for (auto&& button : tasklist_buttons) { - if ((size_t)(button.keynum) - 1 >= arrows.size()) + if (static_cast(button.keynum) - 1 >= arrows.size()) { continue; } @@ -767,10 +767,10 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) RECT thumbnail_pos; if (render_monitors) { - thumbnail_pos.left = (int)((thumb_window->left + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); - thumbnail_pos.top = (int)((thumb_window->top + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); - thumbnail_pos.right = (int)((thumb_window->right + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); - thumbnail_pos.bottom = (int)((thumb_window->bottom + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); + thumbnail_pos.left = static_cast((thumb_window->left + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); + thumbnail_pos.top = static_cast((thumb_window->top + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); + thumbnail_pos.right = static_cast((thumb_window->right + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); + thumbnail_pos.bottom = static_cast((thumb_window->bottom + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); } else { @@ -791,17 +791,17 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) // render the monitors if (render_monitors) { - brushColor = D2D1::ColorF(colors.start_color_menu, miniature_shown ? current_global_windows_shortcuts_anim_value * 0.9f : current_global_windows_shortcuts_anim_value * 0.3f); + brushColor = D2D1::ColorF(colors.start_color_menu, miniature_shown ? static_cast(current_global_windows_shortcuts_anim_value * 0.9) : static_cast(current_global_windows_shortcuts_anim_value * 0.3)); brush = nullptr; winrt::check_hresult(d2d_device_context->CreateSolidColorBrush(brushColor, brush.put())); for (auto& monitor : monitors) { D2D1_RECT_F monitor_rect; const auto monitor_size = monitor.GetScreenSize(true); - monitor_rect.left = (float)((monitor_size.left() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); - monitor_rect.top = (float)((monitor_size.top() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); - monitor_rect.right = (float)((monitor_size.right() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); - monitor_rect.bottom = (float)((monitor_size.bottom() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); + monitor_rect.left = static_cast((monitor_size.left() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); + monitor_rect.top = static_cast((monitor_size.top() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); + monitor_rect.right = static_cast((monitor_size.right() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); + monitor_rect.bottom = static_cast((monitor_size.bottom() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); d2d_device_context->SetTransform(D2D1::Matrix3x2F::Identity()); d2d_device_context->FillRectangle(monitor_rect, brush.get()); } @@ -815,8 +815,8 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) } // Set the animation - move the draw window according to animation step - int global_windows_shortcuts_y_offset = (int)(pos_global_windows_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); - auto popIn = D2D1::Matrix3x2F::Translation(0, (float)global_windows_shortcuts_y_offset); + int global_windows_shortcuts_y_offset = static_cast(pos_global_windows_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); + auto popIn = D2D1::Matrix3x2F::Translation(0, static_cast(global_windows_shortcuts_y_offset)); d2d_device_context->SetTransform(popIn); // Animate keys @@ -824,7 +824,7 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) { auto& animation = key_animations[id]; D2D1_COLOR_F color; - auto value = (float)animation.animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); + auto value = static_cast(animation.animation.value(Animation::AnimFunctions::EASE_OUT_EXPO)); color.a = 1.0f; color.r = animation.original.r + (1.0f - animation.original.r) * value; color.g = animation.original.g + (1.0f - animation.original.g) * value; diff --git a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp index e150cf0c08..2843017ac6 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp @@ -196,7 +196,7 @@ OverlayWindow::OverlayWindow(HWND activeWindow) void OverlayWindow::ShowWindow() { winkey_popup = std::make_unique(); - winkey_popup->apply_overlay_opacity(((float)overlayOpacity.value) / 100.0f); + winkey_popup->apply_overlay_opacity(overlayOpacity.value / 100.0f); winkey_popup->set_theme(theme.value); // The press time only takes effect when the shortcut guide is activated by pressing the win key. @@ -336,7 +336,7 @@ bool OverlayWindow::is_disabled_app(wchar_t* exePath) } auto exePathUpper = std::wstring(exePath); - CharUpperBuffW(exePathUpper.data(), (DWORD)exePathUpper.length()); + CharUpperBuffW(exePathUpper.data(), static_cast(exePathUpper.length())); for (const auto& row : disabled_apps_array) { const auto pos = exePathUpper.rfind(row); @@ -354,7 +354,7 @@ void OverlayWindow::update_disabled_apps() { disabled_apps_array.clear(); auto disabledUppercase = disabledApps.value; - CharUpperBuffW(disabledUppercase.data(), (DWORD)disabledUppercase.length()); + CharUpperBuffW(disabledUppercase.data(), static_cast(disabledUppercase.length())); std::wstring_view view(disabledUppercase); view = trim(view); while (!view.empty()) @@ -418,7 +418,7 @@ ShortcutGuideSettings OverlayWindow::GetSettings() noexcept try { - settings.overlayOpacity = (int)properties.GetNamedObject(OverlayOpacity::name).GetNamedNumber(L"value"); + settings.overlayOpacity = static_cast(properties.GetNamedObject(OverlayOpacity::name).GetNamedNumber(L"value")); } catch (...) { @@ -434,7 +434,7 @@ ShortcutGuideSettings OverlayWindow::GetSettings() noexcept try { - settings.windowsKeyPressTimeForGlobalWindowsShortcuts = (int)properties.GetNamedObject(WindowsKeyPressTimeForGlobalWindowsShortcuts::name).GetNamedNumber(L"value"); + settings.windowsKeyPressTimeForGlobalWindowsShortcuts = static_cast(properties.GetNamedObject(WindowsKeyPressTimeForGlobalWindowsShortcuts::name).GetNamedNumber(L"value")); } catch (...) { @@ -442,7 +442,7 @@ ShortcutGuideSettings OverlayWindow::GetSettings() noexcept try { - settings.windowsKeyPressTimeForTaskbarIconShortcuts = (int)properties.GetNamedObject(WindowsKeyPressTimeForTaskbarIconShortcuts::name).GetNamedNumber(L"value"); + settings.windowsKeyPressTimeForTaskbarIconShortcuts = static_cast(properties.GetNamedObject(WindowsKeyPressTimeForTaskbarIconShortcuts::name).GetNamedNumber(L"value")); } catch (...) { diff --git a/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp b/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp index af77a03ab2..687fc86566 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp @@ -60,16 +60,16 @@ bool Tasklist::update_buttons(std::vector& buttons) double value; pos = 0; SafeArrayGetElement(var_rect.parray, &pos, &value); - button.x = (long)value; + button.x = static_cast(value); pos = 1; SafeArrayGetElement(var_rect.parray, &pos, &value); - button.y = (long)value; + button.y = static_cast(value); pos = 2; SafeArrayGetElement(var_rect.parray, &pos, &value); - button.width = (long)value; + button.width = static_cast(value); pos = 3; SafeArrayGetElement(var_rect.parray, &pos, &value); - button.height = (long)value; + button.height = static_cast(value); } VariantClear(&var_rect); } diff --git a/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp b/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp index de6c3a77ea..297b5ab4bc 100644 --- a/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp @@ -332,11 +332,27 @@ private: { // 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"); + m_shouldReactToPressedWinKey = jsonUseLegacyWinKeyBehaviorObject.GetNamedBoolean(L"value"); auto jsonPressTimeForGlobalWindowsShortcutsObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"press_time"); auto jsonPressTimeForTaskbarIconShortcutsObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"press_time_for_taskbar_icon_shortcuts"); - m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = (UINT)jsonPressTimeForGlobalWindowsShortcutsObject.GetNamedNumber(L"value"); - m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = (UINT)jsonPressTimeForTaskbarIconShortcutsObject.GetNamedNumber(L"value"); + int value = static_cast(jsonPressTimeForGlobalWindowsShortcutsObject.GetNamedNumber(L"value")); + if (value >= 0) + { + m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = value; + } + else + { + throw; + } + value = static_cast(jsonPressTimeForTaskbarIconShortcutsObject.GetNamedNumber(L"value")); + if (value >= 0) + { + m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = value; + } + else + { + throw; + } } catch (...) { diff --git a/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp b/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp index 2cd5f0fb64..0477718793 100644 --- a/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp +++ b/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp @@ -209,7 +209,7 @@ void Toolbar::show(std::wstring position, std::wstring monitorString) wc.hInstance = GetModuleHandleW(nullptr); wc.lpszClassName = CLASS_NAME; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.hbrBackground = reinterpret_cast(COLOR_WINDOW); wc.lpfnWndProc = WindowProcessMessages; RegisterClassW(&wc); @@ -282,7 +282,7 @@ void Toolbar::show(std::wstring position, std::wstring monitorString) auto transparentColorKey = RGB(0, 0, 255); HBRUSH brush = CreateSolidBrush(transparentColorKey); - SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush); + SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, reinterpret_cast(brush)); SetLayeredWindowAttributes(hwnd, transparentColorKey, 0, LWA_COLORKEY); diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp b/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp index 31af9c3289..4887801154 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp +++ b/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp @@ -111,7 +111,7 @@ bool ReencodeJPGImage(BYTE* imageBuf, const DWORD imageSize, DWORD& reencodedSiz auto jpgStreamMemory = static_cast(GlobalLock(streamMemoryHandle)); std::copy(jpgStreamMemory, jpgStreamMemory + jpgStreamSize, imageBuf); auto unlockJpgStreamMemory = wil::scope_exit([jpgStreamMemory] { GlobalUnlock(jpgStreamMemory); }); - reencodedSize = (DWORD)jpgStreamSize; + reencodedSize = static_cast(jpgStreamSize); return true; } diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureProxyFilter.cpp b/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureProxyFilter.cpp index e938ed02f2..2a860a72fa 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureProxyFilter.cpp +++ b/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureProxyFilter.cpp @@ -349,7 +349,7 @@ HRESULT VideoCaptureProxyPin::Get( return E_UNEXPECTED; } - *(GUID*)pPropData = PIN_CATEGORY_CAPTURE; + *static_cast(pPropData) = PIN_CATEGORY_CAPTURE; LOG("VideoCaptureProxyPin::Get SUCCESS"); return S_OK; From 9168f871af8cf5a9f63981227a387ba1d0578eb1 Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 11:17:33 +0000 Subject: [PATCH 035/163] [Analyzers][CPP]Changes to fix warning 26493 on src/runner (#23672) * Changes to fix warning 26493 on src/runner * formating --- src/runner/auto_start_helper.cpp | 6 +++--- src/runner/settings_telemetry.cpp | 10 +++++----- src/runner/settings_window.cpp | 6 +++--- src/runner/tray_icon.cpp | 7 +++---- src/runner/unhandled_exception_handler.cpp | 20 ++++++++++---------- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/runner/auto_start_helper.cpp b/src/runner/auto_start_helper.cpp index e72fa31d78..ebe39d01be 100644 --- a/src/runner/auto_start_helper.cpp +++ b/src/runner/auto_start_helper.cpp @@ -75,7 +75,7 @@ bool create_auto_start_task_for_this_user(bool runElevated) NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. @@ -286,7 +286,7 @@ bool delete_auto_start_task_for_this_user() NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. @@ -351,7 +351,7 @@ bool is_auto_start_task_active_for_this_user() NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. diff --git a/src/runner/settings_telemetry.cpp b/src/runner/settings_telemetry.cpp index 3ef22a9c7a..7bab24d448 100644 --- a/src/runner/settings_telemetry.cpp +++ b/src/runner/settings_telemetry.cpp @@ -48,7 +48,7 @@ void send() { powertoy->send_settings_telemetry(); } - catch(...) + catch (...) { Logger::error(L"Failed to send telemetry for {} module", name); } @@ -59,20 +59,20 @@ void send() void run_interval() { auto time = get_last_send_time(); - long long wait_time = 24*3600; + long long wait_time = 24 * 3600; long long left_to_wait = 0; if (time.has_value()) { left_to_wait = max(0, wait_time - timeutil::diff::in_seconds(timeutil::now(), time.value())); } - Sleep((DWORD)left_to_wait * 1000); + Sleep(static_cast(left_to_wait * 1000)); send(); update_last_send_time(timeutil::now()); while (true) { - Sleep((DWORD)wait_time * 1000); + Sleep(static_cast(wait_time * 1000)); send(); update_last_send_time(timeutil::now()); } @@ -87,7 +87,7 @@ void settings_telemetry::init() } catch (...) { - Logger::error("Failed to send settings telemetry"); + Logger::error("Failed to send settings telemetry"); } }).detach(); } diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 5b65693083..06f853713d 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -242,7 +242,7 @@ void dispatch_received_json(const std::wstring& json_to_parse) void dispatch_received_json_callback(PVOID data) { - std::wstring* msg = (std::wstring*)data; + std::wstring* msg = static_cast(data); dispatch_received_json(*msg); delete msg; } @@ -345,7 +345,7 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L""); } - else if (UuidToString(&temp_uuid, (RPC_WSTR*)&uuid_chars) != RPC_S_OK) + else if (UuidToString(&temp_uuid, reinterpret_cast(&uuid_chars)) != RPC_S_OK) { auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L""); @@ -355,7 +355,7 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op { powertoys_pipe_name += std::wstring(uuid_chars); settings_pipe_name += std::wstring(uuid_chars); - RpcStringFree((RPC_WSTR*)&uuid_chars); + RpcStringFree(reinterpret_cast(&uuid_chars)); uuid_chars = nullptr; } diff --git a/src/runner/tray_icon.cpp b/src/runner/tray_icon.cpp index d29527bc72..60669f7d7c 100644 --- a/src/runner/tray_icon.cpp +++ b/src/runner/tray_icon.cpp @@ -53,7 +53,7 @@ bool dispatch_run_on_main_ui_thread(main_loop_callback_function _callback, PVOID wnd_msg->_callback = _callback; wnd_msg->data = data; - PostMessage(tray_icon_hwnd, wm_run_on_main_ui_thread, 0, (LPARAM)wnd_msg); + PostMessage(tray_icon_hwnd, wm_run_on_main_ui_thread, 0, reinterpret_cast(wnd_msg)); return true; } @@ -93,7 +93,7 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam) } break; case ID_REPORT_BUG_COMMAND: - { + { std::wstring bug_report_path = get_module_folderpath(); bug_report_path += L"\\Tools\\PowerToys.BugReportTool.exe"; SHELLEXECUTEINFOW sei{ sizeof(sei) }; @@ -116,7 +116,6 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam) RunNonElevatedEx(L"https://aka.ms/PowerToysOverview", L"", L""); break; } - } } @@ -242,7 +241,7 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam { if (lparam != NULL) { - struct run_on_main_ui_thread_msg* msg = (struct run_on_main_ui_thread_msg*)lparam; + struct run_on_main_ui_thread_msg* msg = reinterpret_cast(lparam); msg->_callback(msg->data); delete msg; lparam = NULL; diff --git a/src/runner/unhandled_exception_handler.cpp b/src/runner/unhandled_exception_handler.cpp index 373d33ea36..1d01349dd3 100644 --- a/src/runner/unhandled_exception_handler.cpp +++ b/src/runner/unhandled_exception_handler.cpp @@ -7,7 +7,7 @@ #include #include -static IMAGEHLP_SYMBOL64* p_symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(WCHAR)); +static IMAGEHLP_SYMBOL64* p_symbol = static_cast(malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(WCHAR))); static IMAGEHLP_LINE64 line; static bool processing_exception = false; static WCHAR module_path[MAX_PATH]; @@ -106,14 +106,14 @@ void log_stack_trace(std::wstring& generalErrorDescription) #else IMAGE_FILE_MACHINE_AMD64, #endif - process, - thread, - &stack, - &context, - NULL, - SymFunctionTableAccess64, - SymGetModuleBase64, - NULL); + process, + thread, + &stack, + &context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL); p_symbol->MaxNameLength = MAX_PATH; p_symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); @@ -126,7 +126,7 @@ void log_stack_trace(std::wstring& generalErrorDescription) auto module_base = SymGetModuleBase64(process, stack.AddrPC.Offset); if (module_base) { - GetModuleFileName((HINSTANCE)module_base, module_path, MAX_PATH); + GetModuleFileName(reinterpret_cast(module_base), module_path, MAX_PATH); } ss << module_path << "!" << p_symbol->Name From 17475ec705442a9986dcc7de5195bd780a32b39e Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 11:54:16 +0000 Subject: [PATCH 036/163] [Analyzers][CPP] Turn on warning 26492 (#23492) --- CppRuleSet.ruleset | 2 +- src/common/utils/elevation.h | 6 ++++-- src/common/utils/exec.h | 3 ++- .../VideoConferenceProxyFilter/VideoCaptureDevice.cpp | 4 ++++ tools/WebcamReportTool/DirectShowUtils.h | 3 ++- tools/WebcamReportTool/main.cpp | 3 ++- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CppRuleSet.ruleset b/CppRuleSet.ruleset index ad63dc5a1a..540b9f7674 100644 --- a/CppRuleSet.ruleset +++ b/CppRuleSet.ruleset @@ -79,7 +79,7 @@ - + diff --git a/src/common/utils/elevation.h b/src/common/utils/elevation.h index bad925c4e9..269f395d44 100644 --- a/src/common/utils/elevation.h +++ b/src/common/utils/elevation.h @@ -294,8 +294,9 @@ inline bool run_non_elevated(const std::wstring& file, const std::wstring& param siex.StartupInfo.cb = sizeof(siex); PROCESS_INFORMATION pi = { 0 }; + auto succeeded = CreateProcessW(file.c_str(), - const_cast(executable_args.c_str()), + &executable_args[0], nullptr, nullptr, FALSE, @@ -395,8 +396,9 @@ inline bool run_same_elevation(const std::wstring& file, const std::wstring& par STARTUPINFO si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi = { 0 }; + auto succeeded = CreateProcessW(file.c_str(), - const_cast(executable_args.c_str()), + &executable_args[0], nullptr, nullptr, FALSE, diff --git a/src/common/utils/exec.h b/src/common/utils/exec.h index 3c6094e98d..0d4ca2cc6e 100644 --- a/src/common/utils/exec.h +++ b/src/common/utils/exec.h @@ -4,9 +4,10 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// disable warning 26492 - Don't use const_cast to cast away const // Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable : 26471 26497) +#pragma warning(disable : 26471 26492 26497) #include #pragma warning(pop) diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureDevice.cpp b/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureDevice.cpp index 62d2b7933f..7b97891c5b 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureDevice.cpp +++ b/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureDevice.cpp @@ -247,6 +247,10 @@ struct VideoCaptureReceiverPin : winrt::implements(pmt)); } diff --git a/tools/WebcamReportTool/DirectShowUtils.h b/tools/WebcamReportTool/DirectShowUtils.h index bb7b632fba..ccc94cf7c5 100644 --- a/tools/WebcamReportTool/DirectShowUtils.h +++ b/tools/WebcamReportTool/DirectShowUtils.h @@ -6,9 +6,10 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// disable warning 26492 - Don't use const_cast to cast away const on winrt // Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable : 26471 26497) +#pragma warning(disable : 26471 26492 26497) #include #pragma warning(push) diff --git a/tools/WebcamReportTool/main.cpp b/tools/WebcamReportTool/main.cpp index 233b046c8c..5f79b9b3fe 100644 --- a/tools/WebcamReportTool/main.cpp +++ b/tools/WebcamReportTool/main.cpp @@ -6,9 +6,10 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// disable warning 26492 - Don't use const_cast to cast away const // Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable : 26471 26497) +#pragma warning(disable : 26471 26492 26497) #include #pragma warning(push) From c7f761a589491d003c6a8256221ded889c27e86d Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 13:10:28 +0000 Subject: [PATCH 037/163] [Analyzers][CPP]Changes to fix warning 26493 on src/modules/ (P) (#23604) * Changes to fix warning 26493 on src/modules/ --- .../PowerAccentKeyboardService/KeyboardListener.cpp | 4 ++-- .../powerrename/PowerRenameContextMenu/dllmain.cpp | 4 ++-- .../powerrename/PowerRenameUILib/MainWindow.xaml.cpp | 6 +++--- src/modules/powerrename/PowerRenameUILib/pch.h | 6 ++++++ src/modules/powerrename/dll/PowerRenameExt.cpp | 4 ++-- src/modules/powerrename/lib/Helpers.cpp | 10 +++++----- src/modules/powerrename/lib/MRUListHandler.cpp | 6 +++--- src/modules/powerrename/lib/PowerRenameManager.cpp | 2 +- src/modules/powerrename/lib/Settings.cpp | 2 +- .../GcodeThumbnailProvider.cpp | 2 +- .../PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp | 2 +- .../StlThumbnailProviderCpp/StlThumbnailProvider.cpp | 2 +- .../SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp | 2 +- 13 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp index 6c48ae0a04..79aee387fc 100644 --- a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp +++ b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp @@ -94,7 +94,7 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation { std::vector excludedApps; auto excludedUppercase = std::wstring(excludedAppsView); - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length())); std::wstring_view view(excludedUppercase); view = left_trim(trim(view)); @@ -129,7 +129,7 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation return m_prevForegroundAppExcl.second; } auto processPath = get_process_path(foregroundApp); - CharUpperBuffW(processPath.data(), (DWORD)processPath.length()); + CharUpperBuffW(processPath.data(), static_cast(processPath.length())); m_prevForegroundAppExcl = { foregroundApp, find_app_name_in_path(processPath, m_settings.excludedApps) }; diff --git a/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp b/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp index c597e53f34..6bc8b33ebb 100644 --- a/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp +++ b/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp @@ -213,7 +213,7 @@ private: auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L""); } - else if (UuidToString(&temp_uuid, (RPC_WSTR*)&uuid_chars) != RPC_S_OK) + else if (UuidToString(&temp_uuid, reinterpret_cast(& uuid_chars)) != RPC_S_OK) { auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L""); @@ -222,7 +222,7 @@ private: if (uuid_chars != nullptr) { pipe_name += std::wstring(uuid_chars); - RpcStringFree((RPC_WSTR*)&uuid_chars); + RpcStringFree(reinterpret_cast(&uuid_chars)); uuid_chars = nullptr; } create_pipe_thread = std::thread(&PowerRenameContextMenuCommand::StartNamedPipeServerAndSendData, this, pipe_name); diff --git a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp index 9e9176b7e3..1331d66d1f 100644 --- a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp +++ b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp @@ -87,8 +87,8 @@ namespace winrt::PowerRenameUI::implementation winrt::Windows::Graphics::RectInt32 rect; // Scale window size - rect.Width = (int32_t)(width * (float)window_dpi / x_dpi); - rect.Height = (int32_t)(height * (float)window_dpi / x_dpi); + rect.Width = static_cast(width * static_cast(window_dpi) / x_dpi); + rect.Height = static_cast(height * static_cast(window_dpi) / x_dpi); // Center to screen rect.X = displayArea.WorkArea().X + displayArea.WorkArea().Width / 2 - width / 2; rect.Y = displayArea.WorkArea().Y + displayArea.WorkArea().Height / 2 - height / 2; @@ -1070,7 +1070,7 @@ namespace winrt::PowerRenameUI::implementation if (closeUIWindowAfterRenaming) { // Close the window - PostMessage(m_window, WM_CLOSE, (WPARAM)0, (LPARAM)0); + PostMessage(m_window, WM_CLOSE, static_cast(0), static_cast(0)); } else { diff --git a/src/modules/powerrename/PowerRenameUILib/pch.h b/src/modules/powerrename/PowerRenameUILib/pch.h index a5ed89774f..95f939500a 100644 --- a/src/modules/powerrename/PowerRenameUILib/pch.h +++ b/src/modules/powerrename/PowerRenameUILib/pch.h @@ -13,7 +13,13 @@ #include #include #include + +// disable warning 26493 - Don't use C-style casts +#pragma warning(push) +#pragma warning(disable: 26493) #include +#pragma warning(pop) + #include #include #include diff --git a/src/modules/powerrename/dll/PowerRenameExt.cpp b/src/modules/powerrename/dll/PowerRenameExt.cpp index cd0346b246..b4012a1899 100644 --- a/src/modules/powerrename/dll/PowerRenameExt.cpp +++ b/src/modules/powerrename/dll/PowerRenameExt.cpp @@ -88,7 +88,7 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs if (CSettingsInstance().GetShowIconOnMenu()) { - HICON hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_RENAME), IMAGE_ICON, 16, 16, 0); + HICON hIcon = static_cast(LoadImage(g_hInst, MAKEINTRESOURCE(IDI_RENAME), IMAGE_ICON, 16, 16, 0)); if (hIcon) { mii.fMask |= MIIM_BITMAP; @@ -131,7 +131,7 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr // Set the application path based on the location of the dll std::wstring path = get_module_folderpath(g_hInst); path = path + L"\\PowerToys.PowerRename.exe"; - LPTSTR lpApplicationName = (LPTSTR)path.c_str(); + LPTSTR lpApplicationName = path.data(); // Create an anonymous pipe to stream filenames SECURITY_ATTRIBUTES sa; HANDLE hReadPipe; diff --git a/src/modules/powerrename/lib/Helpers.cpp b/src/modules/powerrename/lib/Helpers.cpp index f0476dd1dd..8ef61366b1 100644 --- a/src/modules/powerrename/lib/Helpers.cpp +++ b/src/modules/powerrename/lib/Helpers.cpp @@ -420,7 +420,7 @@ BOOL GetEnumeratedFileName(__out_ecount(cchMax) PWSTR pszUniqueName, UINT cchMax if (!pszRest) { pszRest = PathFindExtension(pszTemplate); - cchStem = (int)(pszRest - pszTemplate); + cchStem = static_cast(pszRest - pszTemplate); hr = StringCchCopy(szFormat, ARRAYSIZE(szFormat), L" (%lu)"); } @@ -428,7 +428,7 @@ BOOL GetEnumeratedFileName(__out_ecount(cchMax) PWSTR pszUniqueName, UINT cchMax { pszRest++; - cchStem = (int)(pszRest - pszTemplate); + cchStem = static_cast(pszRest - pszTemplate); while (*pszRest && *pszRest >= L'0' && *pszRest <= L'9') { @@ -581,7 +581,7 @@ HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p wc.lpfnWndProc = DefWindowProc; wc.cbWndExtra = sizeof(void*); wc.hInstance = hInst; - wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wc.hbrBackground = reinterpret_cast(COLOR_BTNFACE + 1); wc.lpszClassName = wndClassName; RegisterClass(&wc); @@ -590,10 +590,10 @@ HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p 0, wndClassName, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInst, nullptr); if (hwnd) { - SetWindowLongPtr(hwnd, 0, (LONG_PTR)p); + SetWindowLongPtr(hwnd, 0, reinterpret_cast(p)); if (pfnWndProc) { - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnWndProc); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast(pfnWndProc)); } } diff --git a/src/modules/powerrename/lib/MRUListHandler.cpp b/src/modules/powerrename/lib/MRUListHandler.cpp index 0465013a6e..71f796c507 100644 --- a/src/modules/powerrename/lib/MRUListHandler.cpp +++ b/src/modules/powerrename/lib/MRUListHandler.cpp @@ -123,12 +123,12 @@ void MRUListHandler::ParseJson() unsigned int oldSize{ size }; if (json::has(jsonObject, c_maxMRUSize, json::JsonValueType::Number)) { - oldSize = (unsigned int)jsonObject.GetNamedNumber(c_maxMRUSize); + oldSize = static_cast(jsonObject.GetNamedNumber(c_maxMRUSize)); } unsigned int oldPushIdx{ 0 }; if (json::has(jsonObject, c_insertionIdx, json::JsonValueType::Number)) { - oldPushIdx = (unsigned int)jsonObject.GetNamedNumber(c_insertionIdx); + oldPushIdx = static_cast(jsonObject.GetNamedNumber(c_insertionIdx)); if (oldPushIdx < 0 || oldPushIdx >= oldSize) { oldPushIdx = 0; @@ -156,7 +156,7 @@ void MRUListHandler::ParseJson() if (size > oldSize) { std::reverse(std::begin(temp), std::end(temp)); - pushIdx = (unsigned int)temp.size(); + pushIdx = static_cast(temp.size()); temp.resize(size); } else diff --git a/src/modules/powerrename/lib/PowerRenameManager.cpp b/src/modules/powerrename/lib/PowerRenameManager.cpp index f32ee46f7c..baa9ecf92f 100644 --- a/src/modules/powerrename/lib/PowerRenameManager.cpp +++ b/src/modules/powerrename/lib/PowerRenameManager.cpp @@ -526,7 +526,7 @@ LRESULT CALLBACK CPowerRenameManager::s_msgWndProc(_In_ HWND hwnd, _In_ UINT uMs { LRESULT lRes = 0; - CPowerRenameManager* pThis = (CPowerRenameManager*)GetWindowLongPtr(hwnd, 0); + CPowerRenameManager* pThis = reinterpret_cast(GetWindowLongPtr(hwnd, 0)); if (pThis != nullptr) { lRes = pThis->_WndProc(hwnd, uMsg, wParam, lParam); diff --git a/src/modules/powerrename/lib/Settings.cpp b/src/modules/powerrename/lib/Settings.cpp index f1470a1741..655b760e35 100644 --- a/src/modules/powerrename/lib/Settings.cpp +++ b/src/modules/powerrename/lib/Settings.cpp @@ -125,7 +125,7 @@ void CSettings::ParseJson() } if (json::has(jsonSettings, c_maxMRUSize, json::JsonValueType::Number)) { - settings.maxMRUSize = (unsigned int)jsonSettings.GetNamedNumber(c_maxMRUSize); + settings.maxMRUSize = static_cast(jsonSettings.GetNamedNumber(c_maxMRUSize)); } if (json::has(jsonSettings, c_searchText, json::JsonValueType::String)) { diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp index 64e2a8067d..14b07431a2 100644 --- a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp @@ -163,7 +163,7 @@ IFACEMETHODIMP GcodeThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS std::wstring fileNameBmp = filePath + guid + L".bmp"; if (std::filesystem::exists(fileNameBmp)) { - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *phbmp = static_cast(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; std::filesystem::remove(fileNameBmp); } diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp index d74fd9bfab..75a0c7a038 100644 --- a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp @@ -160,7 +160,7 @@ IFACEMETHODIMP PdfThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A std::wstring fileNameBmp = filePath + guid + L".bmp"; if (std::filesystem::exists(fileNameBmp)) { - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *phbmp = static_cast(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; std::filesystem::remove(fileNameBmp); } diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp index b01d331ce7..9b1c8b8415 100644 --- a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp @@ -160,7 +160,7 @@ IFACEMETHODIMP StlThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A std::wstring fileNameBmp = filePath + guid + L".bmp"; if (std::filesystem::exists(fileNameBmp)) { - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *phbmp = static_cast(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; std::filesystem::remove(fileNameBmp); } diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp index 7661095e32..ac749d0127 100644 --- a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp @@ -161,7 +161,7 @@ IFACEMETHODIMP SvgThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A if (std::filesystem::exists(fileNameBmp)) { - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + *phbmp = static_cast(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; std::filesystem::remove(fileNameBmp); } From 10252c3c1e9df368519269f0783735248c03006c Mon Sep 17 00:00:00 2001 From: sosssego Date: Wed, 8 Feb 2023 14:01:40 +0000 Subject: [PATCH 038/163] [Analyzers][CPP]Changes to fix warning 26493 on PowerToys/tools (#23674) --- src/common/utils/exec.h | 3 ++- tools/BugReportTool/BugReportTool/EventViewer.cpp | 2 +- tools/BugReportTool/BugReportTool/InstallationFolder.cpp | 2 +- tools/BugReportTool/BugReportTool/Main.cpp | 2 +- tools/BugReportTool/BugReportTool/RegistryUtils.cpp | 2 +- tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp | 4 ++-- tools/MonitorReportTool/MonitorReportTool.cpp | 4 ++-- tools/WebcamReportTool/DirectShowUtils.h | 5 +++-- tools/WebcamReportTool/main.cpp | 7 ++++--- 9 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/common/utils/exec.h b/src/common/utils/exec.h index 0d4ca2cc6e..ec76c92876 100644 --- a/src/common/utils/exec.h +++ b/src/common/utils/exec.h @@ -5,9 +5,10 @@ // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast // disable warning 26492 - Don't use const_cast to cast away const +// disable warning 26493 - Don't use C-style casts // Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable : 26471 26492 26497) +#pragma warning(disable : 26471 26492 26493 26497) #include #pragma warning(pop) diff --git a/tools/BugReportTool/BugReportTool/EventViewer.cpp b/tools/BugReportTool/BugReportTool/EventViewer.cpp index 81024e2088..f6e9a6baf0 100644 --- a/tools/BugReportTool/BugReportTool/EventViewer.cpp +++ b/tools/BugReportTool/BugReportTool/EventViewer.cpp @@ -57,7 +57,7 @@ namespace if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError())) { dwBufferSize = dwBufferUsed; - pRenderedContent = (LPWSTR)malloc(dwBufferSize); + pRenderedContent = static_cast(malloc(dwBufferSize)); if (pRenderedContent) { EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount); diff --git a/tools/BugReportTool/BugReportTool/InstallationFolder.cpp b/tools/BugReportTool/BugReportTool/InstallationFolder.cpp index 01fcc06d9d..42a4f13653 100644 --- a/tools/BugReportTool/BugReportTool/InstallationFolder.cpp +++ b/tools/BugReportTool/BugReportTool/InstallationFolder.cpp @@ -27,7 +27,7 @@ wstring GetVersion(path filePath) { if (size) { - VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer; + VS_FIXEDFILEINFO* verInfo = static_cast(lpBuffer); if (verInfo->dwSignature == 0xfeef04bd) { version = diff --git a/tools/BugReportTool/BugReportTool/Main.cpp b/tools/BugReportTool/BugReportTool/Main.cpp index c2e1e540e4..5cc8134891 100644 --- a/tools/BugReportTool/BugReportTool/Main.cpp +++ b/tools/BugReportTool/BugReportTool/Main.cpp @@ -166,7 +166,7 @@ void ReportWindowsVersion(const filesystem::path& tmpDir) { NTSTATUS(WINAPI * RtlGetVersion) (LPOSVERSIONINFOEXW) = nullptr; - *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); + *reinterpret_cast(& RtlGetVersion) = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); if (RtlGetVersion) { osInfo.dwOSVersionInfoSize = sizeof(osInfo); diff --git a/tools/BugReportTool/BugReportTool/RegistryUtils.cpp b/tools/BugReportTool/BugReportTool/RegistryUtils.cpp index 693257d9e8..1085d913bc 100644 --- a/tools/BugReportTool/BugReportTool/RegistryUtils.cpp +++ b/tools/BugReportTool/BugReportTool/RegistryUtils.cpp @@ -126,7 +126,7 @@ namespace stream << achValue; } - stream << " > " << (LPCTSTR)value << "\n"; + stream << " > " << reinterpret_cast(value) << "\n"; } else { diff --git a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp index 96e14ac3f6..888f26e48e 100644 --- a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp +++ b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp @@ -15,7 +15,7 @@ namespace }; auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM prm) -> BOOL { - std::wostream& os = *((capture*)prm)->os; + std::wostream& os = *(reinterpret_cast(prm))->os; MONITORINFOEX mi; mi.cbSize = sizeof(mi); @@ -48,7 +48,7 @@ namespace capture c; c.os = &os; - if (EnumDisplayMonitors(nullptr, nullptr, callback, (LPARAM)&c)) + if (EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast(& c))) { os << "EnumDisplayMonitors OK\n"; } diff --git a/tools/MonitorReportTool/MonitorReportTool.cpp b/tools/MonitorReportTool/MonitorReportTool.cpp index 747ea91e1a..289ebf5561 100644 --- a/tools/MonitorReportTool/MonitorReportTool.cpp +++ b/tools/MonitorReportTool/MonitorReportTool.cpp @@ -221,7 +221,7 @@ void LogWMI() // on a particular host computer. IWbemLocator* pLocator = 0; - hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator); + hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast(&pLocator)); if (FAILED(hres)) { Logger::log(L"Failed to create IWbemLocator object. Error code = ", hres); @@ -346,7 +346,7 @@ void LogWMICIMV2() // on a particular host computer. IWbemLocator* pLocator = 0; - hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator); + hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast(&pLocator)); if (FAILED(hres)) { Logger::log(L"Failed to create IWbemLocator object. Error code = ", hres); diff --git a/tools/WebcamReportTool/DirectShowUtils.h b/tools/WebcamReportTool/DirectShowUtils.h index ccc94cf7c5..00e3a6e7c9 100644 --- a/tools/WebcamReportTool/DirectShowUtils.h +++ b/tools/WebcamReportTool/DirectShowUtils.h @@ -7,11 +7,12 @@ // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast // disable warning 26492 - Don't use const_cast to cast away const on winrt +// disable warning 26493 - Don't use C-style casts // Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable : 26471 26492 26497) +#pragma warning(disable : 26471 26492 26493 26497) #include -#pragma warning(push) +#pragma warning(pop) #include diff --git a/tools/WebcamReportTool/main.cpp b/tools/WebcamReportTool/main.cpp index 5f79b9b3fe..e0f0a625b4 100644 --- a/tools/WebcamReportTool/main.cpp +++ b/tools/WebcamReportTool/main.cpp @@ -7,11 +7,12 @@ // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast // disable warning 26492 - Don't use const_cast to cast away const +// disable warning 26493 - Don't use C-style casts // Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable : 26471 26492 26497) +#pragma warning(disable : 26471 26492 26493 26497) #include -#pragma warning(push) +#pragma warning(pop) #include @@ -178,7 +179,7 @@ void ReportAllWebcams() std::string friendlyName; for (wchar_t c : wideFriendlyName) { - friendlyName += (char)c; + friendlyName += static_cast(c); } log() << "Webcam " << friendlyName << '\n'; From 5726c14ed8d7e1dc29b18d021c15edb37513ed36 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Wed, 8 Feb 2023 22:04:38 +0000 Subject: [PATCH 039/163] [Docs]Update WiX3 links (#23989) --- doc/devdocs/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/devdocs/readme.md b/doc/devdocs/readme.md index cadde70700..b1b9a168ea 100644 --- a/doc/devdocs/readme.md +++ b/doc/devdocs/readme.md @@ -73,7 +73,7 @@ The installer can only be compiled in `Release` mode, step 1 and 2 must be done ### Prerequisites for building the MSI installer 1. Install the [WiX Toolset Visual Studio 2022 Extension](https://marketplace.visualstudio.com/items?itemName=WixToolset.WixToolsetVisualStudio2022Extension). -1. Install the [WiX Toolset build tools](https://wixtoolset.org/releases/v3-14-0-6526/). +1. Install the [WiX Toolset build tools](https://wixtoolset.org/docs/v3/releases/v3-14-0-6526/). (installer [direct link](https://wixtoolset.org/downloads/v3.14.0.6526/wix314.exe)) 1. Download [WiX binaries](https://wixtoolset.org/downloads/v3.14.0.6526/wix314-binaries.zip) and extract `wix.targets` to `C:\Program Files (x86)\WiX Toolset v3.14`. ### Locally building the installer prerequisite projects all at once from the command-line From 45ba48498d670b2b7a6e8ca513aea7b5d4815913 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Wed, 8 Feb 2023 22:05:10 +0000 Subject: [PATCH 040/163] [PTRun][Program]Show Steam open shortcuts (#23911) --- .../Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs index f9d31b6ece..f5322fb52a 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs @@ -403,7 +403,7 @@ namespace Microsoft.Plugin.Program.Programs } } - private static readonly Regex InternetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run)\/|^com\.epicgames\.launcher:\/\/apps\/", RegexOptions.Compiled); + private static readonly Regex InternetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run|open)\/|^com\.epicgames\.launcher:\/\/apps\/", RegexOptions.Compiled); // This function filters Internet Shortcut programs private static Win32Program InternetShortcutProgram(string path) From 5e56412e1e0f956ddc5682c4b6e047321b9744c9 Mon Sep 17 00:00:00 2001 From: "Christian L.W" Date: Fri, 10 Feb 2023 00:29:59 +0100 Subject: [PATCH 041/163] Fix typo in keyboard manager (#23819) --- .../keyboardmanager/KeyboardManagerEditor/Resources.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx index eeddf004a0..a7cb9495bd 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx @@ -169,7 +169,7 @@ Target App: - Warning: The follow keys do not have assignments: + Warning: The following keys do not have assignments: Key on a keyboard From f34ff7090f474763dceac4f585490c18df1eb0f8 Mon Sep 17 00:00:00 2001 From: WordlessSafe1 <94203733+WordlessSafe1@users.noreply.github.com> Date: Thu, 9 Feb 2023 17:31:17 -0600 Subject: [PATCH 042/163] Resolve grammatical error in Mouse Highlighter description (#23822) --- src/settings-ui/Settings.UI/Strings/en-us/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 7ca657c387..9ebb38df2e 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2015,7 +2015,7 @@ From there, simply click on one of the supported files in the File Explorer and Mouse as in the hardware peripheral. - Use a keyboard shortcut highlight left and right mouse clicks. + Use a keyboard shortcut to highlight left and right mouse clicks. Mouse as in the hardware peripheral. From 8bcabe17e06c86b858303497b2c5774c0aeec3b5 Mon Sep 17 00:00:00 2001 From: Saulius Kazokas <9000854+saulens22@users.noreply.github.com> Date: Mon, 13 Feb 2023 18:24:36 +0200 Subject: [PATCH 043/163] [QuickAccent]Add support for Lithuanian (#23793) --- .../poweraccent/PowerAccent.Core/Languages.cs | 18 ++++++++++++++++++ .../Settings.UI/Strings/en-us/Resources.resw | 3 +++ .../ViewModels/PowerAccentViewModel.cs | 1 + .../Settings.UI/Views/PowerAccentPage.xaml | 1 + 4 files changed, 23 insertions(+) diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 2546f366b7..c9d7174650 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -26,6 +26,7 @@ namespace PowerAccent.Core IS, IT, KU, + LT, MK, MI, NL, @@ -63,6 +64,7 @@ namespace PowerAccent.Core Language.IS => GetDefaultLetterKeyIS(letter), // Iceland Language.IT => GetDefaultLetterKeyIT(letter), // Italian Language.KU => GetDefaultLetterKeyKU(letter), // Kurdish + Language.LT => GetDefaultLetterKeyLT(letter), // Lithuanian Language.MK => GetDefaultLetterKeyMK(letter), // Macedonian Language.MI => GetDefaultLetterKeyMI(letter), // Maori Language.NL => GetDefaultLetterKeyNL(letter), // Dutch @@ -598,5 +600,21 @@ namespace PowerAccent.Core _ => Array.Empty(), }; } + + // Lithuanian + private static string[] GetDefaultLetterKeyLT(LetterKey letter) + { + return letter switch + { + LetterKey.VK_A => new string[] { "ą" }, + LetterKey.VK_C => new string[] { "č" }, + LetterKey.VK_E => new string[] { "ę", "ė", "€" }, + LetterKey.VK_I => new string[] { "į" }, + LetterKey.VK_S => new string[] { "š" }, + LetterKey.VK_U => new string[] { "ų", "ū" }, + LetterKey.VK_Z => new string[] { "ž" }, + _ => Array.Empty(), + }; + } } } diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 9ebb38df2e..d8a07a7024 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2743,6 +2743,9 @@ Activate by holding the key for the character you want to add an accent to, then Estonian + + Lithuanian + Macedonian diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs index a46fe408d5..9702db1bff 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs @@ -39,6 +39,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels "IS", "IT", "KU", + "LT", "MK", "MI", "NO", diff --git a/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml b/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml index 9d3580467f..971bb300d9 100644 --- a/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml +++ b/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml @@ -76,6 +76,7 @@ + From 4c3b470ec0db04bfa607f23c5401b0d7c47f76d1 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Mon, 13 Feb 2023 17:29:26 +0100 Subject: [PATCH 044/163] [Runner]Improve metered network detection (#24030) --- src/runner/UpdateUtils.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/runner/UpdateUtils.cpp b/src/runner/UpdateUtils.cpp index 672cc7e2dd..8d46ced78c 100644 --- a/src/runner/UpdateUtils.cpp +++ b/src/runner/UpdateUtils.cpp @@ -88,7 +88,26 @@ bool IsMeteredConnection() { using namespace winrt::Windows::Networking::Connectivity; ConnectionProfile internetConnectionProfile = NetworkInformation::GetInternetConnectionProfile(); - return internetConnectionProfile && internetConnectionProfile.IsWwanConnectionProfile(); + if (!internetConnectionProfile) + { + return false; + } + + if (internetConnectionProfile.IsWwanConnectionProfile()) + { + return true; + } + + ConnectionCost connectionCost = internetConnectionProfile.GetConnectionCost(); + if (connectionCost.Roaming() + || connectionCost.OverDataLimit() + || connectionCost.NetworkCostType() == NetworkCostType::Fixed + || connectionCost.NetworkCostType() == NetworkCostType::Variable) + { + return true; + } + + return false; } void ProcessNewVersionInfo(const github_version_info& version_info, From 6e4a2898abee1bd912e0fc09663d56761dba1b0f Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Mon, 13 Feb 2023 17:30:18 +0100 Subject: [PATCH 045/163] [PT Run] Localized file paths (Part 1): Update helper class and Program plugin (#20024) * make helper non-static and add cache * uwp app: add localized path * Win32Program: Rename variable * spell fix * Win32Program: Localized paths * fix invalid var name * spell fix * fix build * test new shell localization helper * fixes * fix crash * replace old helper class * replace old helper class 2 * Helper improvements * last changes * add docs info * remove left-over * remove second left-over --- .../modules/launcher/plugins/program.md | 1 + .../Programs/Win32Tests.cs | 52 ++++++------ .../Storage/Win32ProgramRepositoryTest.cs | 2 +- .../Plugins/Microsoft.Plugin.Program/Main.cs | 3 + .../Microsoft.Plugin.Program/Programs/UWP.cs | 4 + .../Programs/UWPApplication.cs | 5 +- .../Programs/Win32Program.cs | 57 ++++++++----- .../Storage/Win32ProgramRepository.cs | 6 +- .../Common/Interfaces/IShellItem.cs | 46 +++++++++++ .../Wox.Plugin/Common/ShellLocalization.cs | 80 +++++++++---------- .../Wox.Plugin/Common/Win32/NativeMethods.cs | 17 ++++ 11 files changed, 178 insertions(+), 95 deletions(-) create mode 100644 src/modules/launcher/Wox.Plugin/Common/Interfaces/IShellItem.cs diff --git a/doc/devdocs/modules/launcher/plugins/program.md b/doc/devdocs/modules/launcher/plugins/program.md index cc370258ec..1e99e1d44d 100644 --- a/doc/devdocs/modules/launcher/plugins/program.md +++ b/doc/devdocs/modules/launcher/plugins/program.md @@ -41,3 +41,4 @@ There are broadly two different categories of applications: ### Additional Notes - Arguments can be provided to the program plugin by entering them after `--` (a double dash). +- The localization is done using the `Localization Helper`from `Wox.Plugin.Common` hosted at runtime in a variable of plugin's main class. diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs index 4456928c48..0a543b3e7d 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs @@ -22,7 +22,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Imaging Devices", ExecutableName = "imagingdevices.exe", FullPath = "c:\\program files\\windows photo viewer\\imagingdevices.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.Win32Application, }; @@ -31,7 +31,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Notepad", ExecutableName = "notepad.exe", FullPath = "c:\\windows\\system32\\notepad.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -40,7 +40,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Notepad", ExecutableName = "notepad.exe", FullPath = "c:\\windows\\system32\\notepad.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -50,7 +50,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", Arguments = @"/E:ON /V:ON /K ""C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9\\bin\setenv.cmd""", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft azure\\microsoft azure sdk for .net\\v2.9\\microsoft azure command prompt - v2.9.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft azure\\microsoft azure sdk for .net\\v2.9\\microsoft azure command prompt - v2.9.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -60,7 +60,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", Arguments = @"/k ""C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat""", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\visual studio 2019\\visual studio tools\\vc\\x64 native tools command prompt for vs 2019.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\visual studio 2019\\visual studio tools\\vc\\x64 native tools command prompt for vs 2019.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -69,7 +69,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Command Prompt", ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\command prompt.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\command prompt.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -78,7 +78,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "File Explorer", ExecutableName = "File Explorer.lnk", FullPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\file explorer.lnk", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.Win32Application, }; @@ -87,7 +87,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "File Explorer", ExecutableName = "explorer.exe", FullPath = "c:\\windows\\explorer.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.Win32Application, }; @@ -96,7 +96,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Wordpad", ExecutableName = "wordpad.exe", FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\wordpad.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\wordpad.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -105,7 +105,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "WORDPAD", ExecutableName = "WORDPAD.EXE", FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.Win32Application, }; @@ -113,7 +113,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs { Name = "Twitter", FullPath = "c:\\program files (x86)\\google\\chrome\\application\\chrome_proxy.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\chrome apps\\twitter.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\chrome apps\\twitter.lnk", Arguments = " --profile-directory=Default --app-id=jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi", AppType = 0, }; @@ -122,7 +122,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs { Name = "Web page", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\msedge_proxy.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\web page.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\web page.lnk", Arguments = "--profile-directory=Default --app-id=homljgmgpmcbpjbnjpfijnhipfkiclkd", AppType = 0, }; @@ -131,7 +131,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs { Name = "edge - Bing", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\msedge_proxy.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\edge - bing.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\edge - bing.lnk", Arguments = " --profile-directory=Default --app-id=aocfnapldcnfbofgmbbllojgocaelgdd", AppType = 0, }; @@ -141,7 +141,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Microsoft Edge", ExecutableName = "msedge.exe", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\msedge.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft edge.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft edge.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -150,7 +150,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Google Chrome", ExecutableName = "chrome.exe", FullPath = "c:\\program files (x86)\\google\\chrome\\application\\chrome.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\google chrome.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\google chrome.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -159,7 +159,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Proxy App", ExecutableName = "test_proxy.exe", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\test_proxy.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\test proxy.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\test proxy.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -168,7 +168,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "cmd", ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.RunCommand, // Run command }; @@ -178,7 +178,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Description = "Cmder: Lovely Console Emulator", ExecutableName = "Cmder.exe", FullPath = "c:\\tools\\cmder\\cmder.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.RunCommand, // Run command }; @@ -188,7 +188,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "Shop Titans.url", FullPath = "steam://rungameid/1258080", ParentDirectory = "C:\\Users\\temp\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Steam", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.InternetShortcutApplication, }; @@ -198,7 +198,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "Shop Titans.url", FullPath = "steam://rungameid/1258080", ParentDirectory = "C:\\Users\\temp\\Desktop", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.InternetShortcutApplication, }; @@ -208,7 +208,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "dummy.appref-ms", FullPath = "C:\\dummy.appref-ms", ParentDirectory = "C:\\", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.ApprefApplication, }; @@ -218,7 +218,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "application.lnk", FullPath = "C:\\application.lnk", ParentDirectory = "C:\\", - LnkResolvedPath = "C:\\application.lnk", + LnkFilePath = "C:\\application.lnk", AppType = Win32Program.ApplicationType.ShortcutApplication, }; @@ -228,7 +228,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "application.lnk", FullPath = "C:\\dummy\\folder", ParentDirectory = "C:\\dummy\\", - LnkResolvedPath = "C:\\tools\\application.lnk", + LnkFilePath = "C:\\tools\\application.lnk", AppType = Win32Program.ApplicationType.Folder, }; @@ -238,7 +238,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "application.lnk", FullPath = "C:\\dummy\\file.pdf", ParentDirectory = "C:\\dummy\\", - LnkResolvedPath = "C:\\tools\\application.lnk", + LnkFilePath = "C:\\tools\\application.lnk", AppType = Win32Program.ApplicationType.GenericFile, }; @@ -303,7 +303,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs } [TestMethod] - public void DedupFunctionMustRemoveDuplicatesForExeExtensionsWithoutLnkResolvedPath() + public void DedupFunctionMustRemoveDuplicatesForExeExtensionsWithoutLnkFilePath() { // Arrange List prgms = new List @@ -317,7 +317,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs // Assert Assert.AreEqual(1, apps.Count); - Assert.IsTrue(!string.IsNullOrEmpty(apps[0].LnkResolvedPath)); + Assert.IsTrue(!string.IsNullOrEmpty(apps[0].LnkFilePath)); } [TestMethod] diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs index 5ae07c55ad..0d4aafb094 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs @@ -331,7 +331,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage ExecutableName = "path.exe", ParentDirectory = "directory", FullPath = "directory\\path.exe", - LnkResolvedPath = "directory\\path.lnk", // This must be equal for lnk applications + LnkFilePath = "directory\\path.lnk", // This must be equal for lnk applications }; win32ProgramRepository.Add(item); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs index 2aeb1c3f87..146ee7556c 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs @@ -13,6 +13,7 @@ using Microsoft.Plugin.Program.Programs; using Microsoft.Plugin.Program.Storage; using Wox.Infrastructure.Storage; using Wox.Plugin; +using Wox.Plugin.Common; using Stopwatch = Wox.Infrastructure.Stopwatch; namespace Microsoft.Plugin.Program @@ -30,6 +31,8 @@ namespace Microsoft.Plugin.Program internal static ProgramPluginSettings Settings { get; set; } + internal static readonly ShellLocalization ShellLocalizationHelper = new(); + public string Name => Properties.Resources.wox_plugin_program_plugin_name; public string Description => Properties.Resources.wox_plugin_program_plugin_description; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs index ec57218705..22b8326c68 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs @@ -36,6 +36,9 @@ namespace Microsoft.Plugin.Program.Programs public string Location { get; set; } + // Localized path based on windows display language + public string LocationLocalized { get; set; } + public IList Apps { get; private set; } public PackageVersion Version { get; set; } @@ -57,6 +60,7 @@ namespace Microsoft.Plugin.Program.Programs public void InitializeAppInfo(string installedLocation) { Location = installedLocation; + LocationLocalized = Main.ShellLocalizationHelper.GetLocalizedPath(installedLocation); var path = Path.Combine(installedLocation, "AppxManifest.xml"); var namespaces = XmlNamespaces(path); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs index 01e1694e23..04316a3a91 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs @@ -55,6 +55,9 @@ namespace Microsoft.Plugin.Program.Programs public string Location => Package.Location; + // Localized path based on windows display language + public string LocationLocalized => Package.LocationLocalized; + public bool Enabled { get; set; } public bool CanRunElevated { get; set; } @@ -119,7 +122,7 @@ namespace Microsoft.Plugin.Program.Programs // Using CurrentCulture since this is user facing var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_name, result.Title); - var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, Package.Location); + var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, LocationLocalized); result.ToolTipData = new ToolTipData(toolTipTitle, toolTipText); return result; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs index f5322fb52a..b6f76cf3ec 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs @@ -20,7 +20,6 @@ using Microsoft.Win32; using Wox.Infrastructure; using Wox.Infrastructure.FileSystemHelper; using Wox.Plugin; -using Wox.Plugin.Common; using Wox.Plugin.Logger; using DirectoryWrapper = Wox.Infrastructure.FileSystemHelper.DirectoryWrapper; @@ -38,24 +37,35 @@ namespace Microsoft.Plugin.Program.Programs public string Name { get; set; } + // Localized name based on windows display language + public string NameLocalized { get; set; } = string.Empty; + public string UniqueIdentifier { get; set; } public string IcoPath { get; set; } + public string Description { get; set; } = string.Empty; + + // Path of app executable or lnk target executable public string FullPath { get; set; } - public string LnkResolvedPath { get; set; } - - public string LnkResolvedExecutableName { get; set; } - - // Localized name based on windows display language - public string LocalizedName { get; set; } = string.Empty; + // Localized path based on windows display language + public string FullPathLocalized { get; set; } = string.Empty; public string ParentDirectory { get; set; } public string ExecutableName { get; set; } - public string Description { get; set; } = string.Empty; + // Localized executable name based on windows display language + public string ExecutableNameLocalized { get; set; } = string.Empty; + + // Path to the lnk file on LnkProgram + public string LnkFilePath { get; set; } + + public string LnkResolvedExecutableName { get; set; } + + // Localized path based on windows display language + public string LnkResolvedExecutableNameLocalized { get; set; } = string.Empty; public bool Valid { get; set; } @@ -102,11 +112,13 @@ namespace Microsoft.Plugin.Program.Programs private int Score(string query) { var nameMatch = StringMatcher.FuzzySearch(query, Name); - var locNameMatch = StringMatcher.FuzzySearch(query, LocalizedName); + var locNameMatch = StringMatcher.FuzzySearch(query, NameLocalized); var descriptionMatch = StringMatcher.FuzzySearch(query, Description); var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName); + var locExecutableNameMatch = StringMatcher.FuzzySearch(query, ExecutableNameLocalized); var lnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableName); - var score = new[] { nameMatch.Score, locNameMatch.Score, descriptionMatch.Score / 2, executableNameMatch.Score, lnkResolvedExecutableNameMatch.Score }.Max(); + var locLnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableNameLocalized); + var score = new[] { nameMatch.Score, locNameMatch.Score, descriptionMatch.Score / 2, executableNameMatch.Score, locExecutableNameMatch.Score, lnkResolvedExecutableNameMatch.Score, locLnkResolvedExecutableNameMatch.Score }.Max(); return score; } @@ -227,7 +239,7 @@ namespace Microsoft.Plugin.Program.Programs var result = new Result { // To set the title for the result to always be the name of the application - Title = !string.IsNullOrEmpty(LocalizedName) ? LocalizedName : Name, + Title = !string.IsNullOrEmpty(NameLocalized) ? NameLocalized : Name, SubTitle = GetSubtitle(), IcoPath = IcoPath, Score = score, @@ -247,7 +259,8 @@ namespace Microsoft.Plugin.Program.Programs // Using CurrentCulture since this is user facing var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_name, result.Title); - var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, FullPath); + string filePath = !string.IsNullOrEmpty(FullPathLocalized) ? FullPathLocalized : FullPath; + var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, filePath); result.ToolTipData = new ToolTipData(toolTipTitle, toolTipText); return result; @@ -346,7 +359,7 @@ namespace Microsoft.Plugin.Program.Programs { return new ProcessStartInfo { - FileName = LnkResolvedPath ?? FullPath, + FileName = LnkFilePath ?? FullPath, WorkingDirectory = ParentDirectory, UseShellExecute = true, Arguments = programArguments, @@ -376,17 +389,19 @@ namespace Microsoft.Plugin.Program.Programs ExecutableName = Path.GetFileName(path), IcoPath = path, - // Localized name based on windows display language - LocalizedName = ShellLocalization.GetLocalizedName(path), - // Using InvariantCulture since this is user facing - FullPath = path.ToLowerInvariant(), + FullPath = path, UniqueIdentifier = path, ParentDirectory = Directory.GetParent(path).FullName, Description = string.Empty, Valid = true, Enabled = true, AppType = ApplicationType.Win32Application, + + // Localized name, path and executable based on windows display language + NameLocalized = Main.ShellLocalizationHelper.GetLocalizedName(path), + FullPathLocalized = Main.ShellLocalizationHelper.GetLocalizedPath(path), + ExecutableNameLocalized = Path.GetFileName(Main.ShellLocalizationHelper.GetLocalizedPath(path)), }; } catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) @@ -462,7 +477,7 @@ namespace Microsoft.Plugin.Program.Programs Name = Path.GetFileNameWithoutExtension(path), ExecutableName = Path.GetFileName(path), IcoPath = iconPath, - FullPath = urlPath.ToLowerInvariant(), + FullPath = urlPath, UniqueIdentifier = path, ParentDirectory = Directory.GetParent(path).FullName, Valid = true, @@ -500,11 +515,13 @@ namespace Microsoft.Plugin.Program.Programs return InvalidProgram; } - program.LnkResolvedPath = program.FullPath; + program.LnkFilePath = program.FullPath; program.LnkResolvedExecutableName = Path.GetFileName(target); + program.LnkResolvedExecutableNameLocalized = Path.GetFileName(Main.ShellLocalizationHelper.GetLocalizedPath(target)); // Using CurrentCulture since this is user facing - program.FullPath = Path.GetFullPath(target).ToLowerInvariant(); + program.FullPath = Path.GetFullPath(target); + program.FullPathLocalized = Main.ShellLocalizationHelper.GetLocalizedPath(target); program.Arguments = ShellLinkHelper.Arguments; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs index d0360db3c3..d7eae7008c 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs @@ -150,7 +150,7 @@ namespace Microsoft.Plugin.Program.Storage // Using OrdinalIgnoreCase since this is used internally if (extension.Equals(LnkExtension, StringComparison.OrdinalIgnoreCase)) { - app = GetAppWithSameLnkResolvedPath(path); + app = GetAppWithSameLnkFilePath(path); if (app == null) { // Cancelled links won't have a resolved path. @@ -195,12 +195,12 @@ namespace Microsoft.Plugin.Program.Storage // To mitigate the issue faced (as stated above) when a shortcut application is renamed, the Exe FullPath and executable name must be obtained. // Unlike the rename event args, since we do not have a newPath, we iterate through all the programs and find the one with the same LnkResolved path. - private Programs.Win32Program GetAppWithSameLnkResolvedPath(string lnkResolvedPath) + private Programs.Win32Program GetAppWithSameLnkFilePath(string lnkFilePath) { foreach (Programs.Win32Program app in Items) { // Using Invariant / OrdinalIgnoreCase since we're comparing paths - if (lnkResolvedPath.ToUpperInvariant().Equals(app.LnkResolvedPath, StringComparison.OrdinalIgnoreCase)) + if (lnkFilePath.ToUpperInvariant().Equals(app.LnkFilePath, StringComparison.OrdinalIgnoreCase)) { return app; } diff --git a/src/modules/launcher/Wox.Plugin/Common/Interfaces/IShellItem.cs b/src/modules/launcher/Wox.Plugin/Common/Interfaces/IShellItem.cs new file mode 100644 index 0000000000..a110097f16 --- /dev/null +++ b/src/modules/launcher/Wox.Plugin/Common/Interfaces/IShellItem.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using Wox.Plugin.Common.Win32; + +namespace Wox.Plugin.Common.Interfaces +{ + /// + /// The following are ShellItem DisplayName types. + /// + [Flags] + public enum SIGDN : uint + { + NORMALDISPLAY = 0, + PARENTRELATIVEPARSING = 0x80018001, + PARENTRELATIVEFORADDRESSBAR = 0x8001c001, + DESKTOPABSOLUTEPARSING = 0x80028000, + PARENTRELATIVEEDITING = 0x80031001, + DESKTOPABSOLUTEEDITING = 0x8004c000, + FILESYSPATH = 0x80058000, + URL = 0x80068000, + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] + public interface IShellItem + { + void BindToHandler( + IntPtr pbc, + [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, + [MarshalAs(UnmanagedType.LPStruct)] Guid riid, + out IntPtr ppv); + + void GetParent(out IShellItem ppsi); + + void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); + + void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); + + void Compare(IShellItem psi, uint hint, out int piOrder); + } +} diff --git a/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs b/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs index 4ce9e0cb83..97ff8bda38 100644 --- a/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs +++ b/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs @@ -2,67 +2,51 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; -using System.Text; +using Wox.Plugin.Common.Interfaces; +using Wox.Plugin.Common.Win32; namespace Wox.Plugin.Common { /// /// Class to get localized name of shell items like 'My computer'. The localization is based on the 'windows display language'. - /// Reused code from https://stackoverflow.com/questions/41423491/how-to-get-localized-name-of-known-folder for the method /// - public static class ShellLocalization + public class ShellLocalization { - internal const uint DONTRESOLVEDLLREFERENCES = 0x00000001; - internal const uint LOADLIBRARYASDATAFILE = 0x00000002; - - [DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)] - internal static extern int SHGetLocalizedName(string pszPath, StringBuilder pszResModule, ref int cch, out int pidsRes); - - [DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)] - internal static extern int LoadString(IntPtr hModule, int resourceID, StringBuilder resourceValue, int len); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryExW")] - internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); - - [DllImport("kernel32.dll", ExactSpelling = true)] - internal static extern int FreeLibrary(IntPtr hModule); - - [DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, ExactSpelling = true)] - internal static extern uint ExpandEnvironmentStrings(string lpSrc, StringBuilder lpDst, int nSize); + // Cache for already localized names. This makes localization of already localized string faster. + private Dictionary _localizationCache = new Dictionary(); /// /// Returns the localized name of a shell item. /// /// Path to the shell item (e. g. shortcut 'File Explorer.lnk'). /// The localized name as string or . - public static string GetLocalizedName(string path) + public string GetLocalizedName(string path) { - StringBuilder resourcePath = new StringBuilder(1024); - StringBuilder localizedName = new StringBuilder(1024); - int len, id; - len = resourcePath.Capacity; - - // If there is no resource to localize a file name the method returns a non zero value. - if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0) + // Checking cache if path is already localized + if (_localizationCache.ContainsKey(path.ToLowerInvariant())) { - _ = ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity); - IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONTRESOLVEDLLREFERENCES | LOADLIBRARYASDATAFILE); - if (hMod != IntPtr.Zero) - { - if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0) - { - string lString = localizedName.ToString(); - _ = FreeLibrary(hMod); - return lString; - } - - _ = FreeLibrary(hMod); - } + return _localizationCache[path.ToLowerInvariant()]; } - return string.Empty; + Guid shellItemType = ShellItemTypeConstants.ShellItemGuid; + int retCode = NativeMethods.SHCreateItemFromParsingName(path, IntPtr.Zero, ref shellItemType, out IShellItem shellItem); + if (retCode != 0) + { + return string.Empty; + } + + shellItem.GetDisplayName(SIGDN.NORMALDISPLAY, out string filename); + + if (!_localizationCache.ContainsKey(path.ToLowerInvariant())) + { + // The if condition is required to not get timing problems when called from an parallel execution. + // Without the check we will get "key exists" exceptions. + _localizationCache.Add(path.ToLowerInvariant(), filename); + } + + return filename; } /// @@ -70,7 +54,7 @@ namespace Wox.Plugin.Common /// /// The path to localize /// The localized path or the original path if localized version is not available - public static string GetLocalizedPath(string path) + public string GetLocalizedPath(string path) { path = Environment.ExpandEnvironmentVariables(path); string ext = Path.GetExtension(path); @@ -79,6 +63,14 @@ namespace Wox.Plugin.Common for (int i = 0; i < pathParts.Length; i++) { + if (i == 0 && pathParts[i].EndsWith(':')) + { + // Skip the drive letter. + locPath[0] = pathParts[0]; + continue; + } + + // Localize path. int iElements = i + 1; string lName = GetLocalizedName(string.Join("\\", pathParts[..iElements])); locPath[i] = !string.IsNullOrEmpty(lName) ? lName : pathParts[i]; diff --git a/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs b/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs index d7f1228f7e..f54ec9b5af 100644 --- a/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs +++ b/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs @@ -5,6 +5,7 @@ using System; using System.Runtime.InteropServices; using System.Text; +using Wox.Plugin.Common.Interfaces; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; #pragma warning disable SA1649, CA1051, CA1707, CA1028, CA1714, CA1069, SA1402 @@ -114,6 +115,9 @@ namespace Wox.Plugin.Common.Win32 [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] public static extern HRESULT SHCreateStreamOnFileEx(string fileName, STGM grfMode, uint attributes, bool create, System.Runtime.InteropServices.ComTypes.IStream reserved, out System.Runtime.InteropServices.ComTypes.IStream stream); + + [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); } [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "These are the names used by win32.")] @@ -141,6 +145,19 @@ namespace Wox.Plugin.Common.Win32 public const int SC_CLOSE = 0xF060; } + public static class ShellItemTypeConstants + { + /// + /// Guid for type IShellItem. + /// + public static readonly Guid ShellItemGuid = new Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"); + + /// + /// Guid for type IShellItem2. + /// + public static readonly Guid ShellItem2Guid = new Guid("7E9FB0D3-919F-4307-AB2E-9B1860310C93"); + } + public enum HRESULT : uint { /// From f82babb76a5c726f00818e4dc72e12efe717b4b1 Mon Sep 17 00:00:00 2001 From: sosssego Date: Mon, 13 Feb 2023 16:37:26 +0000 Subject: [PATCH 046/163] [Analyzers][CPP] Turn on warning 26493 (#23990) and change the remaining files --- CppRuleSet.ruleset | 2 +- installer/PowerToysSetupCustomActions/CustomAction.cpp | 8 ++++---- tools/StylesReportTool/StylesReportTool.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CppRuleSet.ruleset b/CppRuleSet.ruleset index 540b9f7674..2718c2e3f1 100644 --- a/CppRuleSet.ruleset +++ b/CppRuleSet.ruleset @@ -80,7 +80,7 @@ - + diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp index fdec216592..3e5c5045ab 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp @@ -264,7 +264,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. @@ -490,7 +490,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. @@ -809,7 +809,7 @@ UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall) ExitOnFailure(hr, "Certificate file size not valid", hr); } - pFileContent = (char*)malloc(size); + pFileContent = static_cast(malloc(size)); DWORD sizeread; if (!ReadFile(hfile, pFileContent, size, &sizeread, nullptr)) @@ -820,7 +820,7 @@ UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall) if (!CertAddEncodedCertificateToStore(hCertStore, X509_ASN_ENCODING, - (const BYTE*)pFileContent, + reinterpret_cast(pFileContent), size, CERT_STORE_ADD_ALWAYS, nullptr)) diff --git a/tools/StylesReportTool/StylesReportTool.cpp b/tools/StylesReportTool/StylesReportTool.cpp index 40b1f23f8e..4b84e08dac 100644 --- a/tools/StylesReportTool/StylesReportTool.cpp +++ b/tools/StylesReportTool/StylesReportTool.cpp @@ -49,7 +49,7 @@ inline std::wstring get_process_path(DWORD pid) noexcept { name.resize(MAX_PATH); DWORD name_length = static_cast(name.length()); - if (QueryFullProcessImageNameW(process, 0, (LPWSTR)name.data(), &name_length) == 0) + if (QueryFullProcessImageNameW(process, 0, static_cast(name.data()), &name_length) == 0) { name_length = 0; } @@ -410,7 +410,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) LPCWSTR text = L"Please select the target window (using a mouse or Alt+Tab), \r\nand press Ctrl+Alt+S to capture its styles. \r\nYou can find the output file \"window_styles.txt\" on your desktop."; RECT rc{0,50,600,200}; - DrawText(hdc, text, (int)wcslen(text), &rc, DT_CENTER | DT_WORDBREAK); + DrawText(hdc, text, static_cast(wcslen(text)), &rc, DT_CENTER | DT_WORDBREAK); EndPaint(hWnd, &ps); } From 483e37c8b016f867a85ccc72e50a010af22d53fc Mon Sep 17 00:00:00 2001 From: Jeremy Sinclair <4016293+snickler@users.noreply.github.com> Date: Mon, 13 Feb 2023 12:10:33 -0500 Subject: [PATCH 047/163] [Build]Centralize .NET NuGet Package Versions (#23727) * Add populated Directory.Packages.props * Add package source mapping for community toolkit * Remove package versions from Settings projects * Remove package versions from PT Run plugin projects * Remove package versions from PreviewPane projects * Remove package versions from Launcher projects * Remove package versions from Common projects * Remove package versions from Hosts * Remove package versions from PowerAccent projects * Remove package versions from ImageResizer projects * Remove package versions from Awake * Remove package versions from ColorPicker projects * Remove package versions from FancyZones * Remove package versions from FileLocksmith * Remove package versions from MeasureToolUI * Remove package versions from PowerOCR * Remove package versions from Analyzers entries * Try fix release CI with packageSourceMapping * Actually use PowerToys dependencies source for labs --- .pipelines/release-nuget.config | 6 ++ Directory.Build.props | 4 +- Directory.Packages.props | 60 +++++++++++++++++++ PowerToys.sln | 1 + nuget.config | 14 ++++- src/common/Common.UI/Common.UI.csproj | 2 +- .../GPOWrapperProjection.csproj | 2 +- src/common/ManagedCommon/ManagedCommon.csproj | 2 +- .../Microsoft.Interop.Tests.csproj | 6 +- .../FileLocksmithUI/FileLocksmithUI.csproj | 18 +++--- .../Hosts/Hosts.Tests/Hosts.Tests.csproj | 14 ++--- src/modules/Hosts/Hosts/Hosts.csproj | 18 +++--- .../MeasureToolUI/MeasureToolUI.csproj | 8 +-- src/modules/PowerOCR/PowerOCR/PowerOCR.csproj | 6 +- src/modules/awake/Awake/Awake.csproj | 12 ++-- .../ColorPickerUI/ColorPickerUI.csproj | 12 ++-- .../UnitTest-ColorPickerUI.csproj | 8 +-- .../FancyZonesEditor/FancyZonesEditor.csproj | 6 +- .../tests/ImageResizerUITest.csproj | 12 ++-- .../imageresizer/ui/ImageResizerUI.csproj | 10 ++-- ...s.Run.Plugin.UnitConverter.UnitTest.csproj | 6 +- ....PowerToys.Run.Plugin.UnitConverter.csproj | 2 +- ...werToys.Run.Plugin.VSCodeWorkspaces.csproj | 2 +- .../Microsoft.Plugin.Folder.UnitTests.csproj | 10 ++-- .../Microsoft.Plugin.Folder.csproj | 2 +- .../Microsoft.Plugin.Indexer.csproj | 2 +- .../Microsoft.Plugin.Program.UnitTests.csproj | 8 +-- .../Microsoft.Plugin.Uri.UnitTests.csproj | 6 +- ...osoft.Plugin.WindowWalker.UnitTests.csproj | 8 +-- ...Toys.Run.Plugin.Calculator.UnitTest.csproj | 8 +-- ...oft.PowerToys.Run.Plugin.Calculator.csproj | 2 +- ...rosoft.PowerToys.Run.Plugin.History.csproj | 4 +- ...rosoft.PowerToys.Run.Plugin.OneNote.csproj | 8 +-- ...rToys.Run.Plugin.Registry.UnitTests.csproj | 6 +- ...rosoft.PowerToys.Run.Plugin.Service.csproj | 2 +- ...werToys.Run.Plugin.System.UnitTests.csproj | 8 +-- ...rToys.Run.Plugin.TimeDate.UnitTests.csproj | 8 +-- ...rToys.Run.Plugin.TimeZone.UnitTests.csproj | 8 +-- ...ft.Plugin.WindowsTerminal.UnitTests.csproj | 6 +- .../PowerLauncher/PowerLauncher.csproj | 26 ++++---- .../Wox.Infrastructure.csproj | 8 +-- .../launcher/Wox.Plugin/Wox.Plugin.csproj | 2 +- src/modules/launcher/Wox.Test/Wox.Test.csproj | 10 ++-- .../PowerAccent.Core/PowerAccent.Core.csproj | 10 ++-- .../PowerAccent.UI/PowerAccent.UI.csproj | 4 +- .../GcodePreviewHandler.csproj | 4 +- .../GcodeThumbnailProvider.csproj | 2 +- .../MarkdownPreviewHandler.csproj | 8 +-- .../MonacoPreviewHandler.csproj | 6 +- .../PdfPreviewHandler.csproj | 4 +- .../PdfThumbnailProvider.csproj | 2 +- .../StlThumbnailProvider.csproj | 6 +- .../SvgPreviewHandler.csproj | 4 +- .../SvgThumbnailProvider.csproj | 4 +- .../UnitTests-GcodePreviewHandler.csproj | 10 ++-- .../UnitTests-GcodeThumbnailProvider.csproj | 10 ++-- .../UnitTests-MarkdownPreviewHandler.csproj | 8 +-- .../UnitTests-PdfPreviewHandler.csproj | 10 ++-- .../UnitTests-PdfThumbnailProvider.csproj | 10 ++-- .../UnitTests-PreviewHandlerCommon.csproj | 8 +-- .../UnitTests-StlThumbnailProvider.csproj | 10 ++-- .../UnitTests-SvgPreviewHandler.csproj | 10 ++-- .../UnitTests-SvgThumbnailProvider.csproj | 10 ++-- .../common/PreviewHandlerCommon.csproj | 4 +- .../Settings.UI.Library.csproj | 2 +- .../Settings.UI.UnitTests.csproj | 12 ++-- .../Settings.UI/PowerToys.Settings.csproj | 16 ++--- 67 files changed, 310 insertions(+), 237 deletions(-) create mode 100644 Directory.Packages.props diff --git a/.pipelines/release-nuget.config b/.pipelines/release-nuget.config index e1d43f2b79..822b4f137c 100644 --- a/.pipelines/release-nuget.config +++ b/.pipelines/release-nuget.config @@ -4,6 +4,12 @@ + + + + + + diff --git a/Directory.Build.props b/Directory.Build.props index 01872028f9..86ac4b1278 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,14 +29,14 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000000..493af35f66 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,60 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PowerToys.sln b/PowerToys.sln index 7583cc9b36..63b54af475 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -176,6 +176,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution src\.editorconfig = src\.editorconfig .vsconfig = .vsconfig Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props Solution.props = Solution.props EndProjectSection EndProject diff --git a/nuget.config b/nuget.config index 5a4b9c0fb9..81243afb3c 100644 --- a/nuget.config +++ b/nuget.config @@ -1,5 +1,13 @@ - - - + + + + + + + + + + + \ No newline at end of file diff --git a/src/common/Common.UI/Common.UI.csproj b/src/common/Common.UI/Common.UI.csproj index 9175a0706c..a4475f640c 100644 --- a/src/common/Common.UI/Common.UI.csproj +++ b/src/common/Common.UI/Common.UI.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/common/GPOWrapperProjection/GPOWrapperProjection.csproj b/src/common/GPOWrapperProjection/GPOWrapperProjection.csproj index bd20c2a527..4cfc2521c6 100644 --- a/src/common/GPOWrapperProjection/GPOWrapperProjection.csproj +++ b/src/common/GPOWrapperProjection/GPOWrapperProjection.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/common/ManagedCommon/ManagedCommon.csproj b/src/common/ManagedCommon/ManagedCommon.csproj index 18608ef4f8..7f8419d98d 100644 --- a/src/common/ManagedCommon/ManagedCommon.csproj +++ b/src/common/ManagedCommon/ManagedCommon.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/common/interop/interop-tests/Microsoft.Interop.Tests.csproj b/src/common/interop/interop-tests/Microsoft.Interop.Tests.csproj index 4efdbffc04..a89ba3f7ec 100644 --- a/src/common/interop/interop-tests/Microsoft.Interop.Tests.csproj +++ b/src/common/interop/interop-tests/Microsoft.Interop.Tests.csproj @@ -47,9 +47,9 @@ - - - + + + diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj index 094cd7f13c..c2dfe3edfe 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj +++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj @@ -62,15 +62,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj index 40c7baf060..dc75cf4b59 100644 --- a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj +++ b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj @@ -14,13 +14,13 @@ - - - - - - - + + + + + + + diff --git a/src/modules/Hosts/Hosts/Hosts.csproj b/src/modules/Hosts/Hosts/Hosts.csproj index 0baa5bf015..34f7d3c189 100644 --- a/src/modules/Hosts/Hosts/Hosts.csproj +++ b/src/modules/Hosts/Hosts/Hosts.csproj @@ -46,15 +46,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj b/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj index c43cda95ed..96d510e7b9 100644 --- a/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj +++ b/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj @@ -69,10 +69,10 @@ - - - - + + + + diff --git a/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj b/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj index bfb1a03791..cb56d9cba8 100644 --- a/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj +++ b/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj @@ -48,9 +48,9 @@ - - - + + + diff --git a/src/modules/awake/Awake/Awake.csproj b/src/modules/awake/Awake/Awake.csproj index 01da46c13a..e362bc4b84 100644 --- a/src/modules/awake/Awake/Awake.csproj +++ b/src/modules/awake/Awake/Awake.csproj @@ -56,14 +56,14 @@ - + all - - - - - + + + + + diff --git a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj index d75d01efee..33eb5965cb 100644 --- a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj +++ b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj @@ -49,12 +49,12 @@ - - - - - - + + + + + + diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj index 1c0f56a393..3928e27d46 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj @@ -16,10 +16,10 @@ - - - - + + + + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj b/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj index fa00d92d70..06f630a98d 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj +++ b/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj @@ -55,9 +55,9 @@ - - - + + + diff --git a/src/modules/imageresizer/tests/ImageResizerUITest.csproj b/src/modules/imageresizer/tests/ImageResizerUITest.csproj index e914943fca..18dacef1ff 100644 --- a/src/modules/imageresizer/tests/ImageResizerUITest.csproj +++ b/src/modules/imageresizer/tests/ImageResizerUITest.csproj @@ -49,11 +49,11 @@ - - - - - - + + + + + + diff --git a/src/modules/imageresizer/ui/ImageResizerUI.csproj b/src/modules/imageresizer/ui/ImageResizerUI.csproj index b9fd21cc61..fc3daa760a 100644 --- a/src/modules/imageresizer/ui/ImageResizerUI.csproj +++ b/src/modules/imageresizer/ui/ImageResizerUI.csproj @@ -53,12 +53,10 @@ - - - - - 17.2.3 - + + + + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj index 1e861536ae..2dde83d9ed 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj index 481bcfafe1..646fca5b83 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj @@ -44,7 +44,7 @@ - + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj index 0bd1f34c08..1ad625a6d5 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj @@ -40,7 +40,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/Microsoft.Plugin.Folder.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/Microsoft.Plugin.Folder.UnitTests.csproj index 5877bbc7ec..e7733c997e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/Microsoft.Plugin.Folder.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/Microsoft.Plugin.Folder.UnitTests.csproj @@ -8,11 +8,11 @@ - - - - - + + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj index dfe01a721d..b6b99834a0 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj @@ -53,7 +53,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj index cd8a4d8acb..6742afc6b3 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj @@ -29,7 +29,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Microsoft.Plugin.Program.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Microsoft.Plugin.Program.UnitTests.csproj index 3ce836398c..9c2c7a0480 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Microsoft.Plugin.Program.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Microsoft.Plugin.Program.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/Microsoft.Plugin.Uri.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/Microsoft.Plugin.Uri.UnitTests.csproj index 3c56f13b91..7581eeb4fc 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/Microsoft.Plugin.Uri.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/Microsoft.Plugin.Uri.UnitTests.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/Microsoft.Plugin.WindowWalker.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/Microsoft.Plugin.WindowWalker.UnitTests.csproj index fd08d18471..9514bf32bf 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/Microsoft.Plugin.WindowWalker.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/Microsoft.Plugin.WindowWalker.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj index 4ffbbeeb28..b7617a8f6f 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj index 898d245cb9..cfe563edf8 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj @@ -44,7 +44,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj index 06761565da..f3f1408595 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj @@ -48,8 +48,8 @@ - - + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj index 801aefb986..6e611ea7af 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj @@ -46,13 +46,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj index 0502457f1f..dbe62e3a3e 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj @@ -20,9 +20,9 @@ - - - + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj index 9aff6441da..97ba348518 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/Microsoft.PowerToys.Run.Plugin.System.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/Microsoft.PowerToys.Run.Plugin.System.UnitTests.csproj index 3e244cdfd3..64a2fa7d09 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/Microsoft.PowerToys.Run.Plugin.System.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/Microsoft.PowerToys.Run.Plugin.System.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj index 06d1e489ea..e8d5351386 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.csproj index 71c7f02728..31ffb4d783 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests/Microsoft.Plugin.WindowsTerminal.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests/Microsoft.Plugin.WindowsTerminal.UnitTests.csproj index 09b41a6b4a..43df916a4e 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests/Microsoft.Plugin.WindowsTerminal.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests/Microsoft.Plugin.WindowsTerminal.UnitTests.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj index 4c13b9eb4c..e5c181a31a 100644 --- a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj +++ b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj @@ -84,19 +84,19 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj b/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj index 317f2215bb..efe9beb24e 100644 --- a/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -41,9 +41,9 @@ - - - - + + + + \ No newline at end of file diff --git a/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj b/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj index f9ef5f68fa..c08e738e99 100644 --- a/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj +++ b/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/modules/launcher/Wox.Test/Wox.Test.csproj b/src/modules/launcher/Wox.Test/Wox.Test.csproj index 28220915d4..013bb487f2 100644 --- a/src/modules/launcher/Wox.Test/Wox.Test.csproj +++ b/src/modules/launcher/Wox.Test/Wox.Test.csproj @@ -47,11 +47,11 @@ - - - - - + + + + + diff --git a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj index a622b1d653..3de0a079a3 100644 --- a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj +++ b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj @@ -26,11 +26,11 @@ - - - - - + + + + + diff --git a/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj b/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj index cc01326236..3225385554 100644 --- a/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj +++ b/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj @@ -32,8 +32,8 @@ - - + + diff --git a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj index 954f426d4e..0e6af4e9d0 100644 --- a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj +++ b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj @@ -53,8 +53,8 @@ - - + + diff --git a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj index 11c1c3f3c9..9c3c2e3ff5 100644 --- a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj +++ b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj @@ -40,7 +40,7 @@ - + diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj index 5c90e75c57..04fa49d5a0 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj @@ -59,10 +59,10 @@ - - - - + + + + diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj index b54e276b29..1656499ec7 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj @@ -49,9 +49,9 @@ - - - + + + diff --git a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj index 9624934c81..8092dae64a 100644 --- a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj +++ b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj @@ -54,8 +54,8 @@ - - + + diff --git a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj index af28de12f8..b3b5be4bc8 100644 --- a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj +++ b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj index 016716c50a..ab2cb359a8 100644 --- a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj +++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj @@ -40,9 +40,9 @@ - - - + + + diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj index 0bf93c0376..addc0a3b3d 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj @@ -63,8 +63,8 @@ - - + + diff --git a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj index d3f3a9cb98..8fa55a81d1 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj +++ b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj @@ -42,8 +42,8 @@ - - + + diff --git a/src/modules/previewpane/UnitTests-GcodePreviewHandler/UnitTests-GcodePreviewHandler.csproj b/src/modules/previewpane/UnitTests-GcodePreviewHandler/UnitTests-GcodePreviewHandler.csproj index c6631ed82a..eaa97434a5 100644 --- a/src/modules/previewpane/UnitTests-GcodePreviewHandler/UnitTests-GcodePreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-GcodePreviewHandler/UnitTests-GcodePreviewHandler.csproj @@ -23,13 +23,13 @@ - - + + - - - + + + diff --git a/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/UnitTests-GcodeThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/UnitTests-GcodeThumbnailProvider.csproj index cd699c14ed..e44437cbfa 100644 --- a/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/UnitTests-GcodeThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/UnitTests-GcodeThumbnailProvider.csproj @@ -24,11 +24,11 @@ - - - - - + + + + + diff --git a/src/modules/previewpane/UnitTests-MarkdownPreviewHandler/UnitTests-MarkdownPreviewHandler.csproj b/src/modules/previewpane/UnitTests-MarkdownPreviewHandler/UnitTests-MarkdownPreviewHandler.csproj index 8b4580359c..1331df0748 100644 --- a/src/modules/previewpane/UnitTests-MarkdownPreviewHandler/UnitTests-MarkdownPreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-MarkdownPreviewHandler/UnitTests-MarkdownPreviewHandler.csproj @@ -20,12 +20,12 @@ - + - - - + + + diff --git a/src/modules/previewpane/UnitTests-PdfPreviewHandler/UnitTests-PdfPreviewHandler.csproj b/src/modules/previewpane/UnitTests-PdfPreviewHandler/UnitTests-PdfPreviewHandler.csproj index 0f85737aae..24bbef6441 100644 --- a/src/modules/previewpane/UnitTests-PdfPreviewHandler/UnitTests-PdfPreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-PdfPreviewHandler/UnitTests-PdfPreviewHandler.csproj @@ -25,13 +25,13 @@ - + - - - - + + + + diff --git a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/UnitTests-PdfThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/UnitTests-PdfThumbnailProvider.csproj index ab3f789c23..d6d2e7c397 100644 --- a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/UnitTests-PdfThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/UnitTests-PdfThumbnailProvider.csproj @@ -24,11 +24,11 @@ - - - - - + + + + + diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj index 82b80eb59e..d2ee9ef320 100644 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj @@ -20,16 +20,16 @@ - - - + + + - + diff --git a/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj index c230f3a1b0..8b4b8200fa 100644 --- a/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj @@ -24,11 +24,11 @@ - - - - - + + + + + diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj b/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj index 1214cb3c88..8d99a196d1 100644 --- a/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj @@ -25,13 +25,13 @@ - - - - + + + + - + diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj index f913ab7c08..2ddfad6f2a 100644 --- a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj @@ -25,11 +25,11 @@ - - - - - + + + + + diff --git a/src/modules/previewpane/common/PreviewHandlerCommon.csproj b/src/modules/previewpane/common/PreviewHandlerCommon.csproj index 8a73c2a5b1..6c76eb14cd 100644 --- a/src/modules/previewpane/common/PreviewHandlerCommon.csproj +++ b/src/modules/previewpane/common/PreviewHandlerCommon.csproj @@ -26,8 +26,8 @@ - - + + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj b/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj index 451423bc54..d530c644b5 100644 --- a/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj +++ b/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj b/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj index 70f2f985f0..b083e176cc 100644 --- a/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj +++ b/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj @@ -24,12 +24,12 @@ - - - - - - + + + + + + diff --git a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj index d9b78b6e41..90b499c799 100644 --- a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj +++ b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj @@ -78,14 +78,14 @@ - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs index c781535afb..1d45053b2e 100644 --- a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs @@ -2,8 +2,10 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; using System.Timers; +using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout { @@ -11,6 +13,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout { public bool CanHide { get; set; } + private bool _windows10; + + public bool Windows10 + { + get => _windows10; + set + { + if (_windows10 != value) + { + _windows10 = value; + OnPropertyChanged(); + } + } + } + private Timer hideTimer; public FlyoutViewModel() @@ -20,6 +37,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout hideTimer.Elapsed += HideTimer_Elapsed; hideTimer.Interval = 1000; hideTimer.Enabled = false; + _windows10 = !Helper.Windows11(); } private void HideTimer_Elapsed(object sender, ElapsedEventArgs e) @@ -34,5 +52,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout hideTimer.Stop(); hideTimer.Start(); } + + public event PropertyChangedEventHandler PropertyChanged; + + private void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } } } From 00e10d38c2e8bcd8caf13982c00000042333a6d1 Mon Sep 17 00:00:00 2001 From: Aaron Junker Date: Mon, 13 Feb 2023 19:57:33 +0100 Subject: [PATCH 049/163] [Dev file previewer]Add option to set custom max file size and fix styling issue (#23689) * [Dev file previewer] Add option to set costum max file size and fix style issue --- .../MonacoPreviewHandlerControl.cs | 3 +++ .../MonacoPreviewHandler/Settings.cs | 21 +++++++++++++++---- .../PowerPreviewProperties.cs | 5 +++++ .../Settings.UI/Strings/en-us/Resources.resw | 8 +++++++ .../ViewModels/PowerPreviewViewModel.cs | 20 ++++++++++++++++++ .../Settings.UI/Views/PowerPreviewPage.xaml | 10 +++++++++ 6 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs index bd76aacc0e..72584840c2 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs @@ -212,6 +212,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco downloadLink.Top = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height + 10; downloadLink.Width = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Width + 10; downloadLink.Height = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Height; + downloadLink.ForeColor = Settings.TextColor; Controls.Add(downloadLink); } }); @@ -222,6 +223,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco Controls.Remove(_loadingBar); Controls.Remove(_loadingBackground); Label text = new Label(); + text.ForeColor = Settings.TextColor; text.Text = Resources.Exception_Occurred; text.Text += e.Message; text.Text += "\n" + e.Source; @@ -244,6 +246,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco Controls.Remove(_loadingBackground); Label errorMessage = new Label(); errorMessage.Text = Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture); + errorMessage.ForeColor = Settings.TextColor; errorMessage.Width = 500; errorMessage.Height = 50; Controls.Add(errorMessage); diff --git a/src/modules/previewpane/MonacoPreviewHandler/Settings.cs b/src/modules/previewpane/MonacoPreviewHandler/Settings.cs index e03cbcf409..6bc7a11e76 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Settings.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/Settings.cs @@ -58,11 +58,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco } /// - /// Max file size for displaying (in bytes). + /// Gets Max file size for displaying (in bytes). /// - private readonly long _maxFileSize = 50000; - - public long MaxFileSize => _maxFileSize; + public double MaxFileSize + { + get + { + try + { + return moduleSettings.GetSettings(PowerPreviewSettings.ModuleName).Properties.MonacoPreviewMaxFileSize.Value * 1000; + } + catch (FileNotFoundException) + { + // Couldn't read the settings. + // Assume default of 50000. + return 50000; + } + } + } /// /// Gets the color of the window background. diff --git a/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs b/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs index eee0800e48..2b4d00b248 100644 --- a/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs @@ -13,6 +13,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library public class PowerPreviewProperties { public const string DefaultStlThumbnailColor = "#FFC924"; + public const int DefaultMonacoMaxFileSize = 50; private bool enableSvgPreview = true; @@ -116,6 +117,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + [JsonPropertyName("monaco-previewer-max-file-size")] + public IntProperty MonacoPreviewMaxFileSize { get; set; } + private bool enablePdfPreview; [JsonPropertyName("pdf-previewer-toggle-setting")] @@ -207,6 +211,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library public PowerPreviewProperties() { StlThumbnailColor = new StringProperty(DefaultStlThumbnailColor); + MonacoPreviewMaxFileSize = new IntProperty(DefaultMonacoMaxFileSize); } public override string ToString() diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index d8a07a7024..a08afd5429 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2934,4 +2934,12 @@ Activate by holding the key for the character you want to add an accent to, then Update available + + Maximum file size to preview + Size refers to the disk space used by a file + + + The maximum size, in kilobytes, for files to be displayed. This is a safety mechanism to prevent loading large files into RAM. + "RAM" refers to random access memory; "size" refers to disk space; "bytes" refer to the measurement unit + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs index a6b5fcdab2..dfeddbc508 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs @@ -98,6 +98,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _monacoWrapText = Settings.Properties.EnableMonacoPreviewWordWrap; _monacoPreviewTryFormat = Settings.Properties.MonacoPreviewTryFormat; + _monacoMaxFileSize = Settings.Properties.MonacoPreviewMaxFileSize.Value; _pdfRenderEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredPdfPreviewEnabledValue(); if (_pdfRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _pdfRenderEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) @@ -175,6 +176,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private bool _monacoRenderIsEnabled; private bool _monacoWrapText; private bool _monacoPreviewTryFormat; + private int _monacoMaxFileSize; private GpoRuleConfigured _pdfRenderEnabledGpoRuleConfiguration; private bool _pdfRenderEnabledStateIsGPOConfigured; @@ -353,6 +355,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public int MonacoPreviewMaxFileSize + { + get + { + return _monacoMaxFileSize; + } + + set + { + if (_monacoMaxFileSize != value) + { + _monacoMaxFileSize = value; + Settings.Properties.MonacoPreviewMaxFileSize.Value = value; + RaisePropertyChanged(); + } + } + } + public bool PDFRenderIsEnabled { get diff --git a/src/settings-ui/Settings.UI/Views/PowerPreviewPage.xaml b/src/settings-ui/Settings.UI/Views/PowerPreviewPage.xaml index 3df5647bfa..9d7ee0880a 100644 --- a/src/settings-ui/Settings.UI/Views/PowerPreviewPage.xaml +++ b/src/settings-ui/Settings.UI/Views/PowerPreviewPage.xaml @@ -78,6 +78,16 @@ IsChecked="{x:Bind ViewModel.MonacoPreviewTryFormat, Mode=TwoWay}" IsEnabled="{x:Bind ViewModel.MonacoRenderIsEnabled, Mode=OneWay}" /> + + + From e4e1b5b43d93060291b1e3a3181530333e30c828 Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Mon, 13 Feb 2023 20:15:25 +0100 Subject: [PATCH 050/163] [PTRun][Program]Run commands: Support for MSC and CPL (#23668) * code changes * spell fix * dev docs * update tests * Code improvement --- doc/devdocs/modules/launcher/plugins/program.md | 3 +++ .../Programs/Win32Tests.cs | 17 ++++++++++++++++- .../ProgramPluginSettings.cs | 2 ++ .../Programs/Win32Program.cs | 10 ++++++++-- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/doc/devdocs/modules/launcher/plugins/program.md b/doc/devdocs/modules/launcher/plugins/program.md index 1e99e1d44d..1c3d86ba07 100644 --- a/doc/devdocs/modules/launcher/plugins/program.md +++ b/doc/devdocs/modules/launcher/plugins/program.md @@ -42,3 +42,6 @@ There are broadly two different categories of applications: ### Additional Notes - Arguments can be provided to the program plugin by entering them after `--` (a double dash). - The localization is done using the `Localization Helper`from `Wox.Plugin.Common` hosted at runtime in a variable of plugin's main class. +- The `Run commands` differ in two points from the normal `Win32Programs`: + - The result title contains the executable type. + - The file types `.cpl` and `.msc` are supported too. diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs index 0a543b3e7d..8aaa3dd949 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs @@ -592,12 +592,27 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs var mock = new Mock(); StringMatcher.Instance = new StringMatcher(); + // Act + var result = _chrome.Result("chrome", string.Empty, mock.Object); + + // Assert + // Using Ordinal since this is used internally + Assert.IsTrue(result.Title.Equals(_chrome.Name, StringComparison.Ordinal)); + Assert.IsFalse(result.Title.Equals(_chrome.Description, StringComparison.Ordinal)); + } + + [TestMethod] + public void RunCommandsShouldSetExecutableNameAsTitleWhileCreatingResult() + { + var mock = new Mock(); + StringMatcher.Instance = new StringMatcher(); + // Act var result = _cmderRunCommand.Result("cmder", string.Empty, mock.Object); // Assert // Using Ordinal since this is used internally - Assert.IsTrue(result.Title.Equals(_cmderRunCommand.Name, StringComparison.Ordinal)); + Assert.IsTrue(result.Title.Equals(_cmderRunCommand.ExecutableName, StringComparison.Ordinal)); Assert.IsFalse(result.Title.Equals(_cmderRunCommand.Description, StringComparison.Ordinal)); } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramPluginSettings.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramPluginSettings.cs index 7fab608c86..d78e758b1e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramPluginSettings.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramPluginSettings.cs @@ -17,6 +17,8 @@ namespace Microsoft.Plugin.Program public List ProgramSuffixes { get; set; } = new List() { "bat", "appref-ms", "exe", "lnk", "url" }; + public List RunCommandSuffixes { get; set; } = new List() { "bat", "appref-ms", "exe", "lnk", "url", "cpl", "msc" }; + public bool EnableStartMenuSource { get; set; } = true; public bool EnableDesktopSource { get; set; } = true; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs index b6f76cf3ec..3faa318a1f 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs @@ -91,7 +91,7 @@ namespace Microsoft.Plugin.Program.Programs private const string ShortcutExtension = "lnk"; private const string ApplicationReferenceExtension = "appref-ms"; private const string InternetShortcutExtension = "url"; - private static readonly HashSet ExecutableApplicationExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { "exe", "bat", "bin", "com", "msc", "msi", "cmd", "ps1", "job", "msp", "mst", "sct", "ws", "wsh", "wsf" }; + private static readonly HashSet ExecutableApplicationExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { "exe", "bat", "bin", "com", "cpl", "msc", "msi", "cmd", "ps1", "job", "msp", "mst", "sct", "ws", "wsh", "wsf" }; private const string ProxyWebApp = "_proxy.exe"; private const string AppIdArgument = "--app-id"; @@ -255,6 +255,12 @@ namespace Microsoft.Plugin.Program.Programs }, }; + // Adjust title of RunCommand result + if (AppType == ApplicationType.RunCommand) + { + result.Title = ExecutableName; + } + result.TitleHighlightData = StringMatcher.FuzzySearch(query, result.Title).MatchData; // Using CurrentCulture since this is user facing @@ -1005,7 +1011,7 @@ namespace Microsoft.Plugin.Program.Programs // Run commands are always set as AppType "RunCommand" var runCommandSources = new (bool IsEnabled, Func> GetPaths)[] { - (settings.EnablePathEnvironmentVariableSource, () => PathEnvironmentProgramPaths(settings.ProgramSuffixes)), + (settings.EnablePathEnvironmentVariableSource, () => PathEnvironmentProgramPaths(settings.RunCommandSuffixes)), }; var disabledProgramsList = settings.DisabledProgramSources; From 44e28886d7b879334dfc0ba01dec13d55982923b Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Tue, 14 Feb 2023 16:42:17 +0100 Subject: [PATCH 051/163] [PTRun][Settings]Add missing cpl names and MMC/MSC commands (#23695) * add msc files and mmc * add missing cpl names * spell checker * spell checker 2 * Update Main.cs * Update ResultHelper.cs * review feedback and remove ActiveX * Show mmc note in subtitle * update docs for mmc note * fix typo --- .github/actions/spell-check/expect.txt | 29 + .../launcher/plugins/windowssettings.md | 1 + .../Helper/ResultHelper.cs | 13 +- .../Properties/Resources.Designer.cs | 596 +++++++++++++++++- .../Properties/Resources.resx | 221 +++++++ .../WindowsSettings.json | 160 ++++- 6 files changed, 1015 insertions(+), 5 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 3a50dce6b3..4b8e0ce0a0 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -118,6 +118,7 @@ Avanc awakeness awakeversion AYUV +azman backtracer bak Bashkortostan @@ -163,6 +164,7 @@ BRIGHTGREEN Browsable bsd bstr +bthprops bti btn BTNFACE @@ -193,6 +195,8 @@ CDEF cdpx CENTERALIGN ceq +certlm +certmgr cguid changecursor Changemove @@ -257,11 +261,13 @@ comctl COMDAT comdef comdlg +comexp cominterop commandline COMMANDTITLE commctrl Comoros +compmgmt COMPOSITIONFULL comsupp comsuppw @@ -396,11 +402,13 @@ devdocs devenum DEVMON devpkey +devmgmt DEVSOURCE DIIRFLAG dimm directshow DISABLEASACTIONKEY +diskmgmt DISPLAYCHANGE DISPLAYCONFIG displayname @@ -499,6 +507,7 @@ Eucla EUQ eurochange eventlog +eventvwr everytime evt EWXFORCE @@ -567,6 +576,7 @@ frankychen Froml FROMTOUCH FSCTL +fsmgmt FTYPE Functiondiscoverykeys Futuna @@ -603,7 +613,9 @@ Globbing GMEM GNumber google +gpedit gpo +GPT gpu GSM gtm @@ -639,6 +651,7 @@ HCRYPTHASH HCRYPTPROV hcwhite hdc +hdwwiz hdrop HEB Heiko @@ -797,6 +810,7 @@ ipreviewhandler ipreviewhandlervisualssetfont IProperty IPublic +irprops isbi ISearch ISettings @@ -958,6 +972,7 @@ LTRB LTRREADING Luhansk luid +lusrmgr LWA lwin LZero @@ -992,6 +1007,7 @@ MAXIMIZEBOX MAXSHORTCUTSIZE maxversiontested Mbits +MBR MBs MBUTTON MBUTTONDBLCLK @@ -1020,6 +1036,7 @@ mfobjects mfplat Mfsensorgroup mftransform +Mgmt mic microsoft Midl @@ -1041,6 +1058,8 @@ mjpg mkd mkdn mlcfg +mmc +mmcexe MMdd mmdeviceapi mmi @@ -1120,6 +1139,7 @@ NCMBUTTONUP NCMOUSELEAVE NCMOUSEMOVE NCol +ncpa NCPAINT NCRBUTTONDBLCLK NCRBUTTONDOWN @@ -1289,6 +1309,7 @@ pdw PDWORD pedrolamas PERCEIVEDFLAG +perfmon pesi peteblois PEXCEPTION @@ -1321,6 +1342,7 @@ plocm plugins pluginsmodel PMSIHANDLE +Pnp Pohnpei Popups POPUPWINDOW @@ -1363,6 +1385,7 @@ prevpane prgms pri Primorsky +printmanagement PRINTCLIENT prm proactively @@ -1517,6 +1540,7 @@ RRF rrr RSAT rshift +rsop Rsp Rstrtmgr RTB @@ -1560,6 +1584,7 @@ SDKDDK sdns searchterm secauthz +secpol Secur securityoverview segoe @@ -1786,6 +1811,7 @@ tdbuild TDefault TDevice telem +telephon Templated templatenamespace Tenggara @@ -2057,6 +2083,7 @@ wmain Wman wmi WMICIM +wmimgmt WMKEYDOWN WMKEYUP wmp @@ -2084,6 +2111,7 @@ WResize writefile Wrk wrl +wscui wsf wsh wsl @@ -2107,6 +2135,7 @@ XBUTTONDBLCLK XBUTTONDOWN XBUTTONUP xcopy +XControl XDocument XElement XFile diff --git a/doc/devdocs/modules/launcher/plugins/windowssettings.md b/doc/devdocs/modules/launcher/plugins/windowssettings.md index b057374472..9141afed97 100644 --- a/doc/devdocs/modules/launcher/plugins/windowssettings.md +++ b/doc/devdocs/modules/launcher/plugins/windowssettings.md @@ -62,6 +62,7 @@ A full entry for the `WindowsSettings.json` looks like: * The strings for `Name`, `AltNames`, `Areas`, `Type` and `Note` must not contain whitespace(s) or special characters (#, €, $, etc.) * The strings for `Name`, `AltNames`, `Areas`, `Type` and `Note` are used as ids for the resource file under `Properties\Resources.resx` * When you add new strings make sure you have added all translations for it. +* If a result has `mmc.exe` as command and the note property is filled, the note is shown in the sub title too. (This is for special MMC results where we don't have a .msc file.) ## Scores diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs index ead193f7ad..7240ee0fdc 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs @@ -45,7 +45,18 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Helper AddOptionalToolTip(entry, result); - resultList.Add(result); + // There is a case with MMC snap-ins where we don't have .msc files fort them. Then we need to show the note for this results in subtitle too. + // These results have mmc.exe as command and their note property is filled. + if (entry.Command == "mmc.exe" && !string.IsNullOrEmpty(entry.Note)) + { + result.SubTitle = result.SubTitle + $"\u0020\u0020\u002D\u0020\u0020{Resources.Note}: {entry.Note}"; // "\u0020\u0020\u002D\u0020\u0020" = "" + } + + // To not show duplicate entries we check the existing results on the list before adding the new entry. Example: Device Manager entry for Control Panel and Device Manager entry for MMC. + if (!resultList.Any(x => x.Title == result.Title)) + { + resultList.Add(result); + } } SetScores(resultList, query); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.Designer.cs index 17871ad847..b4264cb128 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.Designer.cs @@ -294,6 +294,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Microsoft Management Console. + /// + internal static string AppMMC { + get { + return ResourceManager.GetString("AppMMC", resourceCulture); + } + } + /// /// Looks up a localized string similar to Apps & Features. /// @@ -609,6 +618,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Authorization Manager. + /// + internal static string AuthorizationManager { + get { + return ResourceManager.GetString("AuthorizationManager", resourceCulture); + } + } + /// /// Looks up a localized string similar to Automatic file downloads. /// @@ -780,6 +798,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to bthprops.cpl. + /// + internal static string bthprops_cpl { + get { + return ResourceManager.GetString("bthprops.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Calendar. /// @@ -843,6 +870,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Certificates - Current User. + /// + internal static string CertificatesCurrentUser { + get { + return ResourceManager.GetString("CertificatesCurrentUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Certificates - Local Computer. + /// + internal static string CertificatesLocalComputer { + get { + return ResourceManager.GetString("CertificatesLocalComputer", resourceCulture); + } + } + /// /// Looks up a localized string similar to Change programs. /// @@ -906,6 +951,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to collab.cpl. + /// + internal static string collab_cpl { + get { + return ResourceManager.GetString("collab.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Color filters. /// @@ -942,6 +996,33 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to COM-Objects. + /// + internal static string ComObjects { + get { + return ResourceManager.GetString("ComObjects", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Component Services. + /// + internal static string ComponentServices { + get { + return ResourceManager.GetString("ComponentServices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Computer Management. + /// + internal static string ComputerManagement { + get { + return ResourceManager.GetString("ComputerManagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Connectable devices. /// @@ -1041,6 +1122,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Create and format hard disk partitions. + /// + internal static string CreateAndFormatHardDiskPartitions { + get { + return ResourceManager.GetString("CreateAndFormatHardDiskPartitions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Credential manager. /// @@ -1149,6 +1239,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Windows Defender Firewall with Advanced Security. + /// + internal static string DefenderFirewallAdvancedSecurity { + get { + return ResourceManager.GetString("DefenderFirewallAdvancedSecurity", resourceCulture); + } + } + /// /// Looks up a localized string similar to Delivery Optimization. /// @@ -1203,6 +1302,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Device Manager. + /// + internal static string DeviceManagerSnapIn { + get { + return ResourceManager.GetString("DeviceManagerSnapIn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Devices and printers. /// @@ -1248,6 +1356,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Disk Management. + /// + internal static string DiskManagement { + get { + return ResourceManager.GetString("DiskManagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Display. /// @@ -1401,6 +1518,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Event Viewer. + /// + internal static string EventViewer { + get { + return ResourceManager.GetString("EventViewer", resourceCulture); + } + } + /// /// Looks up a localized string similar to Exploit Protection. /// @@ -1500,6 +1626,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Firewall.cpl. + /// + internal static string Firewall_cpl { + get { + return ResourceManager.GetString("Firewall.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Focus assist - Quiet hours. /// @@ -1626,6 +1761,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to GPT. + /// + internal static string GPT { + get { + return ResourceManager.GetString("GPT", resourceCulture); + } + } + /// /// Looks up a localized string similar to Graphics settings. /// @@ -1653,6 +1797,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Group Policy. + /// + internal static string GroupPolicy { + get { + return ResourceManager.GetString("GroupPolicy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to hdwwiz.cpl. + /// + internal static string hdwwiz_cpl { + get { + return ResourceManager.GetString("hdwwiz.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Headset display. /// @@ -1806,6 +1968,33 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to IP Security Monitor. + /// + internal static string IpSecurityMonitor { + get { + return ResourceManager.GetString("IpSecurityMonitor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to IP Security Policies on Local Computer. + /// + internal static string IpSecurityPoliciesOnLocalComputer { + get { + return ResourceManager.GetString("IpSecurityPoliciesOnLocalComputer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to irprops.cpl. + /// + internal static string irprops_cpl { + get { + return ResourceManager.GetString("irprops.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Isolated Browsing. /// @@ -1905,6 +2094,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Local Computer Policy. + /// + internal static string LocalGroupPolicy { + get { + return ResourceManager.GetString("LocalGroupPolicy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Local Users and Groups. + /// + internal static string LocalUsersAndGroups { + get { + return ResourceManager.GetString("LocalUsersAndGroups", resourceCulture); + } + } + /// /// Looks up a localized string similar to Location. /// @@ -1968,6 +2175,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to MBR. + /// + internal static string MBR { + get { + return ResourceManager.GetString("MBR", resourceCulture); + } + } + /// /// Looks up a localized string similar to Messaging. /// @@ -2013,6 +2229,195 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to azman.msc. + /// + internal static string MMC_azman { + get { + return ResourceManager.GetString("MMC_azman", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to certlm.msc. + /// + internal static string MMC_certlm { + get { + return ResourceManager.GetString("MMC_certlm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to certmgr.msc. + /// + internal static string MMC_certmgr { + get { + return ResourceManager.GetString("MMC_certmgr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to comexp.msc. + /// + internal static string MMC_comexp { + get { + return ResourceManager.GetString("MMC_comexp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to compmgmt.msc. + /// + internal static string MMC_compmgmt { + get { + return ResourceManager.GetString("MMC_compmgmt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to devmgmt.msc. + /// + internal static string MMC_devmgmt { + get { + return ResourceManager.GetString("MMC_devmgmt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to diskmgmt.msc. + /// + internal static string MMC_diskmgmt { + get { + return ResourceManager.GetString("MMC_diskmgmt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to eventvwr.msc. + /// + internal static string MMC_eventvwr { + get { + return ResourceManager.GetString("MMC_eventvwr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to fsmgmt.msc. + /// + internal static string MMC_fsmgmt { + get { + return ResourceManager.GetString("MMC_fsmgmt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to gpedit.msc. + /// + internal static string MMC_gpedit { + get { + return ResourceManager.GetString("MMC_gpedit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to lusrmgr.msc. + /// + internal static string MMC_lusrmgr { + get { + return ResourceManager.GetString("MMC_lusrmgr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to mmc.exe. + /// + internal static string MMC_mmcexe { + get { + return ResourceManager.GetString("MMC_mmcexe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to perfmon.msc. + /// + internal static string MMC_perfmon { + get { + return ResourceManager.GetString("MMC_perfmon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to printmanagement.msc. + /// + internal static string MMC_printmanagement { + get { + return ResourceManager.GetString("MMC_printmanagement", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to rsop.msc. + /// + internal static string MMC_rsop { + get { + return ResourceManager.GetString("MMC_rsop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to secpol.msc. + /// + internal static string MMC_secpol { + get { + return ResourceManager.GetString("MMC_secpol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to services.msc. + /// + internal static string MMC_services { + get { + return ResourceManager.GetString("MMC_services", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to taskschd.msc. + /// + internal static string MMC_taskschd { + get { + return ResourceManager.GetString("MMC_taskschd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to tpm.msc. + /// + internal static string MMC_tpm { + get { + return ResourceManager.GetString("MMC_tpm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WF.msc. + /// + internal static string MMC_wf { + get { + return ResourceManager.GetString("MMC_wf", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WmiMgmt.msc. + /// + internal static string MMC_wmimgmt { + get { + return ResourceManager.GetString("MMC_wmimgmt", resourceCulture); + } + } + /// /// Looks up a localized string similar to mmsys.cpl. /// @@ -2149,7 +2554,16 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } /// - /// Looks up a localized string similar to Nearby share settings. + /// Looks up a localized string similar to ncpa.cpl. + /// + internal static string ncpa_cpl { + get { + return ResourceManager.GetString("ncpa.cpl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nearby sharing settings. /// internal static string NearbyShareSettings { get { @@ -2211,6 +2625,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Network sessions. + /// + internal static string NetworkSessions { + get { + return ResourceManager.GetString("NetworkSessions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Network Setup Wizard. /// @@ -2409,6 +2832,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to You have to add this snap-in manually.. + /// + internal static string NoteNoMscFileExist { + get { + return ResourceManager.GetString("NoteNoMscFileExist", resourceCulture); + } + } + /// /// Looks up a localized string similar to Added in Windows 10, version 1903 (build 18362).. /// @@ -2706,6 +3138,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Performance Monitor. + /// + internal static string PerformanceMonitor { + get { + return ResourceManager.GetString("PerformanceMonitor", resourceCulture); + } + } + /// /// Looks up a localized string similar to Permissions and history. /// @@ -2850,6 +3291,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to PNP Device. + /// + internal static string PnpDevice { + get { + return ResourceManager.GetString("PnpDevice", resourceCulture); + } + } + /// /// Looks up a localized string similar to Power and sleep. /// @@ -2904,6 +3354,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Printer Spooler. + /// + internal static string PrinterSpooler { + get { + return ResourceManager.GetString("PrinterSpooler", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Print Management. + /// + internal static string PrintManagement { + get { + return ResourceManager.GetString("PrintManagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Print screen. /// @@ -3147,6 +3615,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Resultant Set of Policy. + /// + internal static string ResultantSetOfPolicy { + get { + return ResourceManager.GetString("ResultantSetOfPolicy", resourceCulture); + } + } + /// /// Looks up a localized string similar to Scanners and cameras. /// @@ -3255,6 +3732,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Security Configuration and Analysis. + /// + internal static string SecurityConfigurationAndAnalysis { + get { + return ResourceManager.GetString("SecurityConfigurationAndAnalysis", resourceCulture); + } + } + /// /// Looks up a localized string similar to Security Processor. /// @@ -3264,6 +3750,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Security Templates. + /// + internal static string SecurityTemplates { + get { + return ResourceManager.GetString("SecurityTemplates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Services. + /// + internal static string ServicesSnapIn { + get { + return ResourceManager.GetString("ServicesSnapIn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Session cleanup. /// @@ -3309,6 +3813,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Shared Folders. + /// + internal static string SharedFolders { + get { + return ResourceManager.GetString("SharedFolders", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shortcuts. /// @@ -3354,6 +3867,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to SMB. + /// + internal static string SMB { + get { + return ResourceManager.GetString("SMB", resourceCulture); + } + } + /// /// Looks up a localized string similar to Sound. /// @@ -3507,6 +4029,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to System Tools. + /// + internal static string SystemTools { + get { + return ResourceManager.GetString("SystemTools", resourceCulture); + } + } + /// /// Looks up a localized string similar to System variables. /// @@ -3534,6 +4065,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to TabletPC.cpl. + /// + internal static string TabletPC_cpl { + get { + return ResourceManager.GetString("TabletPC.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Tablet PC settings. /// @@ -3588,6 +4128,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Task Scheduler. + /// + internal static string TaskScheduler { + get { + return ResourceManager.GetString("TaskScheduler", resourceCulture); + } + } + /// /// Looks up a localized string similar to Team Conferencing. /// @@ -3606,6 +4155,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to telephon.cpl. + /// + internal static string telephon_cpl { + get { + return ResourceManager.GetString("telephon.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Text to speech. /// @@ -3687,6 +4245,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to TPM Management. + /// + internal static string TpmManagement { + get { + return ResourceManager.GetString("TpmManagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Transparency. /// @@ -4056,6 +4623,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Windows Management Instrumentation. + /// + internal static string WindowsManagementInstrumentation { + get { + return ResourceManager.GetString("WindowsManagementInstrumentation", resourceCulture); + } + } + /// /// Looks up a localized string similar to Windows Mobility Center. /// @@ -4155,6 +4731,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to WMI Control. + /// + internal static string WmiControl { + get { + return ResourceManager.GetString("WmiControl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Workplace. /// @@ -4173,6 +4758,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to wscui.cpl. + /// + internal static string wscui_cpl { + get { + return ResourceManager.GetString("wscui.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Wubi IME settings. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx index 0eab37985e..e3837eb792 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx @@ -217,6 +217,9 @@ App Short/modern name for application + + Microsoft Management Console + Apps & Features Area Apps @@ -333,6 +336,10 @@ Audio and speech Area MixedReality, only available if the Mixed Reality Portal app is installed. + + Authorization Manager + Name of MMC Snap-In. + Automatic file downloads Area Privacy @@ -406,6 +413,9 @@ Broadcasting Area Gaming + + bthprops.cpl + Calendar Area Privacy @@ -433,6 +443,14 @@ Cellular and SIM Area NetworkAndInternet + + Certificates - Current User + Name of MMC Snap-In. + + + Certificates - Local Computer + Name of MMC Snap-In. + Change programs @@ -458,6 +476,9 @@ Closed captions Area EaseOfAccess + + collab.cpl + Color filters Area EaseOfAccess @@ -474,6 +495,17 @@ Command The command to direct start a setting + + COM-Objects + + + Component Services + Name of MMC Snap-In. + + + Computer Management + Name of MMC Snap-In. + Connectable devices @@ -513,6 +545,9 @@ Cortana - Language Area Cortana + + Create and format hard disk partitions + Credential manager Area Control Panel (legacy settings) @@ -557,6 +592,10 @@ Default Save Locations Area System + + Windows Defender Firewall with Advanced Security + Name of MMC Snap-In. + Delivery Optimization Area UpdateAndSecurity @@ -580,6 +619,10 @@ Device manager Area Control Panel (legacy settings) + + Device Manager + Name of MMC Snap-In. + Devices and printers Area Control Panel (legacy settings) @@ -600,6 +643,10 @@ Direct open your phone Area EaseOfAccess + + Disk Management + Name of MMC Snap-In. + Display Area EaseOfAccess @@ -666,6 +713,10 @@ Ethernet Area NetworkAndInternet + + Event Viewer + Name of MMC Snap-In. + Exploit Protection @@ -708,6 +759,9 @@ Firewall + + Firewall.cpl + Focus assist - Quiet hours Area System @@ -764,6 +818,9 @@ Glance Area Personalization, Deprecated in Windows 10, version 1809 and later + + GPT + Graphics settings Area System @@ -775,6 +832,12 @@ Green week Mean you don't can see green colors + + Group Policy + + + hdwwiz.cpl + Headset display Area MixedReality, only available if the Mixed Reality Portal app is installed. @@ -837,6 +900,17 @@ IP Should not translated + + IP Security Monitor + Name of MMC Snap-In. + + + IP Security Policies on Local Computer + Name of MMC Snap-In. + + + irprops.cpl + Isolated Browsing @@ -876,6 +950,14 @@ Light mode + + Local Computer Policy + Name of MMC Snap-In. + + + Local Users and Groups + Name of MMC Snap-In. + Location Area Privacy @@ -904,6 +986,9 @@ Manage optional features Area Apps + + MBR + Messaging Area Privacy @@ -923,6 +1008,69 @@ mlcfg32.cpl File name, Should not translated + + azman.msc + + + certlm.msc + + + certmgr.msc + + + comexp.msc + + + compmgmt.msc + + + devmgmt.msc + + + diskmgmt.msc + + + eventvwr.msc + + + fsmgmt.msc + + + gpedit.msc + + + lusrmgr.msc + + + mmc.exe + + + perfmon.msc + + + printmanagement.msc + + + rsop.msc + + + secpol.msc + + + services.msc + + + taskschd.msc + + + tpm.msc + + + WF.msc + + + WmiMgmt.msc + mmsys.cpl File name, Should not translated @@ -981,6 +1129,9 @@ Navigation bar Area Personalization + + ncpa.cpl + Nearby sharing settings Area System @@ -1009,6 +1160,9 @@ Network properties Area Control Panel (legacy settings) + + Network sessions + Network Setup Wizard Area Control Panel (legacy settings) @@ -1080,6 +1234,9 @@ Only available on mobile and if the enterprise has deployed a provisioning package. + + You have to add this snap-in manually. + Added in Windows 10, version 1903 (build 18362). @@ -1202,6 +1359,10 @@ Performance information and tools Area Control Panel (legacy settings) + + Performance Monitor + Name of MMC Snap-In. + Permissions and history Area Cortana @@ -1263,6 +1424,9 @@ Windows settings + + PNP Device + Power and sleep Area System @@ -1286,6 +1450,13 @@ Printers and scanners Area Device + + Printer Spooler + + + Print Management + Name of MMC Snap-In. + Print screen Mean the "Print screen" key @@ -1388,6 +1559,10 @@ Repair programs + + Resultant Set of Policy + Name of MMC Snap-In. + Scanners and cameras Area Control Panel (legacy settings) @@ -1433,9 +1608,21 @@ Security Center Area Control Panel (legacy settings) + + Security Configuration and Analysis + Name of MMC Snap-In. + Security Processor + + Security Templates + Name of MMC Snap-In. + + + Services + Name of MMC Snap-In. + Session cleanup Area SurfaceHub @@ -1455,6 +1642,10 @@ Shared experience settings Area System + + Shared Folders + Name of MMC Snap-In. + Shortcuts @@ -1474,6 +1665,9 @@ Size Size for text and symbols + + SMB + Sound Area System @@ -1540,6 +1734,9 @@ System properties and Add New Hardware wizard Area Control Panel (legacy settings) + + System Tools + System variables @@ -1551,6 +1748,9 @@ Tablet mode Area System + + TabletPC.cpl + Tablet PC settings Area Control Panel (legacy settings) @@ -1573,6 +1773,10 @@ Tasks Area Privacy + + Task Scheduler + Name of MMC Snap-In. + Team Conferencing Area SurfaceHub @@ -1581,6 +1785,9 @@ Team device management Area SurfaceHub + + telephon.cpl + Text to speech Area Control Panel (legacy settings) @@ -1613,6 +1820,10 @@ Touchpad Area Device + + TPM Management + Name of MMC Snap-In. + Transparency @@ -1767,6 +1978,9 @@ Windows Insider Program Area UpdateAndSecurity + + Windows Management Instrumentation + Windows Mobility Center Area Control Panel (legacy settings) @@ -1810,6 +2024,10 @@ Wireless + + WMI Control + Name of MMC Snap-In. + Workplace @@ -1817,6 +2035,9 @@ Workplace provisioning Area UserAccounts + + wscui.cpl + Wubi IME settings Area TimeAndLanguage, available if the Microsoft Wubi input method editor is installed diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json index 502e41a642..0f5412f463 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json @@ -1284,6 +1284,7 @@ "Name": "ActionCenter", "Areas": [ "AreaSystemAndSecurity" ], "Type": "AppControlPanel", + "AltNames": [ "wscui.cpl" ], "Command": "control /name Microsoft.ActionCenter" }, { @@ -1331,7 +1332,7 @@ }, { "Name": "BluetoothDevices", - "Areas": [ "AreaHardwareAndSound" ], + "Areas": [ "AreaHardwareAndSound", "bthprops.cpl" ], "Type": "AppControlPanel", "Command": "control /name Microsoft.BluetoothDevices" }, @@ -1378,6 +1379,7 @@ "Name": "DeviceManager", "Areas": [ "AreaHardwareAndSound" ], "Type": "AppControlPanel", + "AltNames": [ "hdwwiz.cpl" ], "Command": "control /name Microsoft.DeviceManager" }, { @@ -1437,6 +1439,7 @@ "Name": "Infrared", "Areas": [ "AreaHardwareAndSound" ], "Type": "AppControlPanel", + "AltNames": [ "irprops.cpl" ], "Command": "control /name Microsoft.Infrared" }, { @@ -1469,6 +1472,7 @@ "Name": "NetworkConnection", "Areas": [ "AreaNetworkAndInternet" ], "Type": "AppControlPanel", + "AltNames": [ "ncpa.cpl" ], "Command": "control netconnections" }, { @@ -1531,13 +1535,14 @@ "Name": "PhoneAndModemOptions", "Areas": [ "AreaNetworkAndInternet" ], "Type": "AppControlPanel", + "AltNames": [ "modem.cpl", "telephon.cpl" ], "Command": "control /name Microsoft.PhoneAndModemOptions" }, { "Name": "PhoneAndModem", "Areas": [ "AreaNetworkAndInternet" ], "Type": "AppControlPanel", - "AltNames": [ "modem.cpl" ], + "AltNames": [ "modem.cpl", "telephon.cpl" ], "Command": "control /name Microsoft.PhoneAndModem" }, { @@ -1632,6 +1637,7 @@ "Name": "TabletPcSettings", "Areas": [ "AreaSystemAndSecurity" ], "Type": "AppControlPanel", + "AltNames": [ "TabletPC.cpl" ], "Command": "control /name Microsoft.TabletPCSettings" }, { @@ -1672,7 +1678,7 @@ }, { "Name": "WindowsFirewall", - "Areas": [ "AreaSystemAndSecurity" ], + "Areas": [ "AreaSystemAndSecurity", "Firewall.cpl" ], "Type": "AppControlPanel", "Command": "control /name Microsoft.WindowsFirewall" }, @@ -1794,6 +1800,154 @@ "Type": "AppSettingsApp", "AltNames": [ "ConnectPanel", "ConnectableDevices", "ConnectWirelessAudio", "DeviceDiscovery" ], "Command": "ms-settings-connectabledevices:devicediscovery" + }, + { + "Name": "AppMMC", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe" ], + "Command": "mmc.exe", + "ShowAsFirstResult" : true + }, + { + "Name": "AuthorizationManager", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_azman" ], + "Command": "azman.msc" + }, + { + "Name": "CertificatesCurrentUser", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_certmgr" ], + "Command": "certmgr.msc" + }, + { + "Name": "CertificatesLocalComputer", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_certlm" ], + "Command": "certlm.msc" + }, + { + "Name": "ComponentServices", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_comexp", "ComObjects" ], + "Command": "comexp.msc" + }, + { + "Name": "ComputerManagement", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_compmgmt", "SystemTools", "TaskScheduler", "EventViewer", "SharedFolders", "NetworkSessions", "SMB", "LocalUsersAndGroups", "PerformanceMonitor", "DeviceManager", "PnpDevice", "Storage", "DiskManagement", "CreateAndFormatHardDiskPartitions", "GPT", "MBR", "ServicesSnapIn", "WmiControl", "WindowsManagementInstrumentation" ], + "Command": "compmgmt.msc" + }, + { + "Name": "DeviceManager", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_devmgmt", "PnpDevice" ], + "Command": "devmgmt.msc" + }, + { + "Name": "DiskManagement", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_diskmgmt", "Storage", "CreateAndFormatHardDiskPartitions", "GPT", "MBR" ], + "Command": "diskmgmt.msc" + }, + { + "Name": "EventViewer", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_eventvwr" ], + "Command": "eventvwr.msc" + }, + { + "Name": "LocalGroupPolicy", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_gpedit", "GroupPolicy" ], + "Command": "gpedit.msc" + }, + { + "Name": "IpSecurityMonitor", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe" ], + "Command": "mmc.exe", + "Note": "NoteNoMscFileExist" + }, + { + "Name": "IpSecurityPoliciesOnLocalComputer", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe" ], + "Command": "mmc.exe", + "Note": "NoteNoMscFileExist" + }, + { + "Name": "LocalUsersAndGroups", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_lusrmgr" ], + "Command": "lusrmgr.msc" + }, + { + "Name": "PerformanceMonitor", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_perfmon" ], + "Command": "perfmon.msc" + }, + { + "Name": "PrintManagement", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_printmanagement", "PrinterSpooler" ], + "Command": "printmanagement.msc" + }, + { + "Name": "ResultantSetOfPolicy", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_rsop" ], + "Command": "rsop.msc" + }, + { + "Name": "SecurityConfigurationAndAnalysis", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_secpol" ], + "Command": "secpol.msc" + }, + { + "Name": "SecurityTemplates", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe" ], + "Command": "mmc.exe", + "Note": "NoteNoMscFileExist" + }, + { + "Name": "ServicesSnapIn", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_services" ], + "Command": "services.msc" + }, + { + "Name": "SharedFolders", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_fsmgmt", "NetworkSessions" ], + "Command": "fsmgmt.msc" + }, + { + "Name": "TaskScheduler", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_taskschd" ], + "Command": "taskschd.msc" + }, + { + "Name": "TpmManagement", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_tpm" ], + "Command": "tpm.msc" + }, + { + "Name": "DefenderFirewallAdvancedSecurity", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_wf" ], + "Command": "wf.msc" + }, + { + "Name": "WmiControl", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_wmimgmt", "WindowsManagementInstrumentation" ], + "Command": "wmimgmt.msc" } ] } From df521b4c9b998fde9da97641ab5e1db432c6f22e Mon Sep 17 00:00:00 2001 From: Sophia Chen <54446929+chenss3@users.noreply.github.com> Date: Tue, 14 Feb 2023 18:38:53 -0800 Subject: [PATCH 052/163] [Settings]New Landing Page Experimentation (#22365) Co-authored-by: Sophia Chen Co-authored-by: Niels Laute Co-authored-by: Sophia Chen --- .github/actions/spell-check/expect.txt | 6 + .github/actions/spell-check/patterns.txt | 3 + .pipelines/ESRPSigning_core.json | 3 + .../ci/templates/build-powertoys-steps.yml | 13 +- .pipelines/release.yml | 1 + .../verifyNoticeMdAgainstNugetPackages.ps1 | 2 + Directory.Packages.props | 5 + PowerToys.sln | 15 ++ installer/PowerToysSetup/Settings.wxs | 17 +- .../AllExperiments/AllExperiments.csproj | 27 +++ src/common/AllExperiments/Experiments.cs | 211 ++++++++++++++++++ src/common/AllExperiments/Logger.cs | 81 +++++++ ...VariantAssignmentClientExtensionMethods.cs | 21 ++ .../Client/VariantAssignmentServiceClient.cs | 23 ++ .../EmptyVariantAssignmentResponse.cs | 48 ++++ .../Contract/IAssignedVariant.cs | 11 + .../Contract/IFeatureVariable.cs | 16 ++ .../Contract/IVariantAssignmentProvider.cs | 18 ++ .../Contract/IVariantAssignmentRequest.cs | 15 ++ .../Contract/IVariantAssignmentResponse.cs | 48 ++++ .../TreatmentAssignmentServiceResponse.cs | 11 + .../VariantAssignmentClientSettings.cs | 31 +++ .../Contract/VariantAssignmentRequest.cs | 21 ++ .../VariantAssignmentServiceResponse.cs | 48 ++++ src/runner/general_settings.cpp | 6 + src/runner/general_settings.h | 1 + src/runner/trace.cpp | 1 + .../Settings.UI.Library/GeneralSettings.cs | 4 + .../Events/OobeVariantAssignmentEvent.cs | 21 ++ .../Settings.UI.UnitTests.csproj | 2 +- .../FluentIconsExperimentation.png | Bin 0 -> 931 bytes .../OOBEPageControl/OOBEPageControl.xaml | 9 +- .../OOBEPageControl/OOBEPageControl.xaml.cs | 7 + .../OOBE/Views/OobeOverviewAlternate.xaml | 193 ++++++++++++++++ .../OOBE/Views/OobeOverviewAlternate.xaml.cs | 50 +++++ .../OOBE/Views/OobeOverviewPlaceholder.xaml | 23 ++ .../Views/OobeOverviewPlaceholder.xaml.cs | 73 ++++++ .../OOBE/Views/OobeShellPage.xaml.cs | 30 ++- .../Settings.UI/PowerToys.Settings.csproj | 14 ++ .../Settings.UI/Strings/en-us/Resources.resw | 37 +++ .../ViewModels/GeneralViewModel.cs | 20 ++ .../Settings.UI/Views/GeneralPage.xaml | 7 +- 42 files changed, 1181 insertions(+), 12 deletions(-) create mode 100644 src/common/AllExperiments/AllExperiments.csproj create mode 100644 src/common/AllExperiments/Experiments.cs create mode 100644 src/common/AllExperiments/Logger.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentClientExtensionMethods.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentServiceClient.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/EmptyVariantAssignmentResponse.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IAssignedVariant.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IFeatureVariable.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentProvider.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentRequest.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentResponse.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/TreatmentAssignmentServiceResponse.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentClientSettings.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentRequest.cs create mode 100644 src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentServiceResponse.cs create mode 100644 src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs create mode 100644 src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsExperimentation.png create mode 100644 src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml create mode 100644 src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml.cs create mode 100644 src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml create mode 100644 src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 4b8e0ce0a0..49012460e6 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -163,6 +163,7 @@ bricelam BRIGHTGREEN Browsable bsd +Bson bstr bthprops bti @@ -221,6 +222,7 @@ CLASSNOTAVAILABLE clickable clickonce CLIENTEDGE +clientid clientside CLIPCHILDREN Clipperton @@ -347,6 +349,7 @@ DARKYELLOW datareader datatemplate Datavalue +dataversion DATAW davidegiacometti Dayof @@ -1859,6 +1862,7 @@ TRAYMOUSEMESSAGE triaging TRK trl +TServer Tshuapa TStr Tuva @@ -1911,6 +1915,7 @@ unmute UNORM unregistering unremapped +Unstub unsubscribe unvirtualized Updatelayout @@ -1936,6 +1941,7 @@ UYVY vabdq validmodulename Vanara +variantassignment vcamp vccorlib vcdl diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt index 774349778a..42ceb68d7f 100644 --- a/.github/actions/spell-check/patterns.txt +++ b/.github/actions/spell-check/patterns.txt @@ -62,6 +62,9 @@ https?://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]* link\.medium\.com/[a-zA-Z0-9]+ \bmedium\.com/\@[^/]+/[-\w]+ +# experimentation urls +https?://default\.exp-tas\.com/[-_a-zA-Z0-9/]* + publicKeyToken=(['"]|)[0-9a-f]+\g{-1} \@sha256:[0-9a-f]{64}\b diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index af84380a24..ce467a6317 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -23,6 +23,7 @@ "PowerToys.Settings.UI.Lib.dll", "PowerToys.GPOWrapper.dll", "PowerToys.GPOWrapperProjection.dll", + "PowerToys.AllExperiments.dll", "modules\\AlwaysOnTop\\PowerToys.AlwaysOnTop.exe", "modules\\AlwaysOnTop\\PowerToys.AlwaysOnTopModuleInterface.dll", @@ -202,6 +203,8 @@ "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "Mono.Cecil.Rocks.dll", + "Newtonsoft.Json.dll", + "Newtonsoft.Json.Bson.dll", "NLog.dll", "HtmlAgilityPack.dll", "Markdig.Signed.dll", diff --git a/.pipelines/ci/templates/build-powertoys-steps.yml b/.pipelines/ci/templates/build-powertoys-steps.yml index 26754babe8..703d520f10 100644 --- a/.pipelines/ci/templates/build-powertoys-steps.yml +++ b/.pipelines/ci/templates/build-powertoys-steps.yml @@ -69,7 +69,7 @@ steps: configPath: NuGet.config restoreSolution: PowerToys.sln restoreDirectory: '$(Build.SourcesDirectory)\packages' - + - task: VSBuild@1 displayName: 'Build PowerToys.sln' inputs: @@ -227,10 +227,17 @@ steps: **\powerpreviewTest.dll **\UnitTests-FancyZones.dll !**\obj\** - + +- task: PowerShell@2 + displayName: Trigger dotnet welcome message so that it does not cause errors on other scripts + inputs: + targetType: 'inline' + script: | + dotnet list $(build.sourcesdirectory)\src\common\Common.UI\Common.UI.csproj package + - task: PowerShell@2 displayName: Verifying Notice.md and Nuget packages match inputs: filePath: '$(build.sourcesdirectory)\.pipelines\verifyNoticeMdAgainstNugetPackages.ps1' arguments: -path '$(build.sourcesdirectory)\' - pwsh: true \ No newline at end of file + pwsh: true diff --git a/.pipelines/release.yml b/.pipelines/release.yml index 01665c74c1..81da06f416 100644 --- a/.pipelines/release.yml +++ b/.pipelines/release.yml @@ -23,6 +23,7 @@ parameters: variables: IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations SkipCppCodeAnalysis: 1 # Skip the code analysis to speed up release CI. It runs on PR CI, anyway + IsExperimentationLive: 1 # The build and installer use this to turn on experimentation name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) resources: diff --git a/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 b/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 index affff4c782..2ee58da745 100644 --- a/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 +++ b/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 @@ -38,6 +38,8 @@ $totalList = $projFiles | ForEach-Object -Parallel { if($nugetTemp -is [array] -and $nugetTemp.count -gt 3) { + # Need to debug this script? Uncomment this line. + # Write-Host $csproj "`r`n" $nugetTemp "`r`n" $temp = New-Object System.Collections.ArrayList $temp.AddRange($nugetTemp) $temp.RemoveRange(0, 3) diff --git a/Directory.Packages.props b/Directory.Packages.props index 493af35f66..7b11df8764 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -57,4 +57,9 @@ + + + + + \ No newline at end of file diff --git a/PowerToys.sln b/PowerToys.sln index 63b54af475..df3b5bf6e7 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -487,6 +487,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StlThumbnailProviderCpp", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SvgThumbnailProviderCpp", "src\modules\previewpane\SvgThumbnailProviderCpp\SvgThumbnailProviderCpp.vcxproj", "{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AllExperiments", "src\common\AllExperiments\AllExperiments.csproj", "{9CE59ED5-7087-4353-88EB-788038A73CEC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -2022,6 +2024,18 @@ Global {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x64.Build.0 = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.ActiveCfg = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.Build.0 = Release|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|ARM64.Build.0 = Debug|ARM64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x64.ActiveCfg = Debug|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x64.Build.0 = Debug|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x86.ActiveCfg = Debug|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x86.Build.0 = Debug|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|ARM64.ActiveCfg = Release|ARM64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|ARM64.Build.0 = Release|ARM64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x64.ActiveCfg = Release|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x64.Build.0 = Release|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x86.ActiveCfg = Release|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2192,6 +2206,7 @@ Global {CA5518ED-0458-4B09-8F53-4122B9888655} = {2F305555-C296-497E-AC20-5FA1B237996A} {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D} = {2F305555-C296-497E-AC20-5FA1B237996A} {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA} = {2F305555-C296-497E-AC20-5FA1B237996A} + {9CE59ED5-7087-4353-88EB-788038A73CEC} = {1AFB6476-670D-4E80-A464-657E01DFF482} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs index a1072b59f6..a162135bd8 100644 --- a/installer/PowerToysSetup/Settings.wxs +++ b/installer/PowerToysSetup/Settings.wxs @@ -4,12 +4,15 @@ - + + + + @@ -18,6 +21,13 @@ + + + + + + + @@ -78,6 +88,11 @@ + + + + + diff --git a/src/common/AllExperiments/AllExperiments.csproj b/src/common/AllExperiments/AllExperiments.csproj new file mode 100644 index 0000000000..44b823cbbc --- /dev/null +++ b/src/common/AllExperiments/AllExperiments.csproj @@ -0,0 +1,27 @@ + + + + net7.0-windows10.0.19041.0 + enable + enable + PowerToys.AllExperiments + .\Microsoft.VariantAssignment\ + + + + + + + + + + + + + + + + + + + diff --git a/src/common/AllExperiments/Experiments.cs b/src/common/AllExperiments/Experiments.cs new file mode 100644 index 0000000000..9663c28b96 --- /dev/null +++ b/src/common/AllExperiments/Experiments.cs @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using System.Runtime.InteropServices; +using System.Text.Json; +using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; +using Microsoft.PowerToys.Telemetry; +using Microsoft.VariantAssignment.Client; +using Microsoft.VariantAssignment.Contract; +using Windows.System.Profile; + +namespace AllExperiments +{ +// The dependencies required to build this project are only available in the official build pipeline and are internal to Microsoft. +// However, this project is not required to build a test version of the application. + public class Experiments + { + public enum ExperimentState + { + Enabled, + Disabled, + NotLoaded, + } + +#pragma warning disable SA1401 // Need to use LandingPageExperiment as a static property in OobeShellPage.xaml.cs + public static ExperimentState LandingPageExperiment = ExperimentState.NotLoaded; +#pragma warning restore SA1401 + + public async Task EnableLandingPageExperimentAsync() + { + if (Experiments.LandingPageExperiment != ExperimentState.NotLoaded) + { + return Experiments.LandingPageExperiment == ExperimentState.Enabled; + } + + Experiments varServ = new Experiments(); + await varServ.VariantAssignmentProvider_Initialize(); + var landingPageExperiment = varServ.IsExperiment; + + Experiments.LandingPageExperiment = landingPageExperiment ? ExperimentState.Enabled : ExperimentState.Disabled; + + return landingPageExperiment; + } + + private async Task VariantAssignmentProvider_Initialize() + { + IsExperiment = false; + string jsonFilePath = CreateFilePath(); + + var vaSettings = new VariantAssignmentClientSettings + { + Endpoint = new Uri("https://default.exp-tas.com/exptas77/a7a397e7-6fbe-4f21-a4e9-3f542e4b000e-exppowertoys/api/v1/tas"), + EnableCaching = true, + ResponseCacheTime = TimeSpan.FromMinutes(5), + }; + + try + { + var vaClient = vaSettings.GetTreatmentAssignmentServiceClient(); + var vaRequest = GetVariantAssignmentRequest(); + using var variantAssignments = await vaClient.GetVariantAssignmentsAsync(vaRequest).ConfigureAwait(false); + + if (variantAssignments.AssignedVariants.Count != 0) + { + var dataVersion = variantAssignments.DataVersion; + var featureVariables = variantAssignments.GetFeatureVariables(); + var assignmentContext = variantAssignments.GetAssignmentContext(); + var featureFlagValue = featureVariables[0].GetStringValue(); + + var experimentGroup = string.Empty; + string json = File.ReadAllText(jsonFilePath); + var jsonDictionary = JsonSerializer.Deserialize>(json); + + if (jsonDictionary != null) + { + if (!jsonDictionary.ContainsKey("dataversion")) + { + jsonDictionary.Add("dataversion", dataVersion); + } + + if (!jsonDictionary.ContainsKey("variantassignment")) + { + jsonDictionary.Add("variantassignment", featureFlagValue); + } + else + { + var jsonDataVersion = jsonDictionary["dataversion"].ToString(); + if (jsonDataVersion != null && int.Parse(jsonDataVersion) < dataVersion) + { + jsonDictionary["dataversion"] = dataVersion; + jsonDictionary["variantassignment"] = featureFlagValue; + } + } + + experimentGroup = jsonDictionary["variantassignment"].ToString(); + + string output = JsonSerializer.Serialize(jsonDictionary); + File.WriteAllText(jsonFilePath, output); + } + + if (experimentGroup == "alternate" && AssignmentUnit != string.Empty) + { + IsExperiment = true; + } + + PowerToysTelemetry.Log.WriteEvent(new OobeVariantAssignmentEvent() { AssignmentContext = assignmentContext, ClientID = AssignmentUnit }); + } + } + catch (HttpRequestException ex) + { + string json = File.ReadAllText(jsonFilePath); + var jsonDictionary = JsonSerializer.Deserialize>(json); + + if (jsonDictionary != null) + { + if (jsonDictionary.ContainsKey("variantassignment")) + { + if (jsonDictionary["variantassignment"].ToString() == "alternate" && AssignmentUnit != string.Empty) + { + IsExperiment = true; + } + } + else + { + jsonDictionary["variantassignment"] = "current"; + } + } + + string output = JsonSerializer.Serialize(jsonDictionary); + File.WriteAllText(jsonFilePath, output); + + Logger.LogError("Error getting to TAS endpoint", ex); + } + catch (Exception ex) + { + Logger.LogError("Error getting variant assignments for experiment", ex); + } + } + + public bool IsExperiment { get; set; } + + private string? AssignmentUnit { get; set; } + + private IVariantAssignmentRequest GetVariantAssignmentRequest() + { + var jsonFilePath = CreateFilePath(); + try + { + if (!File.Exists(jsonFilePath)) + { + AssignmentUnit = Guid.NewGuid().ToString(); + var data = new Dictionary() + { + ["clientid"] = AssignmentUnit, + }; + string jsonData = JsonSerializer.Serialize(data); + File.WriteAllText(jsonFilePath, jsonData); + } + else + { + string json = File.ReadAllText(jsonFilePath); + var jsonDictionary = System.Text.Json.JsonSerializer.Deserialize>(json); + if (jsonDictionary != null) + { + AssignmentUnit = jsonDictionary["clientid"]?.ToString(); + } + } + } + catch (Exception ex) + { + Logger.LogError("Error creating/getting AssignmentUnit", ex); + } + + var attrNames = new List { "FlightRing", "c:InstallLanguage" }; + var attrData = AnalyticsInfo.GetSystemPropertiesAsync(attrNames).AsTask().GetAwaiter().GetResult(); + + var flightRing = string.Empty; + var installLanguage = string.Empty; + + if (attrData.ContainsKey("FlightRing")) + { + flightRing = attrData["FlightRing"]; + } + + if (attrData.ContainsKey("InstallLanguage")) + { + installLanguage = attrData["InstallLanguage"]; + } + + return new VariantAssignmentRequest + { + Parameters = + { + { "installLanguage", installLanguage }, + { "flightRing", flightRing }, + { "clientid", AssignmentUnit }, + }, + }; + } + + private string CreateFilePath() + { + var exeDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var settingsPath = @"Microsoft\PowerToys\experimentation.json"; + var filePath = Path.Combine(exeDir, settingsPath); + return filePath; + } + } +} diff --git a/src/common/AllExperiments/Logger.cs b/src/common/AllExperiments/Logger.cs new file mode 100644 index 0000000000..7604618bdf --- /dev/null +++ b/src/common/AllExperiments/Logger.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO.Abstractions; + +namespace AllExperiments +{ + public static class Logger + { + private static readonly IFileSystem FileSystem = new FileSystem(); + private static readonly IPath Path = FileSystem.Path; + private static readonly IDirectory Directory = FileSystem.Directory; + + private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs\\Experimentation"); + + static Logger() + { + if (!Directory.Exists(ApplicationLogPath)) + { + Directory.CreateDirectory(ApplicationLogPath); + } + + // Using InvariantCulture since this is used for a log file name + var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); + + Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); + + Trace.AutoFlush = true; + } + + public static void LogInfo(string message) + { + Log(message, "INFO"); + } + + public static void LogError(string message) + { + Log(message, "ERROR"); +#if DEBUG + Debugger.Break(); +#endif + } + + public static void LogError(string message, Exception e) + { + Log( + message + Environment.NewLine + + e?.Message + Environment.NewLine + + "Inner exception: " + Environment.NewLine + + e?.InnerException?.Message + Environment.NewLine + + "Stack trace: " + Environment.NewLine + + e?.StackTrace, + "ERROR"); +#if DEBUG + Debugger.Break(); +#endif + } + + private static void Log(string message, string type) + { + Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); + Trace.Indent(); + Trace.WriteLine(GetCallerInfo()); + Trace.WriteLine(message); + Trace.Unindent(); + } + + private static string GetCallerInfo() + { + StackTrace stackTrace = new StackTrace(); + + var methodName = stackTrace.GetFrame(3)?.GetMethod(); + var className = methodName?.DeclaringType?.Name; + return "[Method]: " + methodName?.Name + " [Class]: " + className; + } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentClientExtensionMethods.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentClientExtensionMethods.cs new file mode 100644 index 0000000000..ee08acd718 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentClientExtensionMethods.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.VariantAssignment.Contract; + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Client +{ +#pragma warning disable SA1200 // Using directives should be placed correctly + using TreatmentAssignmentServiceClient = VariantAssignmentServiceClient; +#pragma warning restore SA1200 // Using directives should be placed correctly + + public static class VariantAssignmentClientExtensionMethods + { + public static IVariantAssignmentProvider GetTreatmentAssignmentServiceClient(this VariantAssignmentClientSettings settings) + { + return new TreatmentAssignmentServiceClient(); + } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentServiceClient.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentServiceClient.cs new file mode 100644 index 0000000000..f90952859a --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentServiceClient.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.VariantAssignment.Contract; + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Client +{ + internal partial class VariantAssignmentServiceClient : IVariantAssignmentProvider, IDisposable + where TServerResponse : VariantAssignmentServiceResponse + { + public void Dispose() + { + throw new NotImplementedException(); + } + + public Task GetVariantAssignmentsAsync(IVariantAssignmentRequest request, CancellationToken ct = default) + { + return Task.FromResult(EmptyVariantAssignmentResponse.Instance); + } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/EmptyVariantAssignmentResponse.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/EmptyVariantAssignmentResponse.cs new file mode 100644 index 0000000000..90c797155e --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/EmptyVariantAssignmentResponse.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public class EmptyVariantAssignmentResponse : IVariantAssignmentResponse + { + /// + /// Singleton instance of . + /// + public static readonly IVariantAssignmentResponse Instance = new EmptyVariantAssignmentResponse(); + + public EmptyVariantAssignmentResponse() + { + } + + public long DataVersion => 0; + + public string Thumbprint => string.Empty; + + /// + public IReadOnlyCollection AssignedVariants => Array.Empty(); + + /// +#pragma warning disable CS8603 // Possible null reference return. + public IFeatureVariable GetFeatureVariable(IReadOnlyList path) => null; +#pragma warning restore CS8603 // Possible null reference return. + + /// + public IReadOnlyList GetFeatureVariables(IReadOnlyList prefix) => Array.Empty(); + + void IDisposable.Dispose() + { + } + + string IVariantAssignmentResponse.GetAssignmentContext() + { + throw new NotImplementedException(); + } + + IReadOnlyList IVariantAssignmentResponse.GetFeatureVariables() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IAssignedVariant.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IAssignedVariant.cs new file mode 100644 index 0000000000..6c9a31e8ce --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IAssignedVariant.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public interface IAssignedVariant + { + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IFeatureVariable.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IFeatureVariable.cs new file mode 100644 index 0000000000..fc9193ed0d --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IFeatureVariable.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public interface IFeatureVariable + { + /// + /// Gets the variable's value as a string. + /// + /// String value of the variable. + string GetStringValue(); + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentProvider.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentProvider.cs new file mode 100644 index 0000000000..dad9d39038 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentProvider.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public interface IVariantAssignmentProvider : IDisposable + { + /// + /// Computes variant assignments based on data. + /// + /// Variant assignment parameters. + /// Propagates notification that operations should be canceled. + /// An awaitable task that returns a . + Task GetVariantAssignmentsAsync(IVariantAssignmentRequest request, CancellationToken ct = default); + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentRequest.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentRequest.cs new file mode 100644 index 0000000000..9639a3a58d --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentRequest.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public interface IVariantAssignmentRequest + { + /// + /// Gets inputs used for evaluating filters, assignment units, etc. + /// + IReadOnlyCollection<(string Key, string Value)> Parameters { get; } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentResponse.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentResponse.cs new file mode 100644 index 0000000000..29ee2209de --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentResponse.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + /// + /// Snapshot of variant assignments. + /// + public interface IVariantAssignmentResponse : IDisposable + { + ///// + ///// Gets the serial number of variant assignment configuration snapshot used when assigning variants. + ///// + long DataVersion { get; } + + ///// + ///// Get a hash of the response suitable for caching. + ///// + // string Thumbprint { get; } + + /// + /// Gets the variants assigned based on request parameters and a variant configuration snapshot. + /// + IReadOnlyCollection AssignedVariants { get; } + + /// + /// Gets feature variables assigned by variants in this response. + /// + /// (Optional) Filter feature variables where contains the . + /// Range of matching feature variables. + IReadOnlyList GetFeatureVariables(IReadOnlyList prefix); + + // this actually part of the interface but gets the job done + IReadOnlyList GetFeatureVariables(); + + // this actually part of the interface but gets the job done + string GetAssignmentContext(); + + /// + /// Gets a single feature variable assigned by variants in this response. + /// + /// Exact feature variable path. + /// Matching feature variable or null. + IFeatureVariable GetFeatureVariable(IReadOnlyList path); + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/TreatmentAssignmentServiceResponse.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/TreatmentAssignmentServiceResponse.cs new file mode 100644 index 0000000000..3b082549f7 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/TreatmentAssignmentServiceResponse.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + internal class TreatmentAssignmentServiceResponse : VariantAssignmentServiceResponse + { + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentClientSettings.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentClientSettings.cs new file mode 100644 index 0000000000..1ea295bfda --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentClientSettings.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel.DataAnnotations; + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + /// + /// Configuration for variant assignment service client. + /// + public class VariantAssignmentClientSettings + { + /// + /// Gets or sets the variant assignment service endpoint URL. + /// + [Required] + public Uri? Endpoint { get; set; } + + /// + /// Gets or sets a value indicating whether gets or sets a value whether client side request caching should be enabled. + /// + public bool EnableCaching { get; set; } + + /// + /// Gets or sets the the maximum time a cached variant assignment response may be used without re-validating. + /// + public TimeSpan ResponseCacheTime { get; set; } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentRequest.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentRequest.cs new file mode 100644 index 0000000000..976ce53531 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Specialized; + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public class VariantAssignmentRequest : IVariantAssignmentRequest + { + private NameValueCollection _parameters = new NameValueCollection(); + + /// + /// Gets or sets mutable . + /// + public NameValueCollection Parameters { get => _parameters; set => _parameters = value; } + + IReadOnlyCollection<(string Key, string Value)> IVariantAssignmentRequest.Parameters => (IReadOnlyCollection<(string Key, string Value)>)_parameters; + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentServiceResponse.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentServiceResponse.cs new file mode 100644 index 0000000000..e87425f4d3 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentServiceResponse.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + /// + /// Mutable implementation of for (de)serialization. + /// + internal class VariantAssignmentServiceResponse : IVariantAssignmentResponse, IDisposable + { + /// + public virtual long DataVersion { get; set; } + + public virtual IReadOnlyCollection AssignedVariants { get; set; } = Array.Empty(); + + public IFeatureVariable GetFeatureVariable(IReadOnlyList path) + { + throw new NotImplementedException(); + } + + public IReadOnlyList GetFeatureVariables(IReadOnlyList prefix) + { + throw new NotImplementedException(); + } + + protected virtual void Dispose(bool disposing) + { + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public IReadOnlyList GetFeatureVariables() + { + throw new NotImplementedException(); + } + + public string GetAssignmentContext() + { + return string.Empty; + } + } +} diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index 60f69eccdf..de72d9aa2d 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -16,6 +16,7 @@ static std::wstring settings_theme = L"system"; static bool run_as_elevated = false; static bool download_updates_automatically = true; +static bool enable_experimentation = true; json::JsonObject GeneralSettings::to_json() { @@ -37,6 +38,7 @@ json::JsonObject GeneralSettings::to_json() result.SetNamedValue(L"is_elevated", json::value(isElevated)); result.SetNamedValue(L"run_elevated", json::value(isRunElevated)); result.SetNamedValue(L"download_updates_automatically", json::value(downloadUpdatesAutomatically)); + result.SetNamedValue(L"enable_experimentation", json::value(enableExperimentation)); result.SetNamedValue(L"is_admin", json::value(isAdmin)); result.SetNamedValue(L"theme", json::value(theme)); result.SetNamedValue(L"system_theme", json::value(systemTheme)); @@ -55,6 +57,7 @@ json::JsonObject load_general_settings() } run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false); download_updates_automatically = loaded.GetNamedBoolean(L"download_updates_automatically", true) && check_user_is_admin(); + enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation",true); return loaded; } @@ -67,6 +70,7 @@ GeneralSettings get_general_settings() .isRunElevated = run_as_elevated, .isAdmin = is_user_admin, .downloadUpdatesAutomatically = download_updates_automatically && is_user_admin, + .enableExperimentation = enable_experimentation, .theme = settings_theme, .systemTheme = WindowsColors::is_dark_mode() ? L"dark" : L"light", .powerToysVersion = get_product_version() @@ -89,6 +93,8 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save) download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true); + enable_experimentation = general_configs.GetNamedBoolean(L"enable_experimentation", true); + if (json::has(general_configs, L"startup", json::JsonValueType::Boolean)) { const bool startup = general_configs.GetNamedBoolean(L"startup"); diff --git a/src/runner/general_settings.h b/src/runner/general_settings.h index bc7f59cae6..4f87ffd884 100644 --- a/src/runner/general_settings.h +++ b/src/runner/general_settings.h @@ -11,6 +11,7 @@ struct GeneralSettings bool isRunElevated; bool isAdmin; bool downloadUpdatesAutomatically; + bool enableExperimentation; std::wstring theme; std::wstring systemTheme; std::wstring powerToysVersion; diff --git a/src/runner/trace.cpp b/src/runner/trace.cpp index 5e5bdcbf38..82522bd0f4 100644 --- a/src/runner/trace.cpp +++ b/src/runner/trace.cpp @@ -56,6 +56,7 @@ void Trace::SettingsChanged(const GeneralSettings& settings) TraceLoggingWideString(enabledModules.c_str(), "ModulesEnabled"), TraceLoggingBoolean(settings.isRunElevated, "AlwaysRunElevated"), TraceLoggingBoolean(settings.downloadUpdatesAutomatically, "DownloadUpdatesAutomatically"), + TraceLoggingBoolean(settings.enableExperimentation, "EnableExperimentation"), TraceLoggingWideString(settings.theme.c_str(), "Theme"), ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), diff --git a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs index 25bd1b2a07..e59e685b76 100644 --- a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs +++ b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs @@ -49,12 +49,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("download_updates_automatically")] public bool AutoDownloadUpdates { get; set; } + [JsonPropertyName("enable_experimentation")] + public bool EnableExperimentation { get; set; } + public GeneralSettings() { Startup = false; IsAdmin = false; IsElevated = false; AutoDownloadUpdates = false; + EnableExperimentation = true; Theme = "system"; SystemTheme = "light"; try diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs new file mode 100644 index 0000000000..8f17b58dc3 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events +{ + [EventData] + public class OobeVariantAssignmentEvent : EventBase, IEvent + { + public string AssignmentContext { get; set; } + + public string ClientID { get; set; } + + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj b/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj index b083e176cc..495d233dec 100644 --- a/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj +++ b/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj @@ -3,7 +3,7 @@ net7.0-windows10.0.19041.0 false - + win10-x64;win10-arm64 $(Version).0 diff --git a/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsExperimentation.png b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsExperimentation.png new file mode 100644 index 0000000000000000000000000000000000000000..dc5e0c3169f8b9a75fdeb1b3d4e76ac46d0a0b40 GIT binary patch literal 931 zcmV;U16=%xP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D12{=UK~#8N?b}(X zO;H@j@q-MB5Dz?15;7H`66T6_F|H~El@q4fc)6oTm z%OQA&rXel+;m1!!p>rJGq4`LQt{i^6z7>T|8~}KS;s78maRA`Q#{od069)j^p*R3Y zOB?|B@o@l9=)?hlcPI`3(h>&%eth8vKnt4QAuW`oC3*ZD6gns19h!%S;rr|}DZ-t!19$#;5r3+f?hJjxZBrzhxyhNlf~pExahr+AE;1 zXx8?^+tQYqk!V=@;S;>5?Z_+IfOQ+*-Zkjw4ar$}F;9{AE)MM!^EPD}>Ymw17E)=o zC#xh^jb1^|4|R)X-^cJGE}>rO1>IwK`v+0C==akwqY)W}=>-JMiRv~2o zlAE+wqULmi-*kL~X5SE_w@5w!T1=@qno)gS?jb_WaQFad=XuT1ZSM;-om8*4LjBW~ zE${(2hfI=u>eKJqDd^pQDD>jRYm_y}Bzhr{JbCDzqboAYR+MWfMDL+Xcjj4&`zY6F z2((0|C38Kn<(3W@1MS>c2rYs%n(hQ=Vi00tVyaE4^aoG(_in&Osw@Bi002ovPDHLk FV1g8RweA1_ literal 0 HcmV?d00001 diff --git a/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml b/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml index 391026cee4..98877bd01c 100644 --- a/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml +++ b/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml @@ -10,12 +10,13 @@ - + @@ -23,9 +24,7 @@ Grid.Row="1" Padding="32,24,32,24" VerticalScrollBarVisibility="Auto"> - + - + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml.cs b/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml.cs index abc73ee3e2..72abab90d6 100644 --- a/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml.cs +++ b/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml.cs @@ -32,6 +32,12 @@ namespace Microsoft.PowerToys.Settings.UI.Controls set => SetValue(HeroImageProperty, value); } + public double HeroImageHeight + { + get { return (double)GetValue(HeroImageHeightProperty); } + set { SetValue(HeroImageHeightProperty, value); } + } + public object PageContent { get { return (object)GetValue(PageContentProperty); } @@ -42,5 +48,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(SettingsPageControl), new PropertyMetadata(default(string))); public static readonly DependencyProperty HeroImageProperty = DependencyProperty.Register("HeroImage", typeof(string), typeof(SettingsPageControl), new PropertyMetadata(default(string))); public static readonly DependencyProperty PageContentProperty = DependencyProperty.Register("PageContent", typeof(object), typeof(SettingsPageControl), new PropertyMetadata(new Grid())); + public static readonly DependencyProperty HeroImageHeightProperty = DependencyProperty.Register("HeroImageHeight", typeof(double), typeof(SettingsPageControl), new PropertyMetadata(280.0)); } } diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml new file mode 100644 index 0000000000..ab32d44c5b --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml.cs new file mode 100644 index 0000000000..c5171f4159 --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.OOBE.Enums; +using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; +using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace Microsoft.PowerToys.Settings.UI.OOBE.Views +{ + public sealed partial class OobeOverviewAlternate : Page + { + public OobePowerToysModule ViewModel { get; set; } + + public OobeOverviewAlternate() + { + this.InitializeComponent(); + ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]); + DataContext = ViewModel; + + FancyZonesHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.GetKeysList(); + RunHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.OpenPowerLauncher.GetKeysList(); + ColorPickerHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); + AlwaysOnTopHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.Hotkey.Value.GetKeysList(); + } + + private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + { + if (OobeShellPage.OpenMainWindowCallback != null) + { + OobeShellPage.OpenMainWindowCallback(typeof(GeneralPage)); + } + + ViewModel.LogOpeningSettingsEvent(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + ViewModel.LogOpeningModuleEvent(); + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + ViewModel.LogClosingModuleEvent(); + } + } +} diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml new file mode 100644 index 0000000000..3c2a5abb45 --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml.cs new file mode 100644 index 0000000000..40dcb56aa2 --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using AllExperiments; +using Microsoft.PowerToys.Settings.UI.OOBE.Enums; +using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; +using Microsoft.PowerToys.Settings.UI.Services; +using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace Microsoft.PowerToys.Settings.UI.OOBE.Views +{ + public sealed partial class OobeOverviewPlaceholder : Page + { + public OobePowerToysModule ViewModel { get; set; } + + public OobeOverviewPlaceholder() + { + this.InitializeComponent(); + ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]); + DataContext = ViewModel; + } + + private static async Task GetIsExperiment() + { + Experiments landingPageExp = new Experiments(); + var experimentEnabled = await landingPageExp.EnableLandingPageExperimentAsync(); + return experimentEnabled; + } + + private async void Reload() + { + var isExperiment = await GetIsExperiment(); + + if (isExperiment) + { + this.Frame.Navigate(typeof(OobeOverviewAlternate)); + } + else + { + this.Frame.Navigate(typeof(OobeOverview)); + } + } + + private void Page_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + { + Reload(); + } + + private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + { + if (OobeShellPage.OpenMainWindowCallback != null) + { + OobeShellPage.OpenMainWindowCallback(typeof(GeneralPage)); + } + + ViewModel.LogOpeningSettingsEvent(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + ViewModel.LogOpeningModuleEvent(); + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + ViewModel.LogClosingModuleEvent(); + } + } +} diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml.cs index dcd099285c..05498bd208 100644 --- a/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml.cs +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml.cs @@ -4,6 +4,8 @@ using System; using System.Collections.ObjectModel; +using System.Globalization; +using AllExperiments; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; @@ -47,10 +49,16 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views public ObservableCollection Modules { get; } + private static ISettingsUtils settingsUtils = new SettingsUtils(); + + private bool ExperimentationToggleSwitchEnabled { get; set; } = true; + public OobeShellPage() { InitializeComponent(); + ExperimentationToggleSwitchEnabled = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.EnableExperimentation; + DataContext = ViewModel; OobeShellHandler = this; UpdateUITheme(); @@ -186,7 +194,27 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views { switch (selectedItem.Tag) { - case "Overview": NavigationFrame.Navigate(typeof(OobeOverview)); break; + case "Overview": + if (ExperimentationToggleSwitchEnabled) + { + switch (AllExperiments.Experiments.LandingPageExperiment) + { + case Experiments.ExperimentState.Enabled: + NavigationFrame.Navigate(typeof(OobeOverviewAlternate)); break; + case Experiments.ExperimentState.Disabled: + NavigationFrame.Navigate(typeof(OobeOverview)); break; + case Experiments.ExperimentState.NotLoaded: + NavigationFrame.Navigate(typeof(OobeOverviewPlaceholder)); break; + } + + break; + } + else + { + NavigationFrame.Navigate(typeof(OobeOverview)); + break; + } + case "WhatsNew": NavigationFrame.Navigate(typeof(OobeWhatsNew)); break; case "AlwaysOnTop": NavigationFrame.Navigate(typeof(OobeAlwaysOnTop)); break; case "Awake": NavigationFrame.Navigate(typeof(OobeAwake)); break; diff --git a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj index 90b499c799..7a5f19c1c2 100644 --- a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj +++ b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj @@ -55,6 +55,9 @@ https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json + + + @@ -91,15 +94,19 @@ + + + + MSBuild:Compile @@ -122,10 +129,17 @@ Always + + MSBuild:Compile + MSBuild:Compile + + $(DefaultXamlRuntime) + + $(DefaultXamlRuntime) diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index a08afd5429..c397cbcbd8 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2830,6 +2830,10 @@ Activate by holding the key for the character you want to add an accent to, then Launch Host File Editor "Host File Editor" is a product name + + Launch Host File Editor + "Host File Editor" is a product name + Position of additional content @@ -2891,6 +2895,39 @@ Activate by holding the key for the character you want to add an accent to, then Preferred language + + Pin a window so that: + + + Always On Top + + + To pick a color: + + + Color Picker + + + Here are a few shortcuts to get you started: + + + To open the FancyZones editor, press: + + + FancyZones + + + Get access to your files and more: + + + PowerToys Run + + + Only affects Windows Insider builds + + + Enable experimentation + All apps diff --git a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs index e7447ed1c9..babf4e0158 100644 --- a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs @@ -125,6 +125,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _startup = GeneralSettingsConfig.Startup; _autoDownloadUpdates = GeneralSettingsConfig.AutoDownloadUpdates; + _enableExperimentation = GeneralSettingsConfig.EnableExperimentation; _isElevated = isElevated; _runElevated = GeneralSettingsConfig.RunElevated; @@ -152,6 +153,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private int _themeIndex; private bool _autoDownloadUpdates; + private bool _enableExperimentation; private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate; private string _newAvailableVersion = string.Empty; @@ -284,6 +286,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public bool EnableExperimentation + { + get + { + return _enableExperimentation; + } + + set + { + if (_enableExperimentation != value) + { + _enableExperimentation = value; + GeneralSettingsConfig.EnableExperimentation = value; + NotifyPropertyChanged(); + } + } + } + public static bool AutoUpdatesEnabled { get diff --git a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml index fdb0bcaefd..7ac95c8537 100644 --- a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml +++ b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml @@ -369,8 +369,13 @@ + + + - Date: Fri, 17 Feb 2023 21:23:39 +0000 Subject: [PATCH 053/163] [GPO]Add setting to disallow experimentation (#24131) * [GPO]Add policy to disallow experimentation * Refresh experimentation setting UI * Reflect gpo value in Settings UI * Fix adml errors * Update src/settings-ui/Settings.UI/Strings/en-us/Resources.resw * Update src/settings-ui/Settings.UI/Strings/en-us/Resources.resw * Update src/gpo/assets/en-US/PowerToys.adml * Small grammar fixes * Update src/gpo/assets/en-US/PowerToys.adml --------- Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> --- doc/gpo/README.md | 8 +++++++ src/common/GPOWrapper/GPOWrapper.cpp | 4 ++++ src/common/GPOWrapper/GPOWrapper.h | 1 + src/common/GPOWrapper/GPOWrapper.idl | 1 + src/common/utils/gpo.h | 6 +++++ src/gpo/assets/PowerToys.admx | 15 ++++++++++-- src/gpo/assets/en-US/PowerToys.adml | 10 +++++++- .../OOBE/Views/OobeShellPage.xaml.cs | 3 ++- .../Settings.UI/Strings/en-us/Resources.resw | 10 ++++++-- .../ViewModels/GeneralViewModel.cs | 11 ++++++++- .../Settings.UI/Views/GeneralPage.xaml | 23 ++++++++++++++----- 11 files changed, 79 insertions(+), 13 deletions(-) diff --git a/doc/gpo/README.md b/doc/gpo/README.md index 482c3f6271..929708265f 100644 --- a/doc/gpo/README.md +++ b/doc/gpo/README.md @@ -31,3 +31,11 @@ If you enable this setting, the utility will be always enabled and the user won' If you disable this setting, the utility will be always disabled and the user won't be able to enable it. If you don't configure this setting, users are able to disable or enable the utility. + +### Allow experimentation + +This policy configures whether PowerToys experimentation is allowed. + +Disable this setting to disable the experimentation features in PowerToys, meaning the user won't see the new features being experimented even if it gets selected as part of the test group. + +If this setting is not configured, experimentation is allowed. diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index 08948db0d1..eb813ab616 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -112,4 +112,8 @@ namespace winrt::PowerToys::GPOWrapper::implementation { return static_cast(powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue()); } + GpoRuleConfigured GPOWrapper::GetAllowExperimentationValue() + { + return static_cast(powertoys_gpo::getAllowExperimentationValue()); + } } diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h index 25b8d87445..da74b912d8 100644 --- a/src/common/GPOWrapper/GPOWrapper.h +++ b/src/common/GPOWrapper/GPOWrapper.h @@ -34,6 +34,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue(); static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue(); static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue(); + static GpoRuleConfigured GetAllowExperimentationValue(); }; } diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl index f316b63fe1..5e6deef6d7 100644 --- a/src/common/GPOWrapper/GPOWrapper.idl +++ b/src/common/GPOWrapper/GPOWrapper.idl @@ -38,6 +38,7 @@ namespace PowerToys static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue(); static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue(); static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue(); + static GpoRuleConfigured GetAllowExperimentationValue(); } } } diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index 6c02e2b159..ee887c60ea 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -46,6 +46,7 @@ namespace powertoys_gpo { const std::wstring POLICY_CONFIGURE_ENABLED_SHORTCUT_GUIDE = L"ConfigureEnabledUtilityShortcutGuide"; const std::wstring POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR = L"ConfigureEnabledUtilityTextExtractor"; const std::wstring POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE = L"ConfigureEnabledUtilityVideoConferenceMute"; + const std::wstring POLICY_ALLOW_EXPERIMENTATION = L"AllowExperimentation"; inline gpo_rule_configured_t getConfiguredValue(const std::wstring& registry_value_name) { @@ -235,4 +236,9 @@ namespace powertoys_gpo { return getConfiguredValue(POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE); } + inline gpo_rule_configured_t getAllowExperimentationValue() + { + return getConfiguredValue(POLICY_ALLOW_EXPERIMENTATION); + } + } diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx index 037bf1cc2d..00b0bb2b9d 100644 --- a/src/gpo/assets/PowerToys.admx +++ b/src/gpo/assets/PowerToys.admx @@ -1,14 +1,15 @@ - + - + + @@ -286,5 +287,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml index 9a07cd42f9..17c379b691 100644 --- a/src/gpo/assets/en-US/PowerToys.adml +++ b/src/gpo/assets/en-US/PowerToys.adml @@ -1,7 +1,7 @@ - + PowerToys PowerToys @@ -9,6 +9,7 @@ Microsoft PowerToys PowerToys version 0.64.0 or later + PowerToys version 0.68.0 or later This policy configures the enabled state for a PowerToys utility. @@ -27,6 +28,12 @@ If you enable this setting, the utility will be always enabled and the user won' If you disable this setting, the utility will be always disabled and the user won't be able to enable it. If you don't configure this setting, users are able to disable or enable the utility. + + This policy configures whether PowerToys experimentation is allowed. With experimentation allowed the user sees the new features being experimented if it gets selected as part of the test group. (Experimentation will only happen on Windows Insider builds.) + +If this setting is not configured or enabled, the user can control experimentation in the PowerToys settings menu. + +If this setting is disabled, experimentation is not allowed. Always On Top: Configure enabled state Awake: Configure enabled state @@ -55,6 +62,7 @@ If you don't configure this setting, users are able to disable or enable the uti Shortcut Guide: Configure enabled state Text Extractor: Configure enabled state Video Conference Mute: Configure enabled state + Allow Experimentation diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml.cs index 05498bd208..1c14f8db23 100644 --- a/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml.cs +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Collections.ObjectModel; using System.Globalization; using AllExperiments; +using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; @@ -195,7 +196,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views switch (selectedItem.Tag) { case "Overview": - if (ExperimentationToggleSwitchEnabled) + if (ExperimentationToggleSwitchEnabled && GPOWrapper.GetAllowExperimentationValue() != GpoRuleConfigured.Disabled) { switch (AllExperiments.Experiments.LandingPageExperiment) { diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index c397cbcbd8..08e32c0970 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2922,11 +2922,17 @@ Activate by holding the key for the character you want to add an accent to, then PowerToys Run + + Experimentation + - Only affects Windows Insider builds + Note: Only Windows Insider builds may be selected for experimentation - Enable experimentation + Allow experimentation with new features + + + The system administrator has disabled experimentation. All apps diff --git a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs index babf4e0158..9876249c10 100644 --- a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs @@ -12,6 +12,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text.Json; using System.Threading.Tasks; +using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; @@ -140,6 +141,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _newAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink; _updateCheckedDate = UpdatingSettingsConfig.LastCheckedDateLocalized; + _experimentationIsGpoDisallowed = GPOWrapper.GetAllowExperimentationValue() == GpoRuleConfigured.Disabled; + if (dispatcherAction != null) { _fileWatcher = Helper.GetFileWatcher(string.Empty, UpdatingSettings.SettingsFile, dispatcherAction); @@ -154,6 +157,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private bool _autoDownloadUpdates; private bool _enableExperimentation; + private bool _experimentationIsGpoDisallowed; private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate; private string _newAvailableVersion = string.Empty; @@ -290,7 +294,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { get { - return _enableExperimentation; + return _enableExperimentation && !_experimentationIsGpoDisallowed; } set @@ -304,6 +308,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public bool IsExperimentationGpoDisallowed + { + get => _experimentationIsGpoDisallowed; + } + public static bool AutoUpdatesEnabled { get diff --git a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml index 7ac95c8537..1918c0ec0e 100644 --- a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml +++ b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml @@ -369,12 +369,6 @@ - - - + + + + + + From 0e41684cf0e2b4987fda45eb4e534fe0de177484 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Tue, 21 Feb 2023 20:19:04 -0800 Subject: [PATCH 054/163] Fixing policheck flags --- .github/actions/spell-check/expect.txt | 1 - .../modules/launcher/plugins/timeZone.md | 12 +-- .../FancyZonesData/CustomLayouts.cpp | 4 +- .../FancyZonesData/CustomLayouts.h | 4 +- .../UnitTests/CustomLayoutsTests.Spec.cpp | 8 +- .../FancyZonesTests/UnitTests/Layout.Spec.cpp | 4 +- .../Properties/Resources.Designer.cs | 95 +++++++------------ .../Properties/Resources.resx | 30 +++--- .../timezones.json | 14 ++- .../Exception/ExceptionFormatter.cs | 10 +- .../poweraccent/PowerAccent.Core/Languages.cs | 2 +- 11 files changed, 74 insertions(+), 110 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 49012460e6..0594175f15 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -88,7 +88,6 @@ ARPPRODUCTICON ARRAYSIZE arsinh artanh -Artsakh asdf AShortcut ASingle diff --git a/doc/devdocs/modules/launcher/plugins/timeZone.md b/doc/devdocs/modules/launcher/plugins/timeZone.md index 49710cb947..cec1b7d0ae 100644 --- a/doc/devdocs/modules/launcher/plugins/timeZone.md +++ b/doc/devdocs/modules/launcher/plugins/timeZone.md @@ -32,7 +32,7 @@ A minimum entry for the `TimeZone.json` looks like: ```json { "Offset": "11:55", - "Name": "My crazy time zone", + "Name": "My unique time zone", } ``` @@ -41,26 +41,26 @@ A full entry for the `TimeZone.json` looks like: ```json { "Offset": "11:55", - "Name": "My crazy time zone", + "Name": "My unique time zone", "Shortcut" : "MYTZ", "MilitaryName" : "Order Time Zone", "TimeNamesStandard": [ - "My crazy standard time" + "My unique standard time" ], "ShortcutsStandard": [ "MCST" ], "TimeNamesDaylight": [ - "My crazy daylight time" + "My unique daylight time" ], "ShortcutsDaylight": [ "MCDT" ], "CountriesStandard": [ - "Crazy Land East" + "unique Land East" ], "CountriesDaylight": [ - "Crazy Land West" + "Unique Land West" ] } ``` diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp index 4040273cc7..1aef2b78e4 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp @@ -37,8 +37,8 @@ namespace JsonUtils for (uint32_t i = 0; i < size; ++i) { json::JsonObject zoneJson = zonesJson.GetObjectAt(i); - const int x = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XID)); - const int y = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YID)); + const int x = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XAxisID)); + const int y = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YAxisID)); const int width = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::WidthID)); const int height = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::HeightID)); FancyZonesDataTypes::CanvasLayoutInfo::Rect zone{ x, y, width, height }; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h index 298edf0dde..efa8d88744 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h @@ -30,8 +30,8 @@ namespace NonLocalizable const static wchar_t* RefHeightID = L"ref-height"; const static wchar_t* RefWidthID = L"ref-width"; const static wchar_t* ZonesID = L"zones"; - const static wchar_t* XID = L"X"; - const static wchar_t* YID = L"Y"; + const static wchar_t* XAxisID = L"X"; + const static wchar_t* YAxisID = L"Y"; const static wchar_t* WidthID = L"width"; const static wchar_t* HeightID = L"height"; diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp index f979cf7c69..36edcf3867 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp @@ -32,16 +32,16 @@ namespace FancyZonesUnitTests json::JsonArray zonesArray{}; { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(0)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(0)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(0)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(0)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(1140)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(1040)); zonesArray.Append(zone); } { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(1140)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(649)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(1140)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(649)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(780)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(391)); zonesArray.Append(zone); diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp index 11d0ec3761..4fbd9c163a 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp @@ -65,8 +65,8 @@ namespace FancyZonesUnitTests for (const auto& zoneRect : zones) { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(zoneRect.left)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(zoneRect.top)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(zoneRect.left)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(zoneRect.top)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(zoneRect.right - zoneRect.left)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(zoneRect.bottom - zoneRect.top)); zonesArray.Append(zone); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs index 78735dc479..b61937d4fb 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -411,15 +411,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Artsakh. - /// - internal static string Artsakh { - get { - return ResourceManager.GetString("Artsakh", resourceCulture); - } - } - /// /// Looks up a localized string similar to Aruba. /// @@ -1032,6 +1023,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Cabo Verde. + /// + internal static string Cabo_Verde { + get { + return ResourceManager.GetString("Cabo Verde", resourceCulture); + } + } + /// /// Looks up a localized string similar to Caiguna (Australia). /// @@ -1077,15 +1077,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Cape Verde. - /// - internal static string Cape_Verde { - get { - return ResourceManager.GetString("Cape Verde", resourceCulture); - } - } - /// /// Looks up a localized string similar to Cape Verde Time. /// @@ -1779,15 +1770,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to East Timor. - /// - internal static string East_Timor { - get { - return ResourceManager.GetString("East Timor", resourceCulture); - } - } - /// /// Looks up a localized string similar to Easter Island (Chile). /// @@ -2796,15 +2778,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Ivory Coast. - /// - internal static string Ivory_Coast { - get { - return ResourceManager.GetString("Ivory Coast", resourceCulture); - } - } - /// /// Looks up a localized string similar to Jamaica. /// @@ -3355,11 +3328,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } /// - /// Looks up a localized string similar to Macau. + /// Looks up a localized string similar to Macao SAR. /// - internal static string Macau { + internal static string Macao_SAR { get { - return ResourceManager.GetString("Macau", resourceCulture); + return ResourceManager.GetString("Macao SAR", resourceCulture); } } @@ -4263,15 +4236,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Northern Cyprus. - /// - internal static string Northern_Cyprus { - get { - return ResourceManager.GetString("Northern Cyprus", resourceCulture); - } - } - /// /// Looks up a localized string similar to Northern Mariana Islands. /// @@ -4552,11 +4516,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } /// - /// Looks up a localized string similar to Palestine. + /// Looks up a localized string similar to Palestine Authority. /// - internal static string Palestine { + internal static string Palestine_Authority { get { - return ResourceManager.GetString("Palestine", resourceCulture); + return ResourceManager.GetString("Palestine Authority", resourceCulture); } } @@ -5667,15 +5631,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Sverdlovsk (Russia). - /// - internal static string Sverdlovsk__Russia_ { - get { - return ResourceManager.GetString("Sverdlovsk (Russia)", resourceCulture); - } - } - /// /// Looks up a localized string similar to Sweden. /// @@ -5865,6 +5820,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Timor-Leste. + /// + internal static string Timor_Leste { + get { + return ResourceManager.GetString("Timor-Leste", resourceCulture); + } + } + /// /// Looks up a localized string similar to Timor Leste Time. /// @@ -6576,6 +6540,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Yekaterinburg. + /// + internal static string Yekaterinburg { + get { + return ResourceManager.GetString("Yekaterinburg", resourceCulture); + } + } + /// /// Looks up a localized string similar to Yekaterinburg Time. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx index 61d12501d8..73efd62ffe 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx @@ -235,9 +235,6 @@ Armenia Time - - Artsakh - Aruba @@ -458,8 +455,8 @@ Canary Islands - - Cape Verde + + Cabo Verde Cape Verde Time @@ -695,8 +692,8 @@ East Nusa Tenggara (Indonesia) - - East Timor + + Timor-Leste Easter Island (Chile) @@ -1036,8 +1033,8 @@ Ittoqqortoormiit (Greenland) - - Ivory Coast + + Côte d’Ivoire Jamaica @@ -1224,8 +1221,8 @@ Luxembourg - - Macau + + Macao SAR Macquarie Island Station Time @@ -1528,9 +1525,6 @@ North Maluku (Indonesia) - - Northern Cyprus - Northern Mariana Islands @@ -1626,8 +1620,8 @@ Palau Time - - Palestine + + Palestine Authority Palmyra Atoll @@ -2003,8 +1997,8 @@ Suriname Time - - Sverdlovsk (Russia) + + Yekaterinburg Sweden diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json index fcef0af599..e83ab47d73 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json @@ -514,7 +514,7 @@ "EGST" ], "CountriesStandard": [ - "Cape Verde" + "Cabo Verde" ], "CountriesDaylight": [ "Ittoqqortoormiit (Greenland)", @@ -553,7 +553,7 @@ "Guinea-Bissau", "Guinea", "Iceland", - "Ivory Coast", + "Côte d’Ivoire", "Liberia", "Mali", "Mauritania", @@ -716,8 +716,7 @@ "Lebanon", "Lithuania", "Moldova", - "Northern Cyprus", - "Palestine", + "Palestine Authority", "Romania", "Transnistria", "Syria" @@ -820,7 +819,6 @@ ], "CountriesDaylight": [ "Armenia", - "Artsakh", "Azerbaijan", "Crozet Islands (French Southern and Antarctic Lands)", "Georgia", @@ -896,7 +894,7 @@ "Kurgan (Russia)", "Orenburg (Russia)", "Perm (Russia)", - "Sverdlovsk (Russia)", + "Yekaterinburg", "Tyumen (Russia)", "Yamalia (Russia)", "Tajikistan", @@ -1081,7 +1079,7 @@ "South Kalimantan (Indonesia)", "Sulawesi (Indonesia)", "West Nusa Tenggara (Indonesia)", - "Macau", + "Macao SAR", "Malaysia", "Mongolia (most)", "Philippines", @@ -1132,7 +1130,7 @@ "YAKT" ], "CountriesStandard": [ - "East Timor", + "Timor-Leste", "Maluku (Indonesia)", "North Maluku (Indonesia)", "Papua (Indonesia)", diff --git a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs index 02c17e066d..9baf7a6d94 100644 --- a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs +++ b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs @@ -91,23 +91,23 @@ namespace Wox.Infrastructure.Exception // GlobalAssemblyCache - .NET Core and .NET 5 and later: false in all cases. // Source https://learn.microsoft.com/dotnet/api/system.reflection.assembly.globalassemblycache?view=net-6.0 - foreach (var ass in AppDomain.CurrentDomain.GetAssemblies()) + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { sb.Append("* "); - sb.Append(ass.FullName); + sb.Append(assembly.FullName); sb.Append(" ("); - if (ass.IsDynamic) + if (assembly.IsDynamic) { sb.Append("dynamic assembly doesn't has location"); } - else if (string.IsNullOrEmpty(ass.Location)) + else if (string.IsNullOrEmpty(assembly.Location)) { sb.Append("location is null or empty"); } else { - sb.Append(ass.Location); + sb.Append(assembly.Location); } sb.AppendLine(")"); diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index c9d7174650..5357ece996 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -53,7 +53,7 @@ namespace PowerAccent.Core Language.CUR => GetDefaultLetterKeyCUR(letter), // Currency Language.CY => GetDefaultLetterKeyCY(letter), // Welsh Language.CZ => GetDefaultLetterKeyCZ(letter), // Czech - Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish Gaelic) + Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish) Language.GD => GetDefaultLetterKeyGD(letter), // Gàidhlig (Scottish Gaelic) Language.DE => GetDefaultLetterKeyDE(letter), // German Language.EST => GetDefaultLetterKeyEST(letter), // Estonian From e3b7038003c77238a51ee582134d270e213d3628 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Tue, 21 Feb 2023 20:40:40 -0800 Subject: [PATCH 055/163] few more adjustmnets --- .../Properties/Resources.resx | 14 ++++---------- .../timezones.json | 6 ++---- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx index 73efd62ffe..715c4bd943 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx @@ -238,8 +238,8 @@ Aruba - - Ascension and Tristan da Cunha + + Saint Helena, Ascension and Tristan da Cunha ASEAN Common Time @@ -932,8 +932,8 @@ Honduras - - Hong Kong + + Hong Kong SAR Hong Kong Time @@ -1770,9 +1770,6 @@ Saint Barthélemy - - Saint Helena - Saint Kitts and Nevis @@ -1912,9 +1909,6 @@ Somalia - - Somaliland - Sonora (Mexico) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json index e83ab47d73..1aff0e54f4 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json @@ -545,7 +545,7 @@ "WEST" ], "CountriesStandard": [ - "Ascension and Tristan da Cunha", + "Saint Helena, Ascension and Tristan da Cunha", "Burkina Faso", "Danmarkshavn", "Gambia", @@ -557,7 +557,6 @@ "Liberia", "Mali", "Mauritania", - "Saint Helena", "São Tomé and Príncipe", "Senegal", "Sierra Leone", @@ -764,7 +763,6 @@ "Russia (most of European part)", "Saudi Arabia", "Somalia", - "Somaliland", "Prince Edward Islands (South Africa)", "South Ossetia", "Tanzania", @@ -1071,7 +1069,7 @@ "Australia: Western Australia (most)", "Brunei", "China", - "Hong Kong", + "Hong Kong SAR", "Bali (Indonesia)", "East Kalimantan (Indonesia)", "East Nusa Tenggara (Indonesia)", From 7fda4229c3c6d33d6b2a3fd1575a4edc6a592677 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Tue, 21 Feb 2023 20:50:29 -0800 Subject: [PATCH 056/163] Revert "few more adjustmnets" This reverts commit e3b7038003c77238a51ee582134d270e213d3628. --- .../Properties/Resources.resx | 14 ++++++++++---- .../timezones.json | 6 ++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx index 715c4bd943..73efd62ffe 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx @@ -238,8 +238,8 @@ Aruba - - Saint Helena, Ascension and Tristan da Cunha + + Ascension and Tristan da Cunha ASEAN Common Time @@ -932,8 +932,8 @@ Honduras - - Hong Kong SAR + + Hong Kong Hong Kong Time @@ -1770,6 +1770,9 @@ Saint Barthélemy + + Saint Helena + Saint Kitts and Nevis @@ -1909,6 +1912,9 @@ Somalia + + Somaliland + Sonora (Mexico) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json index 1aff0e54f4..e83ab47d73 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json @@ -545,7 +545,7 @@ "WEST" ], "CountriesStandard": [ - "Saint Helena, Ascension and Tristan da Cunha", + "Ascension and Tristan da Cunha", "Burkina Faso", "Danmarkshavn", "Gambia", @@ -557,6 +557,7 @@ "Liberia", "Mali", "Mauritania", + "Saint Helena", "São Tomé and Príncipe", "Senegal", "Sierra Leone", @@ -763,6 +764,7 @@ "Russia (most of European part)", "Saudi Arabia", "Somalia", + "Somaliland", "Prince Edward Islands (South Africa)", "South Ossetia", "Tanzania", @@ -1069,7 +1071,7 @@ "Australia: Western Australia (most)", "Brunei", "China", - "Hong Kong SAR", + "Hong Kong", "Bali (Indonesia)", "East Kalimantan (Indonesia)", "East Nusa Tenggara (Indonesia)", From aceffe3212b78b633931410b25facc6fad18bfca Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Tue, 21 Feb 2023 20:50:33 -0800 Subject: [PATCH 057/163] Revert "Fixing policheck flags" This reverts commit 0e41684cf0e2b4987fda45eb4e534fe0de177484. --- .github/actions/spell-check/expect.txt | 1 + .../modules/launcher/plugins/timeZone.md | 12 +-- .../FancyZonesData/CustomLayouts.cpp | 4 +- .../FancyZonesData/CustomLayouts.h | 4 +- .../UnitTests/CustomLayoutsTests.Spec.cpp | 8 +- .../FancyZonesTests/UnitTests/Layout.Spec.cpp | 4 +- .../Properties/Resources.Designer.cs | 95 ++++++++++++------- .../Properties/Resources.resx | 30 +++--- .../timezones.json | 14 +-- .../Exception/ExceptionFormatter.cs | 10 +- .../poweraccent/PowerAccent.Core/Languages.cs | 2 +- 11 files changed, 110 insertions(+), 74 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 0594175f15..49012460e6 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -88,6 +88,7 @@ ARPPRODUCTICON ARRAYSIZE arsinh artanh +Artsakh asdf AShortcut ASingle diff --git a/doc/devdocs/modules/launcher/plugins/timeZone.md b/doc/devdocs/modules/launcher/plugins/timeZone.md index cec1b7d0ae..49710cb947 100644 --- a/doc/devdocs/modules/launcher/plugins/timeZone.md +++ b/doc/devdocs/modules/launcher/plugins/timeZone.md @@ -32,7 +32,7 @@ A minimum entry for the `TimeZone.json` looks like: ```json { "Offset": "11:55", - "Name": "My unique time zone", + "Name": "My crazy time zone", } ``` @@ -41,26 +41,26 @@ A full entry for the `TimeZone.json` looks like: ```json { "Offset": "11:55", - "Name": "My unique time zone", + "Name": "My crazy time zone", "Shortcut" : "MYTZ", "MilitaryName" : "Order Time Zone", "TimeNamesStandard": [ - "My unique standard time" + "My crazy standard time" ], "ShortcutsStandard": [ "MCST" ], "TimeNamesDaylight": [ - "My unique daylight time" + "My crazy daylight time" ], "ShortcutsDaylight": [ "MCDT" ], "CountriesStandard": [ - "unique Land East" + "Crazy Land East" ], "CountriesDaylight": [ - "Unique Land West" + "Crazy Land West" ] } ``` diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp index 1aef2b78e4..4040273cc7 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp @@ -37,8 +37,8 @@ namespace JsonUtils for (uint32_t i = 0; i < size; ++i) { json::JsonObject zoneJson = zonesJson.GetObjectAt(i); - const int x = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XAxisID)); - const int y = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YAxisID)); + const int x = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XID)); + const int y = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YID)); const int width = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::WidthID)); const int height = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::HeightID)); FancyZonesDataTypes::CanvasLayoutInfo::Rect zone{ x, y, width, height }; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h index efa8d88744..298edf0dde 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h @@ -30,8 +30,8 @@ namespace NonLocalizable const static wchar_t* RefHeightID = L"ref-height"; const static wchar_t* RefWidthID = L"ref-width"; const static wchar_t* ZonesID = L"zones"; - const static wchar_t* XAxisID = L"X"; - const static wchar_t* YAxisID = L"Y"; + const static wchar_t* XID = L"X"; + const static wchar_t* YID = L"Y"; const static wchar_t* WidthID = L"width"; const static wchar_t* HeightID = L"height"; diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp index 36edcf3867..f979cf7c69 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp @@ -32,16 +32,16 @@ namespace FancyZonesUnitTests json::JsonArray zonesArray{}; { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(0)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(0)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(0)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(0)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(1140)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(1040)); zonesArray.Append(zone); } { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(1140)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(649)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(1140)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(649)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(780)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(391)); zonesArray.Append(zone); diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp index 4fbd9c163a..11d0ec3761 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp @@ -65,8 +65,8 @@ namespace FancyZonesUnitTests for (const auto& zoneRect : zones) { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(zoneRect.left)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(zoneRect.top)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(zoneRect.left)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(zoneRect.top)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(zoneRect.right - zoneRect.left)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(zoneRect.bottom - zoneRect.top)); zonesArray.Append(zone); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs index b61937d4fb..78735dc479 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -411,6 +411,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Artsakh. + /// + internal static string Artsakh { + get { + return ResourceManager.GetString("Artsakh", resourceCulture); + } + } + /// /// Looks up a localized string similar to Aruba. /// @@ -1023,15 +1032,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Cabo Verde. - /// - internal static string Cabo_Verde { - get { - return ResourceManager.GetString("Cabo Verde", resourceCulture); - } - } - /// /// Looks up a localized string similar to Caiguna (Australia). /// @@ -1077,6 +1077,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Cape Verde. + /// + internal static string Cape_Verde { + get { + return ResourceManager.GetString("Cape Verde", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cape Verde Time. /// @@ -1770,6 +1779,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to East Timor. + /// + internal static string East_Timor { + get { + return ResourceManager.GetString("East Timor", resourceCulture); + } + } + /// /// Looks up a localized string similar to Easter Island (Chile). /// @@ -2778,6 +2796,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Ivory Coast. + /// + internal static string Ivory_Coast { + get { + return ResourceManager.GetString("Ivory Coast", resourceCulture); + } + } + /// /// Looks up a localized string similar to Jamaica. /// @@ -3328,11 +3355,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } /// - /// Looks up a localized string similar to Macao SAR. + /// Looks up a localized string similar to Macau. /// - internal static string Macao_SAR { + internal static string Macau { get { - return ResourceManager.GetString("Macao SAR", resourceCulture); + return ResourceManager.GetString("Macau", resourceCulture); } } @@ -4236,6 +4263,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Northern Cyprus. + /// + internal static string Northern_Cyprus { + get { + return ResourceManager.GetString("Northern Cyprus", resourceCulture); + } + } + /// /// Looks up a localized string similar to Northern Mariana Islands. /// @@ -4516,11 +4552,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } /// - /// Looks up a localized string similar to Palestine Authority. + /// Looks up a localized string similar to Palestine. /// - internal static string Palestine_Authority { + internal static string Palestine { get { - return ResourceManager.GetString("Palestine Authority", resourceCulture); + return ResourceManager.GetString("Palestine", resourceCulture); } } @@ -5631,6 +5667,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Sverdlovsk (Russia). + /// + internal static string Sverdlovsk__Russia_ { + get { + return ResourceManager.GetString("Sverdlovsk (Russia)", resourceCulture); + } + } + /// /// Looks up a localized string similar to Sweden. /// @@ -5820,15 +5865,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Timor-Leste. - /// - internal static string Timor_Leste { - get { - return ResourceManager.GetString("Timor-Leste", resourceCulture); - } - } - /// /// Looks up a localized string similar to Timor Leste Time. /// @@ -6540,15 +6576,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Yekaterinburg. - /// - internal static string Yekaterinburg { - get { - return ResourceManager.GetString("Yekaterinburg", resourceCulture); - } - } - /// /// Looks up a localized string similar to Yekaterinburg Time. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx index 73efd62ffe..61d12501d8 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx @@ -235,6 +235,9 @@ Armenia Time + + Artsakh + Aruba @@ -455,8 +458,8 @@ Canary Islands - - Cabo Verde + + Cape Verde Cape Verde Time @@ -692,8 +695,8 @@ East Nusa Tenggara (Indonesia) - - Timor-Leste + + East Timor Easter Island (Chile) @@ -1033,8 +1036,8 @@ Ittoqqortoormiit (Greenland) - - Côte d’Ivoire + + Ivory Coast Jamaica @@ -1221,8 +1224,8 @@ Luxembourg - - Macao SAR + + Macau Macquarie Island Station Time @@ -1525,6 +1528,9 @@ North Maluku (Indonesia) + + Northern Cyprus + Northern Mariana Islands @@ -1620,8 +1626,8 @@ Palau Time - - Palestine Authority + + Palestine Palmyra Atoll @@ -1997,8 +2003,8 @@ Suriname Time - - Yekaterinburg + + Sverdlovsk (Russia) Sweden diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json index e83ab47d73..fcef0af599 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json @@ -514,7 +514,7 @@ "EGST" ], "CountriesStandard": [ - "Cabo Verde" + "Cape Verde" ], "CountriesDaylight": [ "Ittoqqortoormiit (Greenland)", @@ -553,7 +553,7 @@ "Guinea-Bissau", "Guinea", "Iceland", - "Côte d’Ivoire", + "Ivory Coast", "Liberia", "Mali", "Mauritania", @@ -716,7 +716,8 @@ "Lebanon", "Lithuania", "Moldova", - "Palestine Authority", + "Northern Cyprus", + "Palestine", "Romania", "Transnistria", "Syria" @@ -819,6 +820,7 @@ ], "CountriesDaylight": [ "Armenia", + "Artsakh", "Azerbaijan", "Crozet Islands (French Southern and Antarctic Lands)", "Georgia", @@ -894,7 +896,7 @@ "Kurgan (Russia)", "Orenburg (Russia)", "Perm (Russia)", - "Yekaterinburg", + "Sverdlovsk (Russia)", "Tyumen (Russia)", "Yamalia (Russia)", "Tajikistan", @@ -1079,7 +1081,7 @@ "South Kalimantan (Indonesia)", "Sulawesi (Indonesia)", "West Nusa Tenggara (Indonesia)", - "Macao SAR", + "Macau", "Malaysia", "Mongolia (most)", "Philippines", @@ -1130,7 +1132,7 @@ "YAKT" ], "CountriesStandard": [ - "Timor-Leste", + "East Timor", "Maluku (Indonesia)", "North Maluku (Indonesia)", "Papua (Indonesia)", diff --git a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs index 9baf7a6d94..02c17e066d 100644 --- a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs +++ b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs @@ -91,23 +91,23 @@ namespace Wox.Infrastructure.Exception // GlobalAssemblyCache - .NET Core and .NET 5 and later: false in all cases. // Source https://learn.microsoft.com/dotnet/api/system.reflection.assembly.globalassemblycache?view=net-6.0 - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + foreach (var ass in AppDomain.CurrentDomain.GetAssemblies()) { sb.Append("* "); - sb.Append(assembly.FullName); + sb.Append(ass.FullName); sb.Append(" ("); - if (assembly.IsDynamic) + if (ass.IsDynamic) { sb.Append("dynamic assembly doesn't has location"); } - else if (string.IsNullOrEmpty(assembly.Location)) + else if (string.IsNullOrEmpty(ass.Location)) { sb.Append("location is null or empty"); } else { - sb.Append(assembly.Location); + sb.Append(ass.Location); } sb.AppendLine(")"); diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 5357ece996..c9d7174650 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -53,7 +53,7 @@ namespace PowerAccent.Core Language.CUR => GetDefaultLetterKeyCUR(letter), // Currency Language.CY => GetDefaultLetterKeyCY(letter), // Welsh Language.CZ => GetDefaultLetterKeyCZ(letter), // Czech - Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish) + Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish Gaelic) Language.GD => GetDefaultLetterKeyGD(letter), // Gàidhlig (Scottish Gaelic) Language.DE => GetDefaultLetterKeyDE(letter), // German Language.EST => GetDefaultLetterKeyEST(letter), // Estonian From 47999199e93cc45ab6d83b896c25db338d4e675d Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:06:59 +0100 Subject: [PATCH 058/163] [installer] Log installer to separate file (#24045) * [installer] Log installer to separate file * Spellcheck and minor fix * Delete interop hardlinks before creating them --- .github/actions/spell-check/expect.txt | 6 +- installer/PowerToysSetup/Product.wxs | 4 +- .../CustomAction.cpp | 79 +++++++++---------- tools/BugReportTool/BugReportTool/Main.cpp | 5 +- 4 files changed, 42 insertions(+), 52 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 49012460e6..e854c87522 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -40,7 +40,6 @@ ALPHATYPE Altdown alwaysontop amd -AMF AModifier AMPROPERTY AMPROPSETID @@ -206,7 +205,6 @@ CHILDACTIVATE CHILDWINDOW Choibalsan chrdavis -chromaticities Chrzan cht Chukotka @@ -393,7 +391,6 @@ Deondre depersist deprioritized depsfileslistspath -depsjsonpath deref DESKTOPABSOLUTEEDITING DESKTOPABSOLUTEPARSING @@ -931,7 +928,6 @@ logconsole logfile LOGFONT LOGFONTW -LOGMSG logon LOGPIXELSX LOn @@ -2004,7 +2000,7 @@ WANTPALM wbem wbemuuid WBounds -wca +Wca wcautil WCE wcex diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 9d277676d4..21b6372a11 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -177,9 +177,7 @@ Installed AND (REMOVE="ALL") - - Installed AND (REMOVE="ALL") - + Installed AND (REMOVE="ALL") diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp index 3e5c5045ab..c1f9e5a99a 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp @@ -34,24 +34,6 @@ 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}"; -struct WcaSink : spdlog::sinks::base_sink -{ - virtual void sink_it_(const spdlog::details::log_msg& msg) override - { - WcaLog(LOGMSG_STANDARD, msg.payload.data()); - } - virtual void flush_() override - { - // we don't need to flush wca log manually - } -}; - -void initSystemLogger() -{ - static std::once_flag initLoggerFlag; - std::call_once(initLoggerFlag, []() { Logger::init(std::vector{ std::make_shared() }); }); -} - HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring& installationDir) { DWORD len = 0; @@ -70,7 +52,6 @@ LExit: } UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall) { - initSystemLogger(); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; std::wstring installationFolder; @@ -85,14 +66,14 @@ UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall) { if (!changeSet.apply()) { - WcaLog(LOGMSG_STANDARD, "Couldn't apply registry changeSet"); + Logger::error(L"Couldn't apply registry changeSet"); failedToApply = true; } } if (!failedToApply) { - WcaLog(LOGMSG_STANDARD, "All registry changeSets applied successfully"); + Logger::info(L"All registry changeSets applied successfully"); } LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; @@ -101,7 +82,6 @@ LExit: UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall) { - initSystemLogger(); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; std::wstring installationFolder; @@ -131,14 +111,14 @@ UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall) if (auto msix = RcResource::create(IDR_BIN_MSIX_HELLO_PACKAGE, L"BIN", DLL_HANDLE)) { - WcaLog(LOGMSG_STANDARD, "Extracted MSIX"); + Logger::info(L"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"); + Logger::info(L"Saved MSIX"); using namespace winrt::Windows::Management::Deployment; using namespace winrt::Windows::Foundation; @@ -150,7 +130,7 @@ UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall) ExitOnFailure(hr, "Failed to AddPackage"); } - WcaLog(LOGMSG_STANDARD, "MSIX[s] were installed!"); + Logger::info(L"MSIX[s] were installed!"); } else { @@ -181,11 +161,11 @@ UINT __stdcall UninstallEmbeddedMSIXCA(MSIHANDLE hInstall) auto result = pm.RemovePackageAsync(p.Id().FullName()).get(); if (result) { - WcaLog(LOGMSG_STANDARD, "MSIX was uninstalled!"); + Logger::info(L"MSIX was uninstalled!"); } else { - WcaLog(LOGMSG_STANDARD, "Couldn't uninstall MSIX!"); + Logger::error(L"Couldn't uninstall MSIX!"); } } @@ -227,7 +207,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "CreateScheduledTaskCA"); ExitOnFailure(hr, "Failed to initialize"); - WcaLog(LOGMSG_STANDARD, "Initialized."); + Logger::info(L"CreateScheduledTaskCA Initialized."); // ------------------------------------------------------ // Get the Domain/Username for the trigger. @@ -246,7 +226,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) wcscat_s(username_domain, L"\\"); wcscat_s(username_domain, username); - WcaLog(LOGMSG_STANDARD, "Current user detected: %ls", username_domain); + Logger::info(L"Current user detected: {}", username_domain); // Task Name. wstrTaskName = L"Autorun for "; @@ -286,7 +266,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) pRootFolder->Release(); ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr); } - WcaLog(LOGMSG_STANDARD, "PowerToys task folder created."); + Logger::info(L"PowerToys task folder created."); } // If the same task exists, remove it. @@ -334,7 +314,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1")); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put the trigger ID: %x", hr); + Logger::error(L"Cannot put the trigger ID: {}", hr); } // Timing issues may make explorer not be started when the task runs. @@ -342,7 +322,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) hr = pLogonTrigger->put_Delay(_bstr_t(L"PT03S")); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put the trigger delay: %x", hr); + Logger::error(L"Cannot put the trigger delay: {}", hr); } // Define the user. The task will execute when the user logs on. @@ -383,19 +363,19 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) hr = pPrincipal->put_Id(_bstr_t(L"Principal1")); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put the principal ID: %x", hr); + Logger::error(L"Cannot put the principal ID: {}", hr); } hr = pPrincipal->put_UserId(_bstr_t(username_domain)); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put principal user Id: %x", hr); + Logger::error(L"Cannot put principal user Id: {}", hr); } hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put principal logon type: %x", hr); + Logger::error(L"Cannot put principal logon type: {}", hr); } // Run the task with the highest available privileges. @@ -419,7 +399,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) ExitOnFailure(hr, "Error saving the Task : %x", hr); } - WcaLog(LOGMSG_STANDARD, "Scheduled task created for the current user."); + Logger::info(L"Scheduled task created for the current user."); LExit: ReleaseStr(wszExecutablePath); @@ -480,7 +460,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA"); ExitOnFailure(hr, "Failed to initialize"); - WcaLog(LOGMSG_STANDARD, "Initialized."); + Logger::info(L"RemoveScheduledTasksCA Initialized."); // COM and Security Initialization is expected to have been done by the MSI. // It couldn't be done in the DLL, anyway. @@ -503,7 +483,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) if (FAILED(hr)) { // Folder doesn't exist. No need to delete anything. - WcaLog(LOGMSG_STANDARD, "The PowerToys scheduled task folder wasn't found. Nothing to delete."); + Logger::info(L"The PowerToys scheduled task folder wasn't found. Nothing to delete."); hr = S_OK; ExitFunction(); } @@ -529,19 +509,19 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) hr = pTaskFolder->DeleteTask(taskName, 0); if (FAILED(hr)) { - WcaLogError(hr, "Cannot delete the '%S' task: %x", taskName, hr); + Logger::error(L"Cannot delete the {} task: {}", taskName, hr); } SysFreeString(taskName); } else { - WcaLogError(hr, "Cannot get the registered task name: %x", hr); + Logger::error(L"Cannot get the registered task name: {}", hr); } pRegisteredTask->Release(); } else { - WcaLogError(hr, "Cannot get the registered task item at index=%d: %x", i + 1, hr); + Logger::error(L"Cannot get the registered task item at index={}: {}", i + 1, hr); } } @@ -553,7 +533,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) pRootFolder->Release(); ExitOnFailure(hr, "Cannot delete the PowerToys folder: %x", hr); - WcaLog(LOGMSG_STANDARD, "Deleted the PowerToys Task Scheduler folder."); + Logger::info(L"Deleted the PowerToys Task Scheduler folder."); LExit: if (pService) @@ -1400,6 +1380,20 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall) return WcaFinalize(er); } +void initSystemLogger() +{ + static std::once_flag initLoggerFlag; + std::call_once(initLoggerFlag, []() { + WCHAR temp_path[MAX_PATH]; + auto ret = GetTempPath(MAX_PATH, temp_path); + + if (ret) + { + Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L""); + } + }); +} + // DllMain - Initialize and cleanup WiX custom action utils. extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID) { @@ -1407,6 +1401,7 @@ extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in L { case DLL_PROCESS_ATTACH: WcaGlobalInitialize(hInst); + initSystemLogger(); TraceLoggingRegister(g_hProvider); DLL_HANDLE = hInst; break; diff --git a/tools/BugReportTool/BugReportTool/Main.cpp b/tools/BugReportTool/BugReportTool/Main.cpp index 5cc8134891..e0ac22995b 100644 --- a/tools/BugReportTool/BugReportTool/Main.cpp +++ b/tools/BugReportTool/BugReportTool/Main.cpp @@ -256,7 +256,8 @@ void ReportVCMLogs(const filesystem::path& tmpDir, const filesystem::path& repor void ReportInstallerLogs(const filesystem::path& tmpDir, const filesystem::path& reportDir) { - const char* logFilePrefix = "powertoys-bootstrapper-msi-"; + const char* bootstrapperLogFilePrefix = "powertoys-bootstrapper-msi-"; + const char* PTLogFilePrefix = "PowerToysMSIInstaller_"; for (auto& entry : directory_iterator{ tmpDir }) { @@ -267,7 +268,7 @@ void ReportInstallerLogs(const filesystem::path& tmpDir, const filesystem::path& } const auto fileName = entry.path().filename().string(); - if (!fileName.starts_with(logFilePrefix)) + if (!fileName.starts_with(bootstrapperLogFilePrefix) && !fileName.starts_with(PTLogFilePrefix)) { continue; } From 0b281677df524540f3bb6329c728dfb7f067d9f9 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:59:30 +0100 Subject: [PATCH 059/163] [Dev files Preview] Handle access denied error and properly show error messages (#23970) --- .../MonacoPreviewHandlerControl.cs | 46 +++++++------------ .../Properties/Resources.Designer.cs | 13 +++++- .../Properties/Resources.resx | 3 ++ 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs index 72584840c2..6c8d832b2f 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs @@ -137,11 +137,10 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco if (fileSize < _settings.MaxFileSize) { - Task initializeIndexFileAndSelectedFileTask = new Task(() => { InitializeIndexFileAndSelectedFile(filePath); }); - initializeIndexFileAndSelectedFileTask.Start(); - try { + InitializeIndexFileAndSelectedFile(filePath); + Logger.LogInfo("Create WebView2 environment"); ConfiguredTaskAwaitable.ConfiguredTaskAwaiter webView2EnvironmentAwaiter = CoreWebView2Environment @@ -169,9 +168,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco { await _webView.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); - // Wait until html is loaded - initializeIndexFileAndSelectedFileTask.Wait(); - _webView.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, Settings.AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow); Logger.LogInfo("Navigates to string of HTML file"); @@ -217,21 +213,19 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco } }); } + catch (UnauthorizedAccessException e) + { + Logger.LogError(e.Message); + AddTextBoxControl(Resources.Access_Denied_Exception_Message); + } catch (Exception e) { - Controls.Remove(_loading); - Controls.Remove(_loadingBar); - Controls.Remove(_loadingBackground); - Label text = new Label(); - text.ForeColor = Settings.TextColor; - text.Text = Resources.Exception_Occurred; - text.Text += e.Message; - text.Text += "\n" + e.Source; - text.Text += "\n" + e.StackTrace; - text.Width = 500; - text.Height = 10000; - Controls.Add(text); Logger.LogError(e.Message); + string errorMessage = Resources.Exception_Occurred; + errorMessage += e.Message; + errorMessage += "\n" + e.Source; + errorMessage += "\n" + e.StackTrace; + AddTextBoxControl(errorMessage); } this.Resize += FormResize; @@ -239,17 +233,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco else { Logger.LogInfo("File is too big to display. Showing error message"); - - Controls.Remove(_loading); - _loadingBar.Dispose(); - Controls.Remove(_loadingBar); - Controls.Remove(_loadingBackground); - Label errorMessage = new Label(); - errorMessage.Text = Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture); - errorMessage.ForeColor = Settings.TextColor; - errorMessage.Width = 500; - errorMessage.Height = 50; - Controls.Add(errorMessage); + AddTextBoxControl(Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture)); } } @@ -460,6 +444,10 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco /// Message to be displayed in textbox. private void AddTextBoxControl(string message) { + Controls.Remove(_loading); + Controls.Remove(_loadingBar); + Controls.Remove(_loadingBackground); + _textBox = new RichTextBox(); _textBox.Text = message; _textBox.BackColor = Color.LightYellow; diff --git a/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.Designer.cs b/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.Designer.cs index cb056ae013..f318d5e89d 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.Designer.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.Designer.cs @@ -111,9 +111,20 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco.Properties { /// Looks up a localized string for an error when Gpo has the utility disabled. /// internal static string GpoDisabledErrorText { - get { + get + { return ResourceManager.GetString("GpoDisabledErrorText", resourceCulture); } } + + /// + /// Looks up a localized string for an error when access to file is denied. + /// + internal static string Access_Denied_Exception_Message { + get + { + return ResourceManager.GetString("Access_Denied_Exception_Message", resourceCulture); + } + } } } diff --git a/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx b/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx index d0d97c0d42..25489d57a1 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx +++ b/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx @@ -140,4 +140,7 @@ Max file size: %1KB Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator. GPO stands for the Windows Group Policy Object feature. + + Access denied: You do not have permission to open this file. See the owner of the file or an administrator to obtain permission. + \ No newline at end of file From 320cc56b7e9143b6de57e11015369a67c2835ccb Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Wed, 22 Feb 2023 16:23:49 +0100 Subject: [PATCH 060/163] [Shotcut Guide]Dismiss with mouse click (#23991) * dismiss with mouse * fix spellcheck --- .github/actions/spell-check/expect.txt | 1 + .../ShortcutGuide/shortcut_guide.cpp | 37 +++++++++++++++++++ .../ShortcutGuide/shortcut_guide.h | 5 ++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index e854c87522..9eeb1fa1ca 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -176,6 +176,7 @@ buildtask buildtransitive Burkina Buryatia +BUTTONUP BValue BYPOSITION bytearray diff --git a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp index 2843017ac6..f1c843d615 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp @@ -160,6 +160,30 @@ namespace return CallNextHookEx(NULL, nCode, wParam, lParam); } + LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) + { + if (nCode >= 0) + { + switch (wParam) + { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: + // Don't close with mouse click if activation is windows key and the key is pressed + if (!overlay_window_instance->win_key_activation() || !isWinPressed()) + { + overlay_window_instance->CloseWindow(HideWindowType::MOUSE_BUTTONUP); + } + break; + default: + break; + } + } + + return CallNextHookEx(0, nCode, wParam, lParam); + } + std::wstring ToWstring(HideWindowType type) { switch (type) @@ -172,6 +196,8 @@ namespace return L"WIN_SHORTCUT_PRESSED"; case HideWindowType::THE_SHORTCUT_PRESSED: return L"THE_SHORTCUT_PRESSED"; + case HideWindowType::MOUSE_BUTTONUP: + return L"MOUSE_BUTTONUP"; } return L""; @@ -191,6 +217,12 @@ OverlayWindow::OverlayWindow(HWND activeWindow) { Logger::warn(L"Failed to create low level keyboard hook. {}", get_last_error_or_default(GetLastError())); } + + mouseHook = SetWindowsHookEx(WH_MOUSE_LL, LowLevelMouseProc, GetModuleHandle(NULL), NULL); + if (!mouseHook) + { + Logger::warn(L"Failed to create low level mouse hook. {}", get_last_error_or_default(GetLastError())); + } } void OverlayWindow::ShowWindow() @@ -316,6 +348,11 @@ bool OverlayWindow::overlay_visible() const return target_state->active(); } +bool OverlayWindow::win_key_activation() const +{ + return shouldReactToPressedWinKey.value; +} + void OverlayWindow::init_settings() { auto settings = GetSettings(); diff --git a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.h b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.h index 418ddd1862..57f9bf3267 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.h +++ b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.h @@ -18,7 +18,8 @@ enum class HideWindowType ESC_PRESSED, WIN_RELEASED, WIN_SHORTCUT_PRESSED, - THE_SHORTCUT_PRESSED + THE_SHORTCUT_PRESSED, + MOUSE_BUTTONUP }; class OverlayWindow @@ -34,6 +35,7 @@ public: void was_hidden(); bool overlay_visible() const; + bool win_key_activation() const; bool is_disabled_app(wchar_t* exePath); @@ -52,6 +54,7 @@ private: void update_disabled_apps(); HWND activeWindow; HHOOK keyboardHook; + HHOOK mouseHook; struct OverlayOpacity { From 12ce7e767476b9168ec12d71c70584b9afa22bef Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:40:02 +0100 Subject: [PATCH 061/163] [PT Run] System plugin: Setting for separate "Empty Recycle Bin" result (#24057) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Split results and setting * smalöl fixes * fixes --- .../Components/Commands.cs | 48 +++++++++++++++---- .../Components/ResultHelper.cs | 1 - .../Main.cs | 14 +++++- .../Properties/Resources.Designer.cs | 38 ++++++++++++++- .../Properties/Resources.resx | 15 ++++++ 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs index 7e337c2bf5..9278e4941a 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs @@ -5,14 +5,10 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Runtime; -using System.Windows; -using System.Windows.Interop; using Microsoft.PowerToys.Run.Plugin.System.Properties; using Wox.Infrastructure; using Wox.Plugin; using Wox.Plugin.Common.Win32; -using Wox.Plugin.Logger; namespace Microsoft.PowerToys.Run.Plugin.System.Components { @@ -37,11 +33,13 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components /// Returns a list with all system command results /// /// Value indicating if the system is booted in uefi mode + /// Value indicating if we should show two results for Recycle Bin. + /// A value indicating if the user should confirm the system commands + /// Show a success message after empty Recycle Bin. /// The current theme to use for the icons /// The culture to use for the result's title and sub title - /// A value indicating if the user should confirm the system commands /// A list of all results - internal static List GetSystemCommands(bool isUefi, string iconTheme, CultureInfo culture, bool confirmCommands) + internal static List GetSystemCommands(bool isUefi, bool splitRecycleBinResults, bool confirmCommands, bool emptyRBSuccessMessage, string iconTheme, CultureInfo culture) { var results = new List(); results.AddRange(new[] @@ -106,7 +104,39 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components return ResultHelper.ExecuteCommand(confirmCommands, Resources.Microsoft_plugin_sys_hibernate_confirmation, () => NativeMethods.SetSuspendState(true, true, true)); }, }, - new Result + }); + + // Show Recycle Bin results based on setting. + if (splitRecycleBinResults) + { + results.AddRange(new[] + { + new Result + { + Title = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinOpen", culture), + SubTitle = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_description", culture), + IcoPath = $"Images\\recyclebin.{iconTheme}.png", + Action = c => + { + return Helper.OpenInShell("explorer.exe", "shell:RecycleBinFolder"); + }, + }, + new Result + { + Title = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinEmptyResult", culture), + SubTitle = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinEmpty_description", culture), + IcoPath = $"Images\\recyclebin.{iconTheme}.png", + Action = c => + { + ResultHelper.EmptyRecycleBinAsync(emptyRBSuccessMessage); + return true; + }, + }, + }); + } + else + { + results.Add(new Result { Title = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin", culture), SubTitle = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_description", culture), @@ -116,8 +146,8 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components { return Helper.OpenInShell("explorer.exe", "shell:RecycleBinFolder"); }, - }, - }); + }); + } // UEFI command/result. It is only available on systems booted in UEFI mode. if (isUefi) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs index b769e61796..047cc94c3c 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; -using System.Windows.Navigation; using Microsoft.PowerToys.Run.Plugin.System.Properties; using Wox.Plugin; using Wox.Plugin.Common.Win32; diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs index e6efc47c0a..0fdfe8e0d0 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs @@ -26,6 +26,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System private bool _showSuccessOnEmptyRB; private bool _localizeSystemCommands; private bool _reduceNetworkResultScore; + private bool _separateEmptyRB; public string Name => Resources.Microsoft_plugin_sys_plugin_name; @@ -56,6 +57,12 @@ namespace Microsoft.PowerToys.Run.Plugin.System Value = true, }, new PluginAdditionalOption() + { + Key = "SeparateResultEmptyRB", + DisplayLabel = Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate, + Value = false, + }, + new PluginAdditionalOption() { Key = "ReduceNetworkResultScore", DisplayLabel = Resources.Reduce_Network_Result_Score, @@ -90,7 +97,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System } // normal system commands are fast and can be returned immediately - var systemCommands = Commands.GetSystemCommands(IsBootedInUefiMode, IconTheme, culture, _confirmSystemCommands); + var systemCommands = Commands.GetSystemCommands(IsBootedInUefiMode, _separateEmptyRB, _confirmSystemCommands, _showSuccessOnEmptyRB, IconTheme, culture); foreach (var c in systemCommands) { var resultMatch = StringMatcher.FuzzySearch(query.Search, c.Title); @@ -209,6 +216,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System var showSuccessOnEmptyRB = false; var localizeSystemCommands = true; var reduceNetworkResultScore = true; + var separateEmptyRB = false; if (settings != null && settings.AdditionalOptions != null) { @@ -223,12 +231,16 @@ namespace Microsoft.PowerToys.Run.Plugin.System var optionNetworkScore = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "ReduceNetworkResultScore"); reduceNetworkResultScore = optionNetworkScore?.Value ?? reduceNetworkResultScore; + + var optionSeparateEmptyRB = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "SeparateResultEmptyRB"); + separateEmptyRB = optionSeparateEmptyRB?.Value ?? separateEmptyRB; } _confirmSystemCommands = confirmSystemCommands; _showSuccessOnEmptyRB = showSuccessOnEmptyRB; _localizeSystemCommands = localizeSystemCommands; _reduceNetworkResultScore = reduceNetworkResultScore; + _separateEmptyRB = separateEmptyRB; } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.Designer.cs index fe14b50773..21edf832d4 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.Designer.cs @@ -457,7 +457,16 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Properties { } /// - /// Looks up a localized string similar to Show a success message after empty the Recycle Bin. + /// Looks up a localized string similar to Show separate result for Empty Recycle Bin command. + /// + internal static string Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show a success message after emptying the Recycle Bin. /// internal static string Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage { get { @@ -465,6 +474,33 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Properties { } } + /// + /// Looks up a localized string similar to Empty Recycle Bin. + /// + internal static string Microsoft_plugin_sys_RecycleBinEmpty_description { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinEmpty_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty Recycle Bin. + /// + internal static string Microsoft_plugin_sys_RecycleBinEmptyResult { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinEmptyResult", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open Recycle Bin. + /// + internal static string Microsoft_plugin_sys_RecycleBinOpen { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinOpen", resourceCulture); + } + } + /// /// Looks up a localized string similar to Restart. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx index 5d6c53aae8..c762257889 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx @@ -244,6 +244,18 @@ Recycle Bin Means the recycle bin folder in Explorer. + + Empty Recycle Bin + This should align to the action in Windows of emptying the recycle bin on your computer. + + + Empty Recycle Bin + This should align to the action in Windows of emptying the recycle bin on your computer. + + + Open Recycle Bin + Means the recycle bin folder in Explorer. + Empty Recycle Bin (Shift+Delete) This should align to the action in Windows of emptying the recycle bin on your computer. @@ -271,6 +283,9 @@ Empty Recycle Bin This should align to the action in Windows of emptying the recycle bin on your computer. + + Show separate result for Empty Recycle Bin command + Show a success message after emptying the Recycle Bin Means the recycle bin folder in Explorer and "emptying" refers to "Empty Recycle Bin" command. From a2e05b771b5f7611980fef8edca0cf5f01600739 Mon Sep 17 00:00:00 2001 From: ChaseKnowlden Date: Wed, 22 Feb 2023 11:00:48 -0500 Subject: [PATCH 062/163] [Installer]Update logo (#24059) --- installer/PowerToysSetup/Images/logo.png | Bin 311 -> 6246 bytes installer/PowerToysSetup/Images/logo150.png | Bin 1440 -> 3852 bytes installer/PowerToysSetup/Images/logo44.png | Bin 1321 -> 1041 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/installer/PowerToysSetup/Images/logo.png b/installer/PowerToysSetup/Images/logo.png index d130d131bdc02511fb31dfa7ed769e5682a57972..c9f940540507c8abae6f9aba5a388e192cd497dd 100644 GIT binary patch literal 6246 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 D=Ne2f literal 311 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 O0000} zMzA8{f+$l3BZUYks{|Cmv;~nRY++x+P6`2mG=1pwu`}oY&eJ*b&p&fcIvIA~qkTvl zg+lECJg^i^&-zC@w`;h2Af*+B(pn{hG@M$kR;?=AnhHDr5O+w6dp=gLudQ@QMcu8X zvKDcFXYHCwximjjQ+BVRDqm9hsJXsKy|wY7sjx%(yr%4~wXLHY2JhmE@uX1)+DjX% z^T}i~2tq7>zme`L0zxJr2!V_MBr*UZ0Fgos7>OvKe$YlqPjrlZDYBSznk+FMS9V<$o|V+{O4iIGxzSlOhfYC6>2bedd7 z1Ycm`QXI9+{&+zi>d~Us<4IJ3(uR|wwzOR}IJshIymHKV)y90Ci&+aps{*j<>SOAj z1L`hab=TKZU1pSe0a(Mo=Kr&zFuMNN|w9mnSWl9l#D``q0n0qOsEh zlF^>(se!tZ!rM5!$HLg#*^!1+VJtu(x_c0oC*LUsCAoLf-Q5BC$5(xA4p8^w?$MHss{{+2)l<9QLW(HvfMcpQ%# z78*#SQu%R_39%6n41*Aq7#|tW<3bQbAb{=CV%eYK`pP^2ATs@YhGiAKAFJe@weK5> zJUywb0Kcfn^W*ZjTbmo^_1dR2C%^`=%ld9<%uAP&s8_39qGarn55+)Z=vN{D-8TDqg_;q{*`VCZv6? zdhPK2I-`}n531u>9T=Swv)569w_1*M$`@W}R-20d2jMvw(a*5yZ_N24{GougyOTnv zG|f<|PUGjXmJ*{amn=|OQEaj6RNchG_4gYpqnnonx%nJ7w;^+}p^LNXh<8kiy+ih| zb2$|5RZv&!rr|S3Bk5QA7M)i-1=qhUAM8JqbQ>%mN(=13y3obD^)bKy=c@2hi^0iD zCvwU*&a>_ajMu>SG7S=@>V1NCtPbre+>H3^bS}6w$RmqWeZhDK3rWh@bG|6G zdAu?`ICjbIY3i;q#~7%cJaDE{4<<4&%FY#40=mt=<93uN+jUhEJNjv&)W)$6Pn~CvQ@_dor zilQ$Q*nY2d)9al=obTiO3kk65g1Dm*)<)62w_^bH-h#-J{pT?3M)ujIz+sd5_mxv$W?@?L0mUzj%F7s6q?VA0&p<4$SolCZ_monivR!s diff --git a/installer/PowerToysSetup/Images/logo44.png b/installer/PowerToysSetup/Images/logo44.png index 78b8ecf614d112ddf31165b92e8ff8b90fa708f6..b931931dff6c8a3bc077f33ebef5722a8a0adbf1 100644 GIT binary patch delta 1030 zcmV+h1o``^3Xuqq8Gi-<0016@S^)q61K3GKK~#7F)R_gS>_!-cPxfZ-b9bELR>vt; z+^vpV-QC^Y9k(J?+^JwK?o_e*bAQ?VrwlXfPYx4#7PdcxpA5<5+jGyEOt>XU{GX}> zfP;U0e+7WI0c-&P{Sooh$NZFXjgK{_pq}gPpxngf#JdUHxPRlEOFk(PD#0q)5O@I< zkQ^90KxNEt-2w(iL|`%~d#Yfk0oo0(&)AYI(VY42$3Mm*)f4wz1FDBr(M2gy*@4@< z8;-GSGMVIvV^d3z4qMlCI8ap;#;BqwP?jaav9z=#>xt`GW&=cGHk;w_;lp@r^H10{ zITF=2#?IKcy?^>!yu|?R*Oj*btULL z4dCO)k7H+X7&L-52lZY9AhOW38DJ4GbkIQ7fElc2vK&D3esIl>@K;qAYv{z>?%$-U zA#?{u>J&)nMKy;dn<|?TGy!HBbMcf*L#M%$Nm4%GS{Vu@_X={n~_Cq=WY zfBO_>F^ho72C2ufq_DZds_xLxWq~vP1< znY9FszJF?{!bL;j7&^CvcU9QoTP-1>BfAt9bsg9h&3U+*t18xT;6VygL+8bTz(@}& z)WaEtb1x5H)`lvqyEXqMTKT|P&pMnuoB+idxn(T@b8zSeT0((46o$Da#2T%kvy%&* zgZBetkiuyxQ)`@j=$uVLce0k~d()YLvQoIhgMUNUp>SqXjoLs5_8-u%aBkxx9(L&d z&~?|nAfwctr?;5TkqgSs{t4YWA< z`gN;;k*+1%Ht>0s!=zCG5==@M4Zv8}GJ%W24z3f|X|nhL`oyDL>vmvf1K&GwQiJ1N zOQ#<8O}Bf`L9AcDDo&o1*t`GGh}RM%+<(I=yVe^Ye}&c>;aZ1fK6Hy$ouOO2B3pKA zi;Jw5kQ6?6+vWPT>P2>+YZhdz``k}BAauJ4Y|)##zyKYSk%3^t|F?(TfLr25fNz5l znhGlLrv=&%2I>H~o5YQC;QuD4>AVt10rB7m03Oii_@% delta 1313 zcmV++1>X9R2&oE?8Gix*005EZ%~k*a1n@~jK~#7F?U)0wODO^`_E?1m{K`EQ%#GIL9g^;614)57>fPdq-u%b|Lyu(gjwZ3r_ zg<{IsK6VbQ!5i=YTq&GgM zlU9_D!4TVTeSgr@-0t)81(Sgrpr*ka-qvGdo}P|lI4(SCN^}Ir2oeL{m_KEozHdj1 z@er^W0)~e!>VP;lh)E?)qEaK3NyOOLV-7k^34!rv^EcB0a7=z92J;qeJvBHA&;tv1 z{24>{*O9e_LJ|lNArfPhNLZ`}LgruyL&XpQC@EV*ZGU0}1(^JmyfdXihb)~5BBo;i zgvB6W#j{Rgoy0ncbrS0&{x1nio)#n^%Kt(j$bP5q*xI!P<$uv~N)W&naSTzEUqu<$ zxe0Is8wcR(Od>7`O+F(`eiQ0Q*1=!T;Cf);5PBN{Q9%iW%_DXAzPl#|Ib(HaNz?5F z(3lzHCVwofux1$p>mQEK9?Kqp3?*Ezx-k}>3B-*S`2;O}DFtH)8G3zB>6R8E11a*E z51=tWEJG(;B0wxA;Rn%iN9_XyL<9hiBl`>(+0hX&#u5XqH{E1|U3c0_sl>zn+2IAf z`;T-T36LD3jXvf01A`#U2k=+-!9;|J1te^}D}OIO8G#HD5HuQ<@%Kz$u+U2-lrlSQ zzs2=ep62;U8NvC4$lU$m^II;Ko(sLanzV83l?+$f~Dk2 zKrB2H>oux(J@O8jmVLESO2=(+q!=)GbobsvA;TKfs->)-kPCLpZ7)J6>a_}EShyz~ zA&3Yt%cHK#!K_NY)&7Kb@~xR!@t*L!;D7wHj)H7CTu1))_kelxJJPjRN^iUMCj0NR za~!Kw$jTV)U4SHlA4INrM~+9-{r{(+jNv6rGGSC zazdLRh|+iv%0!WZcI;OBOv^Hct8-3BDS*f{uN{K5PbKF}N+Fd7NK(W;p&iGO_6Yz8 zNs}&5PPne@+ka%96XmjFyCnBu9B0p2*sJe|Fp5*5*>e}zO0Aq=_;1gl3m5t6$|g35 zSX823D?j(@SBXJJsqEx&B!w70YJcK@!DAqUab~-(l@pvj_~eJJstA=ziDB`asM_#Z zJjq(fCG<7gVZgE>j8?{=QYs8%&KRsH>^jEEmR43kthdp&Qi>gR^o}`vy5l;qqENBg zWA|NZ>u)2=k=T02gOZuAZ({G}F|pz#NUPe0+itSu-mRX9qR8_@E@(yGRvMOJn125c X&~t5VpVR1w00000NkvXXu0mjfT7zsG From c819b287f3ad6a20e3f5b7f024f422f494f308a7 Mon Sep 17 00:00:00 2001 From: Davide Giacometti Date: Wed, 22 Feb 2023 17:19:01 +0100 Subject: [PATCH 063/163] [Hosts]Big hosts file loading fix and feedback (#24091) --- src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs | 12 ++++++++++-- src/modules/Hosts/Hosts/Views/MainPage.xaml | 13 ++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs b/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs index 23bd229890..bb4754c0b5 100644 --- a/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs +++ b/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs @@ -52,6 +52,9 @@ namespace Hosts.ViewModels [ObservableProperty] private string _additionalLines; + [ObservableProperty] + private bool _isLoading; + private ObservableCollection _entries; public ObservableCollection Entries => _filtered || _showOnlyDuplicates ? GetFilteredEntries() : _entries; @@ -125,6 +128,7 @@ namespace Hosts.ViewModels public void ReadHosts() { FileChanged = false; + IsLoading = true; Task.Run(async () => { @@ -141,8 +145,10 @@ namespace Hosts.ViewModels _entries.CollectionChanged += Entries_CollectionChanged; OnPropertyChanged(nameof(Entries)); - FindDuplicates(); + IsLoading = false; }); + + FindDuplicates(); }); } @@ -243,10 +249,12 @@ namespace Hosts.ViewModels { var hosts = entry.SplittedHosts; - entry.Duplicate = _entries.FirstOrDefault(e => + var duplicate = _entries.FirstOrDefault(e => e != entry && (string.Equals(e.Address, entry.Address, StringComparison.InvariantCultureIgnoreCase) || hosts.Intersect(e.SplittedHosts, StringComparer.InvariantCultureIgnoreCase).Any())) != null; + + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => entry.Duplicate = duplicate); } private ObservableCollection GetFilteredEntries() diff --git a/src/modules/Hosts/Hosts/Views/MainPage.xaml b/src/modules/Hosts/Hosts/Views/MainPage.xaml index 85aaabaa68..566af4feb5 100644 --- a/src/modules/Hosts/Hosts/Views/MainPage.xaml +++ b/src/modules/Hosts/Hosts/Views/MainPage.xaml @@ -26,6 +26,10 @@ NotEmptyValue="Visible" /> + @@ -194,7 +198,8 @@ IsItemClickEnabled="True" ItemClick="Entries_ItemClick" ItemsSource="{Binding Entries, Mode=TwoWay}" - SelectedItem="{Binding Selected, Mode=TwoWay}"> + SelectedItem="{Binding Selected, Mode=TwoWay}" + Visibility="{x:Bind ViewModel.IsLoading, Converter={StaticResource BoolToInvertedVisibilityConverter}, Mode=OneWay}"> + + Date: Wed, 22 Feb 2023 17:25:48 +0100 Subject: [PATCH 064/163] [PTRun][Calculator]Add support for log2 and log10 (#24137) * Add support for log2 and log10 in run * Remove test case as it did not make much sense --- .../ExtendedCalculatorParserTests.cs | 6 ++++++ .../CalculateHelper.cs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs index 82a5b83a61..929fa8489a 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs @@ -74,6 +74,8 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests new object[] { "e*2", 5.43656365691809M }, new object[] { "ln(3)", 1.09861228866810M }, new object[] { "log(3)", 0.47712125471966M }, + new object[] { "log2(3)", 1.58496250072116M }, + new object[] { "log10(3)", 0.47712125471966M }, new object[] { "ln(e)", 1M }, new object[] { "cosh(0)", 1M }, }; @@ -166,6 +168,10 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests [DataTestMethod] [DataRow("log(3)", true)] [DataRow("ln(3)", true)] + [DataRow("log2(3)", true)] + [DataRow("log10(3)", true)] + [DataRow("log2", false)] + [DataRow("log10", false)] [DataRow("log", false)] [DataRow("ln", false)] [DataRow("ceil(2 * (pi ^ 2))", true)] diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs index ca00f679d3..d0908b3de0 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs @@ -12,7 +12,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator private static readonly Regex RegValidExpressChar = new Regex( @"^(" + @"%|" + - @"ceil\s*\(|floor\s*\(|exp\s*\(|max\s*\(|min\s*\(|abs\s*\(|log\s*\(|ln\s*\(|sqrt\s*\(|pow\s*\(|" + + @"ceil\s*\(|floor\s*\(|exp\s*\(|max\s*\(|min\s*\(|abs\s*\(|log(?:2|10)?\s*\(|ln\s*\(|sqrt\s*\(|pow\s*\(|" + @"factorial\s*\(|sign\s*\(|round\s*\(|rand\s*\(|" + @"sin\s*\(|cos\s*\(|tan\s*\(|arcsin\s*\(|arccos\s*\(|arctan\s*\(|" + @"sinh\s*\(|cosh\s*\(|tanh\s*\(|arsinh\s*\(|arcosh\s*\(|artanh\s*\(|" + From 429e19615c149e40076fe77a3b7768acefcb7b51 Mon Sep 17 00:00:00 2001 From: Alvin Meng <35266431+al2me6@users.noreply.github.com> Date: Wed, 22 Feb 2023 17:25:43 -0500 Subject: [PATCH 065/163] [Settings][PTRun]Show icons of user-installed plugins (#24194) --- src/modules/launcher/PowerLauncher/SettingsReader.cs | 3 +-- .../Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs | 7 +------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/modules/launcher/PowerLauncher/SettingsReader.cs b/src/modules/launcher/PowerLauncher/SettingsReader.cs index 8764510486..c3d3f3bbb9 100644 --- a/src/modules/launcher/PowerLauncher/SettingsReader.cs +++ b/src/modules/launcher/PowerLauncher/SettingsReader.cs @@ -215,8 +215,7 @@ namespace PowerLauncher private static string GetIcon(PluginMetadata metadata, string iconPath) { - var pluginDirectory = Path.GetFileName(metadata.PluginDirectory); - return Path.Combine(pluginDirectory, iconPath); + return Path.Combine(metadata.PluginDirectory, iconPath); } private static IEnumerable GetDefaultPluginsSettings() diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs index 5fca11a873..6988c02034 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs @@ -160,12 +160,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels public string IconPath { - get - { - var path = isDark() ? settings.IconPathDark : settings.IconPathLight; - path = Path.Combine(Directory.GetCurrentDirectory(), @"modules\launcher\Plugins", path); - return path; - } + get => isDark() ? settings.IconPathDark : settings.IconPathLight; } public event PropertyChangedEventHandler PropertyChanged; From 995fd2ed50a160ebf3b0befd2cec655954d984b7 Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Wed, 22 Feb 2023 23:29:13 +0100 Subject: [PATCH 066/163] [GPO;Enterprise]Experimentation: Small fixes (#24238) * docs fix * UI changes --- doc/gpo/README.md | 6 +++--- src/settings-ui/Settings.UI/Strings/en-us/Resources.resw | 2 +- src/settings-ui/Settings.UI/Views/GeneralPage.xaml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/gpo/README.md b/doc/gpo/README.md index 929708265f..dc4f6a4b6f 100644 --- a/doc/gpo/README.md +++ b/doc/gpo/README.md @@ -34,8 +34,8 @@ If you don't configure this setting, users are able to disable or enable the uti ### Allow experimentation -This policy configures whether PowerToys experimentation is allowed. +This policy configures whether PowerToys experimentation is allowed. With experimentation allowed the user sees the new features being experimented if it gets selected as part of the test group. (Experimentation will only happen on Windows Insider builds.) -Disable this setting to disable the experimentation features in PowerToys, meaning the user won't see the new features being experimented even if it gets selected as part of the test group. +If this setting is not configured or enabled, the user can control experimentation in the PowerToys settings menu. -If this setting is not configured, experimentation is allowed. +If this setting is disabled, experimentation is not allowed. diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 08e32c0970..a10f244409 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -2887,7 +2887,7 @@ Activate by holding the key for the character you want to add an accent to, then File Locksmith is the name of the utility - The systems administrator is forcing this setting. + The system administrator is forcing this setting. Additional content includes the file header and lines that can't parse diff --git a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml index 1918c0ec0e..e2b4bd33df 100644 --- a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml +++ b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml @@ -381,10 +381,10 @@ x:Uid="General_Experimentation" Visibility="Visible"> + HeaderIcon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsExperimentation.png}" + IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsExperimentationGpoDisallowed, Converter={StaticResource BoolNegationConverter}}"> Date: Wed, 22 Feb 2023 14:55:25 -0800 Subject: [PATCH 067/163] Adjustments (#24254) --- .github/actions/spell-check/expect.txt | 15 +- .../modules/launcher/plugins/timeZone.md | 14 +- .../FancyZonesData/CustomLayouts.cpp | 4 +- .../FancyZonesData/CustomLayouts.h | 4 +- .../UnitTests/CustomLayoutsTests.Spec.cpp | 8 +- .../FancyZonesTests/UnitTests/Layout.Spec.cpp | 4 +- .../Properties/Resources.Designer.cs | 125 +- .../Properties/Resources.resx | 46 +- .../timezones.json | 2743 ++++++++--------- .../Exception/ExceptionFormatter.cs | 10 +- .../poweraccent/PowerAccent.Core/Languages.cs | 2 +- 11 files changed, 1458 insertions(+), 1517 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 9eeb1fa1ca..c2e7270be1 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -87,7 +87,6 @@ ARPPRODUCTICON ARRAYSIZE arsinh artanh -Artsakh asdf AShortcut ASingle @@ -180,6 +179,7 @@ BUTTONUP BValue BYPOSITION bytearray +Cabo Caiguna CALG callbackptr @@ -401,9 +401,9 @@ deu devblogs devdocs devenum +devmgmt DEVMON devpkey -devmgmt DEVSOURCE DIIRFLAG dimm @@ -652,8 +652,8 @@ HCRYPTHASH HCRYPTPROV hcwhite hdc -hdwwiz hdrop +hdwwiz HEB Heiko Helpline @@ -826,6 +826,7 @@ Ittoqqortoormiit IUI IUnknown ivirtualdesktopmanager +Ivoire IWbem IWIC iwr @@ -1385,8 +1386,8 @@ prevpane prgms pri Primorsky -printmanagement PRINTCLIENT +printmanagement prm proactively PROCESSKEY @@ -1691,8 +1692,8 @@ spam spdisp spdlog spdo -spec'ing specialfolder +spec'ing spesi splitwstring spsi @@ -2132,13 +2133,14 @@ WVC Wwan Wwanpp XAttribute +XAxis Xbox XBUTTON XBUTTONDBLCLK XBUTTONDOWN XBUTTONUP -xcopy XControl +xcopy XDocument XElement XFile @@ -2152,6 +2154,7 @@ xsi XStr XVIRTUALSCREEN Yamalia +YAxis YIncrement yinle yinwang diff --git a/doc/devdocs/modules/launcher/plugins/timeZone.md b/doc/devdocs/modules/launcher/plugins/timeZone.md index 49710cb947..d8798e8f33 100644 --- a/doc/devdocs/modules/launcher/plugins/timeZone.md +++ b/doc/devdocs/modules/launcher/plugins/timeZone.md @@ -32,7 +32,7 @@ A minimum entry for the `TimeZone.json` looks like: ```json { "Offset": "11:55", - "Name": "My crazy time zone", + "Name": "My unique time zone", } ``` @@ -41,26 +41,26 @@ A full entry for the `TimeZone.json` looks like: ```json { "Offset": "11:55", - "Name": "My crazy time zone", + "Name": "My unique time zone", "Shortcut" : "MYTZ", "MilitaryName" : "Order Time Zone", "TimeNamesStandard": [ - "My crazy standard time" + "My unique standard time" ], "ShortcutsStandard": [ "MCST" ], "TimeNamesDaylight": [ - "My crazy daylight time" + "My unique daylight time" ], "ShortcutsDaylight": [ "MCDT" ], "CountriesStandard": [ - "Crazy Land East" + "unique Land East" ], "CountriesDaylight": [ - "Crazy Land West" + "Unique Land West" ] } ``` @@ -138,4 +138,4 @@ Because the JSON file must have a object as root type, instead of a array. #### Projects * `Wox.Infrastructure` -* `Wox.Plugin` +* `Wox.Plugin` \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp index 4040273cc7..1aef2b78e4 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp @@ -37,8 +37,8 @@ namespace JsonUtils for (uint32_t i = 0; i < size; ++i) { json::JsonObject zoneJson = zonesJson.GetObjectAt(i); - const int x = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XID)); - const int y = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YID)); + const int x = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XAxisID)); + const int y = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YAxisID)); const int width = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::WidthID)); const int height = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::HeightID)); FancyZonesDataTypes::CanvasLayoutInfo::Rect zone{ x, y, width, height }; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h index 298edf0dde..efa8d88744 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h @@ -30,8 +30,8 @@ namespace NonLocalizable const static wchar_t* RefHeightID = L"ref-height"; const static wchar_t* RefWidthID = L"ref-width"; const static wchar_t* ZonesID = L"zones"; - const static wchar_t* XID = L"X"; - const static wchar_t* YID = L"Y"; + const static wchar_t* XAxisID = L"X"; + const static wchar_t* YAxisID = L"Y"; const static wchar_t* WidthID = L"width"; const static wchar_t* HeightID = L"height"; diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp index f979cf7c69..36edcf3867 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp @@ -32,16 +32,16 @@ namespace FancyZonesUnitTests json::JsonArray zonesArray{}; { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(0)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(0)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(0)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(0)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(1140)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(1040)); zonesArray.Append(zone); } { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(1140)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(649)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(1140)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(649)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(780)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(391)); zonesArray.Append(zone); diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp index 11d0ec3761..4fbd9c163a 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp @@ -65,8 +65,8 @@ namespace FancyZonesUnitTests for (const auto& zoneRect : zones) { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(zoneRect.left)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(zoneRect.top)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(zoneRect.left)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(zoneRect.top)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(zoneRect.right - zoneRect.left)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(zoneRect.bottom - zoneRect.top)); zonesArray.Append(zone); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs index 78735dc479..2696c810a5 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -411,15 +411,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Artsakh. - /// - internal static string Artsakh { - get { - return ResourceManager.GetString("Artsakh", resourceCulture); - } - } - /// /// Looks up a localized string similar to Aruba. /// @@ -429,15 +420,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Ascension and Tristan da Cunha. - /// - internal static string Ascension_and_Tristan_da_Cunha { - get { - return ResourceManager.GetString("Ascension and Tristan da Cunha", resourceCulture); - } - } - /// /// Looks up a localized string similar to ASEAN Common Time. /// @@ -1032,6 +1014,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Cabo Verde. + /// + internal static string Cabo_Verde { + get { + return ResourceManager.GetString("Cabo Verde", resourceCulture); + } + } + /// /// Looks up a localized string similar to Caiguna (Australia). /// @@ -1077,15 +1068,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Cape Verde. - /// - internal static string Cape_Verde { - get { - return ResourceManager.GetString("Cape Verde", resourceCulture); - } - } - /// /// Looks up a localized string similar to Cape Verde Time. /// @@ -1779,15 +1761,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to East Timor. - /// - internal static string East_Timor { - get { - return ResourceManager.GetString("East Timor", resourceCulture); - } - } - /// /// Looks up a localized string similar to Easter Island (Chile). /// @@ -2500,11 +2473,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } /// - /// Looks up a localized string similar to Hong Kong. + /// Looks up a localized string similar to Hong Kong SAR. /// - internal static string Hong_Kong { + internal static string Hong_Kong_SAR { get { - return ResourceManager.GetString("Hong Kong", resourceCulture); + return ResourceManager.GetString("Hong Kong SAR", resourceCulture); } } @@ -2796,15 +2769,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Ivory Coast. - /// - internal static string Ivory_Coast { - get { - return ResourceManager.GetString("Ivory Coast", resourceCulture); - } - } - /// /// Looks up a localized string similar to Jamaica. /// @@ -3355,11 +3319,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } /// - /// Looks up a localized string similar to Macau. + /// Looks up a localized string similar to Macao SAR. /// - internal static string Macau { + internal static string Macao_SAR { get { - return ResourceManager.GetString("Macau", resourceCulture); + return ResourceManager.GetString("Macao SAR", resourceCulture); } } @@ -4263,15 +4227,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Northern Cyprus. - /// - internal static string Northern_Cyprus { - get { - return ResourceManager.GetString("Northern Cyprus", resourceCulture); - } - } - /// /// Looks up a localized string similar to Northern Mariana Islands. /// @@ -4552,11 +4507,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } /// - /// Looks up a localized string similar to Palestine. + /// Looks up a localized string similar to Palestine Authority. /// - internal static string Palestine { + internal static string Palestine_Authority { get { - return ResourceManager.GetString("Palestine", resourceCulture); + return ResourceManager.GetString("Palestine Authority", resourceCulture); } } @@ -4993,11 +4948,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } /// - /// Looks up a localized string similar to Saint Helena. + /// Looks up a localized string similar to Saint Helena, Ascension and Tristan da Cunha. /// - internal static string Saint_Helena { + internal static string Saint_Helena__Ascension_and_Tristan_da_Cunha { get { - return ResourceManager.GetString("Saint Helena", resourceCulture); + return ResourceManager.GetString("Saint Helena, Ascension and Tristan da Cunha", resourceCulture); } } @@ -5415,15 +5370,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Somaliland. - /// - internal static string Somaliland { - get { - return ResourceManager.GetString("Somaliland", resourceCulture); - } - } - /// /// Looks up a localized string similar to Sonora (Mexico). /// @@ -5667,15 +5613,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } - /// - /// Looks up a localized string similar to Sverdlovsk (Russia). - /// - internal static string Sverdlovsk__Russia_ { - get { - return ResourceManager.GetString("Sverdlovsk (Russia)", resourceCulture); - } - } - /// /// Looks up a localized string similar to Sweden. /// @@ -5865,6 +5802,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Timor-Leste. + /// + internal static string Timor_Leste { + get { + return ResourceManager.GetString("Timor-Leste", resourceCulture); + } + } + /// /// Looks up a localized string similar to Timor Leste Time. /// @@ -6576,6 +6522,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { } } + /// + /// Looks up a localized string similar to Yekaterinburg. + /// + internal static string Yekaterinburg { + get { + return ResourceManager.GetString("Yekaterinburg", resourceCulture); + } + } + /// /// Looks up a localized string similar to Yekaterinburg Time. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx index 61d12501d8..add45ef9ae 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx @@ -235,14 +235,11 @@ Armenia Time - - Artsakh - Aruba - - Ascension and Tristan da Cunha + + Saint Helena, Ascension and Tristan da Cunha ASEAN Common Time @@ -458,8 +455,8 @@ Canary Islands - - Cape Verde + + Cabo Verde Cape Verde Time @@ -695,8 +692,8 @@ East Nusa Tenggara (Indonesia) - - East Timor + + Timor-Leste Easter Island (Chile) @@ -935,8 +932,8 @@ Honduras - - Hong Kong + + Hong Kong SAR Hong Kong Time @@ -1036,8 +1033,8 @@ Ittoqqortoormiit (Greenland) - - Ivory Coast + + Côte d’Ivoire Jamaica @@ -1224,8 +1221,8 @@ Luxembourg - - Macau + + Macao SAR Macquarie Island Station Time @@ -1528,9 +1525,6 @@ North Maluku (Indonesia) - - Northern Cyprus - Northern Mariana Islands @@ -1626,8 +1620,8 @@ Palau Time - - Palestine + + Palestine Authority Palmyra Atoll @@ -1776,9 +1770,6 @@ Saint Barthélemy - - Saint Helena - Saint Kitts and Nevis @@ -1918,9 +1909,6 @@ Somalia - - Somaliland - Sonora (Mexico) @@ -2003,8 +1991,8 @@ Suriname Time - - Sverdlovsk (Russia) + + Yekaterinburg Sweden @@ -2346,4 +2334,4 @@ Équateur (Democratic Republic of the Congo) - + \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json index fcef0af599..0d23089452 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json @@ -1,1375 +1,1370 @@ { - "$schema": "./timeZones.schema.json", - "TimeZones": - [ - { - "Offset": "-12:00", - "Name": "International Date Line West time zone", - "Shortcut": "IDLW", - "MilitaryName": "Yankee Time Zone", - "TimeNamesStandard": [ - "Baker Island Time" - ], - "ShortcutsStandard": [ - "BIT" - ], - "CountriesStandard": [ - "Baker Island", - "Howland Island" - ] - }, - { - "Offset": "-11:00", - "Name": "Samoa Time Zone", - "MilitaryName": "X-ray Time Zone", - "TimeNamesStandard": [ - "Niue Time", - "Samoa Standard Time" - ], - "ShortcutsStandard": [ - "NUT", - "SST" - ], - "TimeNamesDaylight": [ - "Samoa Daylight Time" - ], - "ShortcutsDaylight": [ - "SDT" - ], - "CountriesStandard": [ - "American Samoa", - "Jarvis Island", - "Kingman Reef", - "Midway Atoll", - "Niue", - "Palmyra Atoll" - ], - "CountriesDaylight": [ - "American Samoa" - ] - }, - { - "Offset": "-10:00", - "Name": "Hawaii-Aleutian Time Zone", - "MilitaryName": "Whiskey Time Zone", - "TimeNamesStandard": [ - "Hawaii-Aleutian Standard Time", - "Tahiti Time" - ], - "ShortcutsStandard": [ - "HST", - "TAHT" - ], - "TimeNamesDaylight": [ - "Hawaii-Aleutian Daylight Time" - ], - "ShortcutsDaylight": [ - "HDT" - ], - "CountriesStandard": [ - "Cook Islands", - "French Polynesia (most)", - "Johnston Atoll", - "Hawaii (United States)" - ], - "CountriesDaylight": [ - "Andreanof Islands (United States)", - "Islands of Four Mountains (United States)", - "Near Islands (United States)", - "Rat Islands (Aleutian Islands, Alaska, United States)" - ] - }, - { - "Offset": "-09:30", - "TimeNamesStandard": [ - "Marquesas Islands Time" - ], - "ShortcutsStandard": [ - "MART", - "MIT" - ], - "CountriesStandard": [ - "Marquesas Islands (French Polynesia)" - ] - }, - { - "Offset": "-09:00", - "Name": "Alaska Time Zone", - "MilitaryName": "Victor Time Zone", - "TimeNamesStandard": [ - "Alaska Standard Time", - "Gambier Islands Time" - ], - "ShortcutsStandard": [ - "AKST", - "GAMT", - "GIT" - ], - "TimeNamesDaylight": [ - "Alaska Daylight Time" - ], - "ShortcutsDaylight": [ - "AKDT" - ], - "CountriesStandard": [ - "Gambier Islands (French Polynesia)" - ], - "CountriesDaylight": [ - "Alaska (most, United States)" - ] - }, - { - "Offset": "-08:00", - "Name": "Pacific Time Zone", - "Shortcut": "PT", - "MilitaryName": "Uniform Time Zone", - "TimeNamesStandard": [ - "Pacific Standard Time" - ], - "ShortcutsStandard": [ - "PST" - ], - "TimeNamesDaylight": [ - "Pacific Daylight Time" - ], - "ShortcutsDaylight": [ - "PDT" - ], - "CountriesStandard": [ - "Clipperton Island", - "Pitcairn Islands" - ], - "CountriesDaylight": [ - "British Columbia (most, Canada)", - "Baja California (Mexico)", - "California (United States)", - "Idaho (north, United States)", - "Nevada (United States)", - "Oregon (most, United States)", - "Washington (United States)" - ] - }, - { - "Offset": "-07:00", - "Name": "Mountain Time Zone", - "MilitaryName": "Tango Time Zone", - "TimeNamesStandard": [ - "Mountain Standard Time" - ], - "ShortcutsStandard": [ - "MST" - ], - "TimeNamesDaylight": [ - "Mountain Daylight Time" - ], - "ShortcutsDaylight": [ - "MDT" - ], - "CountriesStandard": [ - "British Columbia (northeast, Canada)", - "Yukon (Canada)", - "Sonora (Mexico)", - "Arizona (most, United States)" - ], - "CountriesDaylight": [ - "Alberta (Canada)", - "British Columbia (southeast, Canada)", - "Northwest Territories (Canada)", - "Nunavut (west, Canada)", - "Baja California Sur (Mexico)", - "Chihuahua (Mexico)", - "Nayarit (most, Mexico)", - "Sinaloa (Mexico)", - "Colorado (United States)", - "Idaho (most, United States)", - "Montana (United States)", - "New Mexico (United States)", - "Utah (United States)", - "Wyoming (United States)" - ] - }, - { - "Offset": "-06:00", - "Name": "Central Time Zone", - "Shortcut": "CT", - "MilitaryName": "Sierra Time Zone", - "TimeNamesStandard": [ - "Central Standard Time", - "Easter Island Standard Time", - "Galápagos Time" - ], - "ShortcutsStandard": [ - "CST", - "EAST", - "GALT" - ], - "TimeNamesDaylight": [ - "Central Daylight Time", - "Easter Island Summer Time" - ], - "ShortcutsDaylight": [ - "CDT", - "EASST" - ], - "CountriesStandard": [ - "Belize", - "Saskatchewan (most, Canada)", - "Costa Rica", - "Ecuador: Galápagos", - "El Salvador", - "Guatemala", - "Honduras", - "Nicaragua" - ], - "CountriesDaylight": [ - "Manitoba (Canada)", - "Nunavut (central, Canada)", - "Ontario (west, Canada)", - "Easter Island (Chile)", - "Mexico (most)", - "Alabama (United States)", - "Arkansas (United States)", - "Illinois (United States)", - "Iowa (United States)", - "Kansas (most, United States)", - "Louisiana (United States)", - "Minnesota (United States)", - "Mississippi (United States)", - "Missouri (United States)", - "Nebraska (most, United States)", - "North Dakota (most, United States)", - "Oklahoma, United States", - "South Dakota (most, United States)", - "Tennessee (most, United States)", - "Texas (most, United States)", - "Wisconsin (United States)" - ] - }, - { - "Offset": "-05:00", - "Name": "Eastern Time Zone", - "Shortcut": "ET", - "MilitaryName": "Romeo Time Zone", - "TimeNamesStandard": [ - "Acre Time", - "Colombia Time", - "Cuba Standard Time", - "Ecuador Time", - "Eastern Standard Time", - "Peru Time" - ], - "ShortcutsStandard": [ - "ACT", - "COT", - "CST", - "ECT", - "EST", - "PET" - ], - "TimeNamesDaylight": [ - "Cuba Daylight Time", - "Colombia Summer Time", - "Eastern Daylight Time" - ], - "ShortcutsDaylight": [ - "CDT", - "COST", - "EDT" - ], - "CountriesStandard": [ - "Acre (Brazil)", - "Atikokan (Canada)", - "Mishkeegogamang (Canada)", - "Southampton Island (Canada)", - "Cayman Islands", - "Colombia", - "Ecuador (most)", - "Jamaica", - "Quintana Roo (Mexico)", - "Navassa Island", - "Panama", - "Peru" - ], - "CountriesDaylight": [ - "Bahamas", - "Nunavut (east, Canada)", - "Ontario (most, Canada)", - "Quebec (most, Canada)", - "Cuba", - "Haiti", - "Turks and Caicos Islands", - "Connecticut (United States)", - "Delaware (United States)", - "District of Columbia (United States)", - "Florida (most, United States)", - "Georgia (United States)", - "Indiana (most, United States)", - "Kentucky (most, United States)", - "Maine (United States)", - "Maryland (United States)", - "Massachusetts", - "Michigan (most, United States)", - "New Hampshire (United States)", - "New Jersey (United States)", - "New York (United States)", - "North Carolina (United States)", - "Ohio (United States)", - "Pennsylvania (United States)", - "Rhode Island (United States)", - "South Carolina (United States)", - "Vermont (United States)", - "Virginia (United States)", - "West Virginia (United States)" - ] - }, - { - "Offset": "-04:00", - "Name": "Atlantic Time Zone", - "MilitaryName": "Quebec Time Zone", - "TimeNamesStandard": [ - "Amazon Time", - "Atlantic Standard Time", - "Bolivia Time", - "Eastern Caribbean Time", - "Falkland Islands Time", - "Guyana Time", - "Paraguay Time", - "Venezuelan Standard Time" - ], - "ShortcutsStandard": [ - "AMT", - "AST", - "BOT", - "ECT", - "FKT", - "GYT", - "PYT", - "VET" - ], - "TimeNamesDaylight": [ - "Atlantic Daylight Time", - "Amazon Summer Time", - "Chile Summer Time", - "Falkland Islands Summer Time", - "Paraguay Summer Time" - ], - "ShortcutsDaylight": [ - "AMST", - "ADT", - "CLST", - "FKST", - "PYST" - ], - "CountriesStandard": [ - "Anguilla", - "Antigua and Barbuda", - "Aruba", - "Barbados", - "Bolivia", - "Amazonas (most, Brazil)", - "Mato Grosso (Brazil)", - "Mato Grosso do Sul (Brazil)", - "Rondônia (Brazil)", - "Roraima (Brazil)", - "British Virgin Islands", - "Quebec (east, Canada)", - "Caribbean Netherlands", - "Curaçao", - "Dominica", - "Dominican Republic", - "Grenada", - "Guadeloupe", - "Guyana", - "Martinique", - "Montserrat", - "Puerto Rico", - "Saint Barthélemy", - "Saint Kitts and Nevis", - "Saint Lucia", - "Saint Martin", - "Saint Vincent and the Grenadines", - "Sint Maarten", - "Trinidad and Tobago", - "U.S. Virgin Islands", - "Venezuela" - ], - "CountriesDaylight": [ - "Bermuda", - "Labrador (most, Canada)", - "New Brunswick (Canada)", - "Nova Scotia (Canada)", - "Prince Edward Island (Canada)", - "Chile (most)", - "Thule Air Base (Greenland)", - "Paraguay" - ] - }, - { - "Offset": "-03:30", - "Name": "Newfoundland Time Zone", - "Shortcut": "NT", - "TimeNamesStandard": [ - "Newfoundland Standard Time" - ], - "ShortcutsStandard": [ - "NST" - ], - "TimeNamesDaylight": [ - "Newfoundland Daylight Time" - ], - "ShortcutsDaylight": [ - "NDT" - ], - "CountriesStandard": [ - "Labrador (southeast, Canada)" - ], - "CountriesDaylight": [ - "Newfoundland (Canada)" - ] - }, - { - "Offset": "-03:00", - "MilitaryName": "Papa Time Zone", - "TimeNamesStandard": [ - "Argentina Time", - "Brasília Time", - "French Guiana Time", - "Saint Pierre and Miquelon Standard Time", - "Rothera Research Station Time", - "Suriname Time", - "Uruguay Standard Time", - "West Greenland Time" - ], - "ShortcutsStandard": [ - "ART", - "BRT", - "GFT", - "PMST", - "ROTT", - "SRT", - "UYT", - "WGT" - ], - "TimeNamesDaylight": [ - "Brasília Summer Time", - "Saint Pierre and Miquelon Daylight Time", - "Uruguay Summer Time", - "West Greenland Summer Time" - ], - "ShortcutsDaylight": [ - "BRST", - "PMDT", - "WGST", - "UYST" - ], - "CountriesStandard": [ - "Argentina", - "Brazil (most)", - "Magallanes (Chile)", - "Falkland Islands", - "French Guiana", - "SuriName", - "Uruguay" - ], - "CountriesDaylight": [ - "Greenland (most)", - "Saint Pierre and Miquelon" - ] - }, - { - "Offset": "-02:00", - "MilitaryName": "Oscar Time Zone", - "TimeNamesStandard": [ - "Fernando de Noronha Time", - "South Georgia and the South Sandwich Islands Time" - ], - "ShortcutsStandard": [ - "FNT", - "GST" - ], - "CountriesStandard": [ - "Fernando de Noronha (Brazil)", - "South Georgia and the South Sandwich Islands" - ] - }, - { - "Offset": "-01:00", - "MilitaryName": "November Time Zone", - "TimeNamesStandard": [ - "Azores Standard Time", - "Cape Verde Time", - "Eastern Greenland Time" - ], - "ShortcutsStandard": [ - "AZOT", - "CVT", - "EGT" - ], - "TimeNamesDaylight": [ - "Azores Summer Time", - "Eastern Greenland Summer Time" - ], - "ShortcutsDaylight": [ - "AZOST", - "EGST" - ], - "CountriesStandard": [ - "Cape Verde" - ], - "CountriesDaylight": [ - "Ittoqqortoormiit (Greenland)", - "Azores (Portugal)" - ] - }, - { - "Offset": "00:00", - "Name": "Coordinated Universal Time", - "Shortcut": "UTC", - "MilitaryName": "Zulu Time Zone", - "TimeNamesStandard": [ - "Greenwich Mean Time", - "Western European Time" - ], - "ShortcutsStandard": [ - "GMT", - "WET" - ], - "TimeNamesDaylight": [ - "British Summer Time", - "Irish Standard Time", - "Western European Summer Time" - ], - "ShortcutsDaylight": [ - "BST", - "IST", - "WEST" - ], - "CountriesStandard": [ - "Ascension and Tristan da Cunha", - "Burkina Faso", - "Danmarkshavn", - "Gambia", - "Ghana", - "Guinea-Bissau", - "Guinea", - "Iceland", - "Ivory Coast", - "Liberia", - "Mali", - "Mauritania", - "Saint Helena", - "São Tomé and Príncipe", - "Senegal", - "Sierra Leone", - "Togo" - ], - "CountriesDaylight": [ - "Canary Islands", - "Faroe Islands", - "Guernsey", - "Ireland", - "Isle of Man", - "Jersey", - "Portugal (most)", - "United Kingdom" - ] - }, - { - "Offset": "01:00", - "MilitaryName": "Alpha Time Zone", - "TimeNamesStandard": [ - "Central European Time", - "Middle European Time", - "West Africa Time" - ], - "ShortcutsStandard": [ - "CET", - "MET", - "WAT" - ], - "TimeNamesDaylight": [ - "Central European Summer Time", - "Heure Avancée d'Europe Centrale", - "Middle European Summer Time", - "West Africa Summer Time" - ], - "ShortcutsDaylight": [ - "CEST", - "HAEC", - "MEST", - "WAST" - ], - "CountriesStandard": [ - "Algeria", - "Angola", - "Benin", - "Cameroon", - "Central African Republic", - "Chad", - "Congo", - "Équateur (Democratic Republic of the Congo)", - "Kinshasa (Democratic Republic of the Congo)", - "Kongo Central (Democratic Republic of the Congo)", - "Kwango (Democratic Republic of the Congo)", - "Kwilu (Democratic Republic of the Congo)", - "Mai-Ndombe (Democratic Republic of the Congo)", - "Mongala (Democratic Republic of the Congo)", - "Nord-Ubangi (Democratic Republic of the Congo)", - "Sud-Ubangi (Democratic Republic of the Congo)", - "Tshuapa (Democratic Republic of the Congo)", - "Equatorial Guinea", - "Gabon", - "Morocco", - "Niger", - "Nigeria", - "Tunisia", - "Weste" - ], - "CountriesDaylight": [ - "Albania", - "Andorra", - "Austria", - "Belgium", - "Bosnia and Herzegovina", - "Croatia", - "Czech Republic", - "Denmark", - "France (metropolitan)", - "Germany", - "Gibraltar", - "Hungary", - "Italy", - "Kosovo", - "Liechtenstein", - "Luxembourg", - "Malta", - "Monaco", - "Montenegro", - "Netherlands (European)", - "North Macedonia", - "Norway", - "Poland", - "San Marino", - "Serbia", - "Slovakia", - "Slovenia", - "Spain (most)", - "Sweden", - "Switzerland", - "Vatican City" - ] - }, - { - "Offset": "02:00", - "MilitaryName": "Bravo Time Zone", - "TimeNamesStandard": [ - "Central Africa Time", - "Eastern European Time", - "Israel Standard Time", - "Kaliningrad Time", - "South African Standard Time" - ], - "ShortcutsStandard": [ - "CAT", - "EET", - "IST", - "KALT", - "SAST" - ], - "TimeNamesDaylight": [ - "Eastern European Summer Time", - "Israel Daylight Time" - ], - "ShortcutsDaylight": [ - "EEST", - "IDT" - ], - "CountriesStandard": [ - "Botswana", - "Burundi", - "Democratic Republic of the Congo (most)", - "Egypt", - "Eswatini", - "Lesotho", - "Libya", - "Malawi", - "Mozambique", - "Namibia", - "Kaliningrad (Russia)", - "Rwanda", - "South Africa (most)", - "South Sudan", - "Sudan", - "Zambia", - "Zimbabwe" - ], - "CountriesDaylight": [ - "Akrotiri and Dhekelia", - "Bulgaria", - "Cyprus", - "Estonia", - "Finland", - "Greece", - "Israel", - "Jordan", - "Latvia", - "Lebanon", - "Lithuania", - "Moldova", - "Northern Cyprus", - "Palestine", - "Romania", - "Transnistria", - "Syria" - ] - }, - { - "Offset": "03:00", - "MilitaryName": "Charlie Time Zone", - "TimeNamesStandard": [ - "Arabia Standard Time", - "East Africa Time", - "Further-eastern European Time", - "Indian Ocean Time", - "Moscow Time", - "Showa Station Time", - "Turkey Time" - ], - "ShortcutsStandard": [ - "AST", - "EAT", - "FET", - "IOT", - "MSK", - "SYOT", - "TRT" - ], - "CountriesStandard": [ - "Abkhazia", - "Bahrain", - "Belarus", - "Comoros", - "Crimea", - "Djibouti", - "Donetsk PR", - "Eritrea", - "Ethiopia", - "Scattered Islands (French Southern and Antarctic Lands)", - "Iraq", - "Kenya", - "Kuwait", - "Luhansk PR", - "Madagascar", - "Mayotte", - "Qatar", - "Russia (most of European part)", - "Saudi Arabia", - "Somalia", - "Somaliland", - "Prince Edward Islands (South Africa)", - "South Ossetia", - "Tanzania", - "Turkey", - "Uganda", - "Yemen" - ] - }, - { - "Offset": "03:30", - "TimeNamesStandard": [ - "Iran Standard Time" - ], - "ShortcutsStandard": [ - "IRST" - ], - "TimeNamesDaylight": [ - "Iran Daylight Time" - ], - "ShortcutsDaylight": [ - "IRDT" - ], - "CountriesDaylight": [ - "Iran" - ] - }, - { - "Offset": "04:00", - "MilitaryName": "Delta Time Zone", - "TimeNamesDaylight": [ - "Armenia Time", - "Azerbaijan Time", - "Georgia Standard Time", - "Gulf Standard Time", - "Mauritius Time", - "Réunion Time", - "Samara Time", - "Seychelles Time", - "Volgograd Time", - "United Arab Emirates Standard Time" - ], - "ShortcutsStandard": [ - "AMT", - "AZT", - "GET", - "GST", - "MUT", - "RET", - "SAMT", - "SCT", - "VOLT" - ], - "CountriesDaylight": [ - "Armenia", - "Artsakh", - "Azerbaijan", - "Crozet Islands (French Southern and Antarctic Lands)", - "Georgia", - "Mauritius", - "Oman", - "Astrakhan (Russia)", - "Samara (Russia)", - "Saratov (Russia)", - "Udmurtia (Russia)", - "Ulyanovsk (Russia)", - "Réunion", - "Seychelles", - "United Arab Emirates" - ] - }, - { - "Offset": "04:30", - "TimeNamesStandard": [ - "Afghanistan Time" - ], - "ShortcutsStandard": [ - "AFT" - ], - "CountriesStandard": [ - "Afghanistan" - ] - }, - { - "Offset": "05:00", - "MilitaryName": "Echo Time Zone", - "TimeNamesStandard": [ - "Aqtobe Time", - "Heard and McDonald Islands Time", - "Mawson Station Time", - "Maldives Time", - "Oral Time", - "Pakistan Standard Time", - "French Southern and Antarctic Time", - "Tajikistan Time", - "Turkmenistan Time", - "Uzbekistan Time", - "Yekaterinburg Time" - ], - "ShortcutsStandard": [ - "AQTT", - "HMT", - "MAWT", - "MVT", - "ORAT", - "PKT", - "TFT", - "TJT", - "TMT", - "UZT", - "YEKT" - ], - "CountriesStandard": [ - "Amsterdam Island (French Southern and Antarctic Lands)", - "Kerguelen Islands (French Southern and Antarctic Lands)", - "Saint Paul Island (French Southern and Antarctic Lands)", - "Heard Island and McDonald Islands", - "Aktobe (Kazakhstan)", - "Atyrau (Kazakhstan)", - "Baikonur (Kazakhstan)", - "Kyzylorda (Kazakhstan)", - "Mangystau (Kazakhstan)", - "West Kazakhstan (Kazakhstan)", - "Maldives", - "Pakistan", - "Bashkortostan (Russia)", - "Chelyabinsk (Russia)", - "Khanty-Mansi (Russia)", - "Kurgan (Russia)", - "Orenburg (Russia)", - "Perm (Russia)", - "Sverdlovsk (Russia)", - "Tyumen (Russia)", - "Yamalia (Russia)", - "Tajikistan", - "Turkmenistan", - "Uzbekistan" - ] - }, - { - "Offset": "05:30", - "TimeNamesStandard": [ - "Indian Standard Time", - "Sri Lanka Standard Time" - ], - "ShortcutsStandard": [ - "IST", - "SLST" - ], - "CountriesStandard": [ - "India", - "Sri Lanka" - ] - }, - { - "Offset": "05:45", - "TimeNamesStandard": [ - "Nepal Standard Time" - ], - "ShortcutsStandard": [ - "NPT" - ], - "CountriesStandard": [ - "Nepal" - ] - }, - { - "Offset": "06:00", - "MilitaryName": "Foxtrot Time Zone", - "TimeNamesStandard": [ - "Alma-Ata Time", - "British Indian Ocean Time", - "Bangladesh Standard Time", - "Bhutan Time", - "Kyrgyzstan Time", - "Omsk Time", - "Vostok Station Time" - ], - "ShortcutsStandard": [ - "ALMT", - "BIOT", - "BST", - "BTT", - "KGT", - "OMST", - "VOST" - ], - "CountriesStandard": [ - "Bangladesh", - "Bhutan", - "British Indian Ocean Territory", - "Kazakhstan (most)", - "Kyrgyzstan", - "Omsk (Russia)" - ] - }, - { - "Offset": "06:30", - "TimeNamesStandard": [ - "ASEAN Common Time", - "Cocos Islands Time", - "Myanmar Standard Time" - ], - "ShortcutsStandard": [ - "ACT", - "CCT", - "MMT" - ], - "CountriesStandard": [ - "Cocos Islands", - "Myanmar" - ] - }, - { - "Offset": "07:00", - "MilitaryName": "Golf Time Zone", - "TimeNamesStandard": [ - "Christmas Island Time", - "Davis Time", - "Hovd Time", - "Indochina Time", - "Krasnoyarsk Time", - "Novosibirsk Time", - "Thailand Standard Time", - "Western Indonesian Time" - ], - "ShortcutsStandard": [ - "CXT", - "DAVT", - "HOVT", - "ICT", - "KRAT", - "NOVT", - "THA", - "WIB" - ], - "CountriesStandard": [ - "Cambodia", - "Christmas Island", - "Central Kalimantan (Indonesia)", - "Java (Indonesia)", - "Sumatra (Indonesia)", - "West Kalimantan (Indonesia)", - "Laos", - "Bayan-Ölgii (Mongolia)", - "Khovd (Mongolia)", - "Uvs (Mongolia)", - "Altai Krai (Russia)", - "Altai Republic (Russia)", - "Kemerovo (Russia)", - "Khakassia (Russia)", - "Krasnoyarsk (Russia)", - "Novosibirsk (Russia)", - "Tomsk (Russia)", - "Tuva (Russia)", - "Thailand", - "Vietnam" - ] - }, - { - "Offset": "08:00", - "MilitaryName": "Hotel Time Zone", - "TimeNamesStandard": [ - "Australian Western Standard Time", - "Brunei Time", - "Choibalsan Standard Time", - "Clipperton Island Standard Time", - "China Standard Time", - "Hong Kong Time", - "Irkutsk Time", - "Malaysia Standard Time", - "Malaysia Time", - "Philippine Time", - "Philippine Standard Time", - "Singapore Time", - "Singapore Standard Time", - "Ulaanbaatar Standard Time", - "Central Indonesian Time", - "Western Standard Time", - "Western Australia Standard Time" - ], - "ShortcutsStandard": [ - "AWST", - "BNT", - "CHOT", - "CIST", - "CST", - "HKT", - "IRKT", - "MST", - "MYT", - "PHT", - "PHST", - "SST", - "ULAT", - "WITA", - "WST" - ], - "TimeNamesDaylight": [ - "Ulaanbaatar Summer Time" - ], - "ShortcutsDaylight": [ - "ULAST" - ], - "CountriesStandard": [ - "Australia: Western Australia (most)", - "Brunei", - "China", - "Hong Kong", - "Bali (Indonesia)", - "East Kalimantan (Indonesia)", - "East Nusa Tenggara (Indonesia)", - "North Kalimantan (Indonesia)", - "South Kalimantan (Indonesia)", - "Sulawesi (Indonesia)", - "West Nusa Tenggara (Indonesia)", - "Macau", - "Malaysia", - "Mongolia (most)", - "Philippines", - "Buryatia (Russia)", - "Irkutsk (Russia)", - "Singapore", - "Taiwan" - ] - }, - { - "Offset": "08:45", - "TimeNamesStandard": [ - "Australian Central Western Standard Time", - "Central Western Standard Time" - ], - "ShortcutsStandard": [ - "ACWST", - "CWST" - ], - "CountriesStandard": [ - "Caiguna (Australia)", - "Cocklebiddy (Australia)", - "Eucla (Australia)", - "Madura (Australia)", - "Mundrabilla (Australia)", - "Border Village (Australia)" - ] - }, - { - "Offset": "09:00", - "MilitaryName": "India Time Zone", - "TimeNamesStandard": [ - "Choibalsan Summer Time", - "Japan Standard Time", - "Korea Standard Time", - "Palau Time", - "Timor Leste Time", - "Eastern Indonesian Time", - "Yakutsk Time" - ], - "ShortcutsStandard": [ - "CHOST", - "JST", - "KST", - "PWT", - "TLT", - "WIT", - "YAKT" - ], - "CountriesStandard": [ - "East Timor", - "Maluku (Indonesia)", - "North Maluku (Indonesia)", - "Papua (Indonesia)", - "West Papua (Indonesia)", - "Japan", - "North Korea", - "Palau", - "Amur (Russia)", - "Sakha (most, Russia)", - "Zabaykalsky (Russia)", - "South Korea" - ] - }, - { - "Offset": "09:30", - "TimeNamesStandard": [ - "Australian Central Standard Time" - ], - "ShortcutsStandard": [ - "ACST" - ], - "TimeNamesDaylight": [ - "Australian Central Daylight Saving Time" - ], - "ShortcutsDaylight": [ - "ACDT" - ], - "CountriesStandard": [ - "Northern Territory", - "South Australia (Australia)" - ] - }, - { - "Offset": "10:00", - "Name": "Chamorro Time Zone", - "MilitaryName": "Kilo Time Zone", - "TimeNamesStandard": [ - "Australian Eastern Standard Time", - "Australian Eastern Time", - "Chamorro Standard Time", - "Chuuk Time", - "Dumont d'Urville Time", - "Papua New Guinea Time", - "Vladivostok Time" - ], - "ShortcutsStandard": [ - "AEST", - "AET", - "CHST", - "CHUT", - "DDUT", - "PGT", - "VLAT" - ], - "TimeNamesDaylight": [ - "Australian Eastern Daylight Saving Time", - "Lord Howe Summer Time" - ], - "ShortcutsDaylight": [ - "AEDT", - "LHST" - ], - "CountriesStandard": [ - "Queensland (Australia)", - "Guam", - "Chuuk (Micronesia)", - "Yap (Micronesia)", - "Northern Mariana Islands", - "Papua New Guinea (most)", - "Jewish (Russia)", - "Khabarovsk (Russia)", - "Primorsky (Russia)", - "Sakha (central-east, Russia)" - ], - "CountriesDaylight": [ - "Australian Capital Territory (Australia)", - "Jervis Bay Territory (Australia)", - "New South Wales (most, Australia)", - "Tasmania (Australia)", - "Victoria (Australia)" - ] - }, - { - "Offset": "10:30", - "TimeNamesStandard": [ - "Lord Howe Standard Time" - ], - "ShortcutsStandard": [ - "LHST" - ], - "CountriesDaylight": [ - "Lord Howe Island (Australia)" - ] - }, - { - "Offset": "11:00", - "MilitaryName": "Lima Time Zone", - "TimeNamesStandard": [ - "Bougainville Standard Time", - "Kosrae Time", - "Macquarie Island Station Time", - "New Caledonia Time", - "Norfolk Island Time", - "Pohnpei Standard Time", - "Sakhalin Island Time", - "Solomon Islands Time", - "Srednekolymsk Time", - "Vanuatu Time" - ], - "ShortcutsStandard": [ - "BST", - "KOST", - "MIST", - "NCT", - "NFT", - "PONT", - "SAKT", - "SBT", - "SRET", - "VUT" - ], - "CountriesStandard": [ - "Kosrae (Micronesia)", - "Pohnpei (Micronesia)", - "New Caledonia", - "Bougainville (Papua New Guinea)", - "Magadan (Russia)", - "Sakha (east, Russia)", - "Sakhalin (Russia)", - "Solomon Islands", - "Vanuatu" - ], - "CountriesDaylight": [ - "Norfolk Island" - ] - }, - { - "Offset": "12:00", - "MilitaryName": "Mike Time Zone", - "TimeNamesStandard": [ - "Anadyr Time", - "Fiji Time", - "Gilbert Island Time", - "Magadan Time", - "Marshall Islands Time", - "New Zealand Standard Time", - "Kamchatka Time", - "Tuvalu Time", - "Wake Island Time" - ], - "ShortcutsStandard": [ - "ANAT", - "FJT", - "GILT", - "MAGT", - "MHT", - "NZST", - "PETT", - "TVT", - "WAKT" - ], - "CountriesStandard": [ - "Fiji", - "Gilbert Islands (Kiribati)", - "Marshall Islands", - "Nauru", - "Chukotka (Russia)", - "Kamchatka (Russia)", - "Tuvalu", - "Wake Island", - "Wallis and Futuna" - ], - "TimeNamesDaylight": [ - "New Zealand Daylight Time" - ], - "ShortcutsDaylight": [ - "NZDT" - ], - "CountriesDaylight": [ - "New Zealand" - ] - }, - { - "Offset": "12:45", - "TimeNamesStandard": [ - "Chatham Standard Time" - ], - "ShortcutsStandard": [ - "CHAST" - ], - "TimeNamesDaylight": [ - "Chatham Daylight Time" - ], - "ShortcutsDaylight": [ - "CHADT" - ], - "CountriesStandard": [ - "Chatham Islands (New Zealand)", - "Pitt Islands (New Zealand)", - "South East Island (New Zealand)", - "The Fort (New Zealand)", - "Little Mangere Island (New Zealand)", - "Star Keys (New Zealand)", - "The Sisters (New Zealand)", - "Forty-Fours (New Zealand)" - ] - }, - { - "Offset": "13:00", - "TimeNamesStandard": [ - "Phoenix Island Time", - "Tokelau Time", - "Tonga Time" - ], - "ShortcutsStandard": [ - "PHOT", - "TKT", - "TOT" - ], - "CountriesStandard": [ - "Phoenix Islands (Kiribati)", - "Samoa", - "Tokelau", - "Tonga" - ] - }, - { - "Offset": "14:00", - "TimeNamesStandard": [ - "Line Islands Time" - ], - "ShortcutsStandard": [ - "LINT" - ], - "CountriesStandard": [ - "Line Islands (Kiribati)" - ] - } - ] -} + "$schema": "./timeZones.schema.json", + "TimeZones": [ + { + "Offset": "-12:00", + "Name": "International Date Line West time zone", + "Shortcut": "IDLW", + "MilitaryName": "Yankee Time Zone", + "TimeNamesStandard": [ + "Baker Island Time" + ], + "ShortcutsStandard": [ + "BIT" + ], + "CountriesStandard": [ + "Baker Island", + "Howland Island" + ] + }, + { + "Offset": "-11:00", + "Name": "Samoa Time Zone", + "MilitaryName": "X-ray Time Zone", + "TimeNamesStandard": [ + "Niue Time", + "Samoa Standard Time" + ], + "ShortcutsStandard": [ + "NUT", + "SST" + ], + "TimeNamesDaylight": [ + "Samoa Daylight Time" + ], + "ShortcutsDaylight": [ + "SDT" + ], + "CountriesStandard": [ + "American Samoa", + "Jarvis Island", + "Kingman Reef", + "Midway Atoll", + "Niue", + "Palmyra Atoll" + ], + "CountriesDaylight": [ + "American Samoa" + ] + }, + { + "Offset": "-10:00", + "Name": "Hawaii-Aleutian Time Zone", + "MilitaryName": "Whiskey Time Zone", + "TimeNamesStandard": [ + "Hawaii-Aleutian Standard Time", + "Tahiti Time" + ], + "ShortcutsStandard": [ + "HST", + "TAHT" + ], + "TimeNamesDaylight": [ + "Hawaii-Aleutian Daylight Time" + ], + "ShortcutsDaylight": [ + "HDT" + ], + "CountriesStandard": [ + "Cook Islands", + "French Polynesia (most)", + "Johnston Atoll", + "Hawaii (United States)" + ], + "CountriesDaylight": [ + "Andreanof Islands (United States)", + "Islands of Four Mountains (United States)", + "Near Islands (United States)", + "Rat Islands (Aleutian Islands, Alaska, United States)" + ] + }, + { + "Offset": "-09:30", + "TimeNamesStandard": [ + "Marquesas Islands Time" + ], + "ShortcutsStandard": [ + "MART", + "MIT" + ], + "CountriesStandard": [ + "Marquesas Islands (French Polynesia)" + ] + }, + { + "Offset": "-09:00", + "Name": "Alaska Time Zone", + "MilitaryName": "Victor Time Zone", + "TimeNamesStandard": [ + "Alaska Standard Time", + "Gambier Islands Time" + ], + "ShortcutsStandard": [ + "AKST", + "GAMT", + "GIT" + ], + "TimeNamesDaylight": [ + "Alaska Daylight Time" + ], + "ShortcutsDaylight": [ + "AKDT" + ], + "CountriesStandard": [ + "Gambier Islands (French Polynesia)" + ], + "CountriesDaylight": [ + "Alaska (most, United States)" + ] + }, + { + "Offset": "-08:00", + "Name": "Pacific Time Zone", + "Shortcut": "PT", + "MilitaryName": "Uniform Time Zone", + "TimeNamesStandard": [ + "Pacific Standard Time" + ], + "ShortcutsStandard": [ + "PST" + ], + "TimeNamesDaylight": [ + "Pacific Daylight Time" + ], + "ShortcutsDaylight": [ + "PDT" + ], + "CountriesStandard": [ + "Clipperton Island", + "Pitcairn Islands" + ], + "CountriesDaylight": [ + "British Columbia (most, Canada)", + "Baja California (Mexico)", + "California (United States)", + "Idaho (north, United States)", + "Nevada (United States)", + "Oregon (most, United States)", + "Washington (United States)" + ] + }, + { + "Offset": "-07:00", + "Name": "Mountain Time Zone", + "MilitaryName": "Tango Time Zone", + "TimeNamesStandard": [ + "Mountain Standard Time" + ], + "ShortcutsStandard": [ + "MST" + ], + "TimeNamesDaylight": [ + "Mountain Daylight Time" + ], + "ShortcutsDaylight": [ + "MDT" + ], + "CountriesStandard": [ + "British Columbia (northeast, Canada)", + "Yukon (Canada)", + "Sonora (Mexico)", + "Arizona (most, United States)" + ], + "CountriesDaylight": [ + "Alberta (Canada)", + "British Columbia (southeast, Canada)", + "Northwest Territories (Canada)", + "Nunavut (west, Canada)", + "Baja California Sur (Mexico)", + "Chihuahua (Mexico)", + "Nayarit (most, Mexico)", + "Sinaloa (Mexico)", + "Colorado (United States)", + "Idaho (most, United States)", + "Montana (United States)", + "New Mexico (United States)", + "Utah (United States)", + "Wyoming (United States)" + ] + }, + { + "Offset": "-06:00", + "Name": "Central Time Zone", + "Shortcut": "CT", + "MilitaryName": "Sierra Time Zone", + "TimeNamesStandard": [ + "Central Standard Time", + "Easter Island Standard Time", + "Galápagos Time" + ], + "ShortcutsStandard": [ + "CST", + "EAST", + "GALT" + ], + "TimeNamesDaylight": [ + "Central Daylight Time", + "Easter Island Summer Time" + ], + "ShortcutsDaylight": [ + "CDT", + "EASST" + ], + "CountriesStandard": [ + "Belize", + "Saskatchewan (most, Canada)", + "Costa Rica", + "Ecuador: Galápagos", + "El Salvador", + "Guatemala", + "Honduras", + "Nicaragua" + ], + "CountriesDaylight": [ + "Manitoba (Canada)", + "Nunavut (central, Canada)", + "Ontario (west, Canada)", + "Easter Island (Chile)", + "Mexico (most)", + "Alabama (United States)", + "Arkansas (United States)", + "Illinois (United States)", + "Iowa (United States)", + "Kansas (most, United States)", + "Louisiana (United States)", + "Minnesota (United States)", + "Mississippi (United States)", + "Missouri (United States)", + "Nebraska (most, United States)", + "North Dakota (most, United States)", + "Oklahoma, United States", + "South Dakota (most, United States)", + "Tennessee (most, United States)", + "Texas (most, United States)", + "Wisconsin (United States)" + ] + }, + { + "Offset": "-05:00", + "Name": "Eastern Time Zone", + "Shortcut": "ET", + "MilitaryName": "Romeo Time Zone", + "TimeNamesStandard": [ + "Acre Time", + "Colombia Time", + "Cuba Standard Time", + "Ecuador Time", + "Eastern Standard Time", + "Peru Time" + ], + "ShortcutsStandard": [ + "ACT", + "COT", + "CST", + "ECT", + "EST", + "PET" + ], + "TimeNamesDaylight": [ + "Cuba Daylight Time", + "Colombia Summer Time", + "Eastern Daylight Time" + ], + "ShortcutsDaylight": [ + "CDT", + "COST", + "EDT" + ], + "CountriesStandard": [ + "Acre (Brazil)", + "Atikokan (Canada)", + "Mishkeegogamang (Canada)", + "Southampton Island (Canada)", + "Cayman Islands", + "Colombia", + "Ecuador (most)", + "Jamaica", + "Quintana Roo (Mexico)", + "Navassa Island", + "Panama", + "Peru" + ], + "CountriesDaylight": [ + "Bahamas", + "Nunavut (east, Canada)", + "Ontario (most, Canada)", + "Quebec (most, Canada)", + "Cuba", + "Haiti", + "Turks and Caicos Islands", + "Connecticut (United States)", + "Delaware (United States)", + "District of Columbia (United States)", + "Florida (most, United States)", + "Georgia (United States)", + "Indiana (most, United States)", + "Kentucky (most, United States)", + "Maine (United States)", + "Maryland (United States)", + "Massachusetts", + "Michigan (most, United States)", + "New Hampshire (United States)", + "New Jersey (United States)", + "New York (United States)", + "North Carolina (United States)", + "Ohio (United States)", + "Pennsylvania (United States)", + "Rhode Island (United States)", + "South Carolina (United States)", + "Vermont (United States)", + "Virginia (United States)", + "West Virginia (United States)" + ] + }, + { + "Offset": "-04:00", + "Name": "Atlantic Time Zone", + "MilitaryName": "Quebec Time Zone", + "TimeNamesStandard": [ + "Amazon Time", + "Atlantic Standard Time", + "Bolivia Time", + "Eastern Caribbean Time", + "Falkland Islands Time", + "Guyana Time", + "Paraguay Time", + "Venezuelan Standard Time" + ], + "ShortcutsStandard": [ + "AMT", + "AST", + "BOT", + "ECT", + "FKT", + "GYT", + "PYT", + "VET" + ], + "TimeNamesDaylight": [ + "Atlantic Daylight Time", + "Amazon Summer Time", + "Chile Summer Time", + "Falkland Islands Summer Time", + "Paraguay Summer Time" + ], + "ShortcutsDaylight": [ + "AMST", + "ADT", + "CLST", + "FKST", + "PYST" + ], + "CountriesStandard": [ + "Anguilla", + "Antigua and Barbuda", + "Aruba", + "Barbados", + "Bolivia", + "Amazonas (most, Brazil)", + "Mato Grosso (Brazil)", + "Mato Grosso do Sul (Brazil)", + "Rondônia (Brazil)", + "Roraima (Brazil)", + "British Virgin Islands", + "Quebec (east, Canada)", + "Caribbean Netherlands", + "Curaçao", + "Dominica", + "Dominican Republic", + "Grenada", + "Guadeloupe", + "Guyana", + "Martinique", + "Montserrat", + "Puerto Rico", + "Saint Barthélemy", + "Saint Kitts and Nevis", + "Saint Lucia", + "Saint Martin", + "Saint Vincent and the Grenadines", + "Sint Maarten", + "Trinidad and Tobago", + "U.S. Virgin Islands", + "Venezuela" + ], + "CountriesDaylight": [ + "Bermuda", + "Labrador (most, Canada)", + "New Brunswick (Canada)", + "Nova Scotia (Canada)", + "Prince Edward Island (Canada)", + "Chile (most)", + "Thule Air Base (Greenland)", + "Paraguay" + ] + }, + { + "Offset": "-03:30", + "Name": "Newfoundland Time Zone", + "Shortcut": "NT", + "TimeNamesStandard": [ + "Newfoundland Standard Time" + ], + "ShortcutsStandard": [ + "NST" + ], + "TimeNamesDaylight": [ + "Newfoundland Daylight Time" + ], + "ShortcutsDaylight": [ + "NDT" + ], + "CountriesStandard": [ + "Labrador (southeast, Canada)" + ], + "CountriesDaylight": [ + "Newfoundland (Canada)" + ] + }, + { + "Offset": "-03:00", + "MilitaryName": "Papa Time Zone", + "TimeNamesStandard": [ + "Argentina Time", + "Brasília Time", + "French Guiana Time", + "Saint Pierre and Miquelon Standard Time", + "Rothera Research Station Time", + "Suriname Time", + "Uruguay Standard Time", + "West Greenland Time" + ], + "ShortcutsStandard": [ + "ART", + "BRT", + "GFT", + "PMST", + "ROTT", + "SRT", + "UYT", + "WGT" + ], + "TimeNamesDaylight": [ + "Brasília Summer Time", + "Saint Pierre and Miquelon Daylight Time", + "Uruguay Summer Time", + "West Greenland Summer Time" + ], + "ShortcutsDaylight": [ + "BRST", + "PMDT", + "WGST", + "UYST" + ], + "CountriesStandard": [ + "Argentina", + "Brazil (most)", + "Magallanes (Chile)", + "Falkland Islands", + "French Guiana", + "SuriName", + "Uruguay" + ], + "CountriesDaylight": [ + "Greenland (most)", + "Saint Pierre and Miquelon" + ] + }, + { + "Offset": "-02:00", + "MilitaryName": "Oscar Time Zone", + "TimeNamesStandard": [ + "Fernando de Noronha Time", + "South Georgia and the South Sandwich Islands Time" + ], + "ShortcutsStandard": [ + "FNT", + "GST" + ], + "CountriesStandard": [ + "Fernando de Noronha (Brazil)", + "South Georgia and the South Sandwich Islands" + ] + }, + { + "Offset": "-01:00", + "MilitaryName": "November Time Zone", + "TimeNamesStandard": [ + "Azores Standard Time", + "Cape Verde Time", + "Eastern Greenland Time" + ], + "ShortcutsStandard": [ + "AZOT", + "CVT", + "EGT" + ], + "TimeNamesDaylight": [ + "Azores Summer Time", + "Eastern Greenland Summer Time" + ], + "ShortcutsDaylight": [ + "AZOST", + "EGST" + ], + "CountriesStandard": [ + "Cabo Verde" + ], + "CountriesDaylight": [ + "Ittoqqortoormiit (Greenland)", + "Azores (Portugal)" + ] + }, + { + "Offset": "00:00", + "Name": "Coordinated Universal Time", + "Shortcut": "UTC", + "MilitaryName": "Zulu Time Zone", + "TimeNamesStandard": [ + "Greenwich Mean Time", + "Western European Time" + ], + "ShortcutsStandard": [ + "GMT", + "WET" + ], + "TimeNamesDaylight": [ + "British Summer Time", + "Irish Standard Time", + "Western European Summer Time" + ], + "ShortcutsDaylight": [ + "BST", + "IST", + "WEST" + ], + "CountriesStandard": [ + "Saint Helena, Ascension and Tristan da Cunha", + "Burkina Faso", + "Danmarkshavn", + "Gambia", + "Ghana", + "Guinea-Bissau", + "Guinea", + "Iceland", + "Côte d’Ivoire", + "Liberia", + "Mali", + "Mauritania", + "São Tomé and Príncipe", + "Senegal", + "Sierra Leone", + "Togo" + ], + "CountriesDaylight": [ + "Canary Islands", + "Faroe Islands", + "Guernsey", + "Ireland", + "Isle of Man", + "Jersey", + "Portugal (most)", + "United Kingdom" + ] + }, + { + "Offset": "01:00", + "MilitaryName": "Alpha Time Zone", + "TimeNamesStandard": [ + "Central European Time", + "Middle European Time", + "West Africa Time" + ], + "ShortcutsStandard": [ + "CET", + "MET", + "WAT" + ], + "TimeNamesDaylight": [ + "Central European Summer Time", + "Heure Avancée d'Europe Centrale", + "Middle European Summer Time", + "West Africa Summer Time" + ], + "ShortcutsDaylight": [ + "CEST", + "HAEC", + "MEST", + "WAST" + ], + "CountriesStandard": [ + "Algeria", + "Angola", + "Benin", + "Cameroon", + "Central African Republic", + "Chad", + "Congo", + "Équateur (Democratic Republic of the Congo)", + "Kinshasa (Democratic Republic of the Congo)", + "Kongo Central (Democratic Republic of the Congo)", + "Kwango (Democratic Republic of the Congo)", + "Kwilu (Democratic Republic of the Congo)", + "Mai-Ndombe (Democratic Republic of the Congo)", + "Mongala (Democratic Republic of the Congo)", + "Nord-Ubangi (Democratic Republic of the Congo)", + "Sud-Ubangi (Democratic Republic of the Congo)", + "Tshuapa (Democratic Republic of the Congo)", + "Equatorial Guinea", + "Gabon", + "Morocco", + "Niger", + "Nigeria", + "Tunisia", + "Weste" + ], + "CountriesDaylight": [ + "Albania", + "Andorra", + "Austria", + "Belgium", + "Bosnia and Herzegovina", + "Croatia", + "Czech Republic", + "Denmark", + "France (metropolitan)", + "Germany", + "Gibraltar", + "Hungary", + "Italy", + "Kosovo", + "Liechtenstein", + "Luxembourg", + "Malta", + "Monaco", + "Montenegro", + "Netherlands (European)", + "North Macedonia", + "Norway", + "Poland", + "San Marino", + "Serbia", + "Slovakia", + "Slovenia", + "Spain (most)", + "Sweden", + "Switzerland", + "Vatican City" + ] + }, + { + "Offset": "02:00", + "MilitaryName": "Bravo Time Zone", + "TimeNamesStandard": [ + "Central Africa Time", + "Eastern European Time", + "Israel Standard Time", + "Kaliningrad Time", + "South African Standard Time" + ], + "ShortcutsStandard": [ + "CAT", + "EET", + "IST", + "KALT", + "SAST" + ], + "TimeNamesDaylight": [ + "Eastern European Summer Time", + "Israel Daylight Time" + ], + "ShortcutsDaylight": [ + "EEST", + "IDT" + ], + "CountriesStandard": [ + "Botswana", + "Burundi", + "Democratic Republic of the Congo (most)", + "Egypt", + "Eswatini", + "Lesotho", + "Libya", + "Malawi", + "Mozambique", + "Namibia", + "Kaliningrad (Russia)", + "Rwanda", + "South Africa (most)", + "South Sudan", + "Sudan", + "Zambia", + "Zimbabwe" + ], + "CountriesDaylight": [ + "Akrotiri and Dhekelia", + "Bulgaria", + "Cyprus", + "Estonia", + "Finland", + "Greece", + "Israel", + "Jordan", + "Latvia", + "Lebanon", + "Lithuania", + "Moldova", + "Palestine Authority", + "Romania", + "Transnistria", + "Syria" + ] + }, + { + "Offset": "03:00", + "MilitaryName": "Charlie Time Zone", + "TimeNamesStandard": [ + "Arabia Standard Time", + "East Africa Time", + "Further-eastern European Time", + "Indian Ocean Time", + "Moscow Time", + "Showa Station Time", + "Turkey Time" + ], + "ShortcutsStandard": [ + "AST", + "EAT", + "FET", + "IOT", + "MSK", + "SYOT", + "TRT" + ], + "CountriesStandard": [ + "Abkhazia", + "Bahrain", + "Belarus", + "Comoros", + "Crimea", + "Djibouti", + "Donetsk PR", + "Eritrea", + "Ethiopia", + "Scattered Islands (French Southern and Antarctic Lands)", + "Iraq", + "Kenya", + "Kuwait", + "Luhansk PR", + "Madagascar", + "Mayotte", + "Qatar", + "Russia (most of European part)", + "Saudi Arabia", + "Somalia", + "Prince Edward Islands (South Africa)", + "South Ossetia", + "Tanzania", + "Turkey", + "Uganda", + "Yemen" + ] + }, + { + "Offset": "03:30", + "TimeNamesStandard": [ + "Iran Standard Time" + ], + "ShortcutsStandard": [ + "IRST" + ], + "TimeNamesDaylight": [ + "Iran Daylight Time" + ], + "ShortcutsDaylight": [ + "IRDT" + ], + "CountriesDaylight": [ + "Iran" + ] + }, + { + "Offset": "04:00", + "MilitaryName": "Delta Time Zone", + "TimeNamesDaylight": [ + "Armenia Time", + "Azerbaijan Time", + "Georgia Standard Time", + "Gulf Standard Time", + "Mauritius Time", + "Réunion Time", + "Samara Time", + "Seychelles Time", + "Volgograd Time", + "United Arab Emirates Standard Time" + ], + "ShortcutsStandard": [ + "AMT", + "AZT", + "GET", + "GST", + "MUT", + "RET", + "SAMT", + "SCT", + "VOLT" + ], + "CountriesDaylight": [ + "Armenia", + "Azerbaijan", + "Crozet Islands (French Southern and Antarctic Lands)", + "Georgia", + "Mauritius", + "Oman", + "Astrakhan (Russia)", + "Samara (Russia)", + "Saratov (Russia)", + "Udmurtia (Russia)", + "Ulyanovsk (Russia)", + "Réunion", + "Seychelles", + "United Arab Emirates" + ] + }, + { + "Offset": "04:30", + "TimeNamesStandard": [ + "Afghanistan Time" + ], + "ShortcutsStandard": [ + "AFT" + ], + "CountriesStandard": [ + "Afghanistan" + ] + }, + { + "Offset": "05:00", + "MilitaryName": "Echo Time Zone", + "TimeNamesStandard": [ + "Aqtobe Time", + "Heard and McDonald Islands Time", + "Mawson Station Time", + "Maldives Time", + "Oral Time", + "Pakistan Standard Time", + "French Southern and Antarctic Time", + "Tajikistan Time", + "Turkmenistan Time", + "Uzbekistan Time", + "Yekaterinburg Time" + ], + "ShortcutsStandard": [ + "AQTT", + "HMT", + "MAWT", + "MVT", + "ORAT", + "PKT", + "TFT", + "TJT", + "TMT", + "UZT", + "YEKT" + ], + "CountriesStandard": [ + "Amsterdam Island (French Southern and Antarctic Lands)", + "Kerguelen Islands (French Southern and Antarctic Lands)", + "Saint Paul Island (French Southern and Antarctic Lands)", + "Heard Island and McDonald Islands", + "Aktobe (Kazakhstan)", + "Atyrau (Kazakhstan)", + "Baikonur (Kazakhstan)", + "Kyzylorda (Kazakhstan)", + "Mangystau (Kazakhstan)", + "West Kazakhstan (Kazakhstan)", + "Maldives", + "Pakistan", + "Bashkortostan (Russia)", + "Chelyabinsk (Russia)", + "Khanty-Mansi (Russia)", + "Kurgan (Russia)", + "Orenburg (Russia)", + "Perm (Russia)", + "Yekaterinburg", + "Tyumen (Russia)", + "Yamalia (Russia)", + "Tajikistan", + "Turkmenistan", + "Uzbekistan" + ] + }, + { + "Offset": "05:30", + "TimeNamesStandard": [ + "Indian Standard Time", + "Sri Lanka Standard Time" + ], + "ShortcutsStandard": [ + "IST", + "SLST" + ], + "CountriesStandard": [ + "India", + "Sri Lanka" + ] + }, + { + "Offset": "05:45", + "TimeNamesStandard": [ + "Nepal Standard Time" + ], + "ShortcutsStandard": [ + "NPT" + ], + "CountriesStandard": [ + "Nepal" + ] + }, + { + "Offset": "06:00", + "MilitaryName": "Foxtrot Time Zone", + "TimeNamesStandard": [ + "Alma-Ata Time", + "British Indian Ocean Time", + "Bangladesh Standard Time", + "Bhutan Time", + "Kyrgyzstan Time", + "Omsk Time", + "Vostok Station Time" + ], + "ShortcutsStandard": [ + "ALMT", + "BIOT", + "BST", + "BTT", + "KGT", + "OMST", + "VOST" + ], + "CountriesStandard": [ + "Bangladesh", + "Bhutan", + "British Indian Ocean Territory", + "Kazakhstan (most)", + "Kyrgyzstan", + "Omsk (Russia)" + ] + }, + { + "Offset": "06:30", + "TimeNamesStandard": [ + "ASEAN Common Time", + "Cocos Islands Time", + "Myanmar Standard Time" + ], + "ShortcutsStandard": [ + "ACT", + "CCT", + "MMT" + ], + "CountriesStandard": [ + "Cocos Islands", + "Myanmar" + ] + }, + { + "Offset": "07:00", + "MilitaryName": "Golf Time Zone", + "TimeNamesStandard": [ + "Christmas Island Time", + "Davis Time", + "Hovd Time", + "Indochina Time", + "Krasnoyarsk Time", + "Novosibirsk Time", + "Thailand Standard Time", + "Western Indonesian Time" + ], + "ShortcutsStandard": [ + "CXT", + "DAVT", + "HOVT", + "ICT", + "KRAT", + "NOVT", + "THA", + "WIB" + ], + "CountriesStandard": [ + "Cambodia", + "Christmas Island", + "Central Kalimantan (Indonesia)", + "Java (Indonesia)", + "Sumatra (Indonesia)", + "West Kalimantan (Indonesia)", + "Laos", + "Bayan-Ölgii (Mongolia)", + "Khovd (Mongolia)", + "Uvs (Mongolia)", + "Altai Krai (Russia)", + "Altai Republic (Russia)", + "Kemerovo (Russia)", + "Khakassia (Russia)", + "Krasnoyarsk (Russia)", + "Novosibirsk (Russia)", + "Tomsk (Russia)", + "Tuva (Russia)", + "Thailand", + "Vietnam" + ] + }, + { + "Offset": "08:00", + "MilitaryName": "Hotel Time Zone", + "TimeNamesStandard": [ + "Australian Western Standard Time", + "Brunei Time", + "Choibalsan Standard Time", + "Clipperton Island Standard Time", + "China Standard Time", + "Hong Kong Time", + "Irkutsk Time", + "Malaysia Standard Time", + "Malaysia Time", + "Philippine Time", + "Philippine Standard Time", + "Singapore Time", + "Singapore Standard Time", + "Ulaanbaatar Standard Time", + "Central Indonesian Time", + "Western Standard Time", + "Western Australia Standard Time" + ], + "ShortcutsStandard": [ + "AWST", + "BNT", + "CHOT", + "CIST", + "CST", + "HKT", + "IRKT", + "MST", + "MYT", + "PHT", + "PHST", + "SST", + "ULAT", + "WITA", + "WST" + ], + "TimeNamesDaylight": [ + "Ulaanbaatar Summer Time" + ], + "ShortcutsDaylight": [ + "ULAST" + ], + "CountriesStandard": [ + "Australia: Western Australia (most)", + "Brunei", + "China", + "Hong Kong SAR", + "Bali (Indonesia)", + "East Kalimantan (Indonesia)", + "East Nusa Tenggara (Indonesia)", + "North Kalimantan (Indonesia)", + "South Kalimantan (Indonesia)", + "Sulawesi (Indonesia)", + "West Nusa Tenggara (Indonesia)", + "Macao SAR", + "Malaysia", + "Mongolia (most)", + "Philippines", + "Buryatia (Russia)", + "Irkutsk (Russia)", + "Singapore", + "Taiwan" + ] + }, + { + "Offset": "08:45", + "TimeNamesStandard": [ + "Australian Central Western Standard Time", + "Central Western Standard Time" + ], + "ShortcutsStandard": [ + "ACWST", + "CWST" + ], + "CountriesStandard": [ + "Caiguna (Australia)", + "Cocklebiddy (Australia)", + "Eucla (Australia)", + "Madura (Australia)", + "Mundrabilla (Australia)", + "Border Village (Australia)" + ] + }, + { + "Offset": "09:00", + "MilitaryName": "India Time Zone", + "TimeNamesStandard": [ + "Choibalsan Summer Time", + "Japan Standard Time", + "Korea Standard Time", + "Palau Time", + "Timor Leste Time", + "Eastern Indonesian Time", + "Yakutsk Time" + ], + "ShortcutsStandard": [ + "CHOST", + "JST", + "KST", + "PWT", + "TLT", + "WIT", + "YAKT" + ], + "CountriesStandard": [ + "Timor-Leste", + "Maluku (Indonesia)", + "North Maluku (Indonesia)", + "Papua (Indonesia)", + "West Papua (Indonesia)", + "Japan", + "North Korea", + "Palau", + "Amur (Russia)", + "Sakha (most, Russia)", + "Zabaykalsky (Russia)", + "South Korea" + ] + }, + { + "Offset": "09:30", + "TimeNamesStandard": [ + "Australian Central Standard Time" + ], + "ShortcutsStandard": [ + "ACST" + ], + "TimeNamesDaylight": [ + "Australian Central Daylight Saving Time" + ], + "ShortcutsDaylight": [ + "ACDT" + ], + "CountriesStandard": [ + "Northern Territory", + "South Australia (Australia)" + ] + }, + { + "Offset": "10:00", + "Name": "Chamorro Time Zone", + "MilitaryName": "Kilo Time Zone", + "TimeNamesStandard": [ + "Australian Eastern Standard Time", + "Australian Eastern Time", + "Chamorro Standard Time", + "Chuuk Time", + "Dumont d'Urville Time", + "Papua New Guinea Time", + "Vladivostok Time" + ], + "ShortcutsStandard": [ + "AEST", + "AET", + "CHST", + "CHUT", + "DDUT", + "PGT", + "VLAT" + ], + "TimeNamesDaylight": [ + "Australian Eastern Daylight Saving Time", + "Lord Howe Summer Time" + ], + "ShortcutsDaylight": [ + "AEDT", + "LHST" + ], + "CountriesStandard": [ + "Queensland (Australia)", + "Guam", + "Chuuk (Micronesia)", + "Yap (Micronesia)", + "Northern Mariana Islands", + "Papua New Guinea (most)", + "Jewish (Russia)", + "Khabarovsk (Russia)", + "Primorsky (Russia)", + "Sakha (central-east, Russia)" + ], + "CountriesDaylight": [ + "Australian Capital Territory (Australia)", + "Jervis Bay Territory (Australia)", + "New South Wales (most, Australia)", + "Tasmania (Australia)", + "Victoria (Australia)" + ] + }, + { + "Offset": "10:30", + "TimeNamesStandard": [ + "Lord Howe Standard Time" + ], + "ShortcutsStandard": [ + "LHST" + ], + "CountriesDaylight": [ + "Lord Howe Island (Australia)" + ] + }, + { + "Offset": "11:00", + "MilitaryName": "Lima Time Zone", + "TimeNamesStandard": [ + "Bougainville Standard Time", + "Kosrae Time", + "Macquarie Island Station Time", + "New Caledonia Time", + "Norfolk Island Time", + "Pohnpei Standard Time", + "Sakhalin Island Time", + "Solomon Islands Time", + "Srednekolymsk Time", + "Vanuatu Time" + ], + "ShortcutsStandard": [ + "BST", + "KOST", + "MIST", + "NCT", + "NFT", + "PONT", + "SAKT", + "SBT", + "SRET", + "VUT" + ], + "CountriesStandard": [ + "Kosrae (Micronesia)", + "Pohnpei (Micronesia)", + "New Caledonia", + "Bougainville (Papua New Guinea)", + "Magadan (Russia)", + "Sakha (east, Russia)", + "Sakhalin (Russia)", + "Solomon Islands", + "Vanuatu" + ], + "CountriesDaylight": [ + "Norfolk Island" + ] + }, + { + "Offset": "12:00", + "MilitaryName": "Mike Time Zone", + "TimeNamesStandard": [ + "Anadyr Time", + "Fiji Time", + "Gilbert Island Time", + "Magadan Time", + "Marshall Islands Time", + "New Zealand Standard Time", + "Kamchatka Time", + "Tuvalu Time", + "Wake Island Time" + ], + "ShortcutsStandard": [ + "ANAT", + "FJT", + "GILT", + "MAGT", + "MHT", + "NZST", + "PETT", + "TVT", + "WAKT" + ], + "CountriesStandard": [ + "Fiji", + "Gilbert Islands (Kiribati)", + "Marshall Islands", + "Nauru", + "Chukotka (Russia)", + "Kamchatka (Russia)", + "Tuvalu", + "Wake Island", + "Wallis and Futuna" + ], + "TimeNamesDaylight": [ + "New Zealand Daylight Time" + ], + "ShortcutsDaylight": [ + "NZDT" + ], + "CountriesDaylight": [ + "New Zealand" + ] + }, + { + "Offset": "12:45", + "TimeNamesStandard": [ + "Chatham Standard Time" + ], + "ShortcutsStandard": [ + "CHAST" + ], + "TimeNamesDaylight": [ + "Chatham Daylight Time" + ], + "ShortcutsDaylight": [ + "CHADT" + ], + "CountriesStandard": [ + "Chatham Islands (New Zealand)", + "Pitt Islands (New Zealand)", + "South East Island (New Zealand)", + "The Fort (New Zealand)", + "Little Mangere Island (New Zealand)", + "Star Keys (New Zealand)", + "The Sisters (New Zealand)", + "Forty-Fours (New Zealand)" + ] + }, + { + "Offset": "13:00", + "TimeNamesStandard": [ + "Phoenix Island Time", + "Tokelau Time", + "Tonga Time" + ], + "ShortcutsStandard": [ + "PHOT", + "TKT", + "TOT" + ], + "CountriesStandard": [ + "Phoenix Islands (Kiribati)", + "Samoa", + "Tokelau", + "Tonga" + ] + }, + { + "Offset": "14:00", + "TimeNamesStandard": [ + "Line Islands Time" + ], + "ShortcutsStandard": [ + "LINT" + ], + "CountriesStandard": [ + "Line Islands (Kiribati)" + ] + } + ] +} \ No newline at end of file diff --git a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs index 02c17e066d..9baf7a6d94 100644 --- a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs +++ b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs @@ -91,23 +91,23 @@ namespace Wox.Infrastructure.Exception // GlobalAssemblyCache - .NET Core and .NET 5 and later: false in all cases. // Source https://learn.microsoft.com/dotnet/api/system.reflection.assembly.globalassemblycache?view=net-6.0 - foreach (var ass in AppDomain.CurrentDomain.GetAssemblies()) + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { sb.Append("* "); - sb.Append(ass.FullName); + sb.Append(assembly.FullName); sb.Append(" ("); - if (ass.IsDynamic) + if (assembly.IsDynamic) { sb.Append("dynamic assembly doesn't has location"); } - else if (string.IsNullOrEmpty(ass.Location)) + else if (string.IsNullOrEmpty(assembly.Location)) { sb.Append("location is null or empty"); } else { - sb.Append(ass.Location); + sb.Append(assembly.Location); } sb.AppendLine(")"); diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index c9d7174650..5357ece996 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -53,7 +53,7 @@ namespace PowerAccent.Core Language.CUR => GetDefaultLetterKeyCUR(letter), // Currency Language.CY => GetDefaultLetterKeyCY(letter), // Welsh Language.CZ => GetDefaultLetterKeyCZ(letter), // Czech - Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish Gaelic) + Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish) Language.GD => GetDefaultLetterKeyGD(letter), // Gàidhlig (Scottish Gaelic) Language.DE => GetDefaultLetterKeyDE(letter), // German Language.EST => GetDefaultLetterKeyEST(letter), // Estonian From cbf1c61c5829f321dc46eb80f9a1dc99faf8770f Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Thu, 23 Feb 2023 09:15:43 +0000 Subject: [PATCH 068/163] [Installer]Add missing experimentation fluent icon (#24278) --- installer/PowerToysSetup/Settings.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs index a162135bd8..6165e822da 100644 --- a/installer/PowerToysSetup/Settings.wxs +++ b/installer/PowerToysSetup/Settings.wxs @@ -7,7 +7,7 @@ - + From 1e1429177b7e1d62bdd4708095162f1d92a64773 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Thu, 23 Feb 2023 06:59:49 -0800 Subject: [PATCH 069/163] [PastePlain] Introduce Paste as Plain Text module (#23645) * [PastePlain] Introduce Paste as Plain Text module * fix build * add telemetry * update settings UI * spell * Add navigation items to Settings and OOBE * Add PastePlain to the Quick Access flyout * try to fix PastePlain not being enabled from runner/settings * load dll properly * installer files * Add PastePlain project name * Use win32 APIs in the module interface instead * Fix spellcheck * Fix build errors * Add success, error and invoke telemetry * Add Settings Telemetry * Add GPO definitions * Fix analyzer errors * Use static_cast instead of reinterpret_cast * Add images to Settings * Add note about replacing clipboard contents * Fix learn more text * Add link to readme * Remove unneeded C# app * Fix installer * Fix spellchecker --------- Co-authored-by: Jaime Bernardo --- .github/actions/spell-check/expect.txt | 34 +- .github/actions/spell-check/patterns.txt | 2 +- .pipelines/ESRPSigning_core.json | 2 + PowerToys.sln | 20 +- README.md | 6 +- installer/PowerToysSetup/Common.wxi | 3 +- installer/PowerToysSetup/PastePlain.wxs | 20 + .../PowerToysSetup/PowerToysInstaller.wixproj | 1 + installer/PowerToysSetup/Product.wxs | 5 + installer/PowerToysSetup/Settings.wxs | 6 +- src/common/GPOWrapper/GPOWrapper.cpp | 4 + src/common/GPOWrapper/GPOWrapper.h | 1 + src/common/GPOWrapper/GPOWrapper.idl | 1 + src/common/GPOWrapperProjection/GPOWrapper.cs | 5 + src/common/interop/shared_constants.h | 2 +- src/common/utils/gpo.h | 6 + src/gpo/assets/PowerToys.admx | 10 + src/gpo/assets/en-US/PowerToys.adml | 1 + .../PastePlain.base.rc | 40 ++ .../PastePlainConstants.h | 8 + .../PastePlainModuleInterface.vcxproj | 85 +++ .../PastePlainModuleInterface.vcxproj.filters | 62 +++ .../PastePlainModuleInterface/Resources.resx | 126 +++++ .../PastePlainModuleInterface/dllmain.cpp | 495 ++++++++++++++++++ .../PastePlainModuleInterface/packages.config | 4 + .../PastePlainModuleInterface/pch.cpp | 2 + .../PastePlainModuleInterface/pch.h | 7 + .../PastePlainModuleInterface/resource.base.h | 13 + .../PastePlainModuleInterface/trace.cpp | 82 +++ .../PastePlainModuleInterface/trace.h | 24 + src/runner/main.cpp | 1 + .../Settings.UI.Library/EnabledModules.cs | 17 + .../PastePlainProperties.cs | 21 + .../Settings.UI.Library/PastePlainSettings.cs | 49 ++ src/settings-ui/Settings.UI/App.xaml.cs | 1 + .../FluentIcons/FluentIconsPastePlain.png | Bin 0 -> 538873 bytes .../Assets/Modules/OOBE/PastePlain.gif | Bin 0 -> 26706 bytes .../Settings.UI/Assets/Modules/PastePlain.png | Bin 0 -> 9930 bytes .../Settings.UI/MainWindow.xaml.cs | 3 + .../OOBE/Enums/PowerToysModules.cs | 1 + .../OOBE/Views/OobePastePlain.xaml | 44 ++ .../OOBE/Views/OobePastePlain.xaml.cs | 46 ++ .../Settings.UI/OOBE/Views/OobeShellPage.xaml | 4 + .../OOBE/Views/OobeShellPage.xaml.cs | 7 + .../Settings.UI/Strings/en-us/Resources.resw | 29 + .../ViewModels/Flyout/AllAppsViewModel.cs | 6 + .../ViewModels/PastePlainViewModel.cs | 196 +++++++ .../Settings.UI/Views/PastePlainPage.xaml | 55 ++ .../Settings.UI/Views/PastePlainPage.xaml.cs | 33 ++ .../Settings.UI/Views/ShellPage.xaml | 5 + 50 files changed, 1576 insertions(+), 19 deletions(-) create mode 100644 installer/PowerToysSetup/PastePlain.wxs create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/PastePlain.base.rc create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/PastePlainConstants.h create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj.filters create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/Resources.resx create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/dllmain.cpp create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/packages.config create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/pch.cpp create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/pch.h create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/resource.base.h create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/trace.cpp create mode 100644 src/modules/pasteplain/PastePlainModuleInterface/trace.h create mode 100644 src/settings-ui/Settings.UI.Library/PastePlainProperties.cs create mode 100644 src/settings-ui/Settings.UI.Library/PastePlainSettings.cs create mode 100644 src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPastePlain.png create mode 100644 src/settings-ui/Settings.UI/Assets/Modules/OOBE/PastePlain.gif create mode 100644 src/settings-ui/Settings.UI/Assets/Modules/PastePlain.png create mode 100644 src/settings-ui/Settings.UI/OOBE/Views/OobePastePlain.xaml create mode 100644 src/settings-ui/Settings.UI/OOBE/Views/OobePastePlain.xaml.cs create mode 100644 src/settings-ui/Settings.UI/ViewModels/PastePlainViewModel.cs create mode 100644 src/settings-ui/Settings.UI/Views/PastePlainPage.xaml create mode 100644 src/settings-ui/Settings.UI/Views/PastePlainPage.xaml.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index c2e7270be1..a755131232 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -721,6 +721,7 @@ HTTRANSPARENT HValue Hvci hwb +HWHEEL HWINEVENTHOOK hwnd HWNDFIRST @@ -893,6 +894,8 @@ LCONTROL LCtrl Ldone ldx +LEFTDOWN +LEFTUP LEFTSCROLLBAR lego len @@ -1041,6 +1044,8 @@ Mgmt mic microsoft Midl +MIDDLEDOWN +MIDDLEUP mii MIIM millis @@ -1174,6 +1179,7 @@ NOACTIVATE NOAGGREGATION NOASYNC NOCLOSEPROCESS +NOCOALESCE NOCOPYBITS nodeca nodiscard @@ -1247,7 +1253,7 @@ oldpath oldtheme oleaut OLECHAR -oledb +OLEDB oledbcommand oledbconnection OLIVEGREEN @@ -1292,6 +1298,7 @@ PARENTRELATIVEPARSING PArgb parray PARTIALCONFIRMATIONDIALOGTITLE +pasteplain pathcch Pathto PAUDIO @@ -1377,7 +1384,7 @@ Prefixer Preinstalled prevhost previewer -previewhandlerframeinfo +PREVIEWHANDLERFRAMEINFO previewpane previouscamera PREVIOUSINSTALLFOLDER @@ -1465,7 +1472,7 @@ rectp rects recyclebin redirectedfrom -redist +Redist redistributable reencode reencoded @@ -1522,6 +1529,8 @@ rgs rhs ricardosantos RIDEV +RIGHTDOWN +RIGHTUP RIGHTSCROLLBAR riid RKey @@ -1529,6 +1538,7 @@ RLO RMENU RNumber roadmap +Roamable robmensching Roboto rooler @@ -1588,7 +1598,7 @@ secauthz secpol Secur securityoverview -segoe +Segoe Sekan SENDCHANGE sendinput @@ -1654,7 +1664,7 @@ SHOWMINNOACTIVE SHOWNA SHOWNOACTIVATE SHOWNORMAL -showwindow +SHOWWINDOW shtypes SICHINT sid @@ -1688,6 +1698,7 @@ Soref SOURCECLIENTAREAONLY SOURCEHEADER sourcesdirectory +SPACEBAR spam spdisp spdlog @@ -1721,7 +1732,7 @@ STARTUPINFO STARTUPINFOEX STARTUPINFOW startupscreen -statflag +STATFLAG STATICEDGE STATSTG stdafx @@ -1733,7 +1744,7 @@ STDMETHODCALLTYPE STDMETHODIMP stefan Stereolithography -stgm +STGM STGMEDIUM sticpl stl @@ -1780,7 +1791,7 @@ SYSICONINDEX sysinfo SYSKEY syskeydown -syskeyup +SYSKEYUP SYSMENU SYSTEMAPPS systemroot @@ -1893,6 +1904,7 @@ UIs Ulaanbaatar ULARGE ULONGLONG +UMsg unassign uncompilable UNCPRIORITY @@ -1972,6 +1984,7 @@ VIDEOINFOHEADER viewbox viewmodel vih +VIRTUALDESK virtualkey visiblecolorformats Visibletrue @@ -2062,7 +2075,7 @@ WINL winmd winmm winmsg -winnt +WINNT winres winrt winsdk @@ -2130,6 +2143,7 @@ wtypes Wubi wuceffectsi WVC +WVk Wwan Wwanpp XAttribute @@ -2142,6 +2156,8 @@ XBUTTONUP XControl xcopy XDocument +XDOWN +XUP XElement XFile XIncrement diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt index 42ceb68d7f..f17c5fb2aa 100644 --- a/.github/actions/spell-check/patterns.txt +++ b/.github/actions/spell-check/patterns.txt @@ -56,7 +56,7 @@ https?://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]* /gist\.github\.com/[^/]+/[0-9a-f]+ # msdn -\b(?:download\.visualstudio|docs|msdn)\.microsoft\.com/[-_a-zA-Z0-9()=./]* +\b(?:download\.visualstudio|docs|msdn|learn)\.microsoft\.com/[-_a-zA-Z0-9()=./]* # medium link\.medium\.com/[a-zA-Z0-9]+ diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index ce467a6317..96efd6cd4a 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -36,6 +36,8 @@ "modules\\PowerOCR\\PowerToys.PowerOCR.dll", "modules\\PowerOCR\\PowerToys.PowerOCR.exe", + "modules\\PastePlain\\PowerToys.PastePlainModuleInterface.dll", + "modules\\Awake\\PowerToys.AwakeModuleInterface.dll", "modules\\Awake\\PowerToys.Awake.exe", "modules\\Awake\\PowerToys.Awake.dll", diff --git a/PowerToys.sln b/PowerToys.sln index df3b5bf6e7..725839892c 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -487,7 +487,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StlThumbnailProviderCpp", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SvgThumbnailProviderCpp", "src\modules\previewpane\SvgThumbnailProviderCpp\SvgThumbnailProviderCpp.vcxproj", "{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AllExperiments", "src\common\AllExperiments\AllExperiments.csproj", "{9CE59ED5-7087-4353-88EB-788038A73CEC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pasteplain", "pasteplain", "{9873BA05-4C41-4819-9283-CF45D795431B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PastePlainModuleInterface", "src\modules\pasteplain\PastePlainModuleInterface\PastePlainModuleInterface.vcxproj", "{FC373B24-3293-453C-AAF5-CF2909DCEE6A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AllExperiments", "src\common\AllExperiments\AllExperiments.csproj", "{9CE59ED5-7087-4353-88EB-788038A73CEC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -2024,6 +2028,18 @@ Global {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x64.Build.0 = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.ActiveCfg = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.Build.0 = Release|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|ARM64.Build.0 = Debug|ARM64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x64.ActiveCfg = Debug|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x64.Build.0 = Debug|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x86.ActiveCfg = Debug|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x86.Build.0 = Debug|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|ARM64.ActiveCfg = Release|ARM64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|ARM64.Build.0 = Release|ARM64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|x64.ActiveCfg = Release|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|x64.Build.0 = Release|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|x86.ActiveCfg = Release|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|x86.Build.0 = Release|x64 {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|ARM64.ActiveCfg = Debug|ARM64 {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|ARM64.Build.0 = Debug|ARM64 {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x64.ActiveCfg = Debug|x64 @@ -2206,6 +2222,8 @@ Global {CA5518ED-0458-4B09-8F53-4122B9888655} = {2F305555-C296-497E-AC20-5FA1B237996A} {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D} = {2F305555-C296-497E-AC20-5FA1B237996A} {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA} = {2F305555-C296-497E-AC20-5FA1B237996A} + {9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} + {FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B} {9CE59ED5-7087-4353-88EB-788038A73CEC} = {1AFB6476-670D-4E80-A464-657E01DFF482} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/README.md b/README.md index 5224650e17..d76a97edc2 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline | [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) | [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | | [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) | [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | | [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | -| [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | -| [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | -| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | +| [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | +| [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | +| [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | ## Installing and running Microsoft PowerToys diff --git a/installer/PowerToysSetup/Common.wxi b/installer/PowerToysSetup/Common.wxi index 6aad3632b7..9e5b9ea5bc 100644 --- a/installer/PowerToysSetup/Common.wxi +++ b/installer/PowerToysSetup/Common.wxi @@ -14,7 +14,8 @@ - + + diff --git a/installer/PowerToysSetup/PastePlain.wxs b/installer/PowerToysSetup/PastePlain.wxs new file mode 100644 index 0000000000..cca601a1ca --- /dev/null +++ b/installer/PowerToysSetup/PastePlain.wxs @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj index e4c7dd649e..256ba27132 100644 --- a/installer/PowerToysSetup/PowerToysInstaller.wixproj +++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj @@ -74,6 +74,7 @@ call "..\..\publish.cmd" arm64 + diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 21b6372a11..8ef113c1b3 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -65,6 +65,7 @@ + @@ -478,6 +479,10 @@ + + + + diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs index 6165e822da..17b60bf190 100644 --- a/installer/PowerToysSetup/Settings.wxs +++ b/installer/PowerToysSetup/Settings.wxs @@ -5,9 +5,9 @@ - - - + + + diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index eb813ab616..dba7b29a5d 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -108,6 +108,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation { return static_cast(powertoys_gpo::getConfiguredTextExtractorEnabledValue()); } + GpoRuleConfigured GPOWrapper::GetConfiguredPastePlainEnabledValue() + { + return static_cast(powertoys_gpo::getConfiguredPastePlainEnabledValue()); + } GpoRuleConfigured GPOWrapper::GetConfiguredVideoConferenceMuteEnabledValue() { return static_cast(powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue()); diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h index da74b912d8..fcb559d034 100644 --- a/src/common/GPOWrapper/GPOWrapper.h +++ b/src/common/GPOWrapper/GPOWrapper.h @@ -33,6 +33,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation static GpoRuleConfigured GetConfiguredScreenRulerEnabledValue(); static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue(); static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue(); + static GpoRuleConfigured GetConfiguredPastePlainEnabledValue(); static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue(); static GpoRuleConfigured GetAllowExperimentationValue(); }; diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl index 5e6deef6d7..51189e48da 100644 --- a/src/common/GPOWrapper/GPOWrapper.idl +++ b/src/common/GPOWrapper/GPOWrapper.idl @@ -37,6 +37,7 @@ namespace PowerToys static GpoRuleConfigured GetConfiguredScreenRulerEnabledValue(); static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue(); static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue(); + static GpoRuleConfigured GetConfiguredPastePlainEnabledValue(); static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue(); static GpoRuleConfigured GetAllowExperimentationValue(); } diff --git a/src/common/GPOWrapperProjection/GPOWrapper.cs b/src/common/GPOWrapperProjection/GPOWrapper.cs index 7017a42084..108907d143 100644 --- a/src/common/GPOWrapperProjection/GPOWrapper.cs +++ b/src/common/GPOWrapperProjection/GPOWrapper.cs @@ -41,5 +41,10 @@ namespace PowerToys.GPOWrapperProjection { return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredTextExtractorEnabledValue(); } + + public static GpoRuleConfigured GetConfiguredPastePlainEnabledValue() + { + return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredPastePlainEnabledValue(); + } } } diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index 512e5f3039..43fc2bad20 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -48,7 +48,7 @@ namespace CommonSharedConstants // Path to the event used by MeasureTool const wchar_t MEASURE_TOOL_TRIGGER_EVENT[] = L"Local\\MeasureToolEvent-3d46745f-09b3-4671-a577-236be7abd199"; - + // Path to the event used by GcodePreviewHandler const wchar_t GCODE_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysGcodePreviewResizeEvent-6ff1f9bd-ccbd-4b24-a79f-40a34fb0317d"; diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index ee887c60ea..1e1bcc2140 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -45,6 +45,7 @@ namespace powertoys_gpo { const std::wstring POLICY_CONFIGURE_ENABLED_SCREEN_RULER = L"ConfigureEnabledUtilityScreenRuler"; const std::wstring POLICY_CONFIGURE_ENABLED_SHORTCUT_GUIDE = L"ConfigureEnabledUtilityShortcutGuide"; const std::wstring POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR = L"ConfigureEnabledUtilityTextExtractor"; + const std::wstring POLICY_CONFIGURE_ENABLED_PASTE_PLAIN = L"ConfigureEnabledUtilityPastePlain"; const std::wstring POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE = L"ConfigureEnabledUtilityVideoConferenceMute"; const std::wstring POLICY_ALLOW_EXPERIMENTATION = L"AllowExperimentation"; @@ -231,6 +232,11 @@ namespace powertoys_gpo { return getConfiguredValue(POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR); } + inline gpo_rule_configured_t getConfiguredPastePlainEnabledValue() + { + return getConfiguredValue(POLICY_CONFIGURE_ENABLED_PASTE_PLAIN); + } + inline gpo_rule_configured_t getConfiguredVideoConferenceMuteEnabledValue() { return getConfiguredValue(POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE); diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx index 00b0bb2b9d..4a7fbadafd 100644 --- a/src/gpo/assets/PowerToys.admx +++ b/src/gpo/assets/PowerToys.admx @@ -217,6 +217,16 @@ + + + + + + + + + + diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml index 17c379b691..9dadbafef2 100644 --- a/src/gpo/assets/en-US/PowerToys.adml +++ b/src/gpo/assets/en-US/PowerToys.adml @@ -55,6 +55,7 @@ If this setting is disabled, experimentation is not allowed. Find My Mouse: Configure enabled state Mouse Highlighter: Configure enabled state Mouse Pointer Crosshairs: Configure enabled state + Paste as Plain Text: Configure enabled state Power Rename: Configure enabled state PowerToys Run: Configure enabled state Quick Accent: Configure enabled state diff --git a/src/modules/pasteplain/PastePlainModuleInterface/PastePlain.base.rc b/src/modules/pasteplain/PastePlainModuleInterface/PastePlain.base.rc new file mode 100644 index 0000000000..b30e3923c9 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/PastePlain.base.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/pasteplain/PastePlainModuleInterface/PastePlainConstants.h b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainConstants.h new file mode 100644 index 0000000000..d72f7b3242 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainConstants.h @@ -0,0 +1,8 @@ +#pragma once +#include + +namespace PastePlainConstants +{ + // Name of the powertoy module. + inline const std::wstring ModuleKey = L"PastePlain"; +} \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj new file mode 100644 index 0000000000..8e1b3707b7 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj @@ -0,0 +1,85 @@ + + + + + + + + 15.0 + Win32Proj + {FC373B24-3293-453C-AAF5-CF2909DCEE6A} + PastePlain + PastePlainModuleInterface + PowerToys.PastePlainModuleInterface + + + + DynamicLibrary + v143 + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\PastePlain\ + + + + EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) + + + $(OutDir)$(TargetName)$(TargetExt) + + + + + + + + + + + + + + Create + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + Designer + + + + + + + + + + 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}. + + + + + \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj.filters b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj.filters new file mode 100644 index 0000000000..9f2d585a7d --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj.filters @@ -0,0 +1,62 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {875a08c6-f610-4667-bd0f-80171ed96072} + + + + + Header Files + + + Generated Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Resource Files + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/Resources.resx b/src/modules/pasteplain/PastePlainModuleInterface/Resources.resx new file mode 100644 index 0000000000..3e27759bfd --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/Resources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Paste As Plain Text + + + PowerToys integration to paste clipboard contents as plain text + + \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/dllmain.cpp b/src/modules/pasteplain/PastePlainModuleInterface/dllmain.cpp new file mode 100644 index 0000000000..e3bfdb5600 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/dllmain.cpp @@ -0,0 +1,495 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" + +#include +#include "trace.h" +#include "Generated Files/resource.h" +#include +#include +#include + +#include "PastePlainConstants.h" +#include +#include +#include + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, + DWORD ul_reason_for_call, + LPVOID /*lpReserved*/) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + + return TRUE; +} + +namespace +{ + const wchar_t JSON_KEY_PROPERTIES[] = L"properties"; + const wchar_t JSON_KEY_WIN[] = L"win"; + const wchar_t JSON_KEY_ALT[] = L"alt"; + const wchar_t JSON_KEY_CTRL[] = L"ctrl"; + const wchar_t JSON_KEY_SHIFT[] = L"shift"; + const wchar_t JSON_KEY_CODE[] = L"code"; + const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut"; +} + +struct ModuleSettings +{ +} g_settings; + +class PastePlain : public PowertoyModuleIface +{ +private: + bool m_enabled = false; + + std::wstring app_name; + + //contains the non localized key of the powertoy + std::wstring app_key; + + HANDLE m_hProcess; + + // Time to wait for process to close after sending WM_CLOSE signal + static const int MAX_WAIT_MILLISEC = 10000; + + Hotkey m_hotkey; + + // Handle to event used to invoke PastePlain + HANDLE m_hInvokeEvent; + + void parse_hotkey(PowerToysSettings::PowerToyValues& settings) + { + auto settingsObject = settings.get_raw_json(); + if (settingsObject.GetView().Size()) + { + try + { + auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT); + m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN); + m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT); + m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT); + m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL); + m_hotkey.key = static_cast(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE)); + } + catch (...) + { + Logger::error("Failed to initialize PastePlain start shortcut"); + } + } + else + { + Logger::info("PastePlain settings are empty"); + } + + if (!m_hotkey.key) + { + Logger::info("PastePlain is going to use default shortcut"); + m_hotkey.win = true; + m_hotkey.alt = false; + m_hotkey.shift = false; + m_hotkey.ctrl = true; + m_hotkey.key = 'V'; + } + } + + bool is_process_running() + { + return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT; + } + + void launch_process() + { + Logger::trace(L"Starting PastePlain process"); + unsigned long powertoys_pid = GetCurrentProcessId(); + + std::wstring executable_args = L""; + executable_args.append(std::to_wstring(powertoys_pid)); + + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = L"modules\\PastePlain\\PowerToys.PastePlain.exe"; + sei.nShow = SW_SHOWNORMAL; + sei.lpParameters = executable_args.data(); + if (ShellExecuteExW(&sei)) + { + Logger::trace("Successfully started the PastePlain process"); + } + else + { + Logger::error(L"PastePlain failed to start. {}", get_last_error_or_default(GetLastError())); + } + + m_hProcess = sei.hProcess; + } + + // Load the settings file. + void init_settings() + { + try + { + // Load and parse the settings file for this PowerToy. + PowerToysSettings::PowerToyValues settings = + PowerToysSettings::PowerToyValues::load_from_settings_file(get_key()); + + parse_hotkey(settings); + } + catch (std::exception&) + { + Logger::warn(L"An exception occurred while loading the settings file"); + // Error while loading from the settings file. Let default values stay as they are. + } + } + + void try_inject_modifier_key_up(std::vector &inputs, short modifier) + { + // Most significant bit is set if key is down + if ((GetAsyncKeyState(static_cast(modifier)) & 0x8000) != 0) + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = modifier; + input_event.ki.dwFlags = KEYEVENTF_KEYUP; + inputs.push_back(input_event); + } + } + + void try_to_paste_as_plain_text() + { + std::wstring clipboard_text; + + { + // Read clipboard data begin + + if (!OpenClipboard(NULL)) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't open the clipboard to get the text. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.OpenClipboard"); + return; + } + HANDLE h_clipboard_data = GetClipboardData(CF_UNICODETEXT); + + if (h_clipboard_data == NULL) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Failed to get clipboard data. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.GetClipboardData"); + CloseClipboard(); + return; + } + + wchar_t* pch_data= static_cast(GlobalLock(h_clipboard_data)); + + if (NULL == pch_data ) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't lock the buffer to get the unformatted text from the clipboard. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.GlobalLock"); + CloseClipboard(); + return; + } + + clipboard_text = pch_data; + GlobalUnlock(h_clipboard_data); + + CloseClipboard(); + // Read clipboard data end + } + + { + // Copy text to clipboard begin + UINT no_clipboard_history_or_roaming_format = 0; + + // Get the format identifier for not adding the data to the clipboard history or roaming. + // https://learn.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#cloud-clipboard-and-clipboard-history-formats + if (0 == (no_clipboard_history_or_roaming_format = RegisterClipboardFormat(L"ExcludeClipboardContentFromMonitorProcessing"))) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't get the clipboard data format type that would allow excluding the data from the clipboard history / roaming. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.RegisterClipboardFormat"); + return; + } + + if (!OpenClipboard(NULL)) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't open the clipboard to copy the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.OpenClipboard"); + return; + } + + HGLOBAL h_clipboard_data; + + if (NULL == (h_clipboard_data = GlobalAlloc(GMEM_MOVEABLE, (clipboard_text.length() + 1) * sizeof(wchar_t)))) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't allocate a buffer for the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.GlobalAlloc"); + CloseClipboard(); + return; + } + wchar_t* pch_data = static_cast(GlobalLock(h_clipboard_data)); + + if (NULL == pch_data) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't lock the buffer to send the unformatted text to the clipboard. {}", errorMessage.has_value() ? errorMessage.value() : L""); + GlobalFree(h_clipboard_data); + CloseClipboard(); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.GlobalLock"); + return; + } + + wcscpy_s(pch_data, clipboard_text.length() + 1, clipboard_text.c_str()); + + EmptyClipboard(); + + if (NULL == SetClipboardData(CF_UNICODETEXT, h_clipboard_data)) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't set the clipboard data to the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L""); + GlobalUnlock(h_clipboard_data); + GlobalFree(h_clipboard_data); + CloseClipboard(); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.SetClipboardData"); + return; + } + + // Don't show in history or allow data roaming. + SetClipboardData(no_clipboard_history_or_roaming_format, 0); + + CloseClipboard(); + // Copy text to clipboard end + } + { + // Clear kb state and send Ctrl+V begin + + // we can assume that the last pressed key is... + // (1) not a modifier key and + // (2) marked as handled (so it never gets a key down input event). + // So, let's check which modifiers were pressed, + // and, if they were, inject a key up event for each of them + std::vector inputs; + try_inject_modifier_key_up(inputs, VK_LCONTROL); + try_inject_modifier_key_up(inputs, VK_RCONTROL); + try_inject_modifier_key_up(inputs, VK_LWIN); + try_inject_modifier_key_up(inputs, VK_RWIN); + try_inject_modifier_key_up(inputs, VK_LSHIFT); + try_inject_modifier_key_up(inputs, VK_RSHIFT); + try_inject_modifier_key_up(inputs, VK_LMENU); + try_inject_modifier_key_up(inputs, VK_RMENU); + + // send Ctrl+V (key downs and key ups) + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = VK_CONTROL; + inputs.push_back(input_event); + } + + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = 0x56; // V + inputs.push_back(input_event); + } + + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = 0x56; // V + input_event.ki.dwFlags = KEYEVENTF_KEYUP; + inputs.push_back(input_event); + } + + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = VK_CONTROL; + input_event.ki.dwFlags = KEYEVENTF_KEYUP; + inputs.push_back(input_event); + } + + auto uSent = SendInput(static_cast(inputs.size()), inputs.data(), sizeof(INPUT)); + if (uSent != inputs.size()) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"SendInput failed. Expected to send {} inputs and sent only {}. {}", inputs.size(), uSent, errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"input.SendInput"); + return; + } + + // Clear kb state and send Ctrl+V end + } + Trace::PastePlainSuccess(); + } + +public: + PastePlain() + { + app_name = GET_RESOURCE_STRING(IDS_PASTEPLAIN_NAME); + app_key = PastePlainConstants::ModuleKey; + LoggerHelpers::init_logger(app_key, L"ModuleInterface", "PastePlain"); + init_settings(); + } + + ~PastePlain() + { + if (m_enabled) + { + } + m_enabled = false; + } + + // Destroy the powertoy and free memory + virtual void destroy() override + { + Logger::trace("PastePlain::destroy()"); + delete this; + } + + // Return the localized display name of the powertoy + virtual const wchar_t* get_name() override + { + return app_name.c_str(); + } + + // Return the non localized key of the powertoy, this will be cached by the runner + virtual const wchar_t* get_key() override + { + return app_key.c_str(); + } + + // Return the configured status for the gpo policy for the module + virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override + { + return powertoys_gpo::getConfiguredPastePlainEnabledValue(); + } + + virtual bool get_config(wchar_t* buffer, int* buffer_size) override + { + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + + // Create a Settings object. + PowerToysSettings::Settings settings(hinstance, get_name()); + settings.set_description(GET_RESOURCE_STRING(IDS_PASTEPLAIN_SETTINGS_DESC)); + + settings.set_overview_link(L"https://aka.ms/PowerToysOverview_PastePlain"); + + return settings.serialize_to_buffer(buffer, buffer_size); + } + + virtual void call_custom_action(const wchar_t* /*action*/) override + { + } + + virtual void set_config(const wchar_t* config) override + { + try + { + // Parse the input JSON string. + PowerToysSettings::PowerToyValues values = + PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); + + parse_hotkey(values); + // If you don't need to do any custom processing of the settings, proceed + // to persists the values calling: + values.save_to_settings_file(); + // Otherwise call a custom function to process the settings before saving them to disk: + // save_settings(); + } + catch (std::exception&) + { + // Improper JSON. + } + } + + virtual void enable() + { + Logger::trace("PastePlain::enable()"); + m_enabled = true; + Trace::EnablePastePlain(true); + }; + + virtual void disable() + { + Logger::trace("PastePlain::disable()"); + m_enabled = false; + Trace::EnablePastePlain(false); + } + + + + virtual bool on_hotkey(size_t /*hotkeyId*/) override + { + if (m_enabled) + { + Logger::trace(L"PastePlain hotkey pressed"); + + std::thread([=]() { + // hotkey work should be kept to a minimum, or Windows might deregister our low level keyboard hook. + // Move work to another thread. + try_to_paste_as_plain_text(); + }).detach(); + + Trace::PastePlainInvoked(); + return true; + } + + return false; + } + + virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override + { + if (m_hotkey.key) + { + if (hotkeys && buffer_size >= 1) + { + hotkeys[0] = m_hotkey; + } + + return 1; + } + else + { + return 0; + } + } + + virtual bool is_enabled() override + { + return m_enabled; + } + + virtual void send_settings_telemetry() override + { + Logger::info("Send settings telemetry"); + Trace::SettingsTelemetry(m_hotkey); + } +}; + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new PastePlain(); +} diff --git a/src/modules/pasteplain/PastePlainModuleInterface/packages.config b/src/modules/pasteplain/PastePlainModuleInterface/packages.config new file mode 100644 index 0000000000..48319b8c95 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/pch.cpp b/src/modules/pasteplain/PastePlainModuleInterface/pch.cpp new file mode 100644 index 0000000000..ce9b73991b --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/pch.cpp @@ -0,0 +1,2 @@ +#include "pch.h" + diff --git a/src/modules/pasteplain/PastePlainModuleInterface/pch.h b/src/modules/pasteplain/PastePlainModuleInterface/pch.h new file mode 100644 index 0000000000..eddac0fdc1 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/pch.h @@ -0,0 +1,7 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/resource.base.h b/src/modules/pasteplain/PastePlainModuleInterface/resource.base.h new file mode 100644 index 0000000000..e2a93202be --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/resource.base.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by PowerOCR.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys PastePlain" +#define INTERNAL_NAME "PowerToys.PastePlainModuleInterface" +#define ORIGINAL_FILENAME "PowerToys.PastePlainModuleInterface.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/pasteplain/PastePlainModuleInterface/trace.cpp b/src/modules/pasteplain/PastePlainModuleInterface/trace.cpp new file mode 100644 index 0000000000..a6f06b1ca9 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/trace.cpp @@ -0,0 +1,82 @@ +#include "pch.h" +#include "trace.h" + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} + +// Log if the user has PastePlain enabled or disabled +void Trace::EnablePastePlain(const bool enabled) noexcept +{ + TraceLoggingWrite( + g_hProvider, + "PastePlain_EnablePastePlain", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingBoolean(enabled, "Enabled")); +} + +// Log if the user has invoked PastePlain +void Trace::PastePlainInvoked() noexcept +{ + TraceLoggingWrite( + g_hProvider, + "PastePlain_InvokePastePlain", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +// Log if a PastePlain invocation has succeeded +void Trace::PastePlainSuccess() noexcept +{ + TraceLoggingWrite( + g_hProvider, + "PastePlain_Success", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +// Log if an error occurs in PastePlain +void Trace::PastePlainError(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept +{ + TraceLoggingWrite( + g_hProvider, + "PastePlain_Error", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingValue(methodName.c_str(), "MethodName"), + TraceLoggingValue(errorCode, "ErrorCode"), + TraceLoggingValue(errorMessage.c_str(), "ErrorMessage")); +} + +// Event to send settings telemetry. +void Trace::SettingsTelemetry(PowertoyModuleIface::Hotkey& hotkey) noexcept +{ + std::wstring hotKeyStr = + std::wstring(hotkey.win ? L"Win + " : L"") + + std::wstring(hotkey.ctrl ? L"Ctrl + " : L"") + + std::wstring(hotkey.shift ? L"Shift + " : L"") + + std::wstring(hotkey.alt ? L"Alt + " : L"") + + std::wstring(L"VK ") + std::to_wstring(hotkey.key); + + TraceLoggingWrite( + g_hProvider, + "PastePlain_Settings", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingWideString(hotKeyStr.c_str(), "HotKey") + ); +} diff --git a/src/modules/pasteplain/PastePlainModuleInterface/trace.h b/src/modules/pasteplain/PastePlainModuleInterface/trace.h new file mode 100644 index 0000000000..6b65d0f010 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/trace.h @@ -0,0 +1,24 @@ +#pragma once +#include + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); + + // Log if the user has PastePlain enabled or disabled + static void EnablePastePlain(const bool enabled) noexcept; + + // Log if the user has invoked PastePlain + static void PastePlainInvoked() noexcept; + + // Log if a PastePlain invocation has succeeded + static void Trace::PastePlainSuccess() noexcept; + + // Log if an error occurs in PastePlain + static void Trace::PastePlainError(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept; + + // Event to send settings telemetry. + static void Trace::SettingsTelemetry(PowertoyModuleIface::Hotkey& hotkey) noexcept; +}; diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 442eab5698..bedcba612f 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -162,6 +162,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow L"modules/MouseUtils/PowerToys.MousePointerCrosshairs.dll", L"modules/PowerAccent/PowerToys.PowerAccentModuleInterface.dll", L"modules/PowerOCR/PowerToys.PowerOCRModuleInterface.dll", + L"modules/PastePlain/PowerToys.PastePlainModuleInterface.dll", L"modules/FileLocksmith/PowerToys.FileLocksmithExt.dll", L"modules/MeasureTool/PowerToys.MeasureToolModuleInterface.dll", L"modules/Hosts/PowerToys.HostsModuleInterface.dll", diff --git a/src/settings-ui/Settings.UI.Library/EnabledModules.cs b/src/settings-ui/Settings.UI.Library/EnabledModules.cs index ea055fb4cd..f68735ba3e 100644 --- a/src/settings-ui/Settings.UI.Library/EnabledModules.cs +++ b/src/settings-ui/Settings.UI.Library/EnabledModules.cs @@ -279,6 +279,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + private bool pastePlain = true; + + [JsonPropertyName("PastePlain")] + public bool PastePlain + { + get => pastePlain; + set + { + if (pastePlain != value) + { + LogTelemetryEvent(value); + pastePlain = value; + NotifyChange(); + } + } + } + private bool measureTool = true; [JsonPropertyName("Measure Tool")] diff --git a/src/settings-ui/Settings.UI.Library/PastePlainProperties.cs b/src/settings-ui/Settings.UI.Library/PastePlainProperties.cs new file mode 100644 index 0000000000..fc7c5c8639 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/PastePlainProperties.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class PastePlainProperties + { + public PastePlainProperties() + { + ActivationShortcut = new HotkeySettings(true, true, false, false, 0x56); // Ctrl+Win+V + } + + public HotkeySettings ActivationShortcut { get; set; } + + public override string ToString() + => JsonSerializer.Serialize(this); + } +} diff --git a/src/settings-ui/Settings.UI.Library/PastePlainSettings.cs b/src/settings-ui/Settings.UI.Library/PastePlainSettings.cs new file mode 100644 index 0000000000..bf0ee98cfe --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/PastePlainSettings.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class PastePlainSettings : BasePTModuleSettings, ISettingsConfig + { + public const string ModuleName = "PastePlain"; + + [JsonPropertyName("properties")] + public PastePlainProperties Properties { get; set; } + + public PastePlainSettings() + { + Properties = new PastePlainProperties(); + Version = "1"; + Name = ModuleName; + } + + public virtual void Save(ISettingsUtils settingsUtils) + { + // Save settings to file + var options = new JsonSerializerOptions + { + WriteIndented = true, + }; + + if (settingsUtils == null) + { + throw new ArgumentNullException(nameof(settingsUtils)); + } + + settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName); + } + + public string GetModuleName() + => Name; + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + => false; + } +} diff --git a/src/settings-ui/Settings.UI/App.xaml.cs b/src/settings-ui/Settings.UI/App.xaml.cs index 41486f5d17..737c73a378 100644 --- a/src/settings-ui/Settings.UI/App.xaml.cs +++ b/src/settings-ui/Settings.UI/App.xaml.cs @@ -151,6 +151,7 @@ namespace Microsoft.PowerToys.Settings.UI case "VideoConference": StartupPage = typeof(Views.VideoConferencePage); break; case "MeasureTool": StartupPage = typeof(Views.MeasureToolPage); break; case "Hosts": StartupPage = typeof(Views.HostsPage); break; + case "PastePlain": StartupPage = typeof(Views.PastePlainPage); break; default: Debug.Assert(false, "Unexpected SettingsWindow argument value"); break; } diff --git a/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPastePlain.png b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPastePlain.png new file mode 100644 index 0000000000000000000000000000000000000000..fe16ae575dd4111d639380e42825da560101cf93 GIT binary patch literal 538873 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 Y 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≫ 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%0A~rwAY_U9A6BCZlWqbXvkU7#=Vtx}m@Q)F`YYE$ zyrH4H6M}2v)lUVk6vxQ|)6XD^|8C_V?w;R`u_*xY2dc}B4e><3sIBueH)U_4iLC~z z>rPXO_(j!)NSjK(rLRmGR+>$1osyig)(xRdm~LpLNqK8!sP3pg+JrXi-&;lfP@8{> zz6b>nH#pf=%vL8h+#70GyU?K4&Ff7r=#$rko=~5Rs4)YhyYx(B#%{y$Mg*4 z;u$T<=EdhS^{7Gl3#!U@Y~IE9J-5@7ki)n5L3Q~8*$C_-Bvaw~MPBphfd^Ry5$*_- z_%uD(=Y?^95DzS;vOB!0$qnUVI_tPR$9ZuF^niHzf)6#I8ZkoTKB`*yFq(iQT@$j@ zcGz6C*^rQC|B5(t$C(ZGW`7CL4}q7W9f{r&_2=eujAD1bnw9z!#M!qLQ z5n8{YsNA~5{WSws>?FBgU$Y&627m5igG|+MYm#Ct9Em?q%t&T0uUgS_I@z9n6`lMr zBBEYHE0LLH54e)qzJSbD*B3~wWh5HN_8!0Nu%mtu4B6?8TsTR5$C}~z35VkYc79T8 zXMVN4MD2f4npl{c+%me;Ej`yJwxH$iUrKlf*%LXTr`AIHG5)>L26EbQr$}az)~PLg z8-33yg%_u15c)qxDP90+5m-i5G7kiA@HSx)Yv{#?Rcb~##z~hCfBiDc&!=F6wm&3dE({ zrcC-sW%f=fqa8c`RM=Hww@byYWkXNwZQbJ?Fknm&)Zoc&i`PcQ*G3s4D`s$A>AdrGa|0 zY<6KLTM6^T7TJKQOQCvW(i#$<2&=(Z?xPO`Y%s?dFJ5~1JKxQXtT$lw=GfL65%{w@ z`E>Qd3rbnuw>t6B)S_L011I$`^2vOqpzkmrQ|^w< z<2#@1cn}t>f9BA1imMrrGQZau!f!(zI$AUPcbHz9(%VF4vW6fT&MrItFySjg(k5=3 zbX@R<0C8(L(KsP$j4QY=y4GzFW#SwhXewMzQ2Ql#&8qHL)7?XD=SWy=EIc0^ogn8y z*@tU1#vh)^{{1Wd^|GV~6NueQ^6OLLUQ7S~=z90KB+ox?d~4ro9hU2$WtpxrYw9v1 z&mvpRj+SlBQyxf3sZhxXPY7(SvOF$Zsj0B#%1VJL3gQ7)D&$n|nFT5eQxqyhROEd6 z-R<}M^}KrSzx{*P<#l~N@6+eHyyf&7V9kgC2{dLiZbrLu54DqaJTFQW3Z?(y#F{4rD1{DMOCvN$yRl9kha}b*tesy_=@gYC z#`?$hys|uh57ovL=TmFJg#P=_*&$T#+KV1Kk^q4@K{M1IZvZmzkw)WwyTEpB||{|8HN+k-Y;;e)jwgj>sxpx6md-4O~&c?L&X@ zGSF=X*h}rp9_tyL4-Fg!)BfF(kD(!n1HgmE)Tn1+e!#$!v$7}rGbCPJm!qXO50M6= z2RJJOZ8=a=XR&I92EUDl{JjYLy{<49nN7l}-ol5o`xH^o<9{^D{&lV&i*D>+bQd2* zbtKgm{C>u~-}d~J3w*Y5Byq;6Z-7C~_aOs2U<+bfrlUuh7fbc9&=O&y(t4pxDhl7M z={Q9`ErWEor`EyWa{gL}d~C5VxKz^0TDNR)kS|2~%we*Y$ZcP+fk>^sn%nXPd!Uvlf@wS^Jj?9Qe#dF6SS!@qutfowW# z^w^m0TY~?p8zzm3$BbHo%=pXm*P{sYqf2h9K6^U%+eU7GT+_!Ej)}G^?Mlj3NQDN=`K12X8GIX z5==>IMi*m7`EiT26ndpHAyrZ>59wDewV1vIC4d4QqM?{vtO^h(94(td4m+>EBJvs= z4?bQ!8#8}^HNc5gXQ0Ip+K!V)k*iNP4s3-iJ0naaW=5x3um4_Ef$HB%+Ia}PUZs=> zl`^X9!6eCjsl*m!dzsvEggvFRlyI^eIZxrTgsL>Eqb18H>rbvRj+8&WDOQNR(NvR; zyktKuj~O?Kh6jLUlX0{ImKmD5Mjpam{tCNAH2SBdBMBP5m{T?t8E{t@&0yVIX-4!NJUD-GLs33%j!Fqf;;r6U3dX zK6)T2Oo^QM+QH7rI3D@ysnFH=&#&(j(tK3-xjv3YmtL^#MY)+RnU{+~`tVqwWn(Q_ z>A6gyV<=)ao#y5|-fo@<_Mm(UGfIP%q53WwLyianFE$%_V#sYpRPg<#y5;0j+j%+c8G(MH8(i3s%;f1@Aq z%h^{{1J-S_twB8qd~flgD04m9O6jdNLrNAg=uRwg3#^?hucWETl1~odc0Z@7KEs3r z+fM+<0&_#l4OfH12aD>BufD6;9431`qxV}tRvmoU= zYnTAN!f2t4-R=*!>$SRnbo2R-dkdBtvC&JH$`!l!+$59?_uVDk&39$?G+D^aYf;8uT)o7cBU6HN^(!K)pIpmJxpI1LK9Bt&WIm z^?&){{fJNYCRGer!x{?~AkOzW)x)GON$$R94yF2xI!5(qD6!8j_oPuM!?RdtA;;6Mu9H)ZgyWS5fh%$0Kz7mH5kCn>K#+p5Dfto-E6$5!hyPHK7V;IsSi z&=qii)q1jCB3wfpRGAchp`TA3x6Zj(t4!kiCJ)`~QDgu^1_(ym&{^zTScl$g8Ez-# zwMO+!b7NrmbnX@_CUjp{4p;h_4q-lPa=z3`RqYtDMZAU)i7QOV+hEHg8Ek#xSy1W$w2+S-W~g z`mo=48eg_`gC&eqnz{2|L@Jtz4ba`KUt+#DL$)j)4-gh2UPhdQ4IKD6;nFD~eN$o_ zKnN_{Fc~P+;%-F!x0GWNqkL9XpHq{j5$Qdf$cT{}F&pTdi4fjXI6BjEY)kz-fz38r zmxoinPE*JYGuQR-zD9pZ?fXCun^>JD+0v#awek;CB3CUQDVvh>tf{x>|H{ie4>*j# zG0Kr7?&m{m@AS9W)4r_|(oe`ChkE?&S0%%w54zwh?*p5#oxJey&MyKQq8pN!m$~U( z=M|0!jkJw|vJL}gY2Wbeyj~Vy6yKML1670O|gqhIqC0UIr0FT8gAP8P=O#hlavOUxJSSEO1FPW{36j z#=I&RBK~y8$+wn}1V(mUwfA>DM_;RCaTUO(@cOwBE^c*XN|A%mVwn7#(}Shb2*yu1 zI$8p^1Ux2VL?tkbd!pi{i%{Sn{OKa>oM5QzEE&$?flQc(>Os8xV!u$cpD;*3F_8r# zOq*@;M6N#M6vlu*%Mh6xKjpM!QGoXQJB|`weyDc34Bo3?$F8nIMK-nXYElG>j{On! z9M2GG+EbCXQx%+@Gf_w)^?wBiG6op5vGw=71Ankl&J3G*%Hlx!v2xidqUmT80Zo8yE62DCz0&1{P>&&~iLbRWlKd?08cW-( zEZgfN)b+&h`~yws^d4kR<{C7mT2|SBsYGfn5&D89B5bt|w1EY>p9j;?%!Ya4CWS~| z23FC0FDT*_w$#UcPesOI>K36Nu&E0%h~F@QPGeao!?|DQKQMbUEKQR)EB6uZquI39 ziy??}L~fjSq;TS9uOO}`trpsymDMnODY(wuM6-?eZ7R#_Rq1^YHu!sP)#9ZH6wVR@ z_Q?Cj08t+4r^`@y_s9hc_K_CDB$?FJ>>%?JYX~6(FnP_dv6Ji=)lV26$ojM!{bmW0 zZAS@bKD>j&;@~@<;I6h)qpFV1J-&3VhuRLa^q7M26KDj%qWp&hQ&EwJ9I>Z>mCf(+H%IKkZwCDT)f3YN-Lg zjaM3~mmAU>`dVC%E?~}CZF=E`iBkWG3k>`9MVUQo(o##1#2)vxSy$1wWi zu-bUQv8xOI`QO7A?Z2Su-D%~GzH(|qD>|h72d9l`g>MD-(Rp)tjs<{oZ-YPdDpBW! zHhF5lS5ijN7*C!eceeK(JWfKdDK(V8kyY-lB@5_1!N?q$fQ7OI_R%FGe^D*8+Oi2H zjRNcyq<=(nM;E?p=zv$`d>#5ey8wRK(Hp>821`IbXfJ(D&O;xaKXwE7L+9(WLv-Cej&b7FrLMJf=v(xJceWh?!Xcr<*?at@{7$etLb7VoIf)F z2IzdXuL{o^bw4LBlIiU7YqZXM*P{eRzs<##x|R@_R7m-hW31Ev2EzoSdc5t2{;&rh z*_3bc*lN9c%B#Z*72mI!Pwu${Wo)4it5U^L>MoUjl(|QGWWk4RZn%xivZ?#YlN|1O51jdaaY;v^z^T@{b>?OnQ0&^ z!fB}BT)OQK0;>MSSw>x>7_PohVi~f@VHu}qzGib(#*Mb_;e)1xKozBBxgt;?yI;p< zQ0rpmzPCjD2pdf};y@*Iy!8R1jm`XtOz|4a#OWHT7Mf_^;lw`t>xc>^$aY++?~3?E zsM-nsZWma$t(5E?+U+=4f=ltdt>b6A@{q8}U*pzO^*fD};`#ekLfq>8@r~cNgLH4+ z0ljr}!gxF`zcm$8C^{3zOzIrc&-h`+?JPgp4EyeZSihY(L=IFko48H!!ZdcZ+B;J$ z>2%>Nl90cVD`S4-1V}RB?C4)?{az|wrAlc|xD7g2?5OUz5}~euwI>23qUiK#%k%zE z+}~%0!SAoG2Vm1TtkB=7)QgSz=d$8y*D}xasw($O@}jQm`;N4!di7b53&h#O-}KAc zQ=^D(R{Ewd3X`;xKf1eZ5=Pwov*0&%+eW8f>-TPxp)4{x%QH^S`}LeEW!rQ-@G7V* zG;zREZW+yI$xijRjP5H;IJ35Y$o<*{OHN6X%TfcDaU*3S?OGVM_qq~ zd@7GIjHpwx9*h>T)&P!(^!W1F)w*AmPF28`LDD1>!{Oe5$(iQLzHG2;x>$cEqLj6L z7JE~ZjA&NG68TYE6ixB61RCVFRJ~DxgzaOkob}{#OoCBx7JB#nKPE$bf6>RO1^0 zK_Df!wG(qBhg)bD0ZOA7t*nSeKZmDFhaxuJlP6}!U*i~NfB2DOFDVzX#zuqN)kRFj zYXC#k6aK{Ic_e4Ir#uD!@x1`6&gR0g5{Vwq;TeatUKIJAOATAa!I;&|Nwf1%2hq~+ zO;eYz?aC8JXABk99&);urYZN>qp*B+26T0{)#LiCMfYks-+PD7a} z;VJYUu3j6ZWR0S*(9tx2JG!|Z$792q$Ai~*rXeqw>N9I=X65LbXIs=xG)}d!KrI{z z7wQTzYYD~VIGFsVAqWLF`ic*o{`~#x2lKC1BwQmN`+@eqEP9!=@+0!Cxi(d>IGFu0 zjpOY_`JeUO;j6@E`lnw|9+VHV3k@N@t3F?7+qj{+V2!V+OEGk zIdc${%lJ6595L_vx^TOhA=l00Re;|(KyS*)R$GEkdy^~)pVRqCHG}nyS86O*w5Z_1 z#EWb}0*kHcHfmY#d(Y%s8&86l-E3r}b3d0`F9gp9Ssqd>Uiu>aw3D>K>io$7GYg3R zQu?Ui_cfNA?M+Y~0-D)=OJl^U;6)J){-k)qzdEzse`^S;Cf4U;2mJu&dNSfQfSfj@ zn3Ab0(B39BOQIaw=nVfv2y^UR72?J{HrfAFxm|udyl8+#nUbHEon**R1f) zin(U1e}_A6a;A6a!$3a26w|$AZP4l+_gbwF*_#VnvZ}!p>Itr2a*@V=H#eo|S4&Yj zc4^W+ukTnfZU9M7>in0OpwerXOC}vI96_0L!mAkAG*-Z6gl6N%IVE~a~$z`xo z2rlO>5!WPKaEIRi&<^z|o>H$`@#f%xc90_)vC!aM9erI@U*h&bsXLy9Q0p>kxO(%D zCeclaOYDz&u=mkUOJC(;DWiC6!;+u(u38;=`x})G$7q2m;QGp{C17hO*f^SvJm-tJ zbS0%VU6AL+H(rjmG%GE2J{v7p>^Y!378|;^DxCfi3>foAhb!{ZLuvp_@!?oGn|;_5 zG0PwJ%)5Z@?2N%+y+A4IIg12+K+&IVw#R`(qSJr9;pu%Dlz~mVq}7_#MuK5)f1`5{ zg|I~-?oQM>WsDWcM~4fRV$w%t?ha1YCbAyxg*d*jlcNS#77u%E4V+mcY}$r=5liu^*80 z*e~z>O6aYen!Uc)B2JC^K6rHE*q`!PPhoa*cfw;5b+ z+1g6xJvTj0x|#u%yc6=GCcfd67%sIVW7{c)P@9gEd3x-9vj+h)M`*o|Yfw^hYLqj) z`*`b@qX#GYW`ozOt(V#f_d~tBVWUl*2I~cfu4Zehx?gkEoD)gk4dd$qIgNX`3uCk; z6$w9(Q8?IW*YK%rtB;n!_wI1;@A(9&h(R#q@Ql=`pMoo@-F=M^r<_ zIcI<^yXy5Jll6{Sp(2XjW3sV>uWtYb{L_1Ka>k{c)&xuRX@to5UwukWJ<6W4v$8pC z?O-DCnhu^iZB>$xwo&D5J!VLTdvd;2g0!TxR|_}CpqlN03wIPhulYEtH&+! zWj-)!y^^7Jp(1}ZI`0FBC3i{CBMSo`LZa{sZgpPrzmFu8YU^W@a9)3%sO791AK^Fd zUoVT@hYu&AL$!mFILiehLhP%Z{q-N%hA#kxfUI5eb7a`wRiE8}SvAVGDwGh}z|y6! z+yXMEm;#^WgDTI-b{kJ#JzxYEW!Dk1lcJf>O}ATAIt z%#eW0XgPahcd^2-%yP-0ZR$S#Kk@YHNK(FCvN)>*It%@|U*X;s&Qo_|*IM_Iz1C!W z*Zm!Y^mO1Vnv2ckM1n+>J>(t&P$NVvrM*X zRNw9sh2Uk90nA>iaO8!WUQ?sY#ZdveZ*YhX2?NeoOCqk3Edz~v1Y^~jH|TV)Rhz_l z=r4|jT>ap&PrLOtVzo$ee(RyDxOVZ%IRz!OUk;+Gg4ItciWtwm)jdmnNEUbhXdyNnRL|8!3&bzTm}{`g=!$rQm1KKeKlLhCQh2jX%{v*LdJOnJjxx=DfXadYA?Oul@JfCT!V`h%zq& zhNvJ{;m#}Tx}1v?sYsY$D$h?haR$QGZZ|jayE6|7DxWlF+~Z%@Df&St3aLG}1>R#& zS<7vU$wlBf_cGn$Ky?2M&|kc(drjMJW@jn~@Ts_2cHr{F zYH~_7eMF`i38sasQ`3dzeAUNt2)npkb#Y*Gp+ST-0U?*!z-?EU_lK;zsNr?|`oA99 zbE@x*(#Z(O+wsw^Y>b=0<5|K{ZFbkWlJV#j#>HQ{Y25@jlZU|tk$=^c(J7u37B0IH zk$UR-vX)0&A#%0+J_z%6@oZ^P;D$VI{l`V`qU&U6V`o{($`*kNla*JHIcvcLRm)v# z!!u^5vg9-dD_6r;NSQYnnb>R}o|luziq# zZ3wU*%YNOM7l4FY)Th#K#y;8QJ<9ly-CCc5m zdg&`rFz_HOxWsYqPaP-lXpTq(6g6di3XL|UE17SE_ZbYToKS?VgZ~K7_uAZ}YVJ;G zqjpTL?qO|4Qmv!oxmp)_hP}Ad8eH`nXpCkWv*IM5oD9`#Fa};+b=cccMC;TbS=B$&CP|x8OA&nu$*YeCyjFjAS&g6JmoE;eAF7KV%`+n&&w;+4NhbH?{!Z&20}H*Al!XVG}3vQ{jiDc^$LrJJ)YNex^i=2 zE8%D6SAi$}>ivwafo$%|{3RnHL~mb2Sy~?VOTQBTU9%SuqFAPG7|9K-i+-CP_L>ac z=N#0XoB@>|Zcl1o3uv;@JvWdT`~c7*&6&!wP4`9L$?F{sLcQ<%s`eJibG92z9zFYZ zc1)46)kFxc1AGB~Ub@PX%XdLiYdQ&g{J<^1yJ1-^)cq8!YmX(GSfP0*7h`d{m_H<- zJ^IH7+tJ+hZ`CI*w~O63S~68(*P=vmw=_qtMqLK2*_8GG;xR7ITuygcVH(L~x?1n` zyJ)9u9qEW6%B6qfz4p8beZObH=lNtjiRoV+LFOXa0Qtqs2(fYSE}G5=6Dk22WNOT; zBBSp$Kodf}T2%FaqH2Dk;Gg>7(>i*wB$^JE{dZyG68_EU0fK;mUyDOZPsv$>QH@47T7F`PF?%if9mbTh=O&KlapP}|h z$wk*mDM7~he(zCdKP9lO%du9|iYo16z&D&k7G+-P49MeVuGO_<&#@{p^mM{F5XY;U z@~Fn`Awzr-wAZ9Q@O3gY#dIj-u+X^K4x`G)0|4YUvex{4&|xg~oujKL#|^M#MC!;p zW{^3mQdW+vE6`S@NYlS|{^33Q{~$lT>au;%{F+iRJ$H!p_6^XT*}U=SLgm*k(vipm zcnbJT5ZaRHHf^e!Bj+}StT*_D>gzxKt(dv}=Y!9VJ2br-^Cj$Yf6+LRWjRiqe01}t zykgE9+q{sfi&fIp!~5iWR<&Lx(vr$Y)e^b3Rx0U?OzLJ89cW$#!8A4Zkz=lt zIgjchXIe&MbK*@D<$%ViQ;-59AJiJ~sWo?|GjC{-zxpGjxy`YI7XLy+q0s~^eV~LL zzG?7RiQ1HgZIpQS~?y+WqT_a&nGInAJJsyy={0DJcMK3LS;I%Sl)xJ*GBq`GFe z+CfqV<2yx53M7p$B+HP?8bKt1o0w2CRD7j8IY>lFV5OC&v8~RX}WpV z!|8}TZVRmi75`orJxKhQ*!E@87jP-eLbQ5eyfk3z@^MH50gWwvOlSQ4&z;7Ik1+1J z0o8XhGPMs(={2uYK>@%{ZU7Vkgg{%pi*O$Lh0$9m81T`J&4O7IP_81vzV-Yiqh~sn zD$U*87}ouTh6SZQ@fE(J9~r~sx~*Vx_;*X>w=E}h_UH(+eOj^b{#wavO@Z`eWN>zg zU0vG1BIxkxBzp87#7R){*V|brto$`5nu<1x!N#MI9C*2C`1enVHP@ASGr*$7=`L1w zQb2#vA?7O@KZo&};sxMk{hhADSGpTM2{O@~9c+034OFDZK2&gN>W9pe)T{S~^p{nf z`o;wF%YkmgvHP@ZI4!5zQu$k{BJ3rt8^3e0>boEFj#$aX+pn&w|M9E_V>z>42udh> zK?&tBaX}?_4ph<(P8{P87&a?N+@_m~C-x#2IcDZk%8mNroehwCY0*7V-(BSIYUp zqN?rGzx5Y;V(P5Dj&&4$124N1Uuy zWj?hDnlHD~uyv3|(g~SkJ+f>jOA$u}qZ=wG!0NA4Z_J3~G^m!#8=c_7#5}lTh>8Ar z*%UABLue_GwhpKq6PFG9+*7vA-cs&vP~BQc-1&F-3QZTenk4I(X%Kh^HoWjAB@BD;pTB z)GmgcEQpOzr#2X7bd&eq=$@B@A}Q}uoCh35p| zmmMuT*P3+Qv#hugES|%tr2GQ!QLj())sTJc%0|GKVJS=OIRM>rasE~E1im|p0+asv z)uAe2FC?Te`zr*aNWt)@O|mgg6={X5%2~6^i8e`V5wy45Y$YnZ5@slG%1t36Z3?^` zCC^jPSZuk1cBs4ZQxSanzTC|T*#ISbO3im)la>B;mY9bP5Kh4YePCYWYhL&VPz&^RK{gk2-AoA$UZmXM&vh&0-DJy zedYTNyhEaw2tr1+v2^XofOrhtxOwep%lAjYODBgPc?#gVk;$YvV?wu?KeW=n>TrH=F zfZ>w|!0vxwaMt@(QLO;Wjd4U%tf@Of`n2wuL8T|qh`&T&mnB6Io~eu~FT^RZCCy1} z3WMiM-I-Y$2BwQ$)NME4VR0k_kW&b$XEX9na?L}}ZsL9U9_}dcu`HnBfk^-`jC~sD z4kLC}jr(g;De_k(avduL&uK+Ayx~j7JEFd4uhR^s5bd`jFB9ENbl7@pjQ=m!m&9(vxbz*k>VE#o z^Q#rKmZ3CD#NZOcX4@0E>9E%FgQ;HCE{3NgL{;B0q0zV;k%VQl!&$3xmR+Q-JUf{i zblh@+x?JIuX);tJq5a#LT?Vuz6*Ng%>HHor_mX6ehU@OQ9%X;N zrQi+^O+XOT$(jz*e0cXEN0(`OZ)Q!#>Fx+cpp>?E)x2E~@2&ZGXmZg&v{yqbO_#56 z`W_*VZZaYU0HfU8vxjg6t&LRDKtXL3jU$G@y-?voyumz9j(r*r=yL8EbDpXlI6rdu z2^+>s)sBLInzTS3VkS1`EUW`#t;I*IwNA*l0}Wu1BKH4o`egEUGtDw(%Q(XL#g%h{ zc};UKE(ExX2q~1+`V%@U)5Tk)_;|itbK(GWTyn)PZiPOqU$Vsu(#Y9mWS+BW53y%} z?LX0&3h$`vpKzME+`QuPO&F0`XTAr+pKbtE0QdsgOhjvk7XJ(nH-XNdl(d-!t;X4& ztn66BHN1fATQ%3^R3(?^J>qJD(b6{lcwimK70raXACT=ehW1czKkE;<&ZVY=wsO!v z&FI*;rP=>M&n}@LkAbiZXSeI)D{csY6}RT^)I7t z$tt@`6oVYuaff6Vr$3dtPU2Iddne0|&q zbQ4GA)Pgx-+a1JAxpf!O^e7rc{}L10>c=WSdHpM$HZ}%>i5JO-(q`UX-b0~#7ydPB zsN}LtvgP6Qxe#6oj4K^WlV_SjD<{m(jVyj9zO+JGtL#bj5y$~?9_VO-#nmyT=+C75 zdib06o7-fg1zvo0U1I?*DU}OT5$Bpo5xz(wix;jiex9Gqfz_{OaxT4+GBau#B@V)5 zqJ1EuDB8ie>1E?0asjA1jr(RnA|soi>lLb#p699}|qOv8IUhTx6P^RMJ0{j(~S-+Kw8Y^;A%V$Yf=(00r! zIr!3<^FtinC>pkw6CMosP0;+moP02YG&gUYK5R2@EZE0?YTc$SB$V7mwK zlzQSzhEqb%ga2ZiLHt)3fGa1sR0#-Ml@KcYksZ%*gfK#=uHDToMHXYJpi$a09$cFY z6QMjugGHK71V4h)g7Oshv&`1Z>V^YlX4el10A3B})N{X_<3GQ5ER6PTX7RLsTViMY z;k8z$D)f+eovfS~Lf9!Bx_b#7s_UwYh#NCiwyKtXRR8&J>r6RNu4JpAJ6arzz>b^V z0=9VK+eb&h?Flp6%w(SP(cOeW~@~)dPA&&yM9DDM#g4 zd}e=3X3vqX0y0-)b(`I!06f1+ubPk2K=_4DV7?M>k+34`S_bMqp)a zSgS0c79?}j9Db}i_ph$Wf>KY^xiW$4EP z5xf5}${Qz2Ew_nRPXF=VQ!zw=v5m z@y=rNnnZ3>(H2TYEmkYhA@*8l}d??pNQJGvq zM%)>6QL6`ZIq@0&Mc1F!$l|!)_Z8Hp5DPmQeiOoIZal=dkU{PGN>C+)X!{sI1Wfuo z&C^)tGw!X8x4pEP_POnZOBv-M-H^UZrk>jseU<>zbVVr(>(G{Qax(5SY0gd)&u>Bw ztbP6t8MGCt(PM68a{B_i)HyqAP=A9Dd5!8e{6D1V|JF0XwFVN?*%`4!`?$ z?w#!rgNcR}>KR_u0^}LHOnEz+HhEWkFD9;|v1tu4CPNA3MKUPvSKpc8ja=Y^f?ptN$f*y*s(BVz zpIoC1Ru4L}m!Bh2S*NdY3Z#c{r}H@DOx@8k*LGdf^_yP%xY6@3e6u{C(+rtV8F>$v z0X+6a{Nd;l(cjFA?8{B*O)?ks2=wprE#;oE4SrHiGmLgc9>>5!q1^i%bCu1M9CwTj zs(*dNg%sG<;F)$jc@_}W;i1%0V(0tdzg%r@fHVgXolhZE|JIN_y0JCmAoY`cGMXKv z=5fUK3N5P(I4HpM<$QK@LHeOu8SHp#s=TbXvcdZIy%*xLk$@0+F6_<-3?gggs*~19+Z=jq!!D5GFDFXi z05seETrad;JN1sqn(gosJXEtQP>8~c6 z^F}`Mp15>4wNxm8?+L7Ej4$2ju`I3;UKOjJ@D_k{)Z`VZ~H-Bh-<=-(%V$i4hYE9TmSe?8Ai05#M`J|P}$L9DexsK|0{ zjB9V{z=y)RQ>b;lh>-O|9?#;X$;EbdN9T_HN=uSVl#9r}kkQWp!`(|RRM=rM|BpP^ zcYtLe=0(%WAO42UbBy($#U?&GBOI5}3f6-RX`J(xlZt!$&?0cch_-y+HQHC3M)iBF zW##xjC?=5ZUZ}3mgaYEm@s+U*b@q9MsjD?)V@F5ypD$;jMUwZ?wxc1)?sZ?0vVHGu zQZ(s(lghIc?9}l2KBEWRgWLPSW=qCm4wji;{NE7s{Xct`&UFzl%5!LT z;(_u*FW&Q2vG1KJNTOJlGJ#j=ZI|uA3Z&i?DYeS zDZ|-{ZL&wZ-;1D=b+(BC!Wm1SMPQ=5Amp|=qewDq31P;TPVG}S6|t}kvF+a{RQ1?E z()*Szbq@jci5jJkgT$_hqFRb!YlK+!$a8R}p_g8#m-;k*%iRqj1Qb(%z;0iY=5)I_ z>0wG~?LPjfxCp$qu|TzyevqazEDVYH4@B7_-89A0T-BZ=y%1j- zZsPCA7YpyXdV0Y+a8G{1k<-Rz>N+KHICQJnt&%wYyEaFu8aC0hAGX)rt{ai%ILgm; z-o`q#e9Nh6Bv=o%8;9HvISR4>}WzHHMto`r-$mRfXE4d{5kSfYbeRzZcHsTLK>O z+#$2QK5%Fc43l11s*Qe9v`68OeyQ);^N!C6od#!CNDe-b@NY}Tx zZ1EF+dH|Orhnpbf-tORVgYmTO>`d)&?Ax*OI%@|XUxmN`e8nbJ+7{T1MSuNHC&zxe z6ml1~ssgq#8rjgI2wP*vc}2WjJz+5;asD=C`4w4oJTexxafO`(eUap9$cX-cxJ)Yg z-{tV_haa#uPS^oUJzIRa9zSixbY<0Y?C0)*R@3|lcQmXTxdiVf1SfXl-lEy?rJh4a z+IV@ca+Cg&!-H4Yod!Qi#O=Q?>R9-Og2>B+I9mg8whp}#TxnxGXQI?aJCs56k%t6p zrC;g6_6TQDDAvqK24tNw@>gQu@x%@JCot(GULdVX(N9&`4N3sRycrDEl!WTND#`c& zaD`(?NjIvtoAU^UGxh@{Y2mw4M$&yvrMF-d3r+gtBA}I*Unof?;?($o&yaBr+URtD zDd)Ti+mnT>W(p$BQM!Ib0i=pGOEy$0{O^KcXY*&o)R~9FaF$$_fFhZ?3}g6&SW8zz zNoJ6p!Z9TdbWX2W(nAZy}7RigI55m&wL^kwGx8w<-Nl3 zB2e2a_}~`47fibD`BiUR@4K+D!#`P0H08K8A_in}r!D59&fTyw*?0d|^{O|SE^F;( zZ|O%Egz%T;qAfZtA>0TYkhy;pjdlc{|C$382+hxE#JN>Oz;@|$(R-$872Ci-M~W!6 z@vt1rP!vD%fio-}R4`!ltTm(~23s_3s6Z#BrK3{@=q*l`oHhAsurJlTo(Ps=x1w9o zh#UqG>LJqItWZb+7GA3Xt^BEx(=&e-%*QsSmLe|3XZ3ukiIMvwho!Zz*|ujxMddf- z$Z|)N#^0l1_$z5MwzHkHw4(X}FCR5CzR39$_{Z*XGN zaN$E6HvzZ%tYcbuBywG@{qRYT`LTv`y8SSOnAzgP1V^%R*c+$8v_d#LYKir%+dD`G z$Nmdu<1syQ+h4Lxs!)AC%Y#EWee-L!fNUe0j)S?q8D>j(endqRm#&oJ!%JD6(TY@3 zv3jIZm_ap;6sR5K=%6efJtz*DizW-=YHZ@n@ayi1;^vjn;4BVP@nvSK`*T>sfWl#@ z0XUyA;2@uBV`s8@j>oPgXb%UsSRVea%-zx#jp{j@gXbB_3(r(DS#UJxR4_FvtU_qN ziw}m@pq4GbU^}1Sfya$LzYkHS-+nT+z29sj6(}E*G0f* zTth7E@MCp=6H+^V2Is_i3#e9@bk{9%`+3W6E#;D^=h^^p=vMTBCf_u&jz`bl+3$+k z`2cak?e|hNc>Tus-M`r9hweJjuHH>{6Csy?+VS>~JBye-G)N;+t$lGY@t7AF{D;bg zFns{W5H8uU?H8H`X;BmZP(EYW$wP`2V|2VkV~r~}Y4$U;X7}jc79U@r|E@Ib^_DZp zEf(t`p)wDQm1%_aw`LtH5P>-G8EQo$bdC2tJ9C^Hx1@XuE^CVNECg} z*YR>g;Qb!;(4dPlcfB>3{x*0Biw}{;+6)}oLojFtyYL~odGVlY38$%Zz$*JJqtBZy zV=B_(9TpkZxTsrdTJw^BFS!Z5s*f5jmh!Joxre=7VZpImQ#_&#{|Pjwuf0FfMzTf8 z0Gd8}s*REm<@gFrY~nG=5U&%Ty`Eq{p6@4r#LST$BH|E=OQd$sJyr6IAaeaMsMcOo zH|QXp4L&jFi508f&o=dj|Aj1^8=9YKg5LRo!`oI{Y~3$xFl67mpjJ>sYyFZ_BZPFx zWJ87NWwzjL#^`x@Ug`9jl#VEi`=98He*@C;4Sy%#$$UeoNhk-{`; zgAc||XT3zZfa~DDxB`;P=*+slIfdJnZ_Q&D1eV2>wYL=P3h@fmVk{;@D`xk&A zx}zVODV@){JEfv7`7!#m0+Vjk*pb*~@rjQ?je{sg+tg{Wc$O0AVw>)clr%sP04^?ZaJd6jSzSS!p zaG$7%okuW`atq$GaE_N-;ahbP5+q6zzu}!QgsNy^$eLhOCS;Hw z77sAU^h?#x+DL#eb`O(oNVhhH^?#=JlsHU85!H-OVZw2r|E8?+DF2UzoUbVTc5$C^ znng6ZwJF%JiTKyE7^+8-@+T4&WSoTiSJ(_$qqDa7gt zweI?~faCexcBaF^*4SxHAXkJncy+JySQ9`Cir2LwTg7=UY6$g~H6Svu=akYgEhrik zK>9b;52tPRPOu$6@|uCQ-z6J`rg!Yl3+Y#?VT;Rc^1fEENxSHE*1ty&^Y9TSO{?&^ zrF=qU{Y-8g6Sly48f~=;ju7O>gP*&lzKf)gJG=C~KI|HeMZO^oZz6oIHr>aY+1|F7 zo6Dw9AMz}QgsvsLJp0#FUsa*?*biuGx2D=Iwyb>@-y>1HRm$@dM=U_sf$oc4Mm5;4&S5qL&;uA=3Yv!!4qJNFTEUzR!CEz&~sK3+9(br&9g{dd1_M_RSn>b*W z#nq75x$$`OFrvpNN@28Mp-#GfW|GTfO&mug4Ct`%fK(-3cx}k`s%)SI5a*(FYIj9Uda(N2NO8k zh&8t^IpD5mq%6$5$^mbvAj{L^bdYQ*;U4*713#BcMsldM_dyj`*a}t8x~N88PQ0qL z#V_LoL0dpP6TG1$``sgWqrkHpZHc%9w@kE>)DQxA>3J&+T9xXZ0*Wcxu^LD*&(m5Ou#;F{I z^6pG_z-70g!=`k%K}e-@ulDZw@TkkyD&f)>UkwsZnpM}`y9|sMb3Zh%<04zEcGl%Y z>7zG4=21)uqpL{i)i{V;C|<4W#P6SF1%86)SlJ$l3p_aFkF4`+F2OK4&#*z{@Qie6 zwIIe>Xo53uLgJB7Ae+*8A1s?!il;mApS>3^d=Ujz(Y2za5=<%tala#BzV|{1PdqSu zAr85^MdLyG8(CGvNf~QNYCP@aH_5;dM@fMrI#Dj;2}k@nj;WGUILhgnaA{Eyecx6~W~OYUV7|4)moLs^kMPH}4eLwj z0W@jRBlT*~atj1RdTZGFwEj+kH3x(@!VkckeRyy`t{;>e>RZxI*gx(+2Wr0>PwPlJXSW>A&p&Xt@TwWRtgZ9Bhpe3*{g5A9^TZz^VpX9pL3MT|5XUr_8k|kA?Sg@i zzxxm(!P)0B^SAvKlOx;BiTnX@elXUUw`iAf>I|6(UQocgS?4YrjRW?9=1WgTG8jhY z^u!fTCk2ldEJYSoD62~|3%4L)h!;>8CfxyNIvB#t7+`unyqd0pwMHgwUuTH|KmyeWLmVbAm+?nLyA# z3xfWIt`EhI9s#Z;$OS{Ngc{WpCqz5mQ-YT$Osh?*ji!IoRDDG;F|DzIt7Oof>V z3MeVc%S1p#K+eCH`~1HDd~bekx9>mw;UC?+J$*f&*L6Lv$92C{@Mrn4KV@bXDe^1Q z{S@&ZMz5I-L&00O=`H76Ey*$eO5AZdSDu^8Gz-Bc1{Rlwtb05Nev?XPrTdlQP#B#U zYEEhhTM_^#2ZBqz25UNY^|a)EIu)n5hwV5$eXrABUjBj=x*(kQh| zV0BSvg5bg%bEv*}_(Fb|jWsL;nTf(&!40%OWhD?SiWrHiG`?9d&SJfVV75c#8w1BF zkr9^UX+|HVXa>mF4mXe3>fUpwRhQ&b-k7OgJ*-!vVcU7vRjB+`ImH|kA5KSYUc;7 zG2Hu}y5z+V`DLMZt>x2H%v9dGw~Af6zUXqbkr5XAqa714cWYSZyI}8x37aju0y7<` zofaF4i1CCF5d9Lvn+|vxxi-DiyUQ3N0y9$kWsrj60PQng=hXSteduZBI}$6yu(3|V zh%pcoQK=@4{rMEAwUO2$Z|%H*_)v;EUKW2TKnC7-x)k|dh2@4csK05C`eh+^m3Z$E zBb?ruS)jQE*jlc1{;*nqej`4+L?{c59#-}1?VH@%^8rVBfPP}q z!i})Zh0%*c-h~a2jgT7dm7Yc}1bxmQ zF(e!@vGyuRf1Zb7Dph%ut|$hJD(xmUsoDy>oE$%4%^<1weZp$q7-8XK%nNM#E%zKE zUhz_O55EaTY$J-+Q^^K&XOvtK(1NrOHQ&{CAYH#B15TjoDe1f7;9v}jUN?-63YW$Z zRzpe$4N0A)!4!p{l$`oR9UJs!hU%EIKVSxceqLjkgV|Ku!jZ1x{~JjNrf1saNMiF- zO6mo^=xEiGjgSmj{9lGFQQT@52iD8lBrU*>$RZw~NfdXquM|L6raMFbHy1}g`a@y) zb^5g*?-f(jx!ZoJNiyP;cGmD6;w+n+bgNkgSJA)ZzDy!_pXa5Zp8W1 zpBN^ou-cN^SW{Fm*l2`|)uUE6c@8Pxlr47wJ_{ubX{v%uW`7>>02cP^0EDk-oTc^yI_KiOVv?FUgWu!coTlw<9=ew76ex+X7@e^1hDng}nmi16!3 zV;`uQ`@7>sG_kpvGxw*k4iV7EJx7@;uoA_!fduIk=>!RZNedIYM@fJ1B+wv*E&F11fRg?M1Dz$lhE zg}Z~pFt%(2#*>=h?)=hcFpEr$Si~| zbes*aX(>UL4tJljW#^uSgxZF4cMcw^eRQ{(4$W=ScI99KW)g86)-F2TT&N_9Sn#lm6YehIgAZYU{ zEi;2VTB0|pJ67-S)ks!RpCx6*W!8T5ZV^D;wQCicX;|0MJD)Dd82}@&y!f5?&Bk?M zqVQ)ae?@>VX){c18(hb7T?6~pdln9pJPyV;w*3;QcD=!0?9q!O8*@;x5?+tS&uqT|r1T~w8g5FyM6T3NN?L)>d?ql<{tlaZ_OKR?E z#DY>|UPIn)8GJIi6&wh`I9sCiqY)#*n^-DEa$&P1phfSD;8Uq4wLN(Tx9bRpNmXUv z^Z+GF<`&R9ZAeU1M|ge1VcsCz|VX#fXg7#Q2b^TSOek; zI`PWF8FmQVeO@SgGXSQBjf*BR-0N9B@P+fEo3!BhH775detKu*-g@`Hi)O^PUC#=? z0*78exE#p1Ir*0L3}2t)urjG&870Xs$4nNR~kZ>FQ+=&j37 z3veY2aDI-7;&W{HXpKW{8U=rFOo*844wvJh@-yx#IT{HLC>$ zDpJix?nqX7qD9uiLvm;BLla2FA=~%zz-n(PNsJt_3@ebR%;DYbWtTfZ~0II9U3W| ziH+GAT&_%C=Kd5`_?B+%sJoErSQ-|yWE|sM0GZjJ{FJJfMi)UJICouNh**5d01xc{ zNho~in*NXnW=%Ny^yEDtBkC`b$rbo^exZ`xS(uagl z%SgcI%j_hI-L)@t*z7)4{axgC>$-u{e|e@?F#={QG}7`SQUlJ9z$8Y}E$u({E1rpn zc;BM89iyxpnG3oeyEb7Wg!y?ixI{Qo8?43^mt_lLK*6j$uDG%D>pDn`Z9qJqR-`&8 zC*z+9g4J?aOX=X#u@2wu6uL(iSTQuhf4&-3mekn{Cg3`yW4oO90?^iNap0lm)JCjc zg2Pt5wa~igY*@t0bqx8#$H@z~M}#v)SQD`s2*$s&o44^{XI8r=8C!m2?fzLwd+wSp zvm(8<=_mj2aJj%<#lj)zo$2HZW$aRHtQjr#ce{L>k)6Ux0POV zB%V_$mYfqS9VA4@G@>!2-^GJ6SBIrVEbk8tTSTle`sqmAqd7lSadq8&7 zIBbglJwhxK0$RD#i_oMN;Hq!5#ewn*^4Ov>hvzcYz2Xd~4iQhiOAP@8rXyb$z-R5I znE6oxeAb!q7Kydp>_3W>21OUmbi}RwxNVmo7`y`R{O)WhJeCq51bjAQd0v!y5L{Mu zW1D<%7w^o4PhLc>s3C*AQ8c-V_Dm%E83_c5FI3BM_q*4Nv*}8}-2USk;~l215d#X8 z{3KZZG*cMU$Ix65M8n_G-+H3XMX-^V>YFhQ_F(aP2ui`$!@m zt_=#4 zHR*xG-Jc?vP8V*Ah}_|HOX3BM%!|>?M@#}S*@AE&i?*b>)CFt#V zZrk~1^j-bH;|Lx%WUo-v<+`#?S!O8O;-K2$ZpHD{;A2ZRkmtiB$rW*%yA%Z-Lky0h zk_Cw|r)SWiEuJhzDTDO|pqlFWF-ydY5+yf_MsMJ<(R|U>AvKHd`J_a{Gh+;9y973< z2VGQ-U$|a`C@;p!c*IrTB2AhHvtL54ke+>>Y^pznc}Bj~e~%gsuBWf~zz-KOqy}KikDt$oQ||QpKmrSA$m82xqW?K1QKpO0#BEsq@Cr)$+-*>~%lTf~$dCb2oOi zt2r6Ow>;JU-n?5vg@Lf6*}JoA3HiIB^$`(cm8bIw!BkXG7>#+gq`)(00*5c{US(dt zXA^UH)T^|^`_TQ}_Ynqk2^r;oj?VNf7|E)4FL8^=Io^6ufmoPAO|$F1xDH8&=9FcN za!^P!5pD-3^@;qFUT~;at?nr%!7%;(oGRdIB^*W#uP-ukt1Twt*HBg4c*1@zIP=Dn zEcOy}kE}n`9?|1Eau8x?>}sc4I??Z=uS9()1LL=PZkRiExBOz+*8IF?UnA?iZOR|R zX#XZP6>KFZpx{A&TNBWOuF{cinyJAnfuUkn1;CPVGGH}u zA9bRy2J{$>%m3HJTl|Z$8GNiO%C^WimW|ILe4KML<@f#|!S1R+d&F#NU^^tP?=ZXY zMBErueyk@mX8zt^ux)1aGrmk0cMEc3&q>E#=Mjh1Mw_3&v#+fu`EqS}$Z+$idYJA> zgIO^pB{>93^_K$aP(q8ZW{kyVj?S|mRTH9I+$e~#XO1=dYuxh?1bUZvB<}k=z{ldr zfF?aUM5K=Fw-08QhA9W|$ta<9;`q;SJ4ia|Iy9>;2iCId(LY@kgn#5$;XLDG$voKB zkd&DvOukyn;CttVSnff7D?+l!99DAYH2K=`iG*Icng3H}>x)ibY?H@xz zb;y{25Ob9?c;0H5!VpKf#JxgPU<^!thVZfZOiuA>!zlp+nnfJaM*Umnhou^YI$gUviv7>y?1(jnI;WGB#Nq5AbotKk)-H0+lDYq=5SAv zV!tF^Jc`QDGAD%;xx)Qu_;R$d*y<_{7QV!VULi$miqSu%snRxq>a9o~RWuP*+jk## z6Pr&NUEjK+T~EzQ?SY2H-pMcdUn=b8JFg1PU8M~Rr7{-wcq}P%iJvg+)p@etE4DSa z&Jqxrc-uT0a9|mHi%YP3h`hf0?Qc*g599ZQ-U>-iB92r5UXn)k%LtO}Q~`IdTJ4fc zrpdIQ;V7Ua<|aiQNwNho;dh z!dZyeZH|#?7W&F1qv`?6A59-o*u67 zm$FWjOn6TMXNSapUlb0Vf#p1Onz6TVd*l((4S1rp2g|d(ct}nKEu-5O%}UCz8;&Go zI%yyLD$%J5OkNo=7L~0#q-k4Mx7E)v>uzs^t~b2ONAHj}(}+*apUSsm+RVKdrM}a~HyesV?55H8=g+RA_86Ke0-jPVpy~CQ z-&Muz1f5P}G<*ht7d6#ehx7}6(OcQO&YfE5T|wFjL4Q?majfGjqU@g$JOfYf?*%A# z3v$y0Xn0jqd8Sd6uqTB}dyiZxPwBYipIx8u48$24CTDg^#LSCES0S9x7AchF;)BIv zHstZY+>WRuwXDe5Ow7;@?X%cybsom3AN!OFWxl(&U%Yzi_&i=TwKCAc;);xyjCsQk zP^N$%RhIvmOh-OjERm%iUcVrK?LABMl?%}AwiAY~xa*M4t-YV$N5f-wfVdX~;A3Dv zavT$Rlx2QfpB;YJ4t)dZa!!jw1<(HbwdYv;pMP8gvH2wh*77>{W}0|Eq47SoJ|0;x zb4i;NOE^2MEIM&XJT7Yt?3k==B$-jMWua!GcH)^pUH%*E3-tM_>cpx6kU?w$Tdb?7 zYVS=_je?~4E{C8^jrns$uq$HSs9x1?^_Dx<9lEjm{sxQ4 z;9XfQyVZXpNKV=W&U;vfqxD~9kp#C#!8Q zqwPbE&)RH}*W)qN5T049;YZi<9@jE7RX!Ri|FlItsHhB~NJvTQXfgVy4$g1E!SpiN zp^LEihiKZ!b)++jh1;Z!68GH)jU`(g#z6gbCkdPeTMYdUN{tIAjdxwrMz33@p}pQ( zVi>vUACXMLVnB_%Xa*IqMhidbiusb%C^zMyNU=ILQ!5@m)Xv}TtfPtuw!vx~qcK`P zJ#dD!H`%F2Ce!$ahNTp)yCGu^s62%75GYzk)NyW#hE^s|d?*-tC-J2ft7SA!Z&ky;ilc@N!K$w!2EBxf#!28KDhAG>^4yWVni# z)(z*M5q}mG-ScoZwezPS=~#Dkv6rNgxoxJc5e;s+THH z-1o@Ey-g7-PCVP~(Yd|1{CmbY!B185-q(H__H#!zmocn{-*I0R2fX*5v~s2+D9p}m z5}f(u&(nr~@z?vB4-c+5euY2B4Yt77Qi_|F$F360>&!(T5%c95_~?rfJ`@53-vusG ze!UnA?18S6BSCweD1)NRA_jbxiV=;hZR%{_@*4Y1h!A@+|6o~-b;vlT@ghv_Yk~wuE3TU0t9fYOJC4Cc6Ljg;VxeQ#XAvb-DG1WZ+*SwR8Y|`U z=8sPDJS*!{e)RchC)r#HiRwfkAj=J!$Tg1OAxL@!w(bM>*R<9OACb6i=F)DMPcjke z!TTa$pCv&aoF$&j%;f1&;2MfVGt?)!{t3uf0xDzSpSYsl>DM}Z={{NFQ%%_E5B7H^lzL>2(Gl(+jo36W4}u%c zG<%ME#LH1_Eg1sV--VvRp*`VOO2laDtxr`gjo75Q*z_c+%<4@Hpf!r|I0(#VB!9O) z%7aFf2vGrQnFDcGBSB0{Lm^HM8$`B(_ChlY0*os>j8&)kqNM84(fq_e{v>AX7mvoQ zcI_>iff~V~nv})FNuJ-?DkIYRU)VBwO}CUU{g4nTbJW10%jzW7Kn(Qnq1#o97iXYjCT)Bd|R{2s-4F=7d9v z6`+nl$13c!56+h&>laGG0Q{-X2xkSKJ`lrCvIs_xtTAexn zEBk&+p5@Z?Ws=PFXLzQ(oERp~r1au$vN`EdvT3AjRz8&5tQ=$hYs&B(fsV%So&b2 znw7-7ROur%21fKU=YB_0l|t*1Af8T(fmuH7y`hcx&HTACnBz*g2S<0^dsj#j>sawX80r2=pg{nKt2FrE#8iY5mf{kDHmQQ9#ezJtGTaxl zcQcnekrgf{WhUt{(h5eyBYW!FA@*nZtxAtBlZ5feo_zh;9g8yD)VkZiU%s?GD0;;^ zvysc+Ru7KwNh`jNkZc+$7K!Vfnr0p)y-X1_@;cnoqd-OQ%!zZx7A=z_z66Uq*|ia- zAMT^tqm=I@5?n9C*PNx)kshCE<$oI@R3~cH_#|`_U8q1HQMHY%uhVl+{grJ1shM{& zbvq9x1|1XZ3&R*&grBYUr{(SxKJY?uk*T|30WwB9&ELyFUBE!%s9TWETp_)1eC}vpyVj5 z=x_OSwJZLk!xYhVYs9yd^jJFM9AVoS{2D1jaL9f&IJzq4t+W0gK2)7t`@N)Mia|i> zUYrq&t4xh>aAR*lEjLKHHt%vVLtKO#eWH}^#s*jnXT_3{dHI)&enIcdrafF*bCz9*gIlU>@Pyo)uSz-CsjMY`y}~VdPB)xB87-;btHc3~xp;E;xNNJO;FFSCjiXc(2! zL=(*G2E4cpwx%~1Y-!JX2lH<_HF-uUU*cQnT%&=5E1aSnvl1P7S-lz469dy%>Il}S zJURI+nNprn{e(8W;!Q_nt+QSokz@?I59|1?k=WbDo8MO5rq8b0YF!j4-cPu#GKc9>}Ho;2VuPkq(c z&z{>)^Gi-2!%1&}?M<+grD8)xnsg4h$q}TyH$SsslX@FN!VH&6ap?NIKCpaVuN-@G zrPs(plb{bUQG7E1{TSMNoK0xDW|Q5mp%KgNxp26VL!w1kWuVcl2*AS8jrCZCXvRGK zBdUNi338;nVr7dYlM8-&q(;RB`4VB7>b*R!U_5O*C~bn43SUIxL+c}h-tV8B7Nf99{5Y};;m-d4&~ zKx!aMwid8|pmPTVB73$@Gi|wc_d7YK8Lx6a;g^FB*lr)ALiG0CGi>Lh-d(Se7GcGd z(DLq$9iW22n&LkEwKE)Ada1LSo!LDCn8h^Y=h5C-@XiEJ7TB1bBM?R%G2@7{6)$V7im?KCGX@Z!(2YvDQYtGlYWe-|?GX)zOW% z?OU*-++tpARjsT?io_ss2;X8@87#x|+9PXJd1s`!3-b8{&`t}HI*SSB&0VpIMNV;6 zwxb=kb}UD{O#S&W%X4f~@aU#xGYIKjDK<*2M0Ocz-!yNN+TpsmXU=n;_j)wPUO{Bp z&=oPF3*Y|N)3XOh`{F-zG(W0@Vcp0NQqQjk-^!b&Mi}{ccZ+)@*Im^}w{dBl=IzCX z{z_f^<|CyLbqjuvdT3CtU0H%Xt^G4JRGhZI4DZKoqSyTk1^|}tB-DRqimB)IodNMd zC+Pd1e}U6qgs&U`bESP|D~{WlHF~PGG&jRcXp0vCmC#T`7V2S;x_CQay5UWYE-^Rz zsa}2BR$)#sC!*3pos@agV{AZ*&b8up2TEw-7fp;r!ZQ;P9IKN?DCq#}iyo@vmH3^5 ztt3Nko~v^_TYaIG+Mi?0)9ca95|c4WrNKtozaOtXyGR;|f3~fR>fbINpZoc@aX-UG z&5Mmip+dF%&Er13_UU#Be3r zw*0`|%1|iy9vUix;aDMPzefTVirDpX6?kQ9e1gO?oe%w_r};3|D@QT)((wjQ>S-TM z4Yb@a%={y~gqhlT9U2`a{4sT|))RHp`$1g3Z0wbjbFl%NPu*WEg@X9#XW|Da#|U~R z2!W(FXNau*(t6%a3~^4*Rpr@_e%+{UKYb2jpHl>4^P8b=Do_9fKkGq1Y)b~xtAy4o z-{83>V5myUq?ZX>(g0k^Kliy~u`dxEg8d5|f*t(fb@Gjt^QQQ34>!Q>soreVOYdzh z3%xT{8QUj1@M9Xk>c%{a+GS0?{Om&qu3B5AG3L}C_-@zM1ndxU$$RH}YeQhLg0gfH zO@!>2y%0?oI};wL2hE{sTRT-Lh#Z?VGRHcfMnMpJZ$i)cyU_ zet{iL!vR4c4x1qz)eP}O1Joru||V3~Q>;cu)@<3Ico zCAZ3o+GpEn!Ru8+)FtQe!vblbw1>&_gD+WlFR>GcZk@eQ{AzhlttvPU2Ez)zG8J`i z$Lc26^=~4-*^6o~b~eplE?O-`E{A8S)_v+rAy-(&`Z};DlleN%4xu!ENIma8GZ(_O zm?+{;}W@#o9P{9 z2iDbDg**Pg0GDD9zHQxgTTz%1+x3R~(^gwe^)eme6pE?)y9`$eXX@?c#~9bfKBp^F zvD)OHs#G}Qj-Ik{*G2x?!|sjn&K*N>%sKAc*HS%w&%q?wVq^CPK$Qz%t{Ow2;U6fT-+2RXi+9N)L_=s4iRq_Zf@!b~G+SibUy8@|Q^iw=? zj4Bj^J)4FR#Eto&CV?C%wnCq5(qywiMJ~ZlK^P;JFSCO7jX7-oQ5mH0t@{Kr8HH5b zRe>h)B_O!x^tsFAeBQR%mk!H(fUDO&%lalV>x$j2>-CfOsfZh=YG ziV7@anpS*N_jGFdVdahOD%+UjnAAI!KNePd%SLR8aZfilhb!l(nblc^IovI$FQ!*7oY~ED6wr(s?nKn zQtqlI3V`zi}lU~7=i$`gF9(d z5f_^l@RiiV`-J5!5m>;uUz~&iOsPC7&|$f01bJ19PJiHMw`_)j3k(|{OVXF=X%BhB zEV>0pfD2kAR4Q{@f7xx*{d~s1^ON;UwwE_UwEZm^*x)XR0Jj!0jtKl$f^;DB#?rHE zeR{5a*FT?RM;n6b8C_h)aZ7J1X)*H25bV|AStc372&c@YHI`~fiz zPyrMSCJcKbn9p8CovOzHgQ5H(K^;aD_m>Kwe$PtY|FAo44nyG1XUxTLtK z5ZQXF0Qb-!yXP-DiiW9oC8w$S3;q2|753P^gU2G}t8?h{`+z#~jz4q<)2bwcMdBDk z`LHocDUy`CnttKyVXfyHwL9w-__U_8#(Pg!B&b_4M5;jCy$xqmGBV_8Sb zMO1yFg^tsrH!sBw_l6&yl`m#0jlu{1p!iS>P37Ks1;PUnFkwweNUK~vQ^yx)E^!PW z`33!q=vmS5S-$_Cp%qizJKp{+Y`zSudxY?;uBv}@ zZ`aqa<#?A3!aH#m3_(~d`82sF{^zdC^8L=>z-Y3T6%=hj(NdkVeoVdAS>c2Jn)enA z0i0_Vot~2?Zcu5oWyboOeIA-)9*0`pdylV2ak9&mOEZ@D%YcPC=K{2IUs#a%#_m$t zSe=~X4AvMA^_T{VmF=G7)8A&9;XOI|ACWo>bHElwN^f!ON2{RG`d5mqG(kPoHzP~G zF4jU4hyX*XJ@dDOs?3WdS_ba~T7$`JVL=r_pe4j1UDoxnHheK zY**aZd<$W=m9V3kVPI4(KM{8T`k@}ry`uvK;9v4Z>9_5BKeU%C0{w@;9Y*G;u%aa| zrp26S%7_nazT(yW~|%3OZE_%HDjXo9hT2x=DjHK|%-L{k#X{jTF` z)z7t2hbnPNz|LxbCOW^r))*;PZ@^&g{}a!Fp;N~|4;9&Y)FW zg~&FN|I9HV_EB*%MJG#~A9^MOIPco{v*^qHv6`U^y%W~@;0mLVih2dz+QnY1#nbk5 z8^t4LyI0J7Fdj;eAB)s$ofG$W@b|PU6~L}1(bG4!vwqr2hhO0d1g7M#ULrIy`HLDQ zaN*lOo~mp33c=KFHN2I(>B<@BTy7@EE8S>7Ao&mvF;c3<@XTFT^LGQJWtb?-2{|0+ zuA&llOM5j61}7)eoCT@0lZzp?v-`ue8=Y&c7X~_f;%c+@u(UCL_?s4S7iXvp1Ac!z zo!8x9N@V>LYgOID^pZvn$2`~bD&>z3A4C7G)n)weL24pXW&2w2t9R{)fWx7Cy=Ggz z0}sLDrWFDyG5=#d?5;2G@hBJlh*z*m{AnM9Q&na^*b0s_H>oju7@56LiueFF4Z1Wv zgZ~$JF=|Du;GytqGJdBeJI$ zOc9<1R>T^03*{>he{YESxntiNOMxcS zSNUE<(OXk_fomUTntV`lxl>oqof3ye*V^LM6+R5fY`7%p0l!DtlZO$~B5$o&YE4ollJ*Cp{D#)UKU|~#V zPgG~Pg%lKlde%r1d?_dDgn$}a%A4|oC z&^VaA8Jt}uf;ivQ|Eo%zjqq>JybZFb*l+A_@NIXtfIbGlhWJl1A!W~Vi6ckyrGSNu zX(j!xDgT1;3&EOP8Uo^xl4~*a9=lpLdc578MpB=Ncee3u=ZlrO8;}-5uNL`0hj;00 znLXwwwnm+LWDtTIt|#6j%)Pn5&v2$YMiT#GM*3?M&nr)>S`v-eHX72>$Z2++DTQL{ z(-&xx{~YUTui>JSyp^GL;vSg+u}``Zv&Xjk-O4QaJ|Vg%UXqZ25zH5R2~EY`7*Z>+ zla3C;(z*>7#p2NJ5#`u`%13LA1ey|Z6PztMG#E!>=xwvMq=|!bcEz87 z2wWG7V*g(vxpkxGD1X8D3d@FnX#4)EZizYP!-3%2Ng6@Lk$j7F0LI2c0ajAR;rX0< zml^GMuO6K)_~E%y*xlh6e)~t`d^Dfi6;;f3PWb!{;+1A$E#h=)htS}_?KgodW%lTA zn;#GUCcvceN1}m-2Le7*4J5xXaHvAuv^FAJA%~O0CFz+0h?%t)%b53Y}l;wmdl(N7G!FJ!M@APARaG8 z)@0Z>2yAa8NIQxod4e>mg3VTvKC4Zf3BIrjmKhge&ZgX3*oTokTQyMLg~fiXz93)N z{oYUPHwTw>h!2P~;5)!x>>bdwSXIbnlg26eMG&#T>jGfSOoNSNa@7}@JeYvHcQVElaS$-lXTcL ze_G7kF`MaZB3qE*sVa^>{w-4VcQ{@9v;|#hO0>`0V#aagWHD$a!t2m2)0XeI!gelZ zaPdkztOk}F09@=DVeT1lo{-kfEdMuyGYO87iw;%N4@`Wn5Sqd)zmQ_b#nxl<5J{RP zLzPvXIk_VKlOsJ76%#ScKYxDOB9}y={R>|Urw8044+JvZ`XYwLR@?{nZi(->%o4Ez zR(ElyK2>vJKZwQ@Uuui6Ob_^+U>oF`i^i2E)~gj?rW|i<3>6P0=RPw@Wm8mcggx+| zwjv_85TB9q2wjMS*L!A3U0~}QMMA`{%)Zt63@I{1-?!W|rR^(5*I0{Qgk`C-xxz;s zzJXNT8}Y$D9Tq3vNvURDF`?*kNm7J3|=lKtS>$oJn zN_Z}}QvBR{KNo^x?f&Q!^0PbhA;{Z~BV&wTz%}J0dY7RVr^GwEI_95T{<(Ge=6N-p z_@x8^(Wo7xnDv=+X)0z$W8?Nbf^0iTZkIBtYBDk@-tD;A-N4IaG?=Laoz`djgE8_B- zJ4?EKGt~~v@?ZT&N)Ak0iynt98H#Y%Gau97?&Be%*kM!DUF6;Ka8NuzuEK~^dN%TF z5PXp%$|R)o8CvccZ2yQ)H!|Rf0q55p`B7>~uPFo^6}REm0XFp9%Fpg(F4oDmE|0yf zu57dOgZg4;$?iSx7m0cByHGQIMcP{&{dzd*;xy*KUo87+;X3v5~ zeW_Gp9QE%8CuzQOl26GbGEwt4EX=W9VAm8R3SC9`JxbcU`v_+WJ1uF$Aa#ag4*?y zeg_L5YVL_+1{j)DlB-xbrM#_4>#m?4JQkKBnG&zxONS#Ogoof*sClE*6TA-t66nNE zGYuCKCi8>^#%AHdb*&qL+R1q(8Csd#3mN81w4B;96ZZ>gBNwbBCyd5EGVVdb**1QYg)$;4Y*mCqN`ALVnuzaDoU%guD&n~a9v);ERf zhL;;Gt|L_dkVcgX3D2VM&eQnYoCWt@iLblwPclfofZ|Nf20lVr_@OS*z1zsIi~f#U zXyRLK8-5AHs5B2dw_>C14>Tv7MqogiONt|MYoKL(knJ>ujF4Q{1Qa(39@mDw7Pdsg z>_L2d6nwk15-!Kb<%0d|h^Ws0h6TNRQ$9I!&@o(l(Zet}-!o~Mr=>I}zyo7>RhDJb8R6`)G3>^uDr$dzR+7sO;7PM|Mjtr%jCAMq zGW&VIX*ycwWrBQ{CxhcBxI*te)T$kB@=HUfSBj0R(T}J%M&rIw3JH}`O>pdz`2vFO ze?Pf*Vq#DNYBdaIQ;+`Wav0T!t*!L3$M#~4~>;}pE)-7WCli9(Sp{} z(y(cCSbjFW#FbWnqGn<$3P6lT|JmdFeKf`6rZo1L-MO-qRiUyI&r{AX*$#H9y(lS< zDj&XaAl9mFo)4;1elP4E3tZpJIh$FK75kiw)ov^zh%jfOB6>-JD$jxpCPoxrm3N*# ztWvILGMTb{qUg~XURvL$O3l5a_yu#aisJG9g;ee8PC}f% zZDE*D^hUh281yu^q)6wEY9l40P#e68kdJZ=B~UF_0ls~Cd{1xLaGjgms?kUpb}5;v zq!wW_w2bw9X@-Gy20W>pJ7b{%+Ih zgv)#OBskRp8Mi{A;#iGxFe%exT&OdUHAd+pZ%pR&ja@32alTDtm||qxEfUw1eWwhc zWDzLTdEbpAs3Po1XSz7kj;k+n^6P_7N^Id?w8?XHjAdh#XYp=5ZQ;1K5DI_DpcK(l z>Ja>~hQ2aQ?mJ*ho_SGYtT`?mw8czJ>n*>2M5zig6hr0nQ-^%A07-`U;O?8r229eS zRtC%*yJUosw`Z4;Rp8lSyvuUyiB{9CknVnIqZ-_e6e_qxZ4mSEzAp^jCY0OPf|-63 zwX}fCBdU3R>O2B3?i;Wb5VKfX;_+suH9zcdlG#*#i`u0xx~44uk5Za(d-h^$W3;`% z&Tr&<^JNMA!b43V!jx}!9Qn(NSB5tAD*e}8T=F(|{_9dDOE*!gACT50B0JtBkmoDn z_$BfDX!}=H?tL(JFDM_#D{HtURH|;Ek!^2T->o67ve~ZsM|N59CyaS!yLa2JwJYRK z@M<254SLxd+{Yc9nlw+p6vXA{=-7rVsJ;#~UX;QegQz~E&9|=9{DJCmA2mHvHHHZ8 zwN9iulT^X;p`dH+MGz9%-C^6P*SrWxMrm8jnObgRW-Eto2F(SA)V7>Q@BOs=h)CN| z_-}K{x2A)CHAgovAA5QpvpX$m8uZ!au#iB$?{@UhhllSvO(8|dSMMq1mi7epkdBI! zla$$dCTt@iM}DX=p3Q8`c7+w7GD_p+bin$XYvyUF9S5~%#STlMp0|c~M}cb-#%ZsDvu+EQV_&O9c%_e=a_5*0Q@~)}r&Cc6(se)m&m?}v z^Mu`^mfOGTqP%(s)AdU7N3JK;#y+K_H{2iyLXzJ~8(E_X%TyMVd2~{;6q#QkO*IKy ztoVZUxjrz`$WR3=!o8m%G1d@l%3Ny&4&@$~W|Jw=j7?4x&PRJD3_)lC2^IZT{rO5& z!2!3dyOBrNC=*nWrhmCdyx-RkgHVU9i}^BP*K+Rk)x91s|18_@mC6MgH5i%H);rpW zWG5b}2W^IZ$tre6=9M%{0Z|*U@nX!sxqQK)*5zNMI7%BcHaPEM(uwQ%P`UeYyqf^w z`Sy~O`vfl|kWC>5e3%LaPIV|yDC7Wqi`3DwN;4A8OUlf=SEUCXjE&dUCoaN z^Yf!4Yl|oNLkoz%+0xp!-i&ndr*iBOdh0V?HAaA&>xYthtOF2=M4~Y3 zJM-rbrOBkHH%6h<5+p^ERD#@q-L9d$N$4p1)1fp@k@YT9xFeHnZqxm0>X>)5_<}wy zrawT^*m0KmsC{&lHfm`^9Pl+Ut|tjJ3GhpXjp{buY&0l}EQU3=u6Bmsd3o>0GR>*Y zuFY>KZc!SR4yqYZBVck`!l^=P?)Gb!RPPZ+h+6zX*F6mv>BwnvyJY*a+ft~d~%&o-h-5$eZZ)DywR_*Uf1 z+Wo`N<7&gF{Z7VO&$2x1pG`s zTP)tg-i6<>A-=eh8ha^I+v@{ClY3}Q!>6QBZjLcrE1?g;`iVb#l-w{NVNHLZHFmS=Qg|WUT!GH zZRTVp0X6EI@)5E1|6}jXqmoYlxAFSaj5#edW9w8>Q)$y?yOn}dW}CURT5hzNDJqpB z$=Z}If` zF&9^&sG4Pm-{%f?=}UMQmatiu2$g-ea8Be)g76wQysxP$kSo=6X%4Z!S2Cz{QxJ3$ ziNLahhxgFeAau*s{lzt5IAw&KDR1TlZYn0c(%)XkQA<*Sd=aPBA?y>-ZT(jNBzAU! z4z@n|?NMdSudCM0FYx+U;%M6}*?&bn_>@>U^*?)-zdlL4;^JePwVIeGW5~YyLC2}& zUcj&;k3jvVHc}kr=`(c1i>$Q*UnjLbicQl=cp|ysi5f}g;uPB6@9}1R@(s#uFy+j^ z4CmW7)zs56pYnoBT&Z3!5+zHsd};pBk-9v#gEh@ zBCLz;p|i6>AF3qaX#E=h@W!z7`s_gwg;Ga@gg$iT58fxza{KZy-1f0%hI;b1l5l*@ zj{TfHAy3}U!?=RC^ZtS(&42G9efqSDIyf_xvip*urDf$+_0u(*McK9Ky^xM3A=||BZv^K9?s_7F zX*{wZY8En_K;VVcr63H+a&=-)iJ$S|$DxGsts7DM6Z*2M+sL;*u~Rq6$-KR1**Dr* zPJEZttB^`m9 zPGWwc6w~Qd6La-VvP5z^ze-cGnrMwkY+ZQ8I0GSm+% z4{`M$vVnW*?O7x7_2|Yx3p@e=w z#}tcgn^!u4uxVvAEzmTuPO>smF>Vr4hrQ9}^G_Wvo*3K9 z(Hc#U=M%QxKp|~dxrl9ltTrVCLC1Qi+xJgCH?!P>xK2=F(%ULgS_WI#BuBJ@10MZY zZ}m|VUOY&I(F%W5pJC(TpGMoQo#AJr!`3E#nV*beeI>;gN0*O#MZzb|Lc3`as6h&kxxa9zNa211kvq6w~Om!wY} z&^zCtto!D)U!&}E($u8n(K_U9iWhjbu!)e}r<7C5%awUNmRGob$9{xVE1=VZuqcn_ zX3Wn{VDkcVUuRySqC@q)uj+S?^g8g;Hr5j)A@Q@_57AZ$!b>d^Itjw z9^Hu`>L27DFv(K5%F3rti)F>F4KRHKOUnc&*3!7gt^T`t|@G@84rBSpepN2d>>JyLPtN@7TtFkJbg$zkga$~U{S*=;UQ zbT;dV^xy71ycgxYZLmu?(d&D|lO!1H7WDCnON3ftVmgVT?Awc7GIW%lZB=}JNi zG74k&0v-OYK&iad;L$ohGsfQWW)cOBGs(K%mh3;#Un>E8U;y6VUQu0 z&_Q4Gmdk0an&ByBuOSwkkv17nHXsR?OMbN}o>7)|nAPuGGTo^}ALlCJ8Xs|8+N-qi zS6pCvT~&NGcr7LTfixT;O;em~Egt|c)za_z=D1ws^EmP;Y0Z6Mzi)kuCY?ft`;lg} zMZ7)Vq<86O8`HEUtG!ItPMi)tldY;;j(Lv@n0zKgcVES0i`~zw35fHniAFW5*>t5# zn5U?fm?KLM%PLQJrO*BG!KjPBDiqjU4748%CB<{z1amd(-KrX~jC< ze0dIml17OsZBA-w5i_;TE8wQX@z|xmRB*p_Y`QJ`Q`$U@@Lp7uZ6mR@OX0Q4BuG7c zCcbI!qf+yJD;6S()o;8$<&=>xGp0&W@0<;oeAASdHfH#Ch;nI^-I0>G(~oecosC_2 zlqj1Y3N*@(oc33NMg#x!MKg~03V5Qmehk+o+^y#& z$R_B^4b1+sDWW7L%D3wJJZR&$D(x$TIqQsvPZ`{I8N+@uz2jgOjMMb)`|&?cI6!#Khcf}3KfhpuOMfa>$z~@- zsNJS0-u_DNQ?Ml9El_qAN8Iv^X$s8~l`IX0&r|C-%4V}hsXtFL_ObuV3--$oN7-hT z%t%m?q;GsNlcyrq%US}PbLrCaNxJ!$i#ohX;0W^5l45@4R3m7d2Y%}=qy7!}fLi^_ zHr?0?-NJ>1DXA*c1zC`vo#O{drGk%*V%Rag)Z-A!ES;bLqmoy#N$ngrfO)ug>|S6e z&k()1*uL^)fF=ZwH5PFFg)2*9bHNP@9Z~|rt~8fXR!rFq&+{rP)|KGfNfzMtfp1B? z2cNN5TrwWsWjue67gka{)pWHTrb&PJwO23fnxI%X5y|+=aPAx zY3JB&Y25h;X&ktvP`rpP121?ce=`&v@t^;(7;J{qsq}4);wcq&WG~#u$vif$A{B=y zq5~v4_?>FzN-~T}TK+wO>eaV`v(~@ol9%QqyI4>r%*BDcVgD__!MzS$u}wVI)4vt8 z2;y2Ah=u*#BCo+Y>ZRuIybE3Nsw5UgFJUGj#?e(};r@hI&ml!yK)CoOYUAF5uDC?n zyP)^B7v(vZ;{{$=lZ!*N$21p!X}84@Ur$pkWV8Ca{fS1FU@SLEIo-mk%?R@QoNvvV z^BXxqPgi#5!K1ytv&1i^v%`}7d*US}+Hq!A_^~7wfdIo^P5&4Qzv+c2fV4NTPhat( zxZG&_CMEKn;=#lZtNjVQnBE7yzsI*W@ITe&viiVS&BvVJXTAEQ$P(?tj=d#S<>1l{ zReFiQir&|qxAlhOw=Nd$w^BUlJrm#4zy}!;RDi75yuoCDf??|oyu&w^8eG=sKY4xc zNtX6!@U$Cvmep9$(lpsWx_e$02PZ`sLj&d0xv#PF_zao$i zZeE+W@Q1Ml+i!>^x4D-zhF_kX;gIAw5m@|Z*S1TE`*KIsMth8z6FqU`&>tylR<(>8 z+8gs9({=?3xxwh)QhF^ve`0Ih1)ION&-@YRg;d=E>V&OU@XwzZ6C|jPhM50x=-0um znP{inhLJ&~wtqim%pAAWcy}w~$|O@aIol3l@%Lv(^tRmK86NJlxMxZEYeZmJt3r>L ze)-6u&wJK28~o=_Osvfx9J9K`v}V}n zp2c{6^K%!$=Tzt~Uk6?K|7;m%r1Xyb{Gf3;`LaO9`q^%pFAH@q$k_`1`4ea6B>q2J zo^(s-=jZ(I+Qz?&@(qXicTxUbl#TyKq4i%&`PWkZwUqyBWb$9=KKSUAUa>Y8MPqa@S$wM|6M)sU%1HsCq>c@E93h> ztua*e`>ZdH*>Uq*MF4!Gn+9d1L|H_B{^W=h{{3u`p7X7OscK7>B}t!$GC-qZzWRyI zK#>;mt%9dxZe6rfUnAQDlUGmp{EXieKkIggIj-NTC84)$^xL>fgS5oi;qwoSs6sZc zL>W`R6G8)2b?`Z$3hPz={pY#&2D{$4yh`->IYYPw4sb zs8#e!T$Lf{TUA~ZBDI&{{}2PeW}}~ukm*bvvjksCvE%*;lpYej2arlqjXEBab@L|?fpjt zsNuwACae=I&JT^|4EN_<^&SLte_>4tSH?pQiqZB=T8n}xb+WU)+~ zXQQ8Qr_>w19wyT0wbXzaGOTA!VWmm1^BY@WL8W}HESRz07x&dykBs7`_7e8b&qLKm z)0I=GsP|w8vLj$r7&L3pb4gICeUI%G4fB|r28ocdZht-AC4?La$9>2i*SJ^_5eRV_ zErIFZ%?uKp@fbGw*X0iK`cv}AdX=JGCk?0ASu{UGbuC}!%f$5?`Ls^wE?p3^K zj?{}e_8%6K<;7h1*UAK!CK3AYf2wCouNApzyzQ7ft=Liu?Od?1I+`@t-|q1hUsGE` zchkUM^dGtjVGQ7@GCq=2ZG-nCY*k%N>4lIAq*wki3$+~vv2|!|*sV5r!?9vAswVG> zH=S=t31%=is=wL>^}KUyS`aL(66IGMxg_X?I=%Lwn??Z5z`W9(coVY+{;)s2fx4z8 z4pN3+z^jg?b6Uz)LhsH8L-MUdeIHtC`V_y^xZvw>vA$pUF7MCD&REleG!=@94uL7& z@D@L^MCf)eN_;bq@8GA7Fevr`SCgkK98QNo>zelghrfk?-{x!!g$JSjfMn!j%53md zuhAPGy^8VezXoC9HJQ4!8>?H8@<+5SXc$f0wx%UjG`%|7)GW@yJA6GHtsY`WbQ(%p zYmZwIC%IU6qIbw=HsqzQX(@KkQCk52U@)qx4fR*AUftHy)AQ8|`MkU1rs;5=2elDy z2S!1jxSWAMXQN*b%L__til(zh2^=0ufAj-iB2BZ6pse~RHHK%QYcJBJQ;CDwHF5v2 zgCy=A|C3epeQMXAd#lhJ_!ekWwDdd^S{*Id-lcu@WC*;VHkzdDZt!SdZ64v3zVtuL zfpGHZ=Mhlv3P+*cjrs8U2dL7dDsQJx&dZ|sZW^qy%>!IWdj;QJ#i?*8{6h3q-)8Gl zYwU_TBmxB0h;en(Ffo2L%9o?CfxlU?WzbWETJ(079NYfWfqS~B6+Y@Z1=yS6&NQYd zsynrp8hJbdYz7pb=0t)QSaNpdyzOtZMvpl}b z9vFF+`o?B`4Yy=au9zSj7Q89f5m<#&Vt3XDAMddde$~ni!a!ht4w=d`Hu#gQ8uS)E z)Gy|3Xer;gQOFI4?Ur_7;Vw9s)$szVFJp51(~S*%mP$!FGTFG8yBE?A$g1OCjBpJzK&IFt-qDZGsnUrpa$@IG43 zOy}7$!j6|>(sufynrDrEK7l_+`R>>@5ENv={6&5R^{6RN3kq~`BEU|rcn5yJansOZ z{EJQnOdD?0N2^OxZS<##90f7E;J5qdBsSZwj9cfX=|7_iPwiEPaAwvPFJW5e&LR&^ ztcnRLsE>9sEn=DG$$N*V;akdfSCey>A22oSR(Sx39ZtVA4ZI7rZ&53iS0aIn-3hWX zI%&6_HL;Eew9S8&A zUXqH}027;FPA61I7q$tQipPWwUJ1Sn(?3BzeD~(t`Dt}QR60#%f5%dcc;^mYhqBC( zrpsWo_|xoU81n|2H`E(dZ5V|`CCZYD!+sr$b^?Yo57xzZQ*$(J7r1nCMk*H+M>0xMN&{1=REXwD3dFf^Vi_MLR1dGXZ zdZ2q~iSR{52$u(yVqmrCZNg%B(HrIVB4Fin8QM2jYE5f15?mcmcoz z)5A*R?;dAE@&;tJk9goOER^^RSwZ(;WMdA-I+1Y7Q$CBg8YKQaD<-*CnNQnh#U|>e z3(W}~U?Nht+*j}Z^)wVO;+e!hc-rn@5+xi~jou){ZRNKzJyOh5%0YvNNH?5Jt5Lp6 z!#)+Fw^oD>jo@w9r%$)j(au(E&Ip?LatVI0U)Vqd#+Aej+dd2jjHx)a$W4=EhcF%m z<5!-5!j%9H>=s9xd^k#<1RozCfTwOz0~KJc_AE@i*-Z?Xb^#f|@mJ44b6hx$va)5s z*4s2 zHR_3%*(Yk0xC){v zYd&UMYyqCiF#Z8aHQOC}R*Q}P>C>mnfm^qhExSFW3nn2Lk|a`(q&06PL}ve z8~V?P8a)>#u0fg(HKubIKC13U;<|gfx#lQ&H?1C6mGQ+uK;rfQ8YHic$l^F&ZQYBt4=PP^V~TC!UoUN(t7DKn`BVA= z=fz90yZF1ASmw)CZ84b4;0SmA1si=7qRU_#6*Jtz%0JOd24h-4_lWi#bLoAvQ+tut z;Q$;I7K=KWt!r3$2lN3lv^QPW+4(x9*N+o1Yj;+$B?4y^a2pIC|JF^zVzDrp;0xlJ z?%mj5hv%q!a|)WGy;sVP36jkOp)4=DGZ#U>udQcUwJRw)AC-5$K>#5gT$E^;_Gdm2w5z$VE+ z`9h=79=|+%a%1MMTfpP9f>4_$xr85O^Wp-6rd?R1d?#3Yl`oB|jkar2hGaV~)D65F zx$dDZ0~_gU;%)N-sj&@Ju&JfyXk;jwa6GIO zL#Re??6C###))Q5G^#jGA@KYyJKMCaHXythGQ$j40&c8o;ddvkY=_&oAW>(fx3QRf z&ImF9s@C1i`_hcR${fE@@ZnQ#>p7P`1E67pC3MB!g^3=z>TS}Nmxudg0C7C=T+p1$ zdMubBuBlmGK?$qJ)hR=szOBo1IXTm=W+-y}u{~4Se@#~Ky38DKm1#>~YhQ79I%_ ze6DyNShaJ(sATC~5Cv)3KM$YmJY*?-+s}Of4Cr>pfvl80m7v+Pc#2%s)m2n(u3hRx zA~<|p*`=M6+$_%?WU7uOR<=h8eD0&cZsscSX(1O|Pv?cyDP}$K;W}oP)-BX`y`HOe z1F=N8&CdfV&vMgDT_4L?*3xKiS}dRarO*AWdWP<8DrvBl*Li@i?hYmZW z;E8a+_tM|NrBF6z|DKb#-LcFZ}vGT+{fYwMNU&_@erigFCs zjXxDi@6ckHOS`rArT4xHo+YbRwPe@zqTNz@K~0|oeA8n&z?$L=*3Qt-&?BYGX4+aA zx?U}am5n`naNpzox0`Be9((GHFIxN8UiAWVwiwWX)wdC%61KdcTx`w|pY}fQG^=sf z!o;iHnDQZ<(e2?TdSod+Mi{I$UBe`Qfk+2Ch3buBCs(9-UqQ|QR@&lM9wE02hSn<2 zh=5<7n|*r!(@x`@19S;;Koq<7YSMAib(y5%6Nv27sZ?s6{yYjYJshl1nl(I&qzmJ3 zK;;c9nxpYMR^GAXuz%#RElmu*zb>DLA;WNyr_HcR06l5c5AJL~6%{e3Bw?L9e@-k<}v7LwTWBro!}j&o1WVaZ_84Xqm{0wk!N5ib>* zy>#1LwhR!EUU;emqnM!hocBhIW0^rLs#f6dM@4~3h-O{eZg-4Q3*t?DYoW`~uD%It zY_Y+E;A$Ov_jno)JdWl%fKQi6fUh0!_OP4u!)_r!K_>|WLI#Q#Eyw0avl!a1g6q7B zCH5@^1qCEf9{|kgn%WB(qOH@ZED(eM+O$jDPPSg2=egi*U-m4+r7b;6#+RMQB-0zP zEMneH=@M@gKM$m>9_SR0$7{2cmO`CX0vHC*KBQD^>>(c(9U2yS(hIs0$#FrUbQ;h8 zI2fxfz~t60H9LhM_B=tzWr@NK6F*gBQ$@mu%I~TCAeOhfQdz7E022y!LYDGHo2F;0 zK?>u;4D)-<*)H75*pTbxLeu5|1_Rwde<`z}F$SvID`0WnR&T3SZUPeq2l3x&~=@^l73;PH%qfC=je3nUT9w zCZ{jij2C528Mpj_H4|W|MR0=se z8)RA76Ibi&p<1Dhfe7+RH98Rl6p)O6mr1&dIBh+4SdYG`Y~|vhQgsq1x1oCsWKNyf zx&wP(de@vU+H&daui#_Rp&Q5a10P<;Y`w{kmUltCA$C*ZFuJiRhP9ezN3&?^3f7ZF~AA zva##jioV|u)JDtCtuojEvr#TLq?ui9{1uINwnc=8NL`vqx{0|nJgDr5GFFkMUpQ2I z28CYD<_4MaXd}CgmkvMHoSx*8&5!`ki>UI{fw)TUN!Wts?B+g2lH!QPJEcl*@Zg4E zbPg|0DFW5hvMt=e<1lK!Dv4EIY$*nB4?Nv^<+|iu8Yt-G=2&X30fN5@s73%w@X$>` zq-dv_{Y2gFE3L7A0~|{BwoN-%m0S40Km1U3;ue_-r~|5qRBds{8@KRfB=p)P$QxC~ z2I!rNA6el$I3V@!1~>GU+JhQd9{`9@e^L5y2h7vF6q(jMM3&m|*w`K{#a!ew;OT}V zOsBg9{zS-e?&_CdcFxv3!?CEXoCDd)pIMxZ(oB$%7f$*?!5|BHyfx11-2f4Ro9PSU z)z_BWx8ORHjWQuvf740vtpXZmEM6d9@nd}*dkxDpTm3a zwsT9Nr-Mig$TN^7oCV@vAe9pgRN)i8MIcswEx!{++SgyG3M;ZSbs^$OgXCKUxn}%`jX?ea%Vr!WG25@hw{rXJOEzF}Y&iCP*J$TQWldkG&XAUUZ5xG6{8Sowg zkHMKe<_QQt9$na7;1IbxfI-p?tUR!XzcQa{woY>KF=Gg)1rSh{ZRhyesF9@K$=vI= z($doE%!#>j^z>ZJmde+`R7(U2lZ?D$y}hI5DD4V!bGLVptE+2mG=;ZIerLF)*4x6? zZns_626`SJDl9zV7yEzz1Ck}eN~zc3_ksWU0toP8?9` zdzw_Q^;PGAk{V6g`w^lrvmO)CcwZ3llh z?MLRmX*@7z*le6ql<{P@8d5n$=rm7?=4{^AO$?e@9{0ofQoKRfxFW-w>Q3FWFdo~Et7*pKQCCD~!>pt-r)K>v@Mn_~yuTHC#)hk-a6Iwmm1dQkPKr}YzFM;_-|9MeUR?nGXT+{AIpVp%bv`Jg@>s^2jbQ_D-a zWR~nppwWCt(-S4P@^5;vL0F>EECHPqjXcl9RP|n?(dPUGWkjIxT^DkA!4@B98gO5gIZ0tkME;Xd3O(-Q#{;Jvoz785}npAigyp zKAjOMHh2TV3J1+k;|VB~fxrb8pig4cW+}16T?C)?L=66*M{>m5)pX{R6u0-bl)DmF$Y+qEIVhk*{^nxkVn=dskU{ zB=$bPN#-gxB*~IAA6(^8KfD|TpQw1A40+x5BNbEWFO>6(VOgGEw(sH|HKoy!d^QHy%6ESVB#g}>H5Y6&!C@JywJ zAH4TZP}9F5Sp^+x$cZyOOc*2lOul6ac4gy%Y53deM*9Tv_tB~HZ={%Iu^vP z=@cP8_|GcuJ7qX_$;jJCm2?qr?%+G@&bNQOuWaVr41FfbFav``6(Y(1W0p5;^aCR* zVzx2PW!|%NO1nT=GeijI0H)du;LO>h0gxJO;2}4%w`&ec&Fm!7!Y}UQTK~Jc$?!3X zJ+ZrT2Ck_JE@tlqSyPK6A3kaBbSW>=$_HeYfRe##_oeiPHuAAVJNctw#;im2#j#Aw zsKQJ}$;a_2y-4LVE0B70TNwPi(hZGMM?WWTOn+pqrm%qtWT$E6ecnQPEh-6ZrUG@} zt?(iR{_V7&RUU9#1no&V#_*oCQ}zqB7&0y9-i&QGd5#88BEt*68MwH}0f?Zr+7mIr#^q0C71-N=3UDtE7G&4A9y+ zf-R@q9>}(Z8A^9A3ll_hOkTxY$t=~E+;Q3j)AzjFkL**lH-`O z$1V_hjU`Y|Wnh9)#UTw2Guk%@=?#iR?9)9{w(MMm=;#E}itMhz zqqnNfjVD}&*bGz}ysg;AszadQD6ZSx%e}o0kQrt^Qfbxu6)q?$YPZpY#6kpxzZcDS z`o7ekmY15b-494N4S8Wp*H54&GqAzy@qXVs1P{u7>+-&ZEUAaH_FzZ?S$F3xTibM`EpRnN-ynb{z}DmC7tWk;|wZS+av`*^}Y9l->bKO4TQfo*jJlA_sTDbax{ zY-s2Qu8meMTgo35zh!5{s;fu9m7qy^kc*sqEQc{?XZ1e{snJ0yuq@7IIkk6c5reZ; zt$2axCf5iA>#GpGw0*g)%8(EYM$HmHqku%Am;o-MEpx;_1b`ix!ThS*)w~OA2VH85 zAJKkp0d(u&!3RU^t@`&w2+h%D;}DYrsuAzNK*?p0`=u`rO9SCL_&0fZ6GOA4pB@nd;vox zq+|FZdAuLp6^TJJO1MRA$$E+`;d{IgH^vJjW?cpob?NSF=VHLX-s?H!Z{5vsYX zoQx@A+_TtL00PyYz~POBmtaP&ax_f6=TZ(}vo5^lOc#Lvp0wDzua(b0wfT!R5ACVC6mz<#zPKqMH>4H^nexMViwMqk`BHP@ zMRR^59l5G;6{-g-g@ClPQsRAme40RHeZXQFtVvx>2t9kJ714Xq*QMsJ{*>OH?uJ}I zt3mF7|0f)5KeuRl2CL7Q4=B3b`Q)W}p!{@Xab8o-)`OT%mq@pxNDEKp@jx*KQ%oA` zk=MH?&iSK!W#Puq7M$ zKMH2F|N7Hkt*cH@k>0v?3p5gFswv-Zc=Uo2y*Q~d_hsF$YdVXv_l8)JHl9inHSWgZ z9-Mbt-sRzD-GBo9o{jw70>vR;ur6X-3$&Tp5V$%847N3nZO_hPSF7#%Cm4s>v)TqO zR%dgxb(%^Vzo9_}_sW&DuK_W=R{0xn4^~V>)P6pZCAB*)=dq+0n+EWCj+0z!)*G&@ z7WlMiGo;s;6x*S0)}$ZK=UD&T!3@aUHNDjiD{XuY(&?2Ggq3M$Ni&9%?9t=MK0tM$ z-R&)&KKl8i{IB|3?VI5=d3R??H-xU!@GCSo#34J;FIVF#Z89B2mpYP{D5 zXGyxI80+srnyz+XH*x^JVo==5_<+7bd4n3~X$N>&c!h(07=je}Xm`mQ^7nap6<30E zEB+j^eNA=Bd$jVhWjnGyNC%%Uh*fp@kv8Cxq!$_3k2Wyz9hL>ihV9IZX3&qEa-2jn zzFT~Yrp`J}x_enM5%4FvN)c8f%M0XAz<`~8r~Mw!cdipG7(VN^r#GzUWnR&AZKwlH zCPT&Mp7;exmD{Y0g9=GGfIXHgiuNpYt0QN-y#>JE+$_rBEh<~)o1s%;**n!f14J|K z_h77@ZLDuIGew|-C&=!6IK`7l=E}cc(&cF2O#1?76`4zM2=zdg4u8+Mswzb7opMf~HO$a8)>rve?QO z$Wb-}Eeb6VTbL|OjoRG{G(7*atG_Yqw$?_Ua_>iRj5|;)Fepyp9dB(33-*f5)q0mz_qpqCkbUK3a6UpU|-nKj}+pXYyCz5^?(b~rma%QG9NdV0;Yl!$T##b7qDE_tyH?;OG9Bp?l zh&qrv_=)Y#ze3#y!BI(b$1JQz2>L6=dq+Uq;pkxy%76*myK;vJ7`i%1XLY^()Zj=V zs%I4Co1993Xn55{uLYiU}jXz>*To zO#)Q{ZWN3F%zx@xIhHKNDnUuAD>7!|NRxFJMNSVf5T^Tm7^FwAcflm<8somkHs)QG z^@+5dfT&N^0akEIH<2d2zQRxL(IC2~QmOiAK1^a`U0pcW%uhu6<|g?Ph(xrI>OI-^1X;smioKE_s$L2(-}t*?oE420~{L zKq`N%1F>#H=B@(|C6z~q$P2d9^e*R>P;w~fVK}^`+-%OqOgJuo1W>5JAst7$QA^3% zVeS@bF3X&9F;2Opxxg@pk%gF#xI_fkVf5(F_RuR@H;@*Gy0@C?U9}YX%)W=PS!x@M zhVym4Y7jZla_qqs-yv|i7s}E!&=5ftbT+;MU0Nh-pEU8w4(?PW>lymBH>@9&z=5FQ zOFxZsiCuM zAvH#4brt@i+uXy^9m%)%=Whc=7Ml6CRuKiT+GW76EaqbwkV^dVj zk%Cu%EUd@?W(~Rl4V3#k07f(GA>r#@*R2Qi3R?V$uu{Hk;^z&2R$S2CF5;x}I>4L( zr67%BdSrdD*wCA7j)PrViVAjF`iN!`KoOzSs+Al0;|pANw$X#lYO*H+^kzfkCFxaf zZk=KAE&m0Re3K%8hVjjEZ?W#x4Af;ujI4PKWOAKDS-vTVzs^q{A*@q+2yGdjUpGRk z{*=!NbCkV@AD_l}cyM8Y8JVn_L({G%m|Mtw=}8dIz&l?U0*K@SZt-&Q`#YRh_O&$b zYxG~382KhcTSbIE1vKPG4^Qhlv4goazyX+lsB;w+^ZV!7ju5ftigUfg%$d>vQ2Q00I222E z`{Sgxs#781#@8BQSa}$Kr5`_nP=_!?vOwthawfXpza6lmhD)36KOza)*_$=yojnBt z9|Lf=ucnw&8eqM2F0N5I-2D7FMFazF{YC+7Y zMiAcJpzqCEFbw&&=2Ipq4KsVp=ZStuo1xwzM%+WM%gt>dEFD*N0YGo3EM46OApmQP zO*YOA_S^}l`gDT7+D{yjrVucY+y0?~VC^_{XAh?O4`6dd(8|qrDYO*FcnP2_We;UC z8IEDU4@v4idBZFpo(~_bu$?m%A*W@_@+8+s;2HnF=z|a)-7VXBj_mf6cY!zD+C%QR9O$qNrBWIE#|>}o960VGix`n zC_Bpd4&~&(X+5@HSB1$!mQll_aVL-0NF}ERM=jX}~4)fxk8Hxd*ig*e2Vz**_ zi)fadeR-8X110ENfYiEw_W{vN)W?4WyN2Y58W$P8Rqj0~zF|ra3PciMKLg0wT#2h?8Vv}KCKT!)!o650vf6!v>ve4V$iU+7XQe>_m7D)&rs zv5kPy3>gBXrMXo-71Fsq>qF%ln0PUy%YeoPUQzASAe)}t2U-BW0MSC;N!_hCqjk}DI9>;4@Gy}5yJcV31Ht>LC;o%Kw}3_?7+D){9DZVd`O7w?KJa3 z%Y?=yIOSD{5U=6gRbSP5x~px`u*_VD^huBg{QOe3U>a)>QaFyABr5qLS^gkw zGNsXY`BS{SQq4kJ-B3%y3nBnhjzN*W(>>yW;O_XdOO1QuQX^JnhVdA4yM8er1xj!b z?i2&+V-*oBd!YFSeHqR6%oSNc&(u^`m*DoC?@;E;jquwo4f%nMq}=3f{JM187M5)( zuA7U$h&B@zg!}DfOQ5H^1~q0c^Y&2ANj4v8=qOt*wCg)`i%h4@B8N_dW`#Bgtd=@< z9rl>y0*`RGFP?dG?kQg!`C7}i>Dg#A|0XYJ z35~V}=ymA1oKrm;d}zw~yL;QOk!M|FeH`g$UElt@pU*%@1~JcHha7vp6zD+{UwYeB zW8JrGDc@mk+ZO=>`z&##J7+qruJyp76i7o`3t-oF zTQ?Z|*L`qa>lfdN-Jv62JGsjX%A0S-vY;6MTeZtKCZzgz6(wV~4vX|Ws%|riB1H{N z8Nhd`Z$TZQ_6*qQAyOUaG5E?mG~+^*b>Eq#kZwVip=#%(&@pEZrtz#h8NXa@o$qZ| zYjC^;YKkntjOQ?cS_JRq#ote&`fxXs9WY_hoR3s>OqbF9&kXlMs|`PcX1gmxK(+cY(>3W85Ed=Bp435)l%^E}yFbAKN#L!~ zO+#mEgMsqgdn$GHu)9$v_P-0yu)!Bc_ITG`lzZ4~d*8(DCogEJ_z6V2qv@W7{1`uM zUd%_8)bg4~Rb#1?CPqCL5B6 zzYl10VmzcsAP~HY2EtsM%{y1QI9@-T1t}j8{Resz=<)*KjtX-Y#NY2ITTLxMvUaPB z(HyS{nq}_Nxmv=S@d&x5E)ia0?hT0o!76}uW!H1Wsfn6FsK1^lIyC1C5=eDWvEn$_ zd7|#%ljf=;g4Wo@PB*J`+qH!UHc6kqru&y) zhn015n*&xv|GC7TYu#1=2iR`aOq70V%qxS97svRR3!2NSHXyfIyTK-Ddu9-PlA>il z=tDuv`+n(J(jbTlMBZKCZFUgfIxUc02f`y1Elor!?8gijZhW_V@Y>Q_7wCrah$>(k zz`5oH)<*W92#hJrDvTPEb8h;)|QVPx$ z=qG;yth?7^Om|w(M|uG2kD-GK%7v`FhZz}V2)_U9@N=`1=htN{NEs^UEY1xs5^DVb z9f!>nP4C!nIIPrTMlbC6TgE$Y*2)XrW{^v7j+U1#h6V%brr40P1sWOEA5D%| ze-8c78$hb1sSYW6xG<>_qVaT7ryk7(xCa@7(haE1jWJ?t**a)#KwU3`S6Hb!&7$ctW321qA76)MiHl7sF<4TN3S!S`Bh^SFl0V2&J}>31!+7EGjX&- z+aXZ1IX*(NM2OuBCV}pI*ZHwi_2`~Kt(&>Dts5w=Sh0aKBbQZU++H9}5EK`CQzxjs zN`%n}r|-UM|-=;Gg=_z5WtTzHS2w(S%zo zwse3UfBD(irQ1&HA%Y;rjn51$ssN5KberA(F!_9mq&E)N)EYg8Pw+SWZ zoIl(7h*54)G&zaTuIzl7FWZ;IL7$7b8<;K50)C2v4faN2~ zR9=svfH2DqxiKC~N>lB_L_}zi0@s1m^WI!QL+2gEGQ86sNay*LO9jim;}jIw-#RNF zd6;$xdnZ|X>nqIxWR;nbwfre@dwU&uK|1J=g>|hjjqwu-g=!E*_aUKY-2l5@m+$24 zm~)tHK0mW>@suPaOAr$b_syO4WbhFcOmOBv%WX*4fN?o&NXrFIrSQ$JZq|tgrOo^y z44bJtENU6%+Q0Ky&B7#}pgR~91Y4SVmd=o(0r%|Z(TQDrwe`ZUnnTlMfOmfG>yyvf zsc-nic=Et=!D4A=XRj9n09%Nr|Bf9*pK@1i`=-!*_90O)Nne>{2LL|Mv0G%=8TqA12oS=S3rtH{@5WPm-ES6B(4tv){N& zEzYIqnKc+?%R-Vt^5UoQlz-4FCgEAFBYk4>F~6yC3_1OvydGN|lIVt&ZSaG1ip~|^ zl(L4zv*sk8h5O(Ia2(*|B0(*4+2U-M{amxa6yO;-(9yDyN%kpc5qv!5moHUI8_W&p zvFfTXg3Zxx60O2RBL}NpHzQFGdGK+m_oRFLvMU$EQYB?8VgDal-vQV3{k&gmm0Cq= zodp4{0~g{1Wksdp0K^G0BMKr!h>94=q@v)$0RjS&xBwB7C=ihmf`TF<1Q|&INm#OC z5>`k^Nci6e`&In?eZ90w9eIztd+zSJ=Z*dY8q5$aboiz)?lVTibg{VJm@1Fuhu*%taMr(hcev0 zuR0XVDECuelo;aJFov>R(SZ3l5SAy2vjSiJOug|WWj4WXeyKqf3x=7zeORs@Pi05d z#|zItae}Mj9>TzKsP+7o6uVmme#QF zP~c3mdILO^L?u}MvrTo3IIqKIt>KNj>1D*jraPp@y$chl#?de%CtH}P)99UbGCJH( zp-j^0CLWH1bXC2}BPg{+YwInfvtS;xX!8Ph#Ze&)0@;5DY`q<_Ty*ZBFqOs1=5^#H zim+asH1{Z&Ix}7&OlEh$wVuXcstQX~^$!T4zGm@F%lXq34hA5q3MYV4*4M z{o=zDr}P6d9Pr+(^cunIteGT~?cy)@V8Zm-8Pgw~^txGpLVXqdbMZ>UqtI7y6^0Cb zsfQAKv{5Aug?j3q7%oLt8iT~*Rq6T(6=P83EN7b&hFuvEz_Q6XsarVBTpR6d*s-Lh zmsAAc@++GivEEcl)o|NKN0~aJVB+>~*4x~6vi1O0Q}Iz6TnH~{_V@>M|DN$*2?oFm zH*>k-%+4VUmMOs8y#UQegan$!%G@1`86~?w8jOI^l}{aB!K7KQbBq~e*HdMBRP3QBJ&m0W;TM`w&S4N!9mz`vSN5(3iG2TSrU`B=$3~e%ipUB{2 zK-o1b4h{F@yTKCg8ISCiqJD$PuJpRP2GBS_00~@LBpaO1Q{MkF^+FMvJ;Z0KXMYK3 zXq?Zsx{FDo(wEQrZ6zz`;2aFL@WV#}!tUdsa#^h4hZ?~qzCO_Umg)RbqZy;wilP|@ z@;tj6e9R|});Xhr&BsJ@1XZC}*8XMVA(vxy*b}I=4=kQyczn3#1_0KER4botq6u(d z%a+ara*u9G%UqZMfyQtDg9P46;=7Jw(_K(Fz$Plz1jgxf4hq0C@v;BQwvfOZZ|FqX zG?Z3IiNie`x9a(k_@`2)xn- zbiM_PQM3$Vh3*+Y(P!Vj(|Fr6Z-)m-F_a1)|&{cw^=2TIL%BN@+F=QuCq-H6_yIoRb zP*v)}1$n0^L=6W9R{^TPKN8)rva+h5HBwkuXu+a^n?k&@>_Bgg^0>#392NOSmzkOh z-9FOG&o-+8diP({;aF;;39wkwVP1XxW{2ao(?52o_BurQDey*#;Y@ag_4Rc9_u^`5 zs`sbr4a6p$6D9n-2Z$3cP^h0fqLn!zTGzjsYEfxfSE_JCClz5<)wsHP1O#lESSUK3 zhXueH zjR7^?9_qNpd1zIGu9NiiB&?d*liI9W`ALb)H({c_x|L=2qKpZ`YS-hgjXwyePTN%- zmZkynYROX?VwsNjfMv`p#zY)h^p#gVe_&z!(9*6Xh$lnpU1cYA$@5F$u*FsjCL+cN zKiz!aw`>FGTEJb-li!I1?*&6P#e8X0@wOWC&0E~nGV16G>9ZN%Zm(j~;i3IpX)-J8 zj>tdH)c)1Y9^j7!=X(EEC<@NQ-nL=1T@i%dB2LUg9-kdKT{ne zUVCZVLNLs{N1V4}*T*n9o%jW5MAN{|{Z15d+HP3v|BjU|5>KUJUvr^Z^&s{$d^+{7 zNY(N~gg4&W`iq%12V71JEK%T*hT{PVaN6B)6T+Zejv2i#$Zr{EkA%*2bfG-W3|3S$ zZbi%Vh7Ssb1xj04a`ri(vAKc^WaJ`v$RjMj1-cg-?O*W)9ZyUFBQQ=|?IqaxJRgF9 z+Nx}s#a_SfZFde-|B+elG&W!-gZUi|K6G+D<>w!IZr(ei=G$unJH?qNOH-x7z| zo#9oG)F9u{DZ8-X1l&UQpHZ;BfhG7t7>16r&_+WLDLwsZjW*{_4fI`rQk(q z$j~!XgYj6W=Yg>MZYiBBS)S>^>$IU&T2T#l#A!xFcav=2<*;7OzK#u;J*W zzSqfs*3OS}^;gHLsc1_#we6;zP)NZrvf_DTjg=sS*cgXbIv0*0j(!=ylsJg@WX=|q zS<+YM&Oqf&k2h^bBXTxB$}nsQm<;JH3Bcwk6s{K!c)1`RQyV>23Tc3t1Ra?%mV-BZ z!p2DCW5b$|{{Rk|4j@ck*75+d!(|fXXU2_!;vL^Cq4`l)i*x?#Y^PXWWsm;}$P1fL zUU@imMes{72B2V8&hP-Y*J>`9?xB~`Tt1O{!30U1&Ls!JhhM*yTZ%7=qe7XOg@b`~ z2Ferqv56Oe1)A>(+6VuKb)WJMxGBZ~U;)msjs`5;6&7mY=3SyRyl)e;J0()UZd~9cmyR1_tTVb%OT$RFnmU8 zIP%z-xRf3P_U)?9crZaIGo>2>!hm+A9F|t zM#^tw0wG^?`Pkxk0M-%)?Sj<2ol73d0xklAmN6U4!Hu05PqnJ}!*Kt`4XvP5^{Ia> z7XXaE?uafzlQ#V2)x%9UwCs!HBE9CB0^*m#BnF4P{)ld&MgrPbjeSzj5s0Kpjg4UI z;&037-M6OSjzkISwMar~15S*ttE;Rwu|vX@W^GfxCKha2y{{?f-9k2@DA+;_>Fpa? zfiXT7E>#^BKJk634;+#$QkxJu#zMLa>LaE|d4fNYgA{vFJjYkiXJTRs9cO%RxvqN* z)3GAGKGG8(P3LKfwr3iH*M(V zi6mGQL6x1e13uvC`6>}&HRYMRlc9TxBke^(4isNr2HtgeB@Z=W2jvWfLri5OF8fx4 z>Y^x$;n-uXuWLI#ocg^7btQn8z~=#_jnuvFAQyE2K!dt$r(r^jD5a%5)DQkGBUn`K zP~awQ^iU7huxhFi2)@JV7D}SSO709rO?+#@h5-6s)nV7S<7zHG`DuNe zrZotarwGe7jpe<+`sS$VBvb~l^R03J)f!lQs1NN=J9+yP?EggnB8+ToOqWJD0|V5S>b03T-RVxzxZ*L^DmkVog--THe z%Uo1vxNa>AX7)O8&*&ji3TaRm^M`a1969v+#Gm)P;^%q6zK2mSFnwn>HZ}r2U@XG_ zrU431Q0jj;98|M9bbaWa@pWsB=>d7~c8%-r?M(r#xzDylk?Ay{N(1yNIv)TVerC}eWFyZ=O*_tt=T2L^(FsOsw`NKcq|YC_UD?K9FJdCJQo-`5%#b2&MB51BpIyzzisa>K_O|5G|DKYs{OZo@!3H#r<)i$KKWoIy-Vw z>Of9)!r`Y3Cm!6m!>f)TfNnTHRXw{0;tfM9I|uYVaFzh?w(_TVnAGAkQkzBC>$Uic zqN@#%S4)*JtpIE~(|Fzr6@QIzHTIA19-`Xv&#LQai+}y^0@ELQ79S#xSud_Jhho6; zIsc9Jr?SB(_z2!yn>!c3!ZyJC?FupE|FT9Ti2|q^2RHH)L|-IfQhJJa3uLt3*cC~CLe+iueU8nN1`=ZBD_>UtLvIK}Efc=Vb;OCsW zlHl{Dx)6St3jOxitP>PR9v7r z;v5#k=Qb($I96YUW>F$0s$(+I4ioEVT(jNb`zdToSgT#3=$_y#ekQ^WufJveD@^5+ zc6Rv9##8N!rQO|-p~sbf{s<;zcvcq*`%}Xf9De9q&hj({v{wJb7uF@)1Gh^Go==to z@C>N+u3WfUO=k8`XY`G@d9K+ai4!b65T`Mh7X9(?lt5DUr^?NtqU1v68~-P7id;Q+M{UH~To zpIu}|$j)(;&aaYA26Jkll@sl`$j>~1za|)d{gf#mX)3U>`?iMr@x~#~JLMkIr|Bdp z|L>c%L|c&}^UbbA*f0JJW=M10^P|u2+P?yI1T7hEs0@xssh(Vx(u_<%HE@9%Qjb&& zK~iQK)K`!m732aMn2|;GP^XMTo9#A9pg$o(wC2cPfy{p<0%!93FW0=Ur23q&>H?=` zinOMnm#bgP+Rlf2$e>oA-c4zwRSb5nN6e%Vao@=YP?@$POwHr|3TSJnp{+4)Nq;(o zNUBHs2SLkarPn;3&i(gGn-i(X!h#{}A6}Km=-BFPa31hyoH|d~mUmB}zh)93Tvj)9 z=;eba9}H=MEdkDE==qy~PZVkgd7OSW*0eMV28EaJPi(x4SPR42IoUH1ZA!Cy@WI!* z5*pq-e)S(l0C}yXA!PeCkKmA7Fr=M)q~z(AI*5ZrTx1;&UR&qH>zqeII<>;0;Jd9P ze0&AsS|DNL=;}J~H2u=p(O5l@&1BI0#9mIRVGB-qh+iVZ5T~2Op*%>V>(hiV{+hW9 zvKS~LT%!6ScMU-gjEQ&z#Sx&(h|FApQSGQDL4iTGo&V?8>K~dzw(={=$wr)9O+)gv zyWgGnMdG}ujcutB9>&JTIhNX-)KsJRa5ONI2o0Co_Y?y@^%~1R9l2GW60jyZ+bR=H zX}iyh2)1&ySZw(K1BL}MNMk-5lLU2p@e-{lP&Z77BT$T_Kh6Ah5l<{F_v{(A;^$Pm zI)gFIxdwm-HV8Nfzz}kB1QWrGUVbU)VD1Hc=M!fM6?9W8_^OD^21g-NeQw~OVeK&P zk*BdWoisW!JkXYnaz$$2j753WuDsUBl;+e5d0I|+?D+Ma2l+`i2u(I?2L3sWpuEG= zOtpnICe?llR@%Ivt1Lf7=0PrmqRg!XxOsG(zxoMPT8BmOkikb!Xw;|aY zSq93#)ij!dRY`lyq963ASm{>;>KZ@?l~}!e&swjOX`aT0M1x*=ZVLe0I555dP!x!b z7(B@qxUEbdl&nLX3MjFxW5f7z-2h-&B$e_o%%EL>%wY|(Ego(Kh&Es6#+KCJU3Qq5 z)vn{halhAl7;4>!%(6hkp#!ksy^yPybgC`uw#gA6Z>M&GgX#78m`%~DzNyFB>Y$Z-xOd^~h%ttkDsvGQ3vNZr+x z4;KG^KhN;@u^Q^Lshfz@r zpmw?G#@ue9E?`McQkVIC(LWFe{aFymaR@D#Kf}Y(9>A3^{D5JVW~}z}qgQYL;h60a z%Ck1ySK_IT<#W`jEr8L$sW3&GkcOd5a#jpf_BS=_Ew>*_7C;;%ybtHOooz~yTau1B z1C4S1aabr4H4*?(;p+b;Ho@V9yTv`7=b-WM_y zVFVV*hNWrqpvDr1)a9d=;8F46CR9e>kv_4Xo?G!6;^A|bQ%`@X%Tg!1LbDXkGZp-B zjV+N5Vjk!atC5C#fR(ojCK!ENY46sAVbDBL)@9A*)N@h}Ig50wLeVf7tOKox8;Fk9 z6e|=%^wHHye2cLQiuUq%tLaDyKkyn4rVc|EfJmHDuUwWB`|cso%+VbX96y}IVj);?NR-?Awk3)urZ#Hab)8Qdnd!U69;RpOC1Wl&R9b-GTWFuX&Aji_XtEsUq zX@5=~gd1&w0d)*~Uv^}LQvCwRKjnMim_>ZB?4jm#eU`G(seZAZchV3a#*||4l(bbm zq5FR<5E<0&(R8O$j1{711B9|={7Eu__SZBCB#cv8yXG$c9$$YQUegKWN54gdLs)Zd zz=YSoYV=%JLf$qotgQIU1?i-!m%>Kq$r6o`Qk%d)z1pt?Tu;Oj2vrvLKwp>TSbly4 z`GQ}P#SAp}kDJ8wbj$v0ahd~yf5x3Uxp8neZX?|znU#^(zzdyBWXoTQj%%Tt;W1Abb7euC>LI~o4~mjd+eh0u&- ze~j6^1XIMMU7+#*PyOd69{Q#O(3RL53(|^!?G6r{oodAeBUN6~X}PdqUOIaUzO-g7 zIN*lFm19y_F1kDfoo4T`3@)xQv%ItjOx>4S`P`Wj#1WvLKuT83H;0M$x&zh*HPDJN zQ|mzoSrWZ#VS^#GVp=gxoJ3sn_fsyQAtW%BIQhLv3M)N3_H$k=#=Qd{C-P)aAOIBc z*`lKWEv%6CDrYh2Z!TDJrXzRg9b?lC!;kBAiW(luZ2OL^(s|c^KVvo?*d9#IT6D){ zElgAAa)rLWKA18>^~}##d9qRaMKhqogv+%)i~{swgVbQLW3^#NOw{T<;~n|FcaV`3 zj19GDosdN;sAq2PDW3{KkC0KI^-vQ%7~5G*j#%0-n9GGh+k(I91gaZ>fe4m0WDI)t>77) zW!cD)`OGNot-ohPZv=u|GQu5siJsF_?=I%{AECpH;vrEPjA$ZRHSwh=ECiskQ6A$2 z$jZd>7JJ+q2m9L}-3dQF0<955qlhm+#(DyfiUx!!f$Z${UC2ml>ha5|zUQyL9oZkC z3u+LaEydO@oyDlWh0VNPaoT+A`Jd>xp{@`>VT6d!JH>nZUNWftr+33V$`~yJ5*&JT z*lzJg(5Tx(;tcSOtO?zea*LlPee;F_yEba>Aod2CIUvh+is3!$@psXl*=rU0e&d=Z zBW!|9jy*UB0Uu4cpKj+;N^M1+a8dq+1^+&<(kbYl`^4;hSbu0n>})&^@q%s( zz?$MEuUx@2qMz3J!>-|WRIn8GT} zp40ts@%V6<`&i2>*5C#!I~!_3Sn!dlEvhQ|Ifdy%P;MJMcpWUyRy3w)Ry)n_E->Hw z{&Hz8E^K%}(-|fH`ttd?WiK5plaIv-D76t0)_)^`LEiF^2T$qDn73!F39rD1ozH73LPxBfM?dP5eDBrl6pO+1iqZrm2eu=x@z}!bb zA7cmY6{(dKo^%|-%53z(%-RkJD5EB8W9I(YD_TCrwbG6&S|=*1q$%BIpwUB(MA!yx zc$(HJKNyMm+3N1f0FdsklD*EJz!{x2qD**HxBK>Z5Wk^jbjvEFlpa6)6P>b%3#Kt>l9@ z)YkU3%4ZU{hs)lB$Dp`!uqR4xwkg)*$u(CLXbQW(KVvR_w6tL2zHir~x#=#_{2*)W zKf9?h2ZLGZa5Weq-GFPD=@>7~wzIs@F;)&!#l{akk>IXqxd+tXvlLTnMKT=kD#xA; zat2GrrUl|Q!|gD*|9PwHfkE8Zz`Fdtv01Z7`gIhFGQ*8i227mC<-xBZ&5q^GQSs+v zl6Q_r|Czh2C&e3FPS)~)TLDwqWXNy+Q!j`JMAQpn&9vcli__NGKz1CP8?Z`TNPW82 zQ8o2VjpO;D+>0FbvHmfPqnNtoMV*OCImhO0hwAZS*R>UAj8ROD9en$E02*{O5Jp>u5O=|_;o~} zHL(VNo&-q)(2!DPV`Bx>hx-y!*4ET)wDhsz&Mk`qr5kt4OULL#hIb+n2|&$T(~!S8 z^=Yz4ULHt%wI!t!EDn;UXvf5;hV2Cbx zLUDk)WN^)7glft2_tO5a{sN*;D=9Ac{PRV1OY83o=|2P#P%z-;pK7_Fb-bcVcrOKK zl=I@no?78NK1j`_K+#NF(b<#I+_|wiQ52rSrsb2>c)>5kABq;GMrxgRFg#0mx*Ze_ zieCV?DdJRvo+pT}4k2_(;^9sk3$a7*LLhP_ygkdduoLBG-UnHh*kk~rm(J@(q@Fo% zUJ7%2*DN#SxDz1QZ-vLv$ha*co9aJ6p^_cs&Yzig9DFJ?98=^_2!p}F5;L(+1U0d+ z{?MPp$##@z+}_{5ZOW&o+QKdggK3k@N;}&0%gCB(D3N(AXz0Kg@-=R%$3V4Umlzzt zU7mq`@L^FH2w(Emo?Jcm1@!JCPv`=w|s?p+HooSJ&UG6kuS5FqtJo=(B)6js+H{;)Gw z>(HXSPn^kzoD<*c_O@->GkLdP(gvh7a)F7lnOF!1J{EQ;2haJdFQ1%x;fNi~GB4BV z8NQEPr;1v9o+LGI0$OTch0oBuMaY{_KmqP%GX^P23PBU+i0evwOoX5Db4uiMC;WH- zaxA*N=9VnNI|OFwH;k2hhfipyc{IMOFd9U=W%|Hhbx`!D&x<~NO@^5$K)FPxn#1@{=rtQHv`WZhRE9N(aN*iK^P&2oGoXPO( zw)QLHtCx$1r&bwk`K)^w+=)tSkcj^M7f4O!KE`z=MfhB!hCHjAELL5s(ew}g#?A){ z8Ref3b*e0wT^sz0&VahDpNiXoL1BvIMaFE4RY}mpZFV!CgqDYyHm9;|=jxA7Q8xKN zr?1H$Jjmy2Xh>%aJV?@!B4e4#HQWe%Us@_$o>`c?QXkm@+X5nu}6<%Ge?Kp2zB^BCFQ{dfXYe}Xe% z{3_#)4@4Sy`D++F4^bv@_FCCcjwJ|^GK7w`8e~MvRw_vMUF6XyLM{mn^Ut}JNW9VY zI6MA|wB7v=qjI`|bF@QIRT+V^E}06-qdI_ZJ8i5Dhw7%QJNzgQKADtzCuj6|Jhv?P7}Tt;Z!5Ol{KPi`_@#Zak(UzErtaknrRV?BtO$aU;j(ZM|}J#4;s){FB4%$f zhDguCc-n;yDCMAV$lPM!i4DTtH~tiZhfzT8wK4M`|HCsqw4l89g zG9YZ~UQqYg0KT%o9)1B0rD)x*&-cOo*lW zHyTVdidLMjrEp+Jh}P3@t5%#(9CDY^h#gvUDYtmB)d>gs<`^~S$0if_*_o4x0FPw( z>^u;#Z%mUd%5>o7bmi?cxTEFG%2+n*w{mlHbIV@;2Ru+ldQ5#FbgU7^XQ)AI52-HX z%X{kIffsm~lOty!n#lwAjSc8$@x|89_6NNtrCEXfrWq&*JM?W= zh?Wjy(BUxGG+jfbF^%i$?!jIJk)Bc}lAj;8*F|1va)P3yJadX-41<;}az(9z@)bFM z;r~1WfF`Q;i6Sf(%WHOP%6;yJ@wnpG z+{jwPhuYAFFMbK30w^q`!>Q~R>w}vqE`VWDmA{_VXa!AHJwj zw;!P4QJV*AJf{Yxhe6RzgEnD7+nG&w95IbO&YDP14&QuF_i(vYZ#TK+ZR<^+$+|U-Q>$4KaSv z`XzovOpw|xRFG;4&!mR^V<1)3dRLz=-Ko;n?3I@+Z3^LV;_OZ-Zpp~F77LJZ*B z`r@G1Y3*Zr=?GM8P20<5di%w87xb?^#0a2E2WH^Ak9j#85}Rv$3`F0dkr zO{S*^NN7MGoeYxe{#NCzoUbtwhM0FvB!m1A%s)X@2&xx(oB_Zkjr!v$s=uEpUaWPZ z`=17QdW@hu#^;cM4rH?LA|h-88yTQM+OhzYj1;}Fr49pL_jM+bHZ7Z)opU($Er{|u zmAM+OxT-VPOoR=H-Z7Sv`EaQEV_u_yCb49Qc6nIsD~aB2U}kvUpX4^P&v5vhZ?_;7 zk5B?MxxCH<8is-ldxF3v5;ImG7J`iKbdSHsMNp7vg2<6*bWCxoGJ0K)9!z^! z0x=uXGepP>L6>hL{;{6*0sMjHw?41diAr%;=00h_?Fb0+ts?dgc<8~OjTVL560HWd zrs!-SJsZB;?ZQJOdu)kfL|`h($c$Z0I^7HWffh9QmBogWWBnr)ESuMe7Ren@{K0^CA&s zx%>F+PXosVuU39rn{|Q{g|5P_K0Ax@iuEu%yd59Ka~WWZ92geyLxJY7N<*G{D=<^} z>P4BxH;z&C@s9vYGh$$tGzQOQt>;(G0U9N!7<^fF_4s=d-ntIC``{~(PQ`|1NV^1| zOFMbYCZM^tFP(5O?a~rhBws); zfQZt>{eke;&fazA8NAanrWkMic;KHTlt(;}w7sZ9;c~>-I~RR|_A1IybIEH{WyC+O zj{Rq+8~*WrmAK*?I^dQ2#AF!9>=u1q*!RmV81s;X?EOxCg8lcxc;rwP!!Sl4&H%Cl$qdU9S%ii{OH$iccJw1af*1j zyka5*L5?#w6G^xYc=&6eOXDBL1L2)TKyzit{@_=-+XTU-_eZ+OWE4E{AwooW>GS}5 zn7)Q8yEn^K|A8+q-zg4YQn<75qEuIy&YGg&aV7`_tLKiZVQ&jGgLZ}+ewo?fMZj+; z&IX}HTs{~B*?OcVYpwOPFF*}%F5YVRM^F7ULtTF>=Q$^^&sWuHu7a*Xow8{nM6=fN zMR~&IH+#&Wh_5+k13yUe6D~75r-(@{x3R%7rO=`gcIbcj?&gdAJ^)GOOpTevNV(^(9Y}Hrq`nF{W4)gV?&m_$0X(%4+}#dnOYo_jpGn2w@l9^!GLpM->jzbSO4Wfu zK?$No)nJ%t+DupzD{#;SDnSNiVj<@gDQk)XI_Q_iUGQ7Dgir9Q!jaVhFv#0Sx}3^( z^dqI}$g|BpMUboqE#Q$gXdg%X(_kn?maBYL6%oG9Z7d;xw+~{=c8lq;S|zmRPfvU` zi+!svo8%LZq*0Dlfqo!1kvI&d{?6%XTjfvSJoaKmaV4wnY1pUXYdd+aI-Kf71XHH2+ z8!w~J@CjbTznvYou^ITVHCgiP320r2-5N`X;6O+8u(N|-oqUaG-}#~ZijY2vEl>vX zZE=v#{lE}iD0Kw%4Fyynqye^X&4t1c9zn5)(*~*OZpWi9{(DwJXG+s$rqCf0d^n6G zt7axD#eW!XdcOjsM^z&4FkWJ|U`$F2d9`JzyM6*)XV*0<0*hsL$P}ofx{fuP%U-3E zqtA~-crYu@7s_w;Pe1u>nN^<Y4{9pnKpMQ=A0FSf2k_E#?NW2ny(48W5 zOhMwHCMbYgjW_(!VWZ&vQEc}scl_SCJtZ6vV+dR{#<$3a>y(w7rBdzZZokA-tb>K+!mCL(R|sL4b%CA{g$1Y!0M?*}h5JTiF$FnNNyy_zBc zgmwWm|0Zk@Be5A9h_7?Qu1%%iAu6^)_(CfH8tNt7`KeOyD4t z5JGga*UyE5Khu2ce&MIIk0djeD&xPQ?||}jSZG*0QGDfxG4b9j@`ntq|3nmCmugYFbjAKz+v#qQ>M(l z%%bYs=@nJEBBMeix!EK1Zj6OrMXAh?9_@Ndw2h&YyJyz+ee2YM>_7k&GN{7HBr_lr z52gUz(6nX&)BpcqG35oj7J>T^D^}*Q&d|= z%tX@YWngvcV=0|d%)~<8fV)nR#|PP3e%W9?_#DD9ZjyzWIr2MxHEX6>!RuF^u)f0; zzl@V`+m^6C%(RU^y&t5lfG~)tIRU1ZZ+)OMZ7NiQNY9?&z}n(xZ@;Z$&?zF{UiFax z^)dhl1@L)}xh9p>M^6l=fWZCsid7SCdYBwffqt@b`mt??oR*`$h5nmqk%|U^#6Riz1@@}aR%+l zP>n$z3AP9W!jH~00v<2voA6NQ57PZ7@@QKqhovZrK0??+=2~MWjgf}(&_EoJ=&~*D zgO7((0L@QmoR!K{-YW_PWlLKVb=+A7V}W^ob{Zg{?7 zdh&>W6Zp0!2vb20tC`4%pg{kJI^(**G;C4M-~y)P6!QTbz&{8-qIRFfnv*9=q&+>l zZjei#uv&ig;d10<7m&{o$yrEM4cL3CvV%YY(t76V8woU8fTxYueA}?A2x%%#rNvK^ z|95+e2$(Wwf<`moyF%E5&Pq%F?5(ZP{E>-48N7SU1_zaw=XnRxhmgRDg+sza69L|4 z1tw1r24`z#_F_~2`^@8YjjCtun%>o~+vn!$nhCc>IXv^O^)DIN5=fly4u#JJRhQrj z-9C6yU`*Uc1&uVH7em-|h$`TG1!d4zqih+~EbM#GJwrVF6ZjVRvWB<3$*2Mn8L$TH z_^S_vP1Sz`W9L$Uu;o)UzfjQF<@hLJ~h;^eFMUC{xo?6UI0qm1NlTdxVP6#W{Zn!W6YKpd~c}@&Zs`DqaV7jgT9Ou&rg- zB?n-Q#aE!sUKTABS*{li3r5#UkIehunfxUEaJ>_+>FNUMY@AgJB{GF^E*ioE#tOoQ zB$~NMGXe1r0)u};Ya$QQCtlg#BX@?hk}hL)I1RJ+5cA{|uEnbF zS3WY5psYrpRNDHo*t0YbK?)GFE|`@N4%dS1-Ms(#;Q~5RNVmQ<86i<`+|ZnVW2_qj zq%J>W?sltj-S?f*$)o?kEpW#5fro=45x<7yNrdQ26$)-TJFom zz7ZUwLR=r;ms6#R2+=D2w)gMM28QiM2jAhI`iexW{fytr;_nRABsd_7ZjXlr8x82u z9fT%YwR%?*VNK0i4-Z5Ck67y}@Ib|9^O@#V`L=Whm*9??qBi)}pHtP$)Nw%hD$LUXQH1oX4dC~etBGezwF;S-$ z*m!?i;K*D%P6J7nj>=V3Xg0=W&pD8RDbguk^XL7?YNYAajwyu%LmG^_KLI_xo4f__ zQPRd=;2)bzgx9?uVK;%7N=O{K8#?Zll6O0n$RY^c572gY<;ELZQ)l)8J5M$e4sq8` z7ebk5JA~nUd#9dw9c#nhtxt6Rh8MLEQW@~2&-IXVB=RmYjjCTY+H#8#@%i!)oEST3 zzZ}-6UcLEQ9m2cJ3cmtu>NCyhow*f?o5cM54Sn$l^YZSr|1GWev{?&$sqk}R)9D?1 zHQPv$OX%#!tanKE-shOoDJDVyW00?~*9NO$V|S%8v}$JBt!)trC4 zqai-`MhD&%aEJ)YD0U%{B-0b5<1q1XZ?fhVx4IZ*I5|OQa=$5Jakkk^`}3u(4Dt5z2~sP*Tisq-t0_H#-Ze2DC!7%K!KAe=>B=f_aSk zD`?Tk7144vW|g0K3H{9pr1J2!wzw3bNIfF(BLnl{phC?2iy&&{dBMl@X2gg541yKN zBbCw01izc($44#_FGkPr1!*|piN6Ymw?O<3b10Dt;nVB_ao;j1j!Lo6AW<7PG^Pl# zd^T!P0tALUkZcPayoF5t8`M~o%E;?xqvh65^w0iPs8PF7FTc#oB$wi_gwA@*nk#0fL+PyZnZ#pe1H$CN=I|_7lyD}K0kn$a)nkn4e+B*XKj zYpC|VC376E=D)ns`d46U;nh1YFV}7}zO!?8rZ?f<+4+lHSCvf-*#5!~S9|qn!tSoB z>)4?575##zc?H4PE5AqW!)e*yTwfD5L65yF0`o6R!=;TID}`ZESI#qt}=IQqgvzggfwN zc*Muz5;r}Q>~Mx8XgWNpx`4A$p5x<)DdCdfyL!if_F}d+mTO{rF`CH znslCSUlzyV4Ob1CNM$(9=M0CGR$tPKDJYezfdZ$a{@w&`L9n52eXyWvohk&xcfpBCZP3 zeYv4stZuK!eYr^5`0u|j*GkEcCyykAj{58Mu2g>r_!Nrn_6-W&sf8SL!RZRq7U#D! z`_j*E3M^r^9_5`2D511oG1~KK`F=^mWm@MzmO!^Jz1L+@)ryNwTQ`XKOUyc&DslzT z-dFxpkZ&c&9T$2G6X-Kg^5TOd1*(HQX}L^;tg};jDKawBx#_wvTK3@lh>XM55 zT1IULdBoiL>Ex02 zyn@6H%}fTvlekJ-Q9p3Q-Eq_T-sV2IsPqXaselKs?ZMsz$HsQ<8X#>S7<-PcAVl17 z5#2qh*#9+UW29n7;$<559!)j%+g6-CcC4-E3uiUg?BJ^n1Zdf9C5C0-z4ps=WpOx=cv`rq_&9g5;&OS8fzDHu;j_t)x ze`j21wKHK4azt?p+!k@Qr-MWbYo3(@+aMVt~HEpA`J(qFFYP0F1VbO)zYLkwu)!!}RufMRh_Y}!u zN=OCvq*{xk>FSUUX5A(quRhSZ%*C+MX+^Lg3P&F<+~2u2gt|euNLwMB zS`*B0`bZpmiU_Np4_5N@{NXaBn)zE~-DUKpQvVX>QE%Q2J;xFus^&5$Gjp9EID-82 zpAy*8vpoB|xJRXn+n+cqsTx_Hw#u9Ez z%oly#z95e8Ofpw3By0KxC_XCA-IaTVE*>meQ z2o2|f@dtr;D{O9aXVV(BwFp_dGPo@nZUbeNJb3ci588^2y~_!!D+UPU`Pz!$Ii`U{ zO!Q5O?{Sy)UtjVQyk6>{uwoxMoHoV4VtWbgY3os1|Gr73D=dd>M-pJsWp8Xp>gwun zma^IdM$TKaeoH*moUzTLl z$v*|=vl&(0K0wtf#EG zWv;Lf>|(QX!JHZ2zlE?rUo;6E?Vhb;py7-(ayY{(t?BdcT1<5Df6WZKvzyvjzk7)Y z>?IQ#&>LVUg7*y66D8|+CAHKt_4v&B!bDv3_-iV!)|(vU>Z#-W@nBM zES@I0z0NmAd}$iq0L9X}v&(mZRq*O4nb)a($(4@%fp>{$-&HXg6{bQaonAL=pHyRc zt5X#uOc8b}%iK{^t{_@xKE*u{VMQ<~PQ|%iG*5ibHoY zx)&U}|DoYAxP+TR4rO%8p?@=e_ML5B8mWLDq|G9-APEUQGblbjpQ~3;kBhH<$v4OF zk8@?YoV6kdxI9bB{okJDlGku?zS648G)&@E8fNyX^jieZ~tQgb1>xaQfS`^w1-{(g1prN+urlXFM?;=XU#DKi^{-3}YquuBv}Hst<3S=WsVE z|9scA%KJnib0EsxtPGDS;to`Sjk#iq&~9#t_8cy&($`?8q&x`zPp9?s!a~pD7+_Ge zhq-VTVRAr5NPn2*lD|CHS_Gc4v;hK@YjEMAPIuYVs$fYSx5xnB^?>%&8%l==LOJ4` zRiTLSJUD;$pAohrah~|JjjhgJnE=wRC|pp1skcIi0`*yJ30KxWQEU1=<(DijwJB zH$?Q0+_n@(TN!d?GhNcR1*-4)sWZCOWxkoIzZJq!NXDWTV4dC6h~p!cH&YAjuU5rm zIQ~|rV@2*Y!w%Ew;`N1@YE;GS-0}C2ynnN7W#j0`TkL#|25b4E&+VG8?s{K?tO|Kd z25bGD4DA|eR;mLBjYf|WC6uM|03~UNxHOj{=91Zp11>6W2JIayd@g~n>E%$5JCyXNk9xLQGk^1;BxhsOW`4bs4|DQ}mcjWWFD7tk z^ty$5$hP+KSeTjQVOz+Kk3k2^$`g1ovt$v`bWJt8qgfbC7bhA zS)KnBq|>bEKlgSHkjAdw1LXdp-*vhoel1er;XXjF#NoM_Wie>vJ`f2`xfF(7Doi%sf6cJUg18S&i#!L2Y# zWvaH8!o71he3!2Z8try>n+VX1Y9(*aw+6Sz)9nqS_zLQnhT!BZh4(O+B1cAm4}{vsZea3jlz^# z4YxB+f z(cprXrxzu#(lM>OW_tz&z<%U!;Ts0Q}RZh1Y z{w3JYh;DfG+h9#Lmqfu7n12O_sD)_m>zX>X@fK9e8S<^K>9x z5Gv%pP`Ws86gp=f|Ji!%-afs4?Xr#6^SOHIio=_Ewj-qmgp=@O1!Yt-6P{WV?%Q6) zVzCbN$JfJ)vKA=$+zyRZ2(Iky+qXV0`F{}@yBQuXF1dxjJ$FgJ@P+Q8+A`Z8uEFfU zfn2W@WCS&|-84}X9=zYYNb;7Wl#kRQJo2>kp`*%W*C;|>r!hWCTYTQIg4lp;BB(yhhT=<*oU=E!*xc>R+iDaL%#w)VJm)go{cC(mSW$9Z9NylM(X{_kLq=2Vr+DuF zYwyhhnmo6tVOx*&lvAZW7A-2GwLk?G6#*Gjwbl_(1|wq}kf;$5V}uZrXj{Mvj)=;X z6h&r|3L-`z(ITUepbRk#iAo?c1`?7GGX49-en-FS@8-Yo0#w8p_OqY8*IsKq&s$zF zB{00{p`OWm(hc)(wGl@l&r@MX&-YLrMC;Qo(lriGEqKg)Q;wC?s!_8uN+V(8$1Bm= z>1!ybiKK4BX~vGVAGM{^#ZD8uMVhwVRPwMvX(tkDYijNOsI^OSu5XxbjlmSyUD+k{ zvJ_#%3)JqalmX3%de6&rDO{4)J*la}q*Lgc$`lAS&-?%YKzo(+xz3|ep>8fGQVE@m zJ!^ULd;V0f$S0zno;Y+Tub?ak_G)yP6z(PAXf9808-P$|d8PeR4|3K3Sr9vM|Khb& z>plCYeYMs>`&a5C7EI84k3U157B_fFdSU$E_R{+1{ugI@U|+-Dj$U+B_tyGR*l4wS z%%Sox7%FbjubvLdK29BxS{(&c+@hW%9BD}}B*Z_)JdQyKWiK=LzzOSD6ZTJr` z@tbSar(ZB540#yx_rWV24uXfGi7DTA-6ZeexTi>s|z%QfKsbz3rCCjC8XHv7vdWbZz~ zg%_A|7OlX2zr%LV{yL~!Q7|nVuE1Hh$mi2h$T)7wxtMLAnsf24mz$!!ktoW2Yrg5! z>*Okw-7r?7?m-grR}E*c6H}i z;pdSiv2rz_pJDs+{lXwrDo(fh1ccL#-YA5qK|?~}pr>2#U@q4LtfaVi8?~!x*~fZQ zR7vrpg3qTg#3?n0&cgYSEK;ap=~4b)JH;owB`p^1}k1xGR= zd|iwd)!c>1radoayYgG_3In8XN5rf`COcfcMAz*xGsn*gv4wkuZK}WX zuWgKqU8#pjzCC>+8uv`N?v@=E4VP>9Y5) zWEeg4=Q!aid{;OEL<_%tksb{~VUQKj7Ak${2Eg5*U|R5yQ3R=hB>m~mgn}sq_4N8@ zeET;^#=40ipOHsL>IOQmm2n>GL|l`OkGi+GB%_S|SjUUd4|M)VvAa@t@(=`S;N;;= z75VsNXI?|BOLzYHt^S7iVgtR@9m*DK#~9}?Ah-Ze;ity?L8DUp0EE)UaMQ^4+4UE=r_%bVy(7A={;yQ!a?pPe_*JLrO1 z)s_1eon3B83*{lh9d8kOvjZ{GBB!$c)4b6~*89zp88G}Lto zskFkf^g)<5Xc+4~Eb&Gr8w1PLu&1-zSKDJa*_jA%JOXLu#x3wJ=4~6@)oHm+8YQE6 zFE=RJEttyT3S+luJ>S@HiqY_KDU8g@RF$tZ^k;F%$8-RSbdC#o-!9}W9diACI0^f0 zS&GznL^~bXNda?&PBQdWD6m#AKg9`+TU4ybHff3mj?JCO--l!KV&}O`$Izg=3-Hkl z^X4uf5qK41yaHF}IG~>vr&n>TacHrwUt!bWPvQZ@iTB6N0}O6uxLkJPn2&pOtdgD{ zUGf=K0+(_;;VB-OYhTZe{D)eIsYXW6w2KwZB`*QG#dG*6$v8}ModP*;s|Z|K%`$Ln|;;&Eq+h17D6oEE2FL>+UP3%)9t zZM;0;AytN0#`ymgRdze;$sA6s7-i_;Xtbgd`L8Ve?@i5VHK*3@lYC1Exu$T-kd^_4 zl=lFRL~RrS^TqLknP;9|VfHWfJ;!cS=zu7B!b+JMX*4N=DyVXfV)f(2g*VD=%2d_< zm8)^LT;){dRe!JlLGEC91Q_R~v-7HeAi3SP%Oh zV54}(VwMq?4iob;o&>rOkfPpugcV`h9-e2Nw6QY#JDMuxSE85D!)RFDW2=8*w*;e} zteCHx=;u(T%<0;DD-F+s&WC&i?}eM3)Buq%wz~2f!$dW;uXTZld|FqOB$?gUoN4iu zWFA4kSMrU2Bz6Hnx*D;@xw0W(iS>7YfS?obMJ60F&J<@I$o)xDF3t4-&qcfvDq>qey*T zL;9t*d4EswVaX#?j!||}3ly9&0hUfC(XLRn6ZKvPGB46qd46xojsEhxQ1a_Zldz(n zGBl9AG~3;b23k>RC)8m)6p79VVg%NjV)8q_y3FVq5gq^5`a4)9*8UYNqb9!t5k3?0 ztbeM9TpJ&!hirEv@{QgQ9lxz7l?pBSp`ybp$lk{c{hTs2sY0*p>|_Bi?#98%eLZA$G{4d>N`7WN{6bqrBF!T-RaSJ^uH~DyCE2oHrAzDQO_6u_(h{HU zvwS^6Mlt8^_5hBhYGg3Jr3Z#dcN31$pZ=n9*bn)@b3afakVWDqVFCZXmUZ$0C_-T ze6$39Q5-?AEm2hv$FT3Sg72V#mxUFHl$e$&wc~?dfL{rG>LL)R4JW?i@FUX>7rND#_k&626fe` z3R^&vf9n1_7Nnz2WF7>&;OU!tCI3mSa>*UgICC20i)5~VETSn6suH!kRLZOLlCU7= z<=h=Zi+&$&&f$6v-HIPxlwbuz84A9W2H{uni$PgRtwy&`YFb)a;45_c9u6vI{9pzJ zRDqY!Bh)@+DsdGPvzsB$UQjBTLKtR4B1(H`$*gj~E%)fbYiQvLJEpUC#)3Ds({z!Q z)wbs$&Bcx1_kE8Aa!a@F8;*DUODIKe9vJ2O?I`*6gmNiW+;EL9Tm|J_A+~<9f5q9n z<)kp>9;nwPz#HrC@nBHv_j}!fg?a;-1}GTki1qRaW!Vp#=WmH}=L44c%!vf1qZh5+m)$TOsAS3zev$J{o#jV;E;ghP&%~tyEM(5MSzFtmCtO>a@bnAyD=X zO1>009%u6cmV9-ioDTF?B&R1sS26*@R`&bx?foF!q9UR>-v)8M3*syd!dei*yg5Gx zVSgmt+>l1WVAm2OU{5d*L~VmR;4KWk30KBBIwE8REC&zE&0nzx>5m19;Rd8d=EG_n z@*sFVn5jpxL2Mk*W9d@K;&F|?A8%?Y?hJdz^f|u3ajmx}0+qNJ6|FM<>IO9E7?)oLDLy3mP5~UmoGCQ7LxU)XcCDPOu}n@WA5 zo;j5B1Q$_YN)38F63^RZo1pwf+iUq&(i`P6r?ao+_MFfcmkpbkU!I@x9jL%Ql@l2*ZiQ0siW5f6fY(qhIzW9uQ&HKL9i0U-Y*uu~do3 zDZ}-rfi{cCyrA)guUS&I)Q~ zmH9=L7pl^5i}J=LgL-O4k1V688>o)a%a?x?YoAyH zptF>B#?f)Qm*eYn6u86(ioSFy^w?gKZ0v7WI$a}|rQzjqXN6g5Wc$zppd`odK~34p zz-)_{2ZdC*xD#r=Iu7pI8qhS|z2e>lmubKDLCD{UG{1ZFqaE4r*I>BTdjaJrJlytW z(jC9$Epkl0FW{TKFe@4r`W!PcHb9${u_Mdk| zg#KxDoAtjWM3@YvFbJ|9hH2xxbj2rhRYScj$|n95ajch+>dJ>1TSLNQ;)jB*`Xnrf zKg**YbV}eYGIq74C=P9*=sRQ{|Gfkqjm5KYAQ;L<;!@E8z z?hQ6PQ%;o*6{3HAjT)ztE{j#-s8d?0pN`B}i{c($V-UDOhO)E1X(w(L>b7)tn{E*T zumoxgz-35!99_c$GAE8cO!hDOY%8n>aF|&4n>y7<=J|I29WV>QV@4x+wC>Uhu4e!s zQ}|GZWN46bv{g=KAk4fiR2re({AI2&U7ekst2)kc)klB$1>W~7UBNEfn^4j>@RKof_|{beERWh5_eD@} zFuC5x%Z+e)21!z55iMsX{%bvJdGd881{g_U8C;E_x`ac+g!h_92`RVl!|Jvo3y}UV zp9P29X}tN+RL7i65ydA=ODkS8qnV{8V;VK)yL~AANupkDX9=geoFL?LR4@x6yt|p@ z$Bi`AMm0S(WncOefSEJhmSWz)t0b`~nZF6Ur=@uo|K_sDU zK0=CnavyfN{q212&qF8051}imu+kC`-LeoxO%{hR zAv!-Ib(=7lrrACGm8BGnA3?WrMdq$C;}3#VnG{q6n~A@JFvC};)4Wkfn!0j6qu$EZ zEJGrDj$ggMbtIk}q8%|c*>;5mYFE7iM*4>RhtB5x6d2!?QgkXKIu9UgRhiLazgI2n z#`iHSqe4}2<0HEDt@3PBs*Xq5Up^C0gm8uIu42-Ul>9w85~_{Mg*4Xd<05elya4zh zLpIA>uhOYC@JZs9F+q|=JTyf~!;&;aN78qD8&waq@WfdT6!c*=hdk_P91b$&cD*?^ zuD`*sG5XaLzF=IX6KX z_mSGbRpQ9xWV$8GO3cby)9$pj^QI+I(_`tHX?xKj8F>twA;p*2U4`PJabChA9655= zUL?UoWryj4!?e5YoYY8$l&2w8zTI{)JzbS2*6kE%z$SAb6FTc=+p6&`IC!v6?IJ@< z)a)68iF@rbh~zJMg}=<$*kg_ySRJF>a;*RS++~{v0JTuY941J*@ z@m~i;LwZ~NTWMsRgdq}iQPoVd>69{dteE5SCP}2>s=0%2o$Az)G`GF)8dbWt7{lF` zNQ&MiQ9u4r%z@j2nqLLS1||+PveypP=A=u%k6;_ zZyFc~LwGWPJk_vLGlF8*qy`~YjiC<&F!ID|aZREzXC;5ZqC64O0e}q?vB~h+RmUzx zpguRvItjba0QW&7_3E`pj)^nrvj>ZQu}NAmL0V7fxq$IM3H*IutR2N;j{va(%#b=7 z<)}*m0DIml&>E#)%wHo-C`Ge^;$Qc0TyPTf`A7d}3ynOX>*9u5ft(pWP2D;c*(3y$ z7rxqNI9k#Li^qb!Gcf^;;7Fbg(ngwtpOuK!enXU^ z3g66m-`mo|b5UmR21yKa1*O*>-y3(gZt*xW^yu#+x>@hu3IKD^#h#6Nf^u6~}H0bNp^heZccsx2@JQJRSlAM%BTn4sB5M~`lbG&MqhfkUuE!q^gz`G8B(7C|)9%MwW@_X02Y6_-U z0WwJ)dSvv>??>+RHg$6?BE&`;JqJ+%hhX~HKw|ygB$GBdjKdP*OQb_*ui#wgEtA9Sg>n6M>RQIQL{@KuqmC}2KGO3I zqlw0D+6j@%Wcyso@c0`Wb64%#c+O)TJxl#O(ID7~5$Z()^S6J*2`QTpTPbIg90p_N z%&LI+yHvfsf`T7zaO0@7Z74%l3u>x`Q>ky^s3yxWnMHx-yJ2IchV16ic3!IXE>{)S zs6welEr=kTPG-K5^yWY%3ur;%M!))#I~xWU=t*)`-1a5n6d2!N)k-_lrNccdq+u{> z1Yj3#&x)fsfEld!=nP!SGAzvEde=>|c7_2`z;K?p9A^WoS|Yy?3`;hx4?>2-Sc1^jUKAM-(YgHYf66Mj-UuYlN*VSi z47`MYm#KUGk5^DRmm!gp&`7w0LH)e(jNG z@>6~7i>AX9XMBCLHUby%(CB{kH-_3pn3J$k{wn!9TxfWYz4t*1z4j@Q9SsS2Wk*l<5r>Qk=wLYOBTy)fCSVN z`SWMVC@*i&c|1$H>3kEJ)ciV+yKL8FsA1$yT}Y!Sa2I=+dGUY6uOY()uXw|9mfg1$ zIR#b<*Yr(CTZqh&O!X z__s(?I_Frj|KGu@Z(L5wH+CI=0*=>37%-xxvVPG{_-YXewl(i93UPo!JK}+aahyn< zZfa_BDcTz}r_Xw+gd{B2V^MblP25!L*hSYIy#|JA>eHj#9$UR%h$Q>*xcZZ2 zWM%<8Ov6K!I?_#eOQt2(X6CTRMDPFXF*fnaY z>pgA=7)sGKS5N%-^2?5Mn40S+j(KMZLU}oAN)H$~0DN2EMo;E@OMdRd->tQ~N9puU z^hB0pR;0;{*K`YR47+5)h71&TWYLAd+Mf5$7t9=y@B)X5wbq}XJ=e1Z@ksKdL7*Mm z@UZ9=PNX4;FA#ZZ#_qyd!SV03muw2x1?{b@$y6r3_WH3;78@E zs@^ttux!-aogP*ECVAS;5!0ce)RE)%s`4yMK?`kv67z9fCugr>+EwZdJEp!p21c%& z33pZa5ZN7FLv_DBoaN8NU+8vla1hdU_Uqn})fz|aXx&Wxat4Vmc%Egz3AV_|#i@wv zU0OjoJ~bKS)e=p+&ie}ZTUa{^ za?%c*Gk%(%wX~4r(ON;VnU5XF$0+?&A25AId#ilbG5!X^Wd~)9CE*tQEz{oxuTa38 zA!}buYBXOQ8_wWMVz)9J`|TVhx*SOJL(clTx?fD{5(lS%S(D*=2_RJR%3viTeeeK= zu3{Rg5|}L&*(*@Pqo@A79$j;TwoBRKAi+w-v+h{YO)Y0D#PY2&f#A2xM?Bb(XVb6! zuMl0Rin2o+Q!GsR*^5%7B6~bxY!@5vy@02B5I}TqyY99%xca(Vi70ic;Sv&ovv)X5jyahtrsoQ_$?u*+D4iMtPmuIV&SR3Gse_TN68 zm%Cy_yc%3bWa)2U88gb z@y73KVNZ|i#Q2*UX~PWn@nFPh091Bc#^(ye^2K61@@-C6Tl0%Ry}fYxG>q=aZ9zUBi{zE7>VNGcWY7VJ=aLQk^1F^~WfS9Sxf|=b7TC5atej?Q#TCH*!VGG5p(`xR&i6|%- zG&ie$Rj732eBEJLaLV9Tk_FZ41A-NXrWMV#4P&sY(7?F`3=Hx6#L|j@Xnh?mWsFpc z&CiqQ78P=73I`Am%6TP#QU6e>g`F`#2o&}jUs9#Uo#hv(Sj;&i~JI|3_qF` zI9My7fumg$s@*Kd(6=&cO6s+ZW{3By0?w>CywSVNjWRJnbk--_4Kh z`^=rsy!$oCwn^b8h~8;(zl>?w*#2```f7ZK=lx0`O3AuOT*hU(N|ExHqU|pLD8u0} zY)20usY>M~mI)zqd%You5580jP#eW)8BpA483XxLh4rC$2@NL!whdrK;;+BgB+R}@ z8_95MJ+OyeWcIt5^dYW4TMpWbS<|wRMl9tJ!1=|MLf=|dkK)q5Nm$A3{Umy4D$fdcu=uKSsHtxkS_ zafR~I3j03kw`H~gVCL9FvTd9`${XENFIsgDy+S)=oh;E2h}5mfg186HnY7N~|B%sn z6%((F8@N1T3LO(tA5J}71y<$;QNzo-Jj3#)nEi_SO1*lBX&`qWJ6wZS5-Hfkz+eS{TfY*V0R>=CL$mQOW9XW0Fgm{v?p)(#RhBrk zB=#r+@Sw8u>pyqP_18y8UJSz8sLk$FJ(sEWgd{(?QUhovi-=GS*UN!S>fwL^h2qeb zXnC|GtJil~%j6etSbo{kS;r~qGpan z&RyUpaO}<$rRZw1w<~Qe=ybPDX}k5@eR*Ra&crp~ZWZ=%JEEhX2lYP68){N>1zmWr zjy$>B?nX(X^VyE_vS{yN#&oojY$?5E*^34ayS1zn_-sBu@RA#EYa1E-o_%EYU6_VO z$FwaD3U?d;lpYRB8rl2mRSc%{#3ncZ$~fc(bxG6R4m4?#(An!4v<3F_{rmT?ZNaYP zY8HQS<`9FhY@g)61ox;2coG4ZRht8ol8;=CB)IA%o7qm>r3A?Mk7QY&f@ttINm%D3 zth;V?{`~lv0y8DQ-+qI0plWnmZ^%f=+ei!C_N-*OswPxh+~QeB1;3kR%aqHu;X{zf z^V%lL_WOhl<%Pib(u1B!W9(XZ&yR|Y27pt7V(n4S{G2pa)ankz=a#rJRVL^+-NA5l zv>(gcE|X3lqeX`|@#HUa0JYpZ4h6+4T~3mvH9`p)33Q%p&>uO`DO#fzu28cDvzL4r zXw;3^AWl}1bZyv7Zl}nrcQ>lMPQ37=bM^Obvd3};ni=;aOyHh014a385$wU(>66NLMBt6EKSPCN!* zNpq%eD&pGAkZh}0^5K9qb}1klS{z1VrKW-W0}KrQR#VaODk;|O?o24hCB-J8^|f71 zhu2puB9bO59Q(|}H&9G&6#b9{wF$Zr&NyXQ5>OPAMOL(xAkx`t z2;SRhcM&8x+vsh$v)(fs3WaFZpXe33i^awe{IlU~`Lwn&qM@txs-frD7QN>Swg*h_ zc1Y0>Wl6PrX;%smhUJo4J?5|mrpjz5&UM{ZJjILF{dAiJZ*<+=>%27{FPLJNuJ-H^ z`VVvv4oqQ!|3PH$boE2HukqKy(>H?1(d(1!-|k|Xqh(pU75#Cly}yk$N)i?mEa)Wx zioG?AP#qiI*r?pCL;x0uz#4KV>YAkU6kS*IKvQh%QOTSpDd#VM84wF_Nhs0Z>_LZf z;5TwdF`Hsmf$V@GXY+TLX5<4P!9uP}Pxye(?M{$wL+m_*6O(H$gS$=T7s_P%X6L+o z`9e_;A$j?Z$=a#G>J85n5c zZ=T!7vloMm(ZJKq10A4}B6{XR^J@VdkJ^t%xSP zAj+6nWCfYcT6CMVQg2Kd{|ywzrfVy=1C614n4ksi1`wsa8A@co5vD*e44X6*?PdF{ zWB#p<7a){KIAk~WSOZVdR0~iLzL_N=?4pdJuBu?J5fQg5z0zf136{%y{`pnic^hJT z*Fl1osn4F<*I3#3<`<#Yd{k+L*+t({>JFu+<$^Vqn(lwy9WnV|USH!!#ky5&KugW6 zG>Ku;Q44X;1ug|{Xc>!C7QI7Ro=!LlxH<*Ms)wL-xkv-b$X=bO0K!CZ#VQaRgROcc zIx_^I6KQ*@L{Oeper=Hu>9c-HOq%r3_xzoGWWBH!xVt^`q<|>FSM^sQ=K)A#kP~cC zSC?}cBs6QW(V5t+-D0Hi;;lMZ+p;im28{@n&D|)YJcP6M*zX0EIB2GMlLH!Bu`$pk zwRY(fYL9#L(e@QH$E0U5OTc!}bohS7qTfh$|0K4J-zmo5cn@m9_h*FWXhG2mPHv-b zl#8kiVjF^*4Hh;JC@ppf0m6A?BvmuOeP+#-uygxHoGegu4$DZ-qK7vzLk+DrFpYRz z6HniHo6e*Dh}+$w_BR}Z)8bq>6a41MN=?Gf>Ap}*Mj_{RT-fO+rC@aWFqc*ij7kfC8z1)&uYwjw@k>A=o8J(DT5NrMGpRY~8v>_tnw+6KrhU{IVF&Hr}bBbu%}0 zoa5gL29I*z+sLE$H}xddRhv?yfEa0*Ax40bGHOd5abjHT=u6O!fFVSY!tfI_R@s4e z|MxmGj?o)Ibbv|SF!oT<-jF%+Vh_k=a1^-&CsEV)ju3DxX^a|p`R!r!pE-++5mK~+1$dHmRU zPbIvADjqExQrCVt_ThxPl<}&kCv&4vqFKvj-}?hNa9fqMNs>^fsVz_uI4}4D^hkZ=qj8t7N2l1`thKuhDN9N4 zNpyN&EZC!6fHJ;<2LWBlfb*2NmZ|FTo{Wj2AG1`u+a@4YFA9ht+S&NM9#MaH0-vQUmxNv75@GfTI% zPZ!}M9oDW;SzXSM4rhK(Cu?Gc$oCvYoT;}-VA%ZPn@d(AmHe8sO(Pz7V6OMH>29!6 z=F(rZADTB4;KQPx9)ugA+?21PQms#5rQJnOvUp|) zPZuw>{+0i2y7fhP^DyXPGA?7{CyV)B4K)8bO6v!b$gB)<$qJ6q1XP6v=3BDKgq*@# zrB(|aEWjsn4{Qg{I~v$$^RGsitcNe09lz>^{D!lBqeK|th% z4$36gJkzx71Li`v+od(u=t1r~x zo#HZ(dH@IlY>{?XL~c;C4gWorMfQcMkqOS$m%L*`f6W-{&pAGXSuJhLL{WM&Dx!}0 zy|Ts_)sx!@?1;M=ijIRc|1PB9?--=@L8-9LG=BvujvjG>K(x_MtBs_7=KwSVV3HN> zpi7+%t=U^4uT{G})PC=nzf#9;LGO!z3|IDCm1IMt%d?$OJgCiTRXP1lOILWGxVJSQ zPRE^^0$E_s{OzTA9^W^KFeiEdQCFt?_K^sxZpu=1}iJ!?Qk(zVAW; zveWig2@wPo%G!rU?~oZ}21ZOni4f8Qw@p_q$%P_(d>2Jok$`lK-SS`rE6;El$S3Cm z4RS@Qw(@4WTppc(l#4FF$(o3k%AI#9orz*{#>Vy(x0=m6^<$&zz?$FhM9Ek?S5htL zAI{)hxzXLQbf+@9B34tO?Uw%WYIZ9apO>rg$nactMyOF&~=-^jEb9Urq-@W1ua-%qgH*@0#zb1!}~^c z$^iL8IVK(n4HAtGmW!H#!Z*=z<$bgfTzNnIn_-*kFM3m?>{XhsA2hp|!II$J!iw~> z!eBI9(yOcp(=^qsod+lDjXhL;7^XAHy)Cy_0;ChTuOIpq@pL&8`oTQ9d+r|~RbtX( zIFWf1@GSdE-G3vqoWzk;C5Q&|Jsn0!bGSC6Ge!ha-E;TlTw{gZl`0%|xh+R46DQ|j z<1WAC9T@s+MtMJ1aT9?ONwxhUam%cRp|Rmk>=BkHXiwCcJBraSD!u27?=pFY``*K( zfwYy<2}bpMMnlyrINr5N>naHzitTld-JV9~VY10+Q4EI-IBbqS4zXA3L2@6-fs12~ ztJQ2Uc$7Nt$e$e#MmRY@tBn%=`JdT+=~`04pc)eDokY{MV!MU3>*;o2Os7+|I*0gU z>(!*uWh52RXwtMjmCVPaF%cO?3%RVvQiy_+ zcp!hNPE;|)W#BsMp^s1XT|qD!ssY>eOtUM?cCtX#5l7dJeszbt(wgA@Vu{oy$wx|p zqowmC%WS3EfbprgoDJ@%f=RoZ&hM{re_ayJ2c7yb-ph8Egl$r|XbDut+m|_fK(trU z*{(=o@L&{di3K@C|9%cJ=|N*H#i)sA3t*x^{+`{JZ2kI5hOFobCSI?IU!^8|w>R-~ zgcA}(9|D@9nc045tC_2>%rO8@>15>p_6N9p-@!f|J-Bi>JbkZl9)vcICqDOs$Z^jP zg`v_MB6Yo*>(H^|!%P>VY3_)Uyu@*m?B7IHKe}GM)rV|BJ+grX({g~vV=ExxMo6*> zQ6OQgFdAL@5LW|gbxN*gBpikZcjnL-3#EcCAX(}f^Ot?Ec{s2bpkYa27IU=)lZpH{ z=j|{&8>6)2-9yPMH1I5jz5-RMGc!LKf(VSgke{}l_ttdhJ$iQX`&+0zY?ks&ZrSvG zKDnuS2eR>z;SIn7sSdi%x{z(MT_!C^b(fJqOqmZu+eGd~X%hS$jA8bB8hOGtS=zl- z>cGH2?EW{PIQdY_zG%<>30egRcHO!CAkeoh5)*lK+Fj75)%M@yo9}Obx~((u%jrH# zomOW9W^vf=w6n{vyVdyWqDrY7jzZW*#It_+C~TD;f2ow~!`0 zK4T#btbTl5we>f$S4V|h8eTqDNKz$e;E+fzlv1)QuVg>lsqGBh2mov`?BN+cwlaEu zusgNyHlnx0ZJF0!Ps} z{^eCpd3gtPZ?=6udT8%*x?839+&KbjgxONxq2<}$iAHe9MrK18-`gm$Efm{@8}04v zTCoj;|7kb%<$J&AqRJ2;{Pj`ZsjC}2^}l3<{P2NVkF09$Alm>>6XL?~UE@Sb$erE= zPgOY}O5r%A9mCjF}jT{L7>@ zEdEVWY%j~3@UC#XN=z>DLCYxl7%ZmfIJ`y&&;eu%>8-^x#jUK6LX$?!jwnS0yX!pY zC#i*eiTat8YS5Hi=DdnQ(P9k9iPkzzqB-ReXmt``UlwJI;og$zv7jbw7wg1Tcj!lx zmyAbqH(6rM;r_Ju{8WtJz(sD#oyo&59%Jt}WR)vZnAOW^8qVIv%Fe4IV59~t#Q>II zV`8>V!@)5hnN8nLwf&(yFit_8O09}a{9N7BdK)u$ZpRHec{lW0=QH_YyV0+|7q!i| zKyJ!8yW>Th@j8ghvCtB;(?I+AW+Ss9rf!Q+b*8n4OXAJk0Rye0CT1cPv~K- z?)()1Q+ia#H5-9T+5t?#XX~Ns`gkb%>ZWPXghyw0lQ_1ON|a5o0Xn$L?OLbis3 zTXD?BFVNiUF*AFU+65@@4hf9ERR#^!Agzd{z=54h9B+F=T>U;PAvV&*D}T~1L3j!y znV&l1@0xNu(?6MKAI6=zFLKuF`V)-r+>ZavT)nW26n0Tq@?A*$M97cK_6O-z$Y63u zv&k=6dP!|>ng;`9rAHI|UOm!!gRTlU;>83|%oeY9=;uG(64`9-D1pmIC(A0#_WRB^ zb8gRlhaNh(lD@=pP}RP9htRl@{pQafG>M_oQ~4WiX-5wger)$}Z~|3rjvKzbr*{Km zE}FbaWM5z=(O8!EkKpD3IoEdLMJo1}OA0rb`IasoAuY7(zLGI&<7dwFt*-JemupM1 z2lerbLXO!h7oYR7SHTTKD!*II>9~$Txf$SN^<}74Di8)N=wMZVaD!ory`oyp23tB$ zy!6gX-j84~*!aJwSRMBMGi3EanE84TH`(ED;*K?#JNAPIF`4t~DqOIs9toc_ZQY4R z&gqBtfWxi=Cbo$jt@F3}?VoKnTj(oubpfu}%p1Wpm4xLlmQ2c72w$wiDr=IRdm1DO zs~}B;Zw}^M=Qu4*7i$v4z!D$y-(xTTSPscxO^@Es8G6z;J^m;nh#;=*0@ku<44`ir zkd}ZPI81wiDtXCh(O58HytpDg=H5`Bx?;7*V@odIMwT#1mz104p~K_NN|EJzzC zz)$h7Qcl)4lXkyFK~I!gV&}~uuGM$2aUu6(a<9!yy-#q5z5iOasI1<9b*$~e`Z9;( zh_A(7kZYw0cv|hnfgo77q6-hAJWXUa>Sh4BgYkwa9|{}$@&*QqcN}lsD$YyeC9%_+ ze|8tmw~__#Zj+OSPOTKV{Xh!KP>_aNe-KsNsQvAuI{=A&M;ZFcS9?TdXdg1~(K5FP zhO|*t;e6IhS#GIe{Q`I3UO{J7@tffs@fl3Pc}L*co@r)$(L!{o{-TvoHQA`@!$a$C zsBBbk>F>XVVT5TKoqj$V=ef_{wQa}DkM^2y&*ZN~|LIAs{eJ3gsFoIWs2Wfrl=9Ex z=m|bB-hn~J4-S-3JlY4h>D^ax%xAUV(AMr$6ArGU-N4K>BK*~AY-(-)CU3sEHR0=P zs;xtR9)?3j$6DdtuJw2+HT7zjnwrzZBy5K2!-XQ^OKDNP(Lu%`#U|*pxI$^cG+ya( z;-z06BbMN$=kBc^o2Tsa?X7g|QjK(iT$>)NH(TR}l>*34WX8tzckHQ>4q|5$9i3ca zh_?V+*UB%@C}H~&-#^M=JoX{nj&?M69ebl0oFfTX2&Z~VVKGE`1Cv|yhvDGVn?&=7 zxwBHI)!lKFV_Yc|xe+tVe)NofZmnz0p1=HZXO75@Uko1)pvAcy7mC&p-hF6?-!nZ1 zh91_Z7tZE&B-)Vd+f>c=9cbmH+*gRd8m9^s?Uj#}kJGf>316yGqsqv<2oIH;nkLBA zfRkWjFC3NYAwC^>rlD13xTUg@3$yc@wZJ&Fh>j6?-jZlE0l znLLK~=nZ>lKrLMg(>P4`0U9`izNw&=|0GiwgHdRj9_3I3eG-<0`XE_iz{1 z`Mdu1PkMPA@}7lReU)d@4mk1nGjo$Sfl+L)y1{r2Tm^W1htgxQT$7`vQNH^t=D=UC zZe!ZwtksvZov4`;E4F#**QC@WYqJ`^&7J@VSLbs<7MZqji zg%qz>B@egy-5T>R_E*(SN6FF--O(L zl1}AK#U0F?ApRY+khlE5GrJ#%y0UA*$HgIJZEq)8pgS~_d;xLWt*&ph)V zp!c22mW|gKd(AhW&i*-)i*c>WZ}xa0ZHyihTVFPPuo(jVYRYzkvO23O&8zCkuMo z(yu3vyCuyv(*1de;l#n--^szoBmmSwvb@tow%Xdr60)2b!Sw7Vh2Z$cXf=GMHkRSI9=Z~ z^&S_17b@`fj2&=R)xGjx)j?6!*!w?;P#bP$l)@bPbQESOuJj@@=i z&QCU8bN2bv)gy8LzRqzZ?Jd2X{zzmOJ^p{~_c}#)?*_Cg>-i%J1jd%vL0{Bn^@`U~ zW|LGdt^Gp@L{||ygre>j1jOCr4@-ssIc2!beutg&Ubvmb{5$8hJNgEuIcPIyL-f}G zzYckO-7{a_Y~~==VZhgQg#U#OT+t)h4H2fWF`Lc){wNf<6qfT?L|V~oX=-))pu}4- zM5_%rdz3NOi+f8%Wfb1!_4lI%$1rd>-|GDje&t2Z*C=!`{dv$+`2Y5<^He_A8c%t* z?iOq7MfX+5)c=G1vcf&&%e&|I62Fju>!Vlb``r1r7|o!YjsTAxor0w-1R5e8d7R5ux2Mk4a|3eMEIdmlm-c!SF@s_;%qGVXq1LWNEZFa@MA%&Vv~9a%3EeuRz3;% zbYR9lJnQwmQ15qim#<#g;OEXGS|-bD zNGdBHODWum^puy=2G?bhxn>@hB6|^$?u!V6y7d+)B4b{){p-`t81*s ziuE%b-TVLV)8|hS_!NOp5%?5=PZ9VOflm?m6oF3>_!NOp5%?5=PZ9VOflm?me~G|r zgJ{{(R(OTwU&R{Q2TslSto~nK@YBEj6oF3>_!NOp5%?5=PZ9VOflm?m6oF3>_!NOp y5%?5=PZ9VOflm?m6oF3>_!NQvA4T9C*8b4*Sd{lt)fn=wc5d_hnX%>Ium2wbw!U!y literal 0 HcmV?d00001 diff --git a/src/settings-ui/Settings.UI/Assets/Modules/OOBE/PastePlain.gif b/src/settings-ui/Settings.UI/Assets/Modules/OOBE/PastePlain.gif new file mode 100644 index 0000000000000000000000000000000000000000..a9f6c089b7a11f689505d1b49b33e14ad47faf9c GIT binary patch literal 26706 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 PCM)D}h67-~$lRUt*y5J99m&}z}vJl9MjqGl0E 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 literal 0 HcmV?d00001 diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/MainWindow.xaml.cs index 4fef64531c..f4b9e2746a 100644 --- a/src/settings-ui/Settings.UI/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/MainWindow.xaml.cs @@ -123,6 +123,9 @@ namespace Microsoft.PowerToys.Settings.UI case "MousePointerCrosshairs": needToUpdate = generalSettingsConfig.Enabled.MousePointerCrosshairs != isEnabled; generalSettingsConfig.Enabled.MousePointerCrosshairs = isEnabled; break; + case "PastePlain": + needToUpdate = generalSettingsConfig.Enabled.PastePlain != isEnabled; + generalSettingsConfig.Enabled.PastePlain = isEnabled; break; case "PowerRename": needToUpdate = generalSettingsConfig.Enabled.PowerRename != isEnabled; generalSettingsConfig.Enabled.PowerRename = isEnabled; break; diff --git a/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs b/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs index 148de19256..b7ceb31266 100644 --- a/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs +++ b/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs @@ -24,6 +24,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Enums VideoConference, MeasureTool, Hosts, + PastePlain, WhatsNew, } } diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobePastePlain.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobePastePlain.xaml new file mode 100644 index 0000000000..f85d7928a6 --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobePastePlain.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + +

- internal class EventMonitor + internal sealed class EventMonitor { public EventMonitor(System.Windows.Threading.Dispatcher dispatcher, System.Threading.CancellationToken exitToken) { diff --git a/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHook.cs b/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHook.cs index 3ffe94c2e0..34f0f77160 100644 --- a/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHook.cs +++ b/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHook.cs @@ -10,7 +10,7 @@ using static PowerOCR.OSInterop; namespace PowerOCR.Keyboard; -internal class GlobalKeyboardHook : IDisposable +internal sealed class GlobalKeyboardHook : IDisposable { private IntPtr _windowsHookHandle; private IntPtr _user32LibraryHandle; @@ -39,7 +39,7 @@ internal class GlobalKeyboardHook : IDisposable internal event EventHandler? KeyboardPressed; - protected virtual void Dispose(bool disposing) + public void Dispose(bool disposing) { if (disposing) { diff --git a/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHookEventArgs.cs b/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHookEventArgs.cs index b20bcd645a..23360de470 100644 --- a/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHookEventArgs.cs +++ b/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHookEventArgs.cs @@ -7,7 +7,7 @@ using static PowerOCR.OSInterop; namespace PowerOCR.Keyboard; -internal class GlobalKeyboardHookEventArgs : HandledEventArgs +internal sealed class GlobalKeyboardHookEventArgs : HandledEventArgs { internal GlobalKeyboardHook.KeyboardState KeyboardState { get; private set; } diff --git a/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs b/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs index 5b66d0b937..4a0f8811c9 100644 --- a/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs +++ b/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs @@ -35,7 +35,7 @@ public partial class OCROverlay : Window private Point GetMousePos() => PointToScreen(Mouse.GetPosition(this)); - private Language? selectedLanguage = null; + private Language? selectedLanguage; private MenuItem cancelMenuItem; private System.Windows.Forms.Screen? CurrentScreen @@ -70,8 +70,8 @@ public partial class OCROverlay : Window foreach (Language language in possibleOcrLanguages) { MenuItem menuItem = new MenuItem() { Header = language.NativeName, Tag = language, IsCheckable = true }; - menuItem.IsChecked = language.DisplayName.Equals(selectedLanguageName); - if (language.DisplayName.Equals(selectedLanguageName)) + menuItem.IsChecked = language.DisplayName.Equals(selectedLanguageName, StringComparison.Ordinal); + if (language.DisplayName.Equals(selectedLanguageName, StringComparison.Ordinal)) { selectedLanguage = language; } diff --git a/src/modules/awake/Awake/Program.cs b/src/modules/awake/Awake/Program.cs index 68cfe5c9e7..1d016300ff 100644 --- a/src/modules/awake/Awake/Program.cs +++ b/src/modules/awake/Awake/Program.cs @@ -7,6 +7,7 @@ using System.CommandLine; using System.CommandLine.Invocation; using System.Diagnostics; using System.Drawing; +using System.Globalization; using System.IO; using System.Linq; using System.Reactive.Concurrency; @@ -255,7 +256,7 @@ namespace Awake { try { - DateTime expirationDateTime = DateTime.Parse(expireAt); + DateTime expirationDateTime = DateTime.Parse(expireAt, CultureInfo.CurrentCulture); if (expirationDateTime > DateTime.Now) { // We want to have a dedicated expirable keep-awake logic instead of diff --git a/src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs b/src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs index f9a696d8be..be52e82377 100644 --- a/src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs +++ b/src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs @@ -9,7 +9,7 @@ using Microsoft.PowerToys.Telemetry.Events; namespace Awake.Telemetry { [EventData] - internal class AwakeNoKeepAwakeEvent : EventBase, IEvent + internal sealed class AwakeNoKeepAwakeEvent : EventBase, IEvent { public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; } diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs index ae747b0d58..e09c494e36 100644 --- a/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs @@ -246,9 +246,7 @@ namespace ColorPicker.ViewModels new ColorFormatModel() { FormatName = ColorRepresentationType.HEX.ToString(), -#pragma warning disable CA1304 // Specify CultureInfo - Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HEX.ToString()).ToLower(), -#pragma warning restore CA1304 // Specify CultureInfo + Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HEX.ToString()).ToLowerInvariant(), }); _allColorRepresentations.Add( diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs index f55813bf4c..ac9c8ac7c3 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs @@ -159,7 +159,7 @@ namespace FancyZonesEditor public abstract void Move(int delta); } - private class SnappyHelperMagnetic : SnappyHelperBase + private sealed class SnappyHelperMagnetic : SnappyHelperBase { private List magnetZoneSizes; private int freePosition; @@ -220,7 +220,7 @@ namespace FancyZonesEditor } } - private class SnappyHelperNonMagnetic : SnappyHelperBase + private sealed class SnappyHelperNonMagnetic : SnappyHelperBase { public SnappyHelperNonMagnetic(IList zones, int zoneIndex, bool isX, ResizeMode mode, int screenAxisOrigin, int screenAxisSize) : base(zones, zoneIndex, isX, mode, screenAxisOrigin, screenAxisSize) diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs index ba006e2c13..01e17f70b8 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs @@ -8,7 +8,7 @@ using System.Windows.Controls; namespace FancyZonesEditor.Controls { - internal class CustomSliderAutomationPeer : SliderAutomationPeer + internal sealed class CustomSliderAutomationPeer : SliderAutomationPeer { private string name = string.Empty; diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs index 4a14b82e44..26b0dfa151 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs @@ -175,7 +175,7 @@ namespace FancyZonesEditor.Utils } // custom-layouts.json - private class CanvasInfoWrapper + private sealed class CanvasInfoWrapper { public struct CanvasZoneWrapper { @@ -198,7 +198,7 @@ namespace FancyZonesEditor.Utils } // custom-layouts.json - private class GridInfoWrapper + private sealed class GridInfoWrapper { public int Rows { get; set; } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/NativeMethods.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/NativeMethods.cs index 57cefcf9be..b5d4db9952 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/NativeMethods.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/NativeMethods.cs @@ -9,7 +9,7 @@ using System.Windows.Interop; namespace FancyZonesEditor.Utils { - internal class NativeMethods + internal sealed class NativeMethods { [DllImport("user32.dll", SetLastError = true)] private static extern int GetWindowLong(IntPtr hWnd, int nIndex); diff --git a/src/modules/imageresizer/tests/Test/AssertEx.cs b/src/modules/imageresizer/tests/Test/AssertEx.cs index 5c05ecec44..7961b6f22f 100644 --- a/src/modules/imageresizer/tests/Test/AssertEx.cs +++ b/src/modules/imageresizer/tests/Test/AssertEx.cs @@ -75,7 +75,7 @@ namespace ImageResizer.Test return raisedEvent; } - public class RaisedEvent + public sealed class RaisedEvent { public RaisedEvent(object sender, TArgs args) { diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs index aa25ceb994..bb72b6fa2c 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs @@ -62,7 +62,7 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter return new Result { ContextData = result, - Title = result.ToString(), + Title = result.ToString(null), IcoPath = _icon_path, Score = 300, SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.copy_to_clipboard, result.QuantityInfo.Name), diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SystemPath.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SystemPath.cs index 3b76c8cafc..f937e5e9d4 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SystemPath.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SystemPath.cs @@ -6,7 +6,7 @@ using System.Text.RegularExpressions; namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces { - internal class SystemPath + internal sealed class SystemPath { private static readonly Regex WindowsPath = new Regex(@"^([a-zA-Z]:)", RegexOptions.Compiled); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs index 04316a3a91..737cc1559d 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs @@ -439,9 +439,9 @@ namespace Microsoft.Plugin.Program.Programs paths.Add(path); } - if (_scaleFactors.ContainsKey(Package.Version)) + if (_scaleFactors.TryGetValue(Package.Version, out List factors)) { - foreach (var factor in _scaleFactors[Package.Version]) + foreach (var factor in factors) { if (highContrast) { diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs index cda615809d..c681ae0e03 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components /// /// This class represents the informations for a network connection/interface /// - internal class NetworkConnectionProperties + internal sealed class NetworkConnectionProperties { /// /// Gets the name of the adapter diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs index c6d26c8624..20840e0db3 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs @@ -4,7 +4,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components { - internal class SystemPluginContext + internal sealed class SystemPluginContext { /// /// Gets or sets the type of the result diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSetting.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSetting.cs index 8065dc46a1..8b4d1591bd 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSetting.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSetting.cs @@ -9,7 +9,7 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings /// /// A windows setting /// - internal class WindowsSetting + internal sealed class WindowsSetting { /// /// Initializes a new instance of the class. diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSettings.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSettings.cs index 7ce25eb499..e05532cc4b 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSettings.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSettings.cs @@ -10,7 +10,7 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings /// /// A class that contain all possible windows settings /// - internal class WindowsSettings + internal sealed class WindowsSettings { /// /// Initializes a new instance of the class with an empty settings list. diff --git a/src/modules/launcher/PowerLauncher/CustomSearchBox.cs b/src/modules/launcher/PowerLauncher/CustomSearchBox.cs index 5502fd15c9..20b1becf39 100644 --- a/src/modules/launcher/PowerLauncher/CustomSearchBox.cs +++ b/src/modules/launcher/PowerLauncher/CustomSearchBox.cs @@ -9,7 +9,7 @@ using System.Windows.Controls; namespace PowerLauncher { - public class CustomSearchBox : TextBox + public sealed class CustomSearchBox : TextBox { public List ControlledElements { get; } = new List(); @@ -18,7 +18,7 @@ namespace PowerLauncher return new AutoSuggestTextBoxAutomationPeer(this); } - internal class AutoSuggestTextBoxAutomationPeer : TextBoxAutomationPeer + internal sealed class AutoSuggestTextBoxAutomationPeer : TextBoxAutomationPeer { public AutoSuggestTextBoxAutomationPeer(CustomSearchBox owner) : base(owner) diff --git a/src/modules/launcher/PowerLauncher/Helper/KeyboardHelper.cs b/src/modules/launcher/PowerLauncher/Helper/KeyboardHelper.cs index 6cfdd2fded..97a05cfac4 100644 --- a/src/modules/launcher/PowerLauncher/Helper/KeyboardHelper.cs +++ b/src/modules/launcher/PowerLauncher/Helper/KeyboardHelper.cs @@ -7,7 +7,7 @@ using Wox.Plugin; namespace PowerLauncher.Helper { - internal class KeyboardHelper + internal sealed class KeyboardHelper { public static SpecialKeyState CheckModifiers() { diff --git a/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs b/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs index 1ed201413c..804c162511 100644 --- a/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs +++ b/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs @@ -17,7 +17,7 @@ using Wox.Plugin.Logger; namespace PowerLauncher { - internal partial class ReportWindow + internal sealed partial class ReportWindow { private static readonly IFileSystem FileSystem = new FileSystem(); private static readonly IFile File = FileSystem.File; diff --git a/src/modules/launcher/PowerLauncher/SettingsReader.cs b/src/modules/launcher/PowerLauncher/SettingsReader.cs index e470f85749..98abae4318 100644 --- a/src/modules/launcher/PowerLauncher/SettingsReader.cs +++ b/src/modules/launcher/PowerLauncher/SettingsReader.cs @@ -269,9 +269,9 @@ namespace PowerLauncher var defaultOptions = defaultAdditionalOptions.ToDictionary(x => x.Key); foreach (var option in additionalOptions) { - if (option.Key != null && defaultOptions.ContainsKey(option.Key)) + if (option.Key != null && defaultOptions.TryGetValue(option.Key, out PluginAdditionalOption defaultOption)) { - defaultOptions[option.Key].Value = option.Value; + defaultOption.Value = option.Value; } } diff --git a/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs b/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs index 1aa1b4ea36..8a35469178 100644 --- a/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs +++ b/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs @@ -154,9 +154,7 @@ namespace Wox.Infrastructure.Hotkey if (CharKey != Key.None) { - text += _specialSymbolDictionary.ContainsKey(CharKey) - ? _specialSymbolDictionary[CharKey] - : CharKey.ToString(); + text += _specialSymbolDictionary.TryGetValue(CharKey, out string value) ? value : CharKey.ToString(); } else if (!string.IsNullOrEmpty(text)) { diff --git a/src/modules/launcher/Wox.Infrastructure/Image/ImageHashGenerator.cs b/src/modules/launcher/Wox.Infrastructure/Image/ImageHashGenerator.cs index 69acf84dd2..0846391a01 100644 --- a/src/modules/launcher/Wox.Infrastructure/Image/ImageHashGenerator.cs +++ b/src/modules/launcher/Wox.Infrastructure/Image/ImageHashGenerator.cs @@ -34,12 +34,7 @@ namespace Wox.Infrastructure.Image enc.Frames.Add(bitmapFrame); enc.Save(outStream); var byteArray = outStream.GetBuffer(); - - using (var sha1 = SHA1.Create()) - { - var hash = Convert.ToBase64String(sha1.ComputeHash(byteArray)); - return hash; - } + return Convert.ToBase64String(SHA1.HashData(byteArray)); } } catch (System.Exception e) diff --git a/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs b/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs index 97ff8bda38..2ea86afd02 100644 --- a/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs +++ b/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs @@ -25,9 +25,9 @@ namespace Wox.Plugin.Common public string GetLocalizedName(string path) { // Checking cache if path is already localized - if (_localizationCache.ContainsKey(path.ToLowerInvariant())) + if (_localizationCache.TryGetValue(path.ToLowerInvariant(), out string value)) { - return _localizationCache[path.ToLowerInvariant()]; + return value; } Guid shellItemType = ShellItemTypeConstants.ShellItemGuid; diff --git a/src/modules/launcher/Wox.Plugin/UserSelectedRecord.cs b/src/modules/launcher/Wox.Plugin/UserSelectedRecord.cs index a987eefad1..44ed05640a 100644 --- a/src/modules/launcher/Wox.Plugin/UserSelectedRecord.cs +++ b/src/modules/launcher/Wox.Plugin/UserSelectedRecord.cs @@ -39,11 +39,7 @@ namespace Wox.Plugin throw new ArgumentNullException(nameof(result)); } - var key = result.ToString(); - if (Records.ContainsKey(result.ToString())) - { - Records.Remove(result.ToString()); - } + Records.Remove(result.ToString()); } public void Add(Result result) diff --git a/src/modules/launcher/Wox.Test/WoxTest.cs b/src/modules/launcher/Wox.Test/WoxTest.cs index d682d300cc..6d35aeda66 100644 --- a/src/modules/launcher/Wox.Test/WoxTest.cs +++ b/src/modules/launcher/Wox.Test/WoxTest.cs @@ -14,7 +14,7 @@ namespace Wox.Test public class WoxTest { // A Dummy class to test that OnPropertyChanged() is called while we set the variable - private class DummyTestClass : BaseModel + private sealed class DummyTestClass : BaseModel { public bool IsFunctionCalled { get; set; } diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 2b5ad8615c..2ca2ac8d50 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using PowerToys.PowerAccentKeyboardService; -using Windows.Globalization; namespace PowerAccent.Core { @@ -42,7 +40,7 @@ namespace PowerAccent.Core TK, } - internal class Languages + internal sealed class Languages { public static string[] GetDefaultLetterKey(LetterKey letter, Language lang) { diff --git a/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs b/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs index 6dec635eac..1429be6f34 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs @@ -34,6 +34,7 @@ namespace PowerAccent.Core.Tools return timestamp; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1854:Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method", Justification = "False positive: https://github.com/dotnet/roslyn-analyzers/issues/6390")] public void IncrementUsageFrequency(string character) { if (_characterUsageCounters.ContainsKey(character)) diff --git a/src/modules/poweraccent/PowerAccent.UI/Selector.xaml.cs b/src/modules/poweraccent/PowerAccent.UI/Selector.xaml.cs index d243c3f20d..f83e20c037 100644 --- a/src/modules/poweraccent/PowerAccent.UI/Selector.xaml.cs +++ b/src/modules/poweraccent/PowerAccent.UI/Selector.xaml.cs @@ -17,7 +17,7 @@ public partial class Selector : Window, IDisposable, INotifyPropertyChanged private Visibility _characterNameVisibility = Visibility.Visible; - private int _selectedIndex = 0; + private int _selectedIndex; public event PropertyChangedEventHandler PropertyChanged; diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs index b1fd46d5bc..c4369063d4 100644 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs @@ -13,7 +13,7 @@ namespace PreviewHandlerCommonUnitTests [TestClass] public class FormHandlerControlTests { - private class TestFormControl : FormHandlerControl + private sealed class TestFormControl : FormHandlerControl { } diff --git a/src/modules/previewpane/common/cominterop/NativeMethods.cs b/src/modules/previewpane/common/cominterop/NativeMethods.cs index 3d7bebaef2..611136b1a3 100644 --- a/src/modules/previewpane/common/cominterop/NativeMethods.cs +++ b/src/modules/previewpane/common/cominterop/NativeMethods.cs @@ -10,7 +10,7 @@ namespace PreviewHandlerCommon.ComInterop /// /// Interop methods /// - internal class NativeMethods + internal sealed class NativeMethods { /// /// Changes the parent window of the specified child window. diff --git a/src/modules/previewpane/common/controls/FormHandlerControl.cs b/src/modules/previewpane/common/controls/FormHandlerControl.cs index 7549933382..ab5cfe6a91 100644 --- a/src/modules/previewpane/common/controls/FormHandlerControl.cs +++ b/src/modules/previewpane/common/controls/FormHandlerControl.cs @@ -73,7 +73,7 @@ namespace Common } /// - public void SetRect(Rectangle rect) + public void SetRect(Rectangle windowBounds) { this.UpdateWindowBounds(parentHwnd); } diff --git a/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs b/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs index dbb619ea73..64143f716a 100644 --- a/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs +++ b/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs @@ -60,7 +60,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library private bool FilterKeyboardEvents(KeyboardEvent ev) { +#pragma warning disable CA2020 // Prevent from behavioral change return _filterKeyboardEvent(ev.key, (UIntPtr)ev.dwExtraInfo); +#pragma warning restore CA2020 // Prevent from behavioral change } protected virtual void Dispose(bool disposing) diff --git a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs index eeaa0aa541..36bed6bf48 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs @@ -46,7 +46,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } - private class JsonMergeHelper + private sealed class JsonMergeHelper { // mostly from https://stackoverflow.com/questions/58694837/system-text-json-merge-two-objects // but with some update to prevent array item duplicates @@ -308,10 +308,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library var retoreFullPath = Path.Combine(appBasePath, relativePath); var settingsToRestoreJson = GetExportVersion(backupRetoreSettings, currentFile.Key, currentFile.Value); - if (currentSettingsFiles.ContainsKey(currentFile.Key)) + if (currentSettingsFiles.TryGetValue(currentFile.Key, out string value)) { // we have a setting file to restore to - var currentSettingsFileJson = GetExportVersion(backupRetoreSettings, currentFile.Key, currentSettingsFiles[currentFile.Key]); + var currentSettingsFileJson = GetExportVersion(backupRetoreSettings, currentFile.Key, value); if (JsonNormalizer.Normalize(settingsToRestoreJson) != JsonNormalizer.Normalize(currentSettingsFileJson)) { @@ -659,10 +659,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library var currentSettingsFileToBackup = GetExportVersion(backupRetoreSettings, currentFile.Key, currentFile.Value); var doBackup = false; - if (lastBackupSettingsFiles.ContainsKey(currentFile.Key)) + if (lastBackupSettingsFiles.TryGetValue(currentFile.Key, out string value)) { // there is a previous backup for this, get an export version of it. - var lastSettingsFileDoc = GetExportVersion(backupRetoreSettings, currentFile.Key, lastBackupSettingsFiles[currentFile.Key]); + var lastSettingsFileDoc = GetExportVersion(backupRetoreSettings, currentFile.Key, value); // check to see if the new export version would be same as last export version. if (JsonNormalizer.Normalize(currentSettingsFileToBackup) != JsonNormalizer.Normalize(lastSettingsFileDoc)) @@ -989,7 +989,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library /// Class JsonNormalizer is a utility class to 'normalize' a JSON file so that it can be compared to another JSON file. /// This really just means to fully sort it. This does not work for any JSON file where the order of the node is relevant. /// - private class JsonNormalizer + private sealed class JsonNormalizer { public static string Normalize(string json) { diff --git a/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs b/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs index 7827023cc2..f7b25470e2 100644 --- a/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs +++ b/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs @@ -20,7 +20,7 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility // Using Ordinal since this is used internally for a path private static readonly Expression> SettingsFilterExpression = s => s == null || s.Contains("Microsoft\\PowerToys\\settings.json", StringComparison.Ordinal); - internal class MockSettingsRepository : ISettingsRepository + internal sealed class MockSettingsRepository : ISettingsRepository where T : ISettingsConfig, new() { private readonly ISettingsUtils _settingsUtils; diff --git a/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs b/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs index 56b7701c9f..62e7f7b9af 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs @@ -100,7 +100,7 @@ namespace CommonLibTest .Select(s => s[random.Next(s.Length)]).ToArray()); } - private partial class TestClass : ISettingsConfig + private sealed partial class TestClass : ISettingsConfig { public int TestInt { get; set; } = 100; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs index 1c5c0537d8..db54fa96dc 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs @@ -14,7 +14,7 @@ namespace ViewModelTests [TestClass] public class PowerLauncherViewModelTest { - private class SendCallbackMock + private sealed class SendCallbackMock { public int TimesSent { get; set; } diff --git a/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs b/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs index 2c401c4a39..c501a23e6b 100644 --- a/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs +++ b/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs @@ -9,7 +9,7 @@ using Windows.ApplicationModel.Activation; namespace Microsoft.PowerToys.Settings.UI.Activation { - internal class DefaultActivationHandler : ActivationHandler + internal sealed class DefaultActivationHandler : ActivationHandler { private readonly Type navElement; diff --git a/src/settings-ui/Settings.UI/App.xaml.cs b/src/settings-ui/Settings.UI/App.xaml.cs index 737c73a378..14512c75e3 100644 --- a/src/settings-ui/Settings.UI/App.xaml.cs +++ b/src/settings-ui/Settings.UI/App.xaml.cs @@ -163,8 +163,8 @@ namespace Microsoft.PowerToys.Settings.UI if (containsFlyoutPosition) { // get the flyout position arguments - int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x); - int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y); + _ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x); + _ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y); } RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () => diff --git a/src/settings-ui/Settings.UI/Helpers/Utils.cs b/src/settings-ui/Settings.UI/Helpers/Utils.cs index 53124c7986..7f34815bf8 100644 --- a/src/settings-ui/Settings.UI/Helpers/Utils.cs +++ b/src/settings-ui/Settings.UI/Helpers/Utils.cs @@ -9,7 +9,7 @@ using System.Text.Json; namespace Microsoft.PowerToys.Settings.UI.Helpers { - internal class Utils + internal sealed class Utils { private static string _placementPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\PowerToys\settings-placement.json"); diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs index b086b52336..cd069e9c60 100644 --- a/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs @@ -25,7 +25,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views public sealed partial class OobeWhatsNew : Page { // Contains information for a release. Used to deserialize release JSON info from GitHub. - private class PowerToysReleaseInfo + private sealed class PowerToysReleaseInfo { [JsonPropertyName("published_at")] public DateTimeOffset PublishedDate { get; set; } diff --git a/src/settings-ui/Settings.UI/Services/ActivationService.cs b/src/settings-ui/Settings.UI/Services/ActivationService.cs index bf627dd160..0d6d241631 100644 --- a/src/settings-ui/Settings.UI/Services/ActivationService.cs +++ b/src/settings-ui/Settings.UI/Services/ActivationService.cs @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Services { // For more information on understanding and extending activation flow see // https://github.com/Microsoft/WindowsTemplateStudio/blob/master/docs/activation.md - internal class ActivationService + internal sealed class ActivationService { private readonly App app; private readonly Type defaultNavItem; diff --git a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs index c9c06e4fd9..6f27fbab83 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs @@ -241,7 +241,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { // skip entries with empty name or duplicated name, it should never occur string storedName = storedColorFormat.Key; - if (storedName == string.Empty || ColorFormats.Count(x => x.Name.ToUpperInvariant().Equals(storedName.ToUpperInvariant(), StringComparison.Ordinal)) > 0) + if (storedName == string.Empty || ColorFormats.Any(x => x.Name.ToUpperInvariant().Equals(storedName.ToUpperInvariant(), StringComparison.Ordinal))) { continue; } diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs index c8b184b2f1..129f961d56 100644 --- a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs @@ -36,7 +36,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } - public Action EnabledChangedCallback { get; set; } = null; + public Action EnabledChangedCallback { get; set; } public bool Visible { diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs index 1d45053b2e..d97237c43f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutViewModel.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Timers; @@ -9,8 +10,11 @@ using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout { - public class FlyoutViewModel + public class FlyoutViewModel : IDisposable { + private Timer _hideTimer; + private bool _disposed; + public bool CanHide { get; set; } private bool _windows10; @@ -28,29 +32,27 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout } } - private Timer hideTimer; - public FlyoutViewModel() { CanHide = true; - hideTimer = new Timer(); - hideTimer.Elapsed += HideTimer_Elapsed; - hideTimer.Interval = 1000; - hideTimer.Enabled = false; + _hideTimer = new Timer(); + _hideTimer.Elapsed += HideTimer_Elapsed; + _hideTimer.Interval = 1000; + _hideTimer.Enabled = false; _windows10 = !Helper.Windows11(); } private void HideTimer_Elapsed(object sender, ElapsedEventArgs e) { CanHide = true; - hideTimer.Stop(); + _hideTimer.Stop(); } internal void DisableHiding() { CanHide = false; - hideTimer.Stop(); - hideTimer.Start(); + _hideTimer.Stop(); + _hideTimer.Start(); } public event PropertyChangedEventHandler PropertyChanged; @@ -59,5 +61,23 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _hideTimer?.Dispose(); + _disposed = true; + } + } + } } } diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs index 7998a3c1e7..0911051bb1 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs @@ -178,12 +178,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels AvailableLanguages.Clear(); foreach (Language language in possibleOcrLanguages) { - if (_powerOcrSettings.Properties.PreferredLanguage?.Equals(language.DisplayName) == true) + if (_powerOcrSettings.Properties.PreferredLanguage?.Equals(language.DisplayName, StringComparison.Ordinal) == true) { preferredLanguageIndex = AvailableLanguages.Count; } - if (systemCulture.DisplayName.Equals(language.DisplayName) || systemCulture.Parent.DisplayName.Equals(language.DisplayName)) + if (systemCulture.DisplayName.Equals(language.DisplayName, StringComparison.Ordinal) || systemCulture.Parent.DisplayName.Equals(language.DisplayName, StringComparison.Ordinal)) { systemLanguageIndex = AvailableLanguages.Count; } diff --git a/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs b/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs index 33effd94b5..35dccfbb50 100644 --- a/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs @@ -100,7 +100,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views string csMessage = csIpcMessage.ToJsonString(); string outMessage = outIpcMessage.ToJsonString(); - if (!csMessage.Equals(outMessage)) + if (!csMessage.Equals(outMessage, StringComparison.Ordinal)) { Logger.LogInfo($"Saved Awake settings from Awake page."); _sendConfigMsg(outMessage); diff --git a/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs b/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs index fdaf5dec21..273fda48d3 100644 --- a/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs @@ -333,7 +333,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views IJsonValue whatToShowJson; if (json.TryGetValue("ShowYourself", out whatToShowJson)) { - if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("flyout")) + if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("flyout", StringComparison.Ordinal)) { POINT? p = null; @@ -351,7 +351,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views OpenFlyoutCallback(p); } - else if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("main_page")) + else if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("main_page", StringComparison.Ordinal)) { OpenMainWindowCallback(); } From e6767c8e8bb87e508646a2ac8a80c34f051b3900 Mon Sep 17 00:00:00 2001 From: Michael Clayton Date: Fri, 17 Mar 2023 18:12:27 +0000 Subject: [PATCH 098/163] [Mouse Jump] - screenshot performance (#24607) (#24630) * [Mouse Jump] - screenshot performance (#24607) * [Mouse Jump] - add words to spellchecker (#24607) * [Mouse Jump] - progressive activation (#24607) * [Mouse Jump] - fixing build (#24607) * [Mouse Jump] - fixing jump location, add unit tests, refactor (#24607) * [Mouse Jump] - removed testing code (#24607) * [Mouse Jump] added failing tests for * [Mouse Jump] - fix problem with some monitor layouts (#24607) * [Mouse Jump] - cleaning up some comments and namepsaces * [Mouse Jump] - added another screen layout test (#24607) --- .github/actions/spell-check/expect.txt | 25 + .../Drawing/RectangleInfoTests.cs | 141 +++++ .../Drawing/SizeInfoTests.cs | 105 ++++ .../Helpers/DrawingHelperTests.cs | 223 ++++++++ .../Helpers/LayoutHelperTests.cs | 514 ------------------ .../Helpers/MouseHelperTests.cs | 74 +++ .../Drawing/Models/LayoutConfig.cs | 109 ++++ .../MouseJumpUI/Drawing/Models/LayoutInfo.cs | 111 ++++ .../MouseJumpUI/Drawing/Models/PaddingInfo.cs | 65 +++ .../MouseJumpUI/Drawing/Models/PointInfo.cs | 50 ++ .../Drawing/Models/RectangleInfo.cs | 123 +++++ .../MouseJumpUI/Drawing/Models/SizeInfo.cs | 87 +++ .../MouseJumpUI/Helpers/DrawingHelper.cs | 237 ++++++++ .../MouseJumpUI/Helpers/LayoutHelper.cs | 191 ------- .../MouseUtils/MouseJumpUI/Helpers/Logger.cs | 5 +- .../MouseJumpUI/Helpers/MouseHelper.cs | 87 +++ .../MouseJumpUI/Helpers/NativeMethods.cs | 195 ++++--- .../MouseJumpUI/MainForm.Designer.cs | 2 +- .../MouseUtils/MouseJumpUI/MainForm.cs | 289 +++++----- .../MouseUtils/MouseJumpUI/MouseJumpUI.csproj | 1 + .../MouseJumpUI/NativeMethods/Core/BOOL.cs | 36 ++ .../MouseJumpUI/NativeMethods/Core/HDC.cs | 29 + .../MouseJumpUI/NativeMethods/Core/HWND.cs | 29 + .../NativeMethods/Gdi32/Gdi32.ROP_CODE.cs | 37 ++ .../Gdi32/Gdi32.STRETCH_BLT_MODE.cs | 26 + .../Gdi32/Gdi32.SetStretchBltMode.cs | 27 + .../NativeMethods/Gdi32/Gdi32.StretchBlt.cs | 38 ++ .../MouseJumpUI/NativeMethods/Libraries.cs | 11 + .../User32/User32.GetDesktopWindow.cs | 24 + .../User32/User32.GetWindowDC.cs | 31 ++ .../NativeMethods/User32/User32.ReleaseDC.cs | 28 + .../Gdi32/Gdi32.SetStretchBltMode.cs | 21 + .../NativeWrappers/Gdi32/Gdi32.StretchBlt.cs | 43 ++ .../User32/User32.GetDesktopWindow.cs | 16 + .../User32/User32.GetWindowDC.cs | 21 + .../NativeWrappers/User32/User32.ReleaseDC.cs | 21 + 36 files changed, 2121 insertions(+), 951 deletions(-) create mode 100644 src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/RectangleInfoTests.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/SizeInfoTests.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/LayoutHelperTests.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs delete mode 100644 src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/Libraries.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs create mode 100644 src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 4a557498d8..7246134353 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -49,6 +49,7 @@ angularsen Animatable ansicolor ANull +ANDSCANS AOC aocfnapldcnfbofgmbbllojgocaelgdd APARTMENTTHREADED @@ -139,7 +140,9 @@ BITMAPINFOHEADER bitmask BITSPIXEL bla +BLACKONWHITE Blockquotes +Blt blogs BLUEGRAY Bluetooth @@ -185,6 +188,7 @@ CALG callbackptr Cangjie CANRENAME +CAPTUREBLT CAPTURECHANGED CAtl cch @@ -258,6 +262,7 @@ colorformat colorhistory colorhistorylimit COLORKEY +COLORONCOLOR colorpicker COLORREF comctl @@ -305,6 +310,7 @@ CProj CREATESCHEDULEDTASK CREATESTRUCT CREATEWINDOWFAILED +createcompatibledc critsec Crossdevice CRSEL @@ -368,6 +374,7 @@ dcomp dcompi DComposition DCR +DCs DDevice ddf DDxgi @@ -388,6 +395,7 @@ DEFERERASE DEFPUSHBUTTON deinitialization DELA +DELETESCANS deletethis Delimarsky dend @@ -442,6 +450,7 @@ drawingcolor dreamsofameaningfullife drivedetectionwarning dshow +DSTINVERT dutil DVASPECT DVASPECTINFO @@ -1035,6 +1044,8 @@ Melman MENUITEMINFO MENUITEMINFOW menurc +MERGECOPY +MERGEPAINT Metadatas metafile mfapi @@ -1193,6 +1204,7 @@ NOINHERITLAYOUT NOINTERFACE NOLINKINFO NOMINMAX +NOMIRRORBITMAP NOMOVE NONAME nonclient @@ -1224,6 +1236,8 @@ notmatch Noto NOTOPMOST NOTRACK +NOTSRCCOPY +NOTSRCERASE NOUPDATE NOZORDER NPH @@ -1273,6 +1287,7 @@ openxmlformats OPTIMIZEFORINVOKE ORAW ORPHANEDDIALOGTITLE +ORSCANS oss ostr OSVERSIONINFOEX @@ -1304,8 +1319,11 @@ PArgb parray PARTIALCONFIRMATIONDIALOGTITLE pasteplain +PATCOPY pathcch Pathto +PATINVERT +PATPAINT PAUDIO pbc Pbgra @@ -1547,6 +1565,7 @@ Roamable robmensching Roboto rooler +rop roslyn Rothera roundf @@ -1716,8 +1735,12 @@ spsi spsia spsrm spsv +SRCAND SRCCOPY +SRCERASE Srch +SRCINVERT +SRCPAINT sre Srednekolymsk SResize @@ -2039,6 +2062,7 @@ website wekyb Wevtapi wgpocpl +WHITEONBLACK whitespaces WIC wifi @@ -2073,6 +2097,7 @@ winevt winexe winforms winfx +wingdi winget wingetcreate Winhook diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/RectangleInfoTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/RectangleInfoTests.cs new file mode 100644 index 0000000000..722b0e786d --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/RectangleInfoTests.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJumpUI.Drawing.Models; + +namespace MouseJumpUI.UnitTests.Drawing; + +[TestClass] +public static class RectangleInfoTests +{ + [TestClass] + public class CenterTests + { + public class TestCase + { + public TestCase(RectangleInfo rectangle, PointInfo point, RectangleInfo expectedResult) + { + this.Rectangle = rectangle; + this.Point = point; + this.ExpectedResult = expectedResult; + } + + public RectangleInfo Rectangle { get; set; } + + public PointInfo Point { get; set; } + + public RectangleInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // zero-sized + yield return new[] { new TestCase(new(0, 0, 0, 0), new(0, 0), new(0, 0, 0, 0)), }; + + // zero-origin + yield return new[] { new TestCase(new(0, 0, 200, 200), new(100, 100), new(0, 0, 200, 200)), }; + yield return new[] { new TestCase(new(0, 0, 200, 200), new(500, 500), new(400, 400, 200, 200)), }; + yield return new[] { new TestCase(new(0, 0, 800, 600), new(1000, 1000), new(600, 700, 800, 600)), }; + + // non-zero origin + yield return new[] { new TestCase(new(1000, 2000, 200, 200), new(100, 100), new(0, 0, 200, 200)), }; + yield return new[] { new TestCase(new(1000, 2000, 200, 200), new(500, 500), new(400, 400, 200, 200)), }; + yield return new[] { new TestCase(new(1000, 2000, 800, 600), new(1000, 1000), new(600, 700, 800, 600)), }; + + // negative result + yield return new[] { new TestCase(new(0, 0, 1000, 1200), new(300, 300), new(-200, -300, 1000, 1200)), }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = data.Rectangle.Center(data.Point); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.X, actual.X); + Assert.AreEqual(expected.Y, actual.Y); + Assert.AreEqual(expected.Width, actual.Width); + Assert.AreEqual(expected.Height, actual.Height); + } + } + + [TestClass] + public class ClampTests + { + public class TestCase + { + public TestCase(RectangleInfo inner, RectangleInfo outer, RectangleInfo expectedResult) + { + this.Inner = inner; + this.Outer = outer; + this.ExpectedResult = expectedResult; + } + + public RectangleInfo Inner { get; set; } + + public RectangleInfo Outer { get; set; } + + public RectangleInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // already inside - obj fills bounds exactly + yield return new[] + { + new TestCase(new(0, 0, 100, 100), new(0, 0, 100, 100), new(0, 0, 100, 100)), + }; + + // already inside - obj exactly in each corner + yield return new[] + { + new TestCase(new(0, 0, 100, 100), new(0, 0, 200, 200), new(0, 0, 100, 100)), + }; + yield return new[] + { + new TestCase(new(100, 0, 100, 100), new(0, 0, 200, 200), new(100, 0, 100, 100)), + }; + yield return new[] + { + new TestCase(new(0, 100, 100, 100), new(0, 0, 200, 200), new(0, 100, 100, 100)), + }; + yield return new[] + { + new TestCase(new(100, 100, 100, 100), new(0, 0, 200, 200), new(100, 100, 100, 100)), + }; + + // move inside - obj outside each corner + yield return new[] + { + new TestCase(new(-50, -50, 100, 100), new(0, 0, 200, 200), new(0, 0, 100, 100)), + }; + yield return new[] + { + new TestCase(new(250, -50, 100, 100), new(0, 0, 200, 200), new(100, 0, 100, 100)), + }; + yield return new[] + { + new TestCase(new(-50, 250, 100, 100), new(0, 0, 200, 200), new(0, 100, 100, 100)), + }; + yield return new[] + { + new TestCase(new(150, 150, 100, 100), new(0, 0, 200, 200), new(100, 100, 100, 100)), + }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = data.Inner.Clamp(data.Outer); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.X, actual.X); + Assert.AreEqual(expected.Y, actual.Y); + Assert.AreEqual(expected.Width, actual.Width); + Assert.AreEqual(expected.Height, actual.Height); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/SizeInfoTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/SizeInfoTests.cs new file mode 100644 index 0000000000..9410e66bb0 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Drawing/SizeInfoTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJumpUI.Drawing.Models; + +namespace MouseJumpUI.UnitTests.Drawing; + +public sealed class SizeInfoTests +{ + [TestClass] + public class ScaleToFitTests + { + public class TestCase + { + public TestCase(SizeInfo obj, SizeInfo bounds, SizeInfo expectedResult) + { + this.Obj = obj; + this.Bounds = bounds; + this.ExpectedResult = expectedResult; + } + + public SizeInfo Obj { get; set; } + + public SizeInfo Bounds { get; set; } + + public SizeInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // identity tests + yield return new[] { new TestCase(new(512, 384), new(512, 384), new(512, 384)), }; + yield return new[] { new TestCase(new(1024, 768), new(1024, 768), new(1024, 768)), }; + + // general tests + yield return new[] { new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536)), }; + yield return new[] { new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768)), }; + + // scale to fit width + yield return new[] { new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536)), }; + + // scale to fit height + yield return new[] { new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536)), }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = data.Obj.ScaleToFit(data.Bounds); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.Width, actual.Width); + Assert.AreEqual(expected.Height, actual.Height); + } + } + + [TestClass] + public class ScaleToFitRatioTests + { + public class TestCase + { + public TestCase(SizeInfo obj, SizeInfo bounds, decimal expectedResult) + { + this.Obj = obj; + this.Bounds = bounds; + this.ExpectedResult = expectedResult; + } + + public SizeInfo Obj { get; set; } + + public SizeInfo Bounds { get; set; } + + public decimal ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // identity tests + yield return new[] { new TestCase(new(512, 384), new(512, 384), 1), }; + yield return new[] { new TestCase(new(1024, 768), new(1024, 768), 1), }; + + // general tests + yield return new[] { new TestCase(new(512, 384), new(2048, 1536), 4), }; + yield return new[] { new TestCase(new(2048, 1536), new(1024, 768), 0.5M), }; + + // scale to fit width + yield return new[] { new TestCase(new(512, 384), new(2048, 3072), 4), }; + + // scale to fit height + yield return new[] { new TestCase(new(512, 384), new(4096, 1536), 4), }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = data.Obj.ScaleToFitRatio(data.Bounds); + var expected = data.ExpectedResult; + Assert.AreEqual(expected, actual); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs new file mode 100644 index 0000000000..cf2fb5562a --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs @@ -0,0 +1,223 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Drawing; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJumpUI.Drawing.Models; +using MouseJumpUI.Helpers; + +namespace MouseJumpUI.UnitTests.Helpers; + +[TestClass] +public static class DrawingHelperTests +{ + [TestClass] + public class CalculateLayoutInfoTests + { + public class TestCase + { + public TestCase(LayoutConfig layoutConfig, LayoutInfo expectedResult) + { + this.LayoutConfig = layoutConfig; + this.ExpectedResult = expectedResult; + } + + public LayoutConfig LayoutConfig { get; set; } + + public LayoutInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // happy path - check the preview form is shown + // at the correct size and position on a single screen + // + // +----------------+ + // | | + // | 0 | + // | | + // +----------------+ + var layoutConfig = new LayoutConfig( + virtualScreen: new(0, 0, 5120, 1440), + screenBounds: new List + { + new(0, 0, 5120, 1440), + }, + activatedLocation: new(5120 / 2, 1440 / 2), + activatedScreen: 0, + maximumFormSize: new(1600, 1200), + formPadding: new(5, 5, 5, 5), + previewPadding: new(0, 0, 0, 0)); + var layoutInfo = new LayoutInfo( + layoutConfig: layoutConfig, + formBounds: new(1760, 491.40625M, 1600, 457.1875M), + previewBounds: new(0, 0, 1590, 447.1875M), + screenBounds: new List + { + new(0, 0, 1590, 447.1875M), + }, + activatedScreen: new(0, 0, 5120, 1440)); + yield return new[] { new TestCase(layoutConfig, layoutInfo) }; + + // primary monitor not topmost / leftmost - if there are screens + // that are further left or higher than the primary monitor + // they'll have negative coordinates which has caused some + // issues with calculations in the past. this test will make + // sure we handle negative coordinates gracefully + // + // +-------+ + // | 0 +----------------+ + // +-------+ | + // | 1 | + // | | + // +----------------+ + layoutConfig = new LayoutConfig( + virtualScreen: new(-1920, -472, 7040, 1912), + screenBounds: new List + { + new(-1920, -472, 1920, 1080), + new(0, 0, 5120, 1440), + }, + activatedLocation: new(-960, -236), + activatedScreen: 0, + maximumFormSize: new(1600, 1200), + formPadding: new(5, 5, 5, 5), + previewPadding: new(0, 0, 0, 0)); + layoutInfo = new LayoutInfo( + layoutConfig: layoutConfig, + formBounds: new( + -1760, + -456.91477M, // -236 - (((decimal)(1600-10) / 7040 * 1912) + 10) / 2 + 1600, + 441.829545M // ((decimal)(1600-10) / 7040 * 1912) + 10 + ), + previewBounds: new(0, 0, 1590, 431.829545M), + screenBounds: new List + { + new(0, 0, 433.63636M, 243.92045M), + new(433.63636M, 106.602270M, 1156.36363M, 325.22727M), + }, + activatedScreen: new(-1920, -472, 1920, 1080)); + yield return new[] { new TestCase(layoutConfig, layoutInfo) }; + + // check we handle rounding errors in scaling the preview form + // that might make the form *larger* than the current screen - + // e.g. a desktop 7168 x 1440 scaled to a screen 1024 x 768 + // with a 5px form padding border: + // + // ((decimal)1014 / 7168) * 7168 = 1014.0000000000000000000000002 + // + // +----------------+ + // | | + // | 1 +-------+ + // | | 0 | + // +----------------+-------+ + layoutConfig = new LayoutConfig( + virtualScreen: new(0, 0, 7168, 1440), + screenBounds: new List + { + new(6144, 0, 1024, 768), + new(0, 0, 6144, 1440), + }, + activatedLocation: new(6656, 384), + activatedScreen: 0, + maximumFormSize: new(1600, 1200), + formPadding: new(5, 5, 5, 5), + previewPadding: new(0, 0, 0, 0)); + layoutInfo = new LayoutInfo( + layoutConfig: layoutConfig, + formBounds: new(6144, 277.14732M, 1024, 213.70535M), + previewBounds: new(0, 0, 1014, 203.70535M), + screenBounds: new List + { + new(869.14285M, 0, 144.85714M, 108.642857M), + new(0, 0, 869.142857M, 203.705357M), + }, + activatedScreen: new(6144, 0, 1024, 768)); + yield return new[] { new TestCase(layoutConfig, layoutInfo) }; + + // check we handle rounding errors in scaling the preview form + // that might make the form a pixel *smaller* than the current screen - + // e.g. a desktop 7168 x 1440 scaled to a screen 1024 x 768 + // with a 5px form padding border: + // + // ((decimal)1280 / 7424) * 7424 = 1279.9999999999999999999999999 + // + // +----------------+ + // | | + // | 1 +-------+ + // | | 0 | + // +----------------+-------+ + layoutConfig = new LayoutConfig( + virtualScreen: new(0, 0, 7424, 1440), + screenBounds: new List + { + new(6144, 0, 1280, 768), + new(0, 0, 6144, 1440), + }, + activatedLocation: new(6784, 384), + activatedScreen: 0, + maximumFormSize: new(1600, 1200), + formPadding: new(5, 5, 5, 5), + previewPadding: new(0, 0, 0, 0)); + layoutInfo = new LayoutInfo( + layoutConfig: layoutConfig, + formBounds: new( + 6144, + 255.83189M, // (768 - (((decimal)(1280-10) / 7424 * 1440) + 10)) / 2 + 1280, + 256.33620M // ((decimal)(1280 - 10) / 7424 * 1440) + 10 + ), + previewBounds: new(0, 0, 1270, 246.33620M), + screenBounds: new List + { + new(1051.03448M, 0, 218.96551M, 131.37931M), + new(0, 0M, 1051.03448M, 246.33620M), + }, + activatedScreen: new(6144, 0, 1280, 768)); + yield return new[] { new TestCase(layoutConfig, layoutInfo) }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + // note - even if values are within 0.0001M of each other they could + // still round to different values - e.g. + // (int)1279.999999999999 -> 1279 + // vs + // (int)1280.000000000000 -> 1280 + // so we'll compare the raw values, *and* convert to an int-based + // Rectangle to compare rounded values + var actual = DrawingHelper.CalculateLayoutInfo(data.LayoutConfig); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.FormBounds.X, actual.FormBounds.X, 0.00001M, "FormBounds.X"); + Assert.AreEqual(expected.FormBounds.Y, actual.FormBounds.Y, 0.00001M, "FormBounds.Y"); + Assert.AreEqual(expected.FormBounds.Width, actual.FormBounds.Width, 0.00001M, "FormBounds.Width"); + Assert.AreEqual(expected.FormBounds.Height, actual.FormBounds.Height, 0.00001M, "FormBounds.Height"); + Assert.AreEqual(expected.FormBounds.ToRectangle(), actual.FormBounds.ToRectangle(), "FormBounds.ToRectangle"); + Assert.AreEqual(expected.PreviewBounds.X, actual.PreviewBounds.X, 0.00001M, "PreviewBounds.X"); + Assert.AreEqual(expected.PreviewBounds.Y, actual.PreviewBounds.Y, 0.00001M, "PreviewBounds.Y"); + Assert.AreEqual(expected.PreviewBounds.Width, actual.PreviewBounds.Width, 0.00001M, "PreviewBounds.Width"); + Assert.AreEqual(expected.PreviewBounds.Height, actual.PreviewBounds.Height, 0.00001M, "PreviewBounds.Height"); + Assert.AreEqual(expected.PreviewBounds.ToRectangle(), actual.PreviewBounds.ToRectangle(), "PreviewBounds.ToRectangle"); + Assert.AreEqual(expected.ScreenBounds.Count, actual.ScreenBounds.Count, "ScreenBounds.Count"); + for (var i = 0; i < expected.ScreenBounds.Count; i++) + { + Assert.AreEqual(expected.ScreenBounds[i].X, actual.ScreenBounds[i].X, 0.00001M, $"ScreenBounds[{i}].X"); + Assert.AreEqual(expected.ScreenBounds[i].Y, actual.ScreenBounds[i].Y, 0.00001M, $"ScreenBounds[{i}].Y"); + Assert.AreEqual(expected.ScreenBounds[i].Width, actual.ScreenBounds[i].Width, 0.00001M, $"ScreenBounds[{i}].Width"); + Assert.AreEqual(expected.ScreenBounds[i].Height, actual.ScreenBounds[i].Height, 0.00001M, $"ScreenBounds[{i}].Height"); + Assert.AreEqual(expected.ScreenBounds[i].ToRectangle(), actual.ScreenBounds[i].ToRectangle(), "ActivatedScreen.ToRectangle"); + } + + Assert.AreEqual(expected.ActivatedScreen.X, actual.ActivatedScreen.X, "ActivatedScreen.X"); + Assert.AreEqual(expected.ActivatedScreen.Y, actual.ActivatedScreen.Y, "ActivatedScreen.Y"); + Assert.AreEqual(expected.ActivatedScreen.Width, actual.ActivatedScreen.Width, "ActivatedScreen.Width"); + Assert.AreEqual(expected.ActivatedScreen.Height, actual.ActivatedScreen.Height, "ActivatedScreen.Height"); + Assert.AreEqual(expected.ActivatedScreen.ToRectangle(), actual.ActivatedScreen.ToRectangle(), "ActivatedScreen.ToRectangle"); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/LayoutHelperTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/LayoutHelperTests.cs deleted file mode 100644 index 64db5b4491..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/LayoutHelperTests.cs +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Drawing; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace MouseJumpUI.Helpers.Tests; - -[TestClass] -public static class LayoutHelperTests -{ - [TestClass] - public class CenterObjectTests - { - public class TestCase - { - public TestCase(Size obj, Point midpoint, Point expectedResult) - { - this.Obj = obj; - this.Midpoint = midpoint; - this.ExpectedResult = expectedResult; - } - - public Size Obj { get; set; } - - public Point Midpoint { get; set; } - - public Point ExpectedResult { get; set; } - } - - public static IEnumerable GetTestCases() - { - // zero-sized object should center exactly on the midpoint - yield return new[] { new TestCase(new(0, 0), new(0, 0), new(0, 0)), }; - - // odd-sized objects should center above/left of the midpoint - yield return new[] { new TestCase(new(1, 1), new(1, 1), new(0, 0)), }; - yield return new[] { new TestCase(new(1, 1), new(5, 5), new(4, 4)), }; - - // even-sized objects should center exactly on the midpoint - yield return new[] { new TestCase(new(2, 2), new(1, 1), new(0, 0)), }; - yield return new[] { new TestCase(new(2, 2), new(5, 5), new(4, 4)), }; - yield return new[] { new TestCase(new(800, 600), new(1000, 1000), new(600, 700)), }; - - // negative result position - yield return new[] { new TestCase(new(1000, 1200), new(300, 300), new(-200, -300)), }; - } - - [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] - public void RunTestCases(TestCase data) - { - var actual = LayoutHelper.CenterObject(data.Obj, data.Midpoint); - var expected = data.ExpectedResult; - Assert.AreEqual(expected, actual); - } - } - - [TestClass] - public class CombineRegionsTests - { - public class TestCase - { - public TestCase(List bounds, Rectangle expectedResult) - { - this.Bounds = bounds; - this.ExpectedResult = expectedResult; - } - - public List Bounds { get; set; } - - public Rectangle ExpectedResult { get; set; } - } - - public static IEnumerable GetTestCases() - { - // empty list - yield return new[] - { - new TestCase( - new(), - Rectangle.Empty), - }; - - // empty bounds - yield return new[] - { - new TestCase( - new() - { - Rectangle.Empty, - }, - Rectangle.Empty), - }; - - // single region - // - // +---+ - // | 0 | - // +---+ - yield return new[] - { - new TestCase( - new() - { - new(100, 100, 100, 100), - }, - new(100, 100, 100, 100)), - }; - - // multi-monitor desktop - // - // +----------------+ - // | | - // | 1 +-------+ - // | | 0 | - // +----------------+-------+ - yield return new[] - { - new TestCase( - new() - { - new(5120, 0, 1920, 1080), - new(0, 0, 5120, 1440), - }, - new(0, 0, 7040, 1440)), - }; - - // multi-monitor desktop - // - // note - windows puts the *primary* monitor at the origin (0,0), - // so screens positioned *above* or *left* will have negative coordinates - // - // +-------+ - // | 0 | - // +-------+--------+ - // | | - // | 1 | - // | | - // +----------------+ - yield return new[] - { - new TestCase( - new() - { - new(0, -1000, 1920, 1080), - new(0, 0, 5120, 1440), - }, - new(0, -1000, 5120, 2440)), - }; - - // multi-monitor desktop - // - // note - windows puts the *primary* monitor at the origin (0,0), - // so screens positioned *above* or *left* will have negative coordinates - // - // +-------+----------------+ - // | 0 | | - // +-------+ 1 | - // | | - // +----------------+ - yield return new[] - { - new TestCase( - new() - { - new(-1920, 0, 1920, 1080), - new(0, 0, 5120, 1440), - }, - new(-1920, 0, 7040, 1440)), - }; - - // non-contiguous regions - // - // +---+ - // | 0 | +-------+ - // +---+ | | - // | 1 | - // | | - // +-------+ - yield return new[] - { - new TestCase( - new() - { - new(0, 0, 100, 100), - new(200, 150, 200, 200), - }, - new(0, 0, 400, 350)), - }; - } - - [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] - public void RunTestCases(TestCase data) - { - var actual = LayoutHelper.CombineRegions(data.Bounds); - var expected = data.ExpectedResult; - Assert.AreEqual(expected, actual); - } - } - - [TestClass] - public class GetMidpointTests - { - } - - [TestClass] - public class MoveInsideTests - { - public class TestCase - { - public TestCase(Rectangle obj, Rectangle bounds, Rectangle expectedResult) - { - this.Obj = obj; - this.Bounds = bounds; - this.ExpectedResult = expectedResult; - } - - public Rectangle Obj { get; set; } - - public Rectangle Bounds { get; set; } - - public Rectangle ExpectedResult { get; set; } - } - - public static IEnumerable GetTestCases() - { - // already inside - obj fills bounds exactly - yield return new[] - { - new TestCase(new(0, 0, 100, 100), new(0, 0, 100, 100), new(0, 0, 100, 100)), - }; - - // already inside - obj exactly in each corner - yield return new[] - { - new TestCase(new(0, 0, 100, 100), new(0, 0, 200, 200), new(0, 0, 100, 100)), - }; - yield return new[] - { - new TestCase(new(100, 0, 100, 100), new(0, 0, 200, 200), new(100, 0, 100, 100)), - }; - yield return new[] - { - new TestCase(new(0, 100, 100, 100), new(0, 0, 200, 200), new(0, 100, 100, 100)), - }; - yield return new[] - { - new TestCase(new(100, 100, 100, 100), new(0, 0, 200, 200), new(100, 100, 100, 100)), - }; - - // move inside - obj outside each corner - yield return new[] - { - new TestCase(new(-50, -50, 100, 100), new(0, 0, 200, 200), new(0, 0, 100, 100)), - }; - yield return new[] - { - new TestCase(new(250, -50, 100, 100), new(0, 0, 200, 200), new(100, 0, 100, 100)), - }; - yield return new[] - { - new TestCase(new(-50, 250, 100, 100), new(0, 0, 200, 200), new(0, 100, 100, 100)), - }; - yield return new[] - { - new TestCase(new(150, 150, 100, 100), new(0, 0, 200, 200), new(100, 100, 100, 100)), - }; - } - - [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] - public void RunTestCases(TestCase data) - { - var actual = LayoutHelper.MoveInside(data.Obj, data.Bounds); - var expected = data.ExpectedResult; - Assert.AreEqual(expected, actual); - } - } - - [TestClass] - public class ScaleLocationTests - { - } - - [TestClass] - public class ScaleToFitTests - { - public class TestCase - { - public TestCase(Size obj, Size bounds, Size expectedResult) - { - this.Obj = obj; - this.Bounds = bounds; - this.ExpectedResult = expectedResult; - } - - public Size Obj { get; set; } - - public Size Bounds { get; set; } - - public Size ExpectedResult { get; set; } - } - - public static IEnumerable GetTestCases() - { - // identity tests - yield return new[] - { - new TestCase(new(0, 0), new(0, 0), new(0, 0)), - }; - yield return new[] - { - new TestCase(new(512, 384), new(512, 384), new(512, 384)), - }; - yield return new[] - { - new TestCase(new(1024, 768), new(1024, 768), new(1024, 768)), - }; - - // integer scaling factor tests - yield return new[] - { - new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536)), - }; - yield return new[] - { - new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768)), - }; - - // scale to fit width - yield return new[] - { - new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536)), - }; - - // scale to fit height - yield return new[] - { - new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536)), - }; - } - - [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] - public void RunTestCases(TestCase data) - { - var actual = LayoutHelper.ScaleToFit(data.Obj, data.Bounds); - var expected = data.ExpectedResult; - Assert.AreEqual(expected, actual); - } - } - - [TestClass] - public class GetPreviewFormBoundsTests - { - public class TestCase - { - public TestCase( - Rectangle desktopBounds, - Point cursorPosition, - Rectangle currentMonitorBounds, - Size maximumPreviewImageSize, - Size previewImagePadding, - Rectangle expectedResult) - { - this.DesktopBounds = desktopBounds; - this.CursorPosition = cursorPosition; - this.CurrentMonitorBounds = currentMonitorBounds; - this.MaximumPreviewImageSize = maximumPreviewImageSize; - this.PreviewImagePadding = previewImagePadding; - this.ExpectedResult = expectedResult; - } - - public Rectangle DesktopBounds { get; set; } - - public Point CursorPosition { get; set; } - - public Rectangle CurrentMonitorBounds { get; set; } - - public Size MaximumPreviewImageSize { get; set; } - - public Size PreviewImagePadding { get; set; } - - public Rectangle ExpectedResult { get; set; } - } - - public static IEnumerable GetTestCases() - { - // multi-monitor desktop - // - // +----------------+ - // | | - // | 1 +-------+ - // | | 0 | - // +----------------+-------+ - // - // clicked near top left corner so that the - // preview box overhangs the top and left - // - // +----------------+ - // | * | - // | 1 +-------+ - // | | 0 | - // +----------------+-------+ - // - // form is centered on mouse cursor and then - // nudged back into the top left corner - // - // +-----+----------+ - // | * | | - // +-----+ 1 +-------+ - // | | 0 | - // +----------------+-------+ - yield return new[] - { - new TestCase( - desktopBounds: new(-5120, -359, 7040, 1440), - cursorPosition: new(-5020, -259), - currentMonitorBounds: new(-5120, -359, 5120, 1440), - maximumPreviewImageSize: new(1600, 1200), - previewImagePadding: new(10, 10), - expectedResult: new(-5120, -359, 1610, 337)), - }; - - // multi-monitor desktop - // - // +----------------+ - // | | - // | 1 +-------+ - // | | 0 | - // +----------------+-------+ - // - // clicked in the center of the second monitor - // - // +----------------+ - // | | - // | * +-------+ - // | | 0 | - // +----------------+-------+ - // - // form is centered on the mouse cursor - // - // +----------------+ - // | +-----+ | - // | | * | +-------+ - // | +-----+ | 0 | - // +----------------+-------+ - yield return new[] - { - new TestCase( - desktopBounds: new(-5120, -359, 7040, 1440), - cursorPosition: new(-2560, 361), - currentMonitorBounds: new(-5120, -359, 5120, 1440), - maximumPreviewImageSize: new(1600, 1200), - previewImagePadding: new(10, 10), - expectedResult: new(-3365, 192, 1610, 337)), - }; - - // multi-monitor desktop - // - // +----------------+ - // | | - // | 1 +-------+ - // | | 0 | - // +----------------+-------+ - // - // clicked in the center of the monitor - // - // +----------------+ - // | | - // | * +-------+ - // | | 0 | - // +----------------+-------+ - // - // max preview is larger than monitor, - // form is scaled to monitor size, with - // consideration for image padding - // - // *----------------* - // |+--------------+| - // || * |+-------+ - // |+--------------+| 0 | - // +----------------+-------+ - yield return new[] - { - new TestCase( - desktopBounds: new(-5120, -359, 7040, 1440), - cursorPosition: new(-2560, 361), - currentMonitorBounds: new(-5120, -359, 5120, 1440), - maximumPreviewImageSize: new(160000, 120000), - previewImagePadding: new(10, 10), - expectedResult: new(-5120, -166, 5120, 1055)), - }; - } - - [TestMethod] - [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] - public void RunTestCases(TestCase data) - { - var actual = LayoutHelper.GetPreviewFormBounds( - desktopBounds: data.DesktopBounds, - activatedPosition: data.CursorPosition, - activatedMonitorBounds: data.CurrentMonitorBounds, - maximumThumbnailImageSize: data.MaximumPreviewImageSize, - thumbnailImagePadding: data.PreviewImagePadding); - var expected = data.ExpectedResult; - Assert.AreEqual(expected, actual); - } - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs new file mode 100644 index 0000000000..c080ac6979 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJumpUI.Drawing.Models; +using MouseJumpUI.Helpers; + +namespace MouseJumpUI.UnitTests.Helpers; + +[TestClass] +public static class MouseHelperTests +{ + [TestClass] + public class GetJumpLocationTests + { + public class TestCase + { + public TestCase(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds, PointInfo expectedResult) + { + this.PreviewLocation = previewLocation; + this.PreviewSize = previewSize; + this.DesktopBounds = desktopBounds; + this.ExpectedResult = expectedResult; + } + + public PointInfo PreviewLocation { get; set; } + + public SizeInfo PreviewSize { get; set; } + + public RectangleInfo DesktopBounds { get; set; } + + public PointInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // screen corners and midpoint with a zero origin + yield return new[] { new TestCase(new(0, 0), new(160, 120), new(0, 0, 1600, 1200), new(0, 0)) }; + yield return new[] { new TestCase(new(160, 0), new(160, 120), new(0, 0, 1600, 1200), new(1600, 0)) }; + yield return new[] { new TestCase(new(0, 120), new(160, 120), new(0, 0, 1600, 1200), new(0, 1200)) }; + yield return new[] { new TestCase(new(160, 120), new(160, 120), new(0, 0, 1600, 1200), new(1600, 1200)) }; + yield return new[] { new TestCase(new(80, 60), new(160, 120), new(0, 0, 1600, 1200), new(800, 600)) }; + + // screen corners and midpoint with a positive origin + yield return new[] { new TestCase(new(0, 0), new(160, 120), new(1000, 1000, 1600, 1200), new(1000, 1000)) }; + yield return new[] { new TestCase(new(160, 0), new(160, 120), new(1000, 1000, 1600, 1200), new(2600, 1000)) }; + yield return new[] { new TestCase(new(0, 120), new(160, 120), new(1000, 1000, 1600, 1200), new(1000, 2200)) }; + yield return new[] { new TestCase(new(160, 120), new(160, 120), new(1000, 1000, 1600, 1200), new(2600, 2200)) }; + yield return new[] { new TestCase(new(80, 60), new(160, 120), new(1000, 1000, 1600, 1200), new(1800, 1600)) }; + + // screen corners and midpoint with a negative origin + yield return new[] { new TestCase(new(0, 0), new(160, 120), new(-1000, -1000, 1600, 1200), new(-1000, -1000)) }; + yield return new[] { new TestCase(new(160, 0), new(160, 120), new(-1000, -1000, 1600, 1200), new(600, -1000)) }; + yield return new[] { new TestCase(new(0, 120), new(160, 120), new(-1000, -1000, 1600, 1200), new(-1000, 200)) }; + yield return new[] { new TestCase(new(160, 120), new(160, 120), new(-1000, -1000, 1600, 1200), new(600, 200)) }; + yield return new[] { new TestCase(new(80, 60), new(160, 120), new(-1000, -1000, 1600, 1200), new(-200, -400)) }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = MouseHelper.GetJumpLocation( + data.PreviewLocation, + data.PreviewSize, + data.DesktopBounds); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.X, actual.X); + Assert.AreEqual(expected.Y, actual.Y); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs new file mode 100644 index 0000000000..bc80d991d4 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutConfig.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +namespace MouseJumpUI.Drawing.Models; + +/// +/// Represents a collection of values needed for calculating the MainForm layout. +/// +public sealed class LayoutConfig +{ + public LayoutConfig( + Rectangle virtualScreen, + IEnumerable screenBounds, + Point activatedLocation, + int activatedScreen, + Size maximumFormSize, + Padding formPadding, + Padding previewPadding) + { + // make sure the virtual screen entirely contains all of the individual screen bounds + ArgumentNullException.ThrowIfNull(screenBounds); + if (screenBounds.Any(screen => !virtualScreen.Contains(screen))) + { + throw new ArgumentException($"'{nameof(virtualScreen)}' must contain all of the screens in '{nameof(screenBounds)}'", nameof(virtualScreen)); + } + + this.VirtualScreen = new RectangleInfo(virtualScreen); + this.ScreenBounds = new( + screenBounds.Select(screen => new RectangleInfo(screen)).ToList()); + this.ActivatedLocation = new(activatedLocation); + this.ActivatedScreen = activatedScreen; + this.MaximumFormSize = new(maximumFormSize); + this.FormPadding = new(formPadding); + this.PreviewPadding = new(previewPadding); + } + + /// + /// Gets the coordinates of the entire virtual screen. + /// + /// + /// The Virtual Screen is the bounding rectangle of all the monitors. + /// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen + /// + public RectangleInfo VirtualScreen + { + get; + } + + /// + /// Gets the bounds of all of the screens connected to the system. + /// + public ReadOnlyCollection ScreenBounds + { + get; + } + + /// + /// Gets the point where the cursor was located when the form was activated. + /// + /// + /// The preview form will be centered on this location unless there are any + /// constraints such as the being too close to edge of a screen, in which case + /// the form will be displayed as close as possible to this location. + /// + public PointInfo ActivatedLocation + { + get; + } + + /// + /// Gets the index of the screen the cursor was on when the form was activated. + /// + public int ActivatedScreen + { + get; + } + + /// + /// Gets the maximum size of the screen preview form. + /// + public SizeInfo MaximumFormSize + { + get; + } + + /// + /// Gets the padding border around the screen preview form. + /// + public PaddingInfo FormPadding + { + get; + } + + /// + /// Gets the padding border inside the screen preview image. + /// + public PaddingInfo PreviewPadding + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs new file mode 100644 index 0000000000..30eeed17c6 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/LayoutInfo.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace MouseJumpUI.Drawing.Models; + +public sealed class LayoutInfo +{ + public sealed class Builder + { + public Builder() + { + this.ScreenBounds = new(); + } + + public LayoutConfig? LayoutConfig + { + get; + set; + } + + public RectangleInfo? FormBounds + { + get; + set; + } + + public RectangleInfo? PreviewBounds + { + get; + set; + } + + public List ScreenBounds + { + get; + set; + } + + public RectangleInfo? ActivatedScreen + { + get; + set; + } + + public LayoutInfo Build() + { + return new LayoutInfo( + layoutConfig: this.LayoutConfig ?? throw new InvalidOperationException(), + formBounds: this.FormBounds ?? throw new InvalidOperationException(), + previewBounds: this.PreviewBounds ?? throw new InvalidOperationException(), + screenBounds: this.ScreenBounds ?? throw new InvalidOperationException(), + activatedScreen: this.ActivatedScreen ?? throw new InvalidOperationException()); + } + } + + public LayoutInfo( + LayoutConfig layoutConfig, + RectangleInfo formBounds, + RectangleInfo previewBounds, + IEnumerable screenBounds, + RectangleInfo activatedScreen) + { + this.LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig)); + this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds)); + this.PreviewBounds = previewBounds ?? throw new ArgumentNullException(nameof(previewBounds)); + this.ScreenBounds = new( + (screenBounds ?? throw new ArgumentNullException(nameof(screenBounds))) + .ToList()); + this.ActivatedScreen = activatedScreen ?? throw new ArgumentNullException(nameof(activatedScreen)); + } + + /// + /// Gets the original LayoutConfig settings used to calculate coordinates. + /// + public LayoutConfig LayoutConfig + { + get; + } + + /// + /// Gets the size and location of the preview form. + /// + public RectangleInfo FormBounds + { + get; + } + + /// + /// Gets the size and location of the preview image. + /// + public RectangleInfo PreviewBounds + { + get; + } + + public ReadOnlyCollection ScreenBounds + { + get; + } + + public RectangleInfo ActivatedScreen + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs new file mode 100644 index 0000000000..2d7a5791b9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PaddingInfo.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows.Forms; + +namespace MouseJumpUI.Drawing.Models; + +/// +/// Immutable version of a System.Windows.Forms.Padding object with some extra utility methods. +/// +public sealed class PaddingInfo +{ + public PaddingInfo(decimal all) + : this(all, all, all, all) + { + } + + public PaddingInfo(Padding padding) + : this(padding.Left, padding.Top, padding.Right, padding.Bottom) + { + } + + public PaddingInfo(decimal left, decimal top, decimal right, decimal bottom) + { + this.Left = left; + this.Top = top; + this.Right = right; + this.Bottom = bottom; + } + + public decimal Left + { + get; + } + + public decimal Top + { + get; + } + + public decimal Right + { + get; + } + + public decimal Bottom + { + get; + } + + public decimal Horizontal => this.Left + this.Right; + + public decimal Vertical => this.Top + this.Bottom; + + public override string ToString() + { + return "{" + + $"{nameof(this.Left)}={this.Left}," + + $"{nameof(this.Top)}={this.Top}," + + $"{nameof(this.Right)}={this.Right}," + + $"{nameof(this.Bottom)}={this.Bottom}" + + "}"; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs new file mode 100644 index 0000000000..1e8481df4c --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/PointInfo.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; + +namespace MouseJumpUI.Drawing.Models; + +/// +/// Immutable version of a System.Drawing.Point object with some extra utility methods. +/// +public sealed class PointInfo +{ + public PointInfo(decimal x, decimal y) + { + this.X = x; + this.Y = y; + } + + public PointInfo(Point point) + : this(point.X, point.Y) + { + } + + public decimal X + { + get; + } + + public decimal Y + { + get; + } + + public SizeInfo Size => new((int)this.X, (int)this.Y); + + public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor); + + public PointInfo Offset(PointInfo amount) => new(this.X + amount.X, this.Y + amount.Y); + + public Point ToPoint() => new((int)this.X, (int)this.Y); + + public override string ToString() + { + return "{" + + $"{nameof(this.X)}={this.X}," + + $"{nameof(this.Y)}={this.Y}" + + "}"; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs new file mode 100644 index 0000000000..ebfaeb0b9e --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/RectangleInfo.cs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Drawing; + +namespace MouseJumpUI.Drawing.Models; + +/// +/// Immutable version of a System.Drawing.Rectangle object with some extra utility methods. +/// +public sealed class RectangleInfo +{ + public RectangleInfo(decimal x, decimal y, decimal width, decimal height) + { + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + } + + public RectangleInfo(Rectangle rectangle) + : this(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height) + { + } + + public RectangleInfo(Point location, SizeInfo size) + : this(location.X, location.Y, size.Width, size.Height) + { + } + + public RectangleInfo(SizeInfo size) + : this(0, 0, size.Width, size.Height) + { + } + + public decimal X + { + get; + } + + public decimal Y + { + get; + } + + public decimal Width + { + get; + } + + public decimal Height + { + get; + } + + public decimal Left => this.X; + + public decimal Top => this.Y; + + public decimal Right => this.X + this.Width; + + public decimal Bottom => this.Y + this.Height; + + public SizeInfo Size => new(this.Width, this.Height); + + public PointInfo Location => new(this.X, this.Y); + + public decimal Area => this.Width * this.Height; + + public RectangleInfo Enlarge(PaddingInfo padding) => new( + this.X + padding.Left, + this.Y + padding.Top, + this.Width + padding.Horizontal, + this.Height + padding.Vertical); + + public RectangleInfo Offset(SizeInfo amount) => this.Offset(amount.Width, amount.Height); + + public RectangleInfo Offset(decimal dx, decimal dy) => new(this.X + dx, this.Y + dy, this.Width, this.Height); + + public RectangleInfo Scale(decimal scalingFactor) => new( + this.X * scalingFactor, + this.Y * scalingFactor, + this.Width * scalingFactor, + this.Height * scalingFactor); + + public RectangleInfo Center(PointInfo point) => new( + x: point.X - (this.Width / 2), + y: point.Y - (this.Height / 2), + width: this.Width, + height: this.Height); + + public PointInfo Midpoint => new( + x: this.X + (this.Width / 2), + y: this.Y + (this.Height / 2)); + + public RectangleInfo Clamp(RectangleInfo outer) + { + if ((this.Width > outer.Width) || (this.Height > outer.Height)) + { + throw new ArgumentException($"Value cannot be larger than {nameof(outer)}."); + } + + return new( + x: Math.Clamp(this.X, outer.X, outer.Right - this.Width), + y: Math.Clamp(this.Y, outer.Y, outer.Bottom - this.Height), + width: this.Width, + height: this.Height); + } + + public Rectangle ToRectangle() => new((int)this.X, (int)this.Y, (int)this.Width, (int)this.Height); + + public override string ToString() + { + return "{" + + $"{nameof(this.Left)}={this.Left}," + + $"{nameof(this.Top)}={this.Top}," + + $"{nameof(this.Width)}={this.Width}," + + $"{nameof(this.Height)}={this.Height}" + + "}"; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs new file mode 100644 index 0000000000..4167c89df5 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Drawing/Models/SizeInfo.cs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Drawing; + +namespace MouseJumpUI.Drawing.Models; + +/// +/// Immutable version of a System.Drawing.Size object with some extra utility methods. +/// +public sealed class SizeInfo +{ + public SizeInfo(decimal width, decimal height) + { + this.Width = width; + this.Height = height; + } + + public SizeInfo(Size size) + : this(size.Width, size.Height) + { + } + + public decimal Width + { + get; + } + + public decimal Height + { + get; + } + + public SizeInfo Negate() => new(-this.Width, -this.Height); + + public SizeInfo Shrink(PaddingInfo padding) => new(this.Width - padding.Horizontal, this.Height - padding.Vertical); + + public SizeInfo Intersect(SizeInfo size) => new( + Math.Min(this.Width, size.Width), + Math.Min(this.Height, size.Height)); + + public RectangleInfo PlaceAt(decimal x, decimal y) => new(x, y, this.Width, this.Height); + + public SizeInfo ScaleToFit(SizeInfo bounds) + { + var widthRatio = bounds.Width / this.Width; + var heightRatio = bounds.Height / this.Height; + return widthRatio.CompareTo(heightRatio) switch + { + < 0 => new(bounds.Width, this.Height * widthRatio), + 0 => bounds, + > 0 => new(this.Width * heightRatio, bounds.Height), + }; + } + + /// + /// Get the scaling ratio to scale obj by so that it fits inside the specified bounds + /// without distorting the aspect ratio. + /// + public decimal ScaleToFitRatio(SizeInfo bounds) + { + if (bounds.Width == 0 || bounds.Height == 0) + { + throw new ArgumentException($"{nameof(bounds.Width)} or {nameof(bounds.Height)} cannot be zero", nameof(bounds)); + } + + var widthRatio = bounds.Width / this.Width; + var heightRatio = bounds.Height / this.Height; + var scalingRatio = Math.Min(widthRatio, heightRatio); + + return scalingRatio; + } + + public Size ToSize() => new((int)this.Width, (int)this.Height); + + public Point ToPoint() => new((int)this.Width, (int)this.Height); + + public override string ToString() + { + return "{" + + $"{nameof(this.Width)}={this.Width}," + + $"{nameof(this.Height)}={this.Height}" + + "}"; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs new file mode 100644 index 0000000000..bc99d3a7f8 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs @@ -0,0 +1,237 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Windows.Forms; +using MouseJumpUI.Drawing.Models; +using MouseJumpUI.NativeMethods.Core; +using MouseJumpUI.NativeWrappers; + +namespace MouseJumpUI.Helpers; + +internal static class DrawingHelper +{ + public static LayoutInfo CalculateLayoutInfo( + LayoutConfig layoutConfig) + { + if (layoutConfig is null) + { + throw new ArgumentNullException(nameof(layoutConfig)); + } + + var builder = new LayoutInfo.Builder + { + LayoutConfig = layoutConfig, + }; + + builder.ActivatedScreen = layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen]; + + // work out the maximum *constrained* form size + // * can't be bigger than the activated screen + // * can't be bigger than the max form size + var maxFormSize = builder.ActivatedScreen.Size + .Intersect(layoutConfig.MaximumFormSize); + + // the drawing area for screen images is inside the + // form border and inside the preview border + var maxDrawingSize = maxFormSize + .Shrink(layoutConfig.FormPadding) + .Shrink(layoutConfig.PreviewPadding); + + // scale the virtual screen to fit inside the drawing bounds + var scalingRatio = layoutConfig.VirtualScreen.Size + .ScaleToFitRatio(maxDrawingSize); + + // position the drawing bounds inside the preview border + var drawingBounds = layoutConfig.VirtualScreen.Size + .ScaleToFit(maxDrawingSize) + .PlaceAt(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top); + + // now we know the size of the drawing area we can work out the preview size + builder.PreviewBounds = drawingBounds.Enlarge(layoutConfig.PreviewPadding); + + // ... and the form size + // * center the form to the activated position, but nudge it back + // inside the visible area of the activated screen if it falls outside + builder.FormBounds = builder.PreviewBounds.Size + .PlaceAt(0, 0) + .Enlarge(layoutConfig.FormPadding) + .Center(layoutConfig.ActivatedLocation) + .Clamp(builder.ActivatedScreen); + + // now calculate the positions of each of the screen images on the preview + builder.ScreenBounds = layoutConfig.ScreenBounds + .Select( + screen => screen + .Offset(layoutConfig.VirtualScreen.Location.Size.Negate()) + .Scale(scalingRatio) + .Offset(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top)) + .ToList(); + + return builder.Build(); + } + + /// + /// Resize and position the specified form. + /// + public static void PositionForm( + Form form, RectangleInfo formBounds) + { + // note - do this in two steps rather than "this.Bounds = formBounds" as there + // appears to be an issue in WinForms with dpi scaling even when using PerMonitorV2, + // where the form scaling uses either the *primary* screen scaling or the *previous* + // screen's scaling when the form is moved to a different screen. i've got no idea + // *why*, but the exact sequence of calls below seems to be a workaround... + // see https://github.com/mikeclayton/FancyMouse/issues/2 + var bounds = formBounds.ToRectangle(); + form.Location = bounds.Location; + _ = form.PointToScreen(Point.Empty); + form.Size = bounds.Size; + } + + /// + /// Draw the preview background. + /// + public static void DrawPreviewBackground( + Graphics previewGraphics, RectangleInfo previewBounds, IEnumerable screenBounds) + { + using var backgroundBrush = new LinearGradientBrush( + previewBounds.Location.ToPoint(), + previewBounds.Size.ToPoint(), + Color.FromArgb(13, 87, 210), // light blue + Color.FromArgb(3, 68, 192)); // darker blue + + // it's faster to build a region with the screen areas excluded + // and fill that than it is to fill the entire bounding rectangle + var backgroundRegion = new Region(previewBounds.ToRectangle()); + foreach (var screen in screenBounds) + { + backgroundRegion.Exclude(screen.ToRectangle()); + } + + previewGraphics.FillRegion(backgroundBrush, backgroundRegion); + } + + public static void EnsureDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc) + { + if (desktopHwnd.IsNull) + { + desktopHwnd = User32.GetDesktopWindow(); + } + + if (desktopHdc.IsNull) + { + desktopHdc = User32.GetWindowDC(desktopHwnd); + } + } + + public static void FreeDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc) + { + if (!desktopHwnd.IsNull && !desktopHdc.IsNull) + { + _ = User32.ReleaseDC(desktopHwnd, desktopHdc); + } + + desktopHwnd = HWND.Null; + desktopHdc = HDC.Null; + } + + /// + /// Checks if the device context handle exists, and creates a new one from the + /// Graphics object if not. + /// + public static void EnsurePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc) + { + if (previewHdc.IsNull) + { + previewHdc = new HDC(previewGraphics.GetHdc()); + _ = Gdi32.SetStretchBltMode(previewHdc, MouseJumpUI.NativeMethods.Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE); + } + } + + /// + /// Free the specified device context handle if it exists. + /// + public static void FreePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc) + { + if ((previewGraphics is not null) && !previewHdc.IsNull) + { + previewGraphics.ReleaseHdc(previewHdc.Value); + previewHdc = HDC.Null; + } + } + + /// + /// Draw placeholder images for any non-activated screens on the preview. + /// Will release the specified device context handle if it needs to draw anything. + /// + public static void DrawPreviewPlaceholders( + Graphics previewGraphics, IEnumerable screenBounds) + { + // we can exclude the activated screen because we've already draw + // the screen capture image for that one on the preview + if (screenBounds.Any()) + { + var brush = Brushes.Black; + previewGraphics.FillRectangles(brush, screenBounds.Select(screen => screen.ToRectangle()).ToArray()); + } + } + + /// + /// Draws screen captures from the specified desktop handle onto the target device context. + /// + public static void DrawPreviewScreen( + HDC sourceHdc, + HDC targetHdc, + RectangleInfo sourceBounds, + RectangleInfo targetBounds) + { + var source = sourceBounds.ToRectangle(); + var target = targetBounds.ToRectangle(); + _ = Gdi32.StretchBlt( + targetHdc, + target.X, + target.Y, + target.Width, + target.Height, + sourceHdc, + source.X, + source.Y, + source.Width, + source.Height, + MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY); + } + + /// + /// Draws screen captures from the specified desktop handle onto the target device context. + /// + public static void DrawPreviewScreens( + HDC sourceHdc, + HDC targetHdc, + IList sourceBounds, + IList targetBounds) + { + for (var i = 0; i < sourceBounds.Count; i++) + { + var source = sourceBounds[i].ToRectangle(); + var target = targetBounds[i].ToRectangle(); + _ = Gdi32.StretchBlt( + targetHdc, + target.X, + target.Y, + target.Width, + target.Height, + sourceHdc, + source.X, + source.Y, + source.Width, + source.Height, + MouseJumpUI.NativeMethods.Gdi32.ROP_CODE.SRCCOPY); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs deleted file mode 100644 index 3fafa63c3c..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; - -namespace MouseJumpUI.Helpers; - -internal static class LayoutHelper -{ - /// - /// Center an object on the given origin. - /// - public static Point CenterObject(Size obj, Point origin) - { - return new Point( - x: (int)(origin.X - ((float)obj.Width / 2)), - y: (int)(origin.Y - ((float)obj.Height / 2))); - } - - /// - /// Combines the specified regions and returns the smallest rectangle that contains them. - /// - /// The regions to combine. - /// - /// Returns the smallest rectangle that contains all the specified regions. - /// - public static Rectangle CombineRegions(List regions) - { - if (regions == null) - { - throw new ArgumentNullException(nameof(regions)); - } - - if (regions.Count == 0) - { - return Rectangle.Empty; - } - - var combined = regions.Aggregate( - seed: regions[0], - func: Rectangle.Union); - return combined; - } - - /// - /// Returns the midpoint of the given region. - /// - public static Point GetMidpoint(Rectangle region) - { - return new Point( - (region.Left + region.Right) / 2, - (region.Top + region.Bottom) / 2); - } - - /// - /// Returns the largest Size object that can fit inside - /// all of the given sizes. (Equivalent to a Size - /// object with the smallest Width and smallest Height from - /// all of the specified sizes). - /// - public static Size IntersectSizes(params Size[] sizes) - { - return new Size( - sizes.Min(s => s.Width), - sizes.Min(s => s.Height)); - } - - /// - /// Returns the location to move the inner rectangle so that it sits entirely inside - /// the outer rectangle. Returns the inner rectangle's current position if it is - /// already inside the outer rectangle. - /// - public static Rectangle MoveInside(Rectangle inner, Rectangle outer) - { - if ((inner.Width > outer.Width) || (inner.Height > outer.Height)) - { - throw new ArgumentException($"{nameof(inner)} cannot be larger than {nameof(outer)}."); - } - - return inner with - { - X = Math.Clamp(inner.X, outer.X, outer.Right - inner.Width), - Y = Math.Clamp(inner.Y, outer.Y, outer.Bottom - inner.Height), - }; - } - - /// - /// Scales a location within a reference region onto a new region - /// so that it's proportionally in the same position in the new region. - /// - public static Point ScaleLocation(Rectangle originalBounds, Point originalLocation, Rectangle scaledBounds) - { - return new Point( - (int)(originalLocation.X / (double)originalBounds.Width * scaledBounds.Width) + scaledBounds.Left, - (int)(originalLocation.Y / (double)originalBounds.Height * scaledBounds.Height) + scaledBounds.Top); - } - - /// - /// Scale an object to fit inside the specified bounds while maintaining aspect ratio. - /// - public static Size ScaleToFit(Size obj, Size bounds) - { - if (bounds.Width == 0 || bounds.Height == 0) - { - return Size.Empty; - } - - var widthRatio = (double)obj.Width / bounds.Width; - var heightRatio = (double)obj.Height / bounds.Height; - var scaledSize = (widthRatio > heightRatio) - ? bounds with - { - Height = (int)(obj.Height / widthRatio), - } - : bounds with - { - Width = (int)(obj.Width / heightRatio), - }; - return scaledSize; - } - - /// - /// Calculates the position to show the preview form based on a number of factors. - /// - /// - /// The bounds of the entire desktop / virtual screen. Might start at a negative - /// x, y if a non-primary screen is located left of or above the primary screen. - /// - /// - /// The current position of the cursor on the virtual desktop. - /// - /// - /// The bounds of the screen the cursor is currently on. Might start at a negative - /// x, y if a non-primary screen is located left of or above the primary screen. - /// - /// - /// The largest allowable size of the preview image. This is literally the just - /// image itself, not including padding around the image. - /// - /// - /// The total width and height of padding around the preview image. - /// - /// - /// The size and location to use when showing the preview image form. - /// - public static Rectangle GetPreviewFormBounds( - Rectangle desktopBounds, - Point activatedPosition, - Rectangle activatedMonitorBounds, - Size maximumThumbnailImageSize, - Size thumbnailImagePadding) - { - // see https://learn.microsoft.com/en-gb/windows/win32/gdi/the-virtual-screen - // calculate the maximum size the form is allowed to be - var maxFormSize = LayoutHelper.IntersectSizes( - new[] - { - // can't be bigger than the current screen - activatedMonitorBounds.Size, - - // can't be bigger than the max preview image - // *plus* the padding around the preview image - // (max thumbnail image size doesn't include the padding) - maximumThumbnailImageSize + thumbnailImagePadding, - }); - - // calculate the actual form size by scaling the entire - // desktop bounds into the max thumbnail size while accounting - // for the size of the padding around the preview - var thumbnailImageSize = LayoutHelper.ScaleToFit( - obj: desktopBounds.Size, - bounds: maxFormSize - thumbnailImagePadding); - var formSize = thumbnailImageSize + thumbnailImagePadding; - - // center the form to the activated position, but nudge it back - // inside the visible area of the screen if it falls outside - var formBounds = LayoutHelper.MoveInside( - inner: new Rectangle( - LayoutHelper.CenterObject( - obj: formSize, - origin: activatedPosition), - formSize), - outer: activatedMonitorBounds); - - return formBounds; - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/Logger.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/Logger.cs index a1d4d0f9af..84e2c29f70 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/Logger.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/Logger.cs @@ -67,10 +67,9 @@ namespace MouseJumpUI.Helpers private static string GetCallerInfo() { - StackTrace stackTrace = new StackTrace(); - + var stackTrace = new StackTrace(); var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; + var className = methodName?.DeclaringType?.Name; return "[Method]: " + methodName?.Name + " [Class]: " + className; } } diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs new file mode 100644 index 0000000000..d96f2eabd9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; +using System.Windows.Forms; +using MouseJumpUI.Drawing.Models; + +namespace MouseJumpUI.Helpers; + +internal static class MouseHelper +{ + /// + /// Calculates where to move the cursor to by projecting a point from + /// the preview image onto the desktop and using that as the target location. + /// + /// + /// The preview image origin is (0, 0) but the desktop origin may be non-zero, + /// or even negative if the primary monitor is not the at the top-left of the + /// entire desktop rectangle, so results may contain negative coordinates. + /// + public static PointInfo GetJumpLocation(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds) + { + return previewLocation + .Scale(previewSize.ScaleToFitRatio(desktopBounds.Size)) + .Offset(desktopBounds.Location); + } + + /// + /// Moves the cursor to the specified location. + /// + /// + /// See https://github.com/mikeclayton/FancyMouse/pull/3 + /// + public static void JumpCursor(PointInfo location) + { + // set the new cursor position *twice* - the cursor sometimes end up in + // the wrong place if we try to cross the dead space between non-aligned + // monitors - e.g. when trying to move the cursor from (a) to (b) we can + // *sometimes* - for no clear reason - end up at (c) instead. + // + // +----------------+ + // |(c) (b) | + // | | + // | | + // | | + // +---------+ | + // | (a) | | + // +---------+----------------+ + // + // setting the position a second time seems to fix this and moves the + // cursor to the expected location (b) + var point = location.ToPoint(); + Cursor.Position = point; + Cursor.Position = point; + } + + /// + /// Sends an input simulating an absolute mouse move to the new location. + /// + /// + /// See https://github.com/microsoft/PowerToys/issues/24523 + /// https://github.com/microsoft/PowerToys/pull/24527 + /// + public static void SimulateMouseMovementEvent(Point location) + { + var mouseMoveInput = new NativeMethods.INPUT + { + type = NativeMethods.INPUTTYPE.INPUT_MOUSE, + data = new NativeMethods.InputUnion + { + mi = new NativeMethods.MOUSEINPUT + { + dx = NativeMethods.CalculateAbsoluteCoordinateX(location.X), + dy = NativeMethods.CalculateAbsoluteCoordinateY(location.Y), + mouseData = 0, + dwFlags = (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_MOVE + | (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_ABSOLUTE, + time = 0, + dwExtraInfo = 0, + }, + }, + }; + var inputs = new NativeMethods.INPUT[] { mouseMoveInput }; + _ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs index 9627c27e04..bdc27b3c95 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/NativeMethods.cs @@ -5,108 +5,107 @@ using System; using System.Runtime.InteropServices; -namespace MouseJumpUI.Helpers +namespace MouseJumpUI.Helpers; + +// Win32 functions required for temporary workaround for issue #1273 +internal static class NativeMethods { - // Win32 functions required for temporary workaround for issue #1273 - internal class NativeMethods + [DllImport("user32.dll")] + internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); + + [DllImport("user32.dll")] + internal static extern int GetSystemMetrics(SystemMetric smIndex); + + [StructLayout(LayoutKind.Sequential)] + public struct INPUT { - [DllImport("user32.dll")] - internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); + internal INPUTTYPE type; + internal InputUnion data; - [DllImport("user32.dll")] - internal static extern int GetSystemMetrics(SystemMetric smIndex); - - [StructLayout(LayoutKind.Sequential)] - public struct INPUT + internal static int Size { - internal INPUTTYPE type; - internal InputUnion data; - - internal static int Size - { - get { return Marshal.SizeOf(typeof(INPUT)); } - } - } - - [StructLayout(LayoutKind.Explicit)] - internal struct InputUnion - { - [FieldOffset(0)] - internal MOUSEINPUT mi; - [FieldOffset(0)] - internal KEYBDINPUT ki; - [FieldOffset(0)] - internal HARDWAREINPUT hi; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct MOUSEINPUT - { - internal int dx; - internal int dy; - internal int mouseData; - internal uint dwFlags; - internal uint time; - internal UIntPtr dwExtraInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct KEYBDINPUT - { - internal short wVk; - internal short wScan; - internal uint dwFlags; - internal int time; - internal UIntPtr dwExtraInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HARDWAREINPUT - { - internal int uMsg; - internal short wParamL; - internal short wParamH; - } - - internal enum INPUTTYPE : uint - { - INPUT_MOUSE = 0, - INPUT_KEYBOARD = 1, - INPUT_HARDWARE = 2, - } - - internal enum MOUSE_INPUT_FLAGS : uint - { - MOUSEEVENTF_MOVE = 0x0001, - MOUSEEVENTF_LEFTDOWN = 0x0002, - MOUSEEVENTF_LEFTUP = 0x0004, - MOUSEEVENTF_RIGHTDOWN = 0x0008, - MOUSEEVENTF_RIGHTUP = 0x0010, - MOUSEEVENTF_MIDDLEDOWN = 0x0020, - MOUSEEVENTF_MIDDLEUP = 0x0040, - MOUSEEVENTF_XDOWN = 0x0080, - MOUSEEVENTF_XUP = 0x0100, - MOUSEEVENTF_WHEEL = 0x0800, - MOUSEEVENTF_HWHEEL = 0x1000, - MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000, - MOUSEEVENTF_VIRTUALDESK = 0x4000, - MOUSEEVENTF_ABSOLUTE = 0x8000, - } - - internal enum SystemMetric - { - SM_CXSCREEN = 0, - SM_CYSCREEN = 1, - } - - internal static int CalculateAbsoluteCoordinateX(int x) - { - return (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN); - } - - internal static int CalculateAbsoluteCoordinateY(int y) - { - return (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN); + get { return Marshal.SizeOf(typeof(INPUT)); } } } + + [StructLayout(LayoutKind.Explicit)] + internal struct InputUnion + { + [FieldOffset(0)] + internal MOUSEINPUT mi; + [FieldOffset(0)] + internal KEYBDINPUT ki; + [FieldOffset(0)] + internal HARDWAREINPUT hi; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MOUSEINPUT + { + internal int dx; + internal int dy; + internal int mouseData; + internal uint dwFlags; + internal uint time; + internal UIntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct KEYBDINPUT + { + internal short wVk; + internal short wScan; + internal uint dwFlags; + internal int time; + internal UIntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct HARDWAREINPUT + { + internal int uMsg; + internal short wParamL; + internal short wParamH; + } + + internal enum INPUTTYPE : uint + { + INPUT_MOUSE = 0, + INPUT_KEYBOARD = 1, + INPUT_HARDWARE = 2, + } + + internal enum MOUSE_INPUT_FLAGS : uint + { + MOUSEEVENTF_MOVE = 0x0001, + MOUSEEVENTF_LEFTDOWN = 0x0002, + MOUSEEVENTF_LEFTUP = 0x0004, + MOUSEEVENTF_RIGHTDOWN = 0x0008, + MOUSEEVENTF_RIGHTUP = 0x0010, + MOUSEEVENTF_MIDDLEDOWN = 0x0020, + MOUSEEVENTF_MIDDLEUP = 0x0040, + MOUSEEVENTF_XDOWN = 0x0080, + MOUSEEVENTF_XUP = 0x0100, + MOUSEEVENTF_WHEEL = 0x0800, + MOUSEEVENTF_HWHEEL = 0x1000, + MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000, + MOUSEEVENTF_VIRTUALDESK = 0x4000, + MOUSEEVENTF_ABSOLUTE = 0x8000, + } + + internal enum SystemMetric + { + SM_CXSCREEN = 0, + SM_CYSCREEN = 1, + } + + internal static int CalculateAbsoluteCoordinateX(int x) + { + return (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN); + } + + internal static int CalculateAbsoluteCoordinateY(int y) + { + return (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN); + } } diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs index 073ffbf468..8a8fa9110a 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs @@ -60,7 +60,7 @@ partial class MainForm Thumbnail.Location = new System.Drawing.Point(5, 5); Thumbnail.Name = "Thumbnail"; Thumbnail.Size = new System.Drawing.Size(790, 440); - Thumbnail.SizeMode = PictureBoxSizeMode.StretchImage; + Thumbnail.SizeMode = PictureBoxSizeMode.Normal; Thumbnail.TabIndex = 1; Thumbnail.TabStop = false; Thumbnail.Click += Thumbnail_Click; diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs index a7bfbf8067..542046147c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs @@ -3,11 +3,14 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Windows.Forms; +using MouseJumpUI.Drawing.Models; using MouseJumpUI.Helpers; +using MouseJumpUI.NativeMethods.Core; namespace MouseJumpUI; @@ -33,172 +36,170 @@ internal partial class MainForm : Form private void MainForm_Deactivate(object sender, EventArgs e) { - // dispose the existing image if there is one - if (Thumbnail.Image != null) - { - Thumbnail.Image.Dispose(); - Thumbnail.Image = null; - } - this.Close(); - } - // Sends an input simulating an absolute mouse move to the new location. - private void SimulateMouseMovementEvent(Point location) - { - NativeMethods.INPUT mouseMoveInput = new NativeMethods.INPUT - { - type = NativeMethods.INPUTTYPE.INPUT_MOUSE, - data = new NativeMethods.InputUnion - { - mi = new NativeMethods.MOUSEINPUT - { - dx = NativeMethods.CalculateAbsoluteCoordinateX(location.X), - dy = NativeMethods.CalculateAbsoluteCoordinateY(location.Y), - mouseData = 0, - dwFlags = (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_MOVE | (uint)NativeMethods.MOUSE_INPUT_FLAGS.MOUSEEVENTF_ABSOLUTE, - time = 0, - dwExtraInfo = 0, - }, - }, - }; - NativeMethods.INPUT[] inputs = new NativeMethods.INPUT[] { mouseMoveInput }; - _ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size); - } - - private void Thumbnail_Click(object sender, EventArgs e) - { - var mouseEventArgs = (MouseEventArgs)e; - Logger.LogInfo($"Reporting mouse event args \n\tbutton = {mouseEventArgs.Button}\n\tlocation = {mouseEventArgs.Location} "); - - if (mouseEventArgs.Button == MouseButtons.Left) - { - // plain click - move mouse pointer - var desktopBounds = LayoutHelper.CombineRegions( - Screen.AllScreens.Select( - screen => screen.Bounds).ToList()); - Logger.LogInfo($"desktop bounds = {desktopBounds}"); - - var mouseEvent = (MouseEventArgs)e; - - var scaledLocation = LayoutHelper.ScaleLocation( - originalBounds: Thumbnail.Bounds, - originalLocation: new Point(mouseEvent.X, mouseEvent.Y), - scaledBounds: desktopBounds); - Logger.LogInfo($"scaled location = {scaledLocation}"); - - // set the new cursor position *twice* - the cursor sometimes end up in - // the wrong place if we try to cross the dead space between non-aligned - // monitors - e.g. when trying to move the cursor from (a) to (b) we can - // *sometimes* - for no clear reason - end up at (c) instead. - // - // +----------------+ - // |(c) (b) | - // | | - // | | - // | | - // +---------+ | - // | (a) | | - // +---------+----------------+ - // - // setting the position a second time seems to fix this and moves the - // cursor to the expected location (b) - for more details see - // https://github.com/mikeclayton/FancyMouse/pull/3 - Cursor.Position = scaledLocation; - Cursor.Position = scaledLocation; - - // Simulate mouse input for handlers that won't just catch the Cursor change - SimulateMouseMovementEvent(scaledLocation); - - Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); - } - - this.Close(); - } - - public void ShowThumbnail() - { - if (this.Thumbnail.Image != null) + if (this.Thumbnail.Image is not null) { var tmp = this.Thumbnail.Image; this.Thumbnail.Image = null; tmp.Dispose(); } + } + private void Thumbnail_Click(object sender, EventArgs e) + { + var mouseEventArgs = (MouseEventArgs)e; + Logger.LogInfo(string.Join( + '\n', + $"Reporting mouse event args", + $"\tbutton = {mouseEventArgs.Button}", + $"\tlocation = {mouseEventArgs.Location}")); + + if (mouseEventArgs.Button == MouseButtons.Left) + { + // plain click - move mouse pointer + var scaledLocation = MouseHelper.GetJumpLocation( + new PointInfo(mouseEventArgs.X, mouseEventArgs.Y), + new SizeInfo(this.Thumbnail.Size), + new RectangleInfo(SystemInformation.VirtualScreen)); + Logger.LogInfo($"scaled location = {scaledLocation}"); + MouseHelper.JumpCursor(scaledLocation); + + // Simulate mouse input for handlers that won't just catch the Cursor change + MouseHelper.SimulateMouseMovementEvent(scaledLocation.ToPoint()); + Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); + } + + this.OnDeactivate(EventArgs.Empty); + } + + public void ShowThumbnail() + { var screens = Screen.AllScreens; foreach (var i in Enumerable.Range(0, screens.Length)) { var screen = screens[i]; - Logger.LogInfo($"screen[{i}] = \"{screen.DeviceName}\"\n\tprimary = {screen.Primary}\n\tbounds = {screen.Bounds}\n\tworking area = {screen.WorkingArea}"); + Logger.LogInfo(string.Join( + '\n', + $"screen[{i}] = \"{screen.DeviceName}\"", + $"\tprimary = {screen.Primary}", + $"\tbounds = {screen.Bounds}", + $"\tworking area = {screen.WorkingArea}")); } - var desktopBounds = LayoutHelper.CombineRegions( - screens.Select(screen => screen.Bounds).ToList()); - Logger.LogInfo( - $"desktop bounds = {desktopBounds}"); + // collect together some values that we need for calculating layout + var activatedLocation = Cursor.Position; + var layoutConfig = new LayoutConfig( + virtualScreen: SystemInformation.VirtualScreen, + screenBounds: Screen.AllScreens.Select(screen => screen.Bounds), + activatedLocation: activatedLocation, + activatedScreen: Array.IndexOf(Screen.AllScreens, Screen.FromPoint(activatedLocation)), + maximumFormSize: new Size(1600, 1200), + formPadding: this.panel1.Padding, + previewPadding: new Padding(0)); + Logger.LogInfo(string.Join( + '\n', + $"Layout config", + $"-------------", + $"virtual screen = {layoutConfig.VirtualScreen}", + $"activated location = {layoutConfig.ActivatedLocation}", + $"activated screen = {layoutConfig.ActivatedScreen}", + $"maximum form size = {layoutConfig.MaximumFormSize}", + $"form padding = {layoutConfig.FormPadding}", + $"preview padding = {layoutConfig.PreviewPadding}")); - var activatedPosition = Cursor.Position; - Logger.LogInfo( - $"activated position = {activatedPosition}"); + // calculate the layout coordinates for everything + var layoutInfo = DrawingHelper.CalculateLayoutInfo(layoutConfig); + Logger.LogInfo(string.Join( + '\n', + $"Layout info", + $"-----------", + $"form bounds = {layoutInfo.FormBounds}", + $"preview bounds = {layoutInfo.PreviewBounds}", + $"activated screen = {layoutInfo.ActivatedScreen}")); - var previewImagePadding = new Size( - panel1.Padding.Left + panel1.Padding.Right, - panel1.Padding.Top + panel1.Padding.Bottom); - Logger.LogInfo( - $"image padding = {previewImagePadding}"); + DrawingHelper.PositionForm(this, layoutInfo.FormBounds); - var maxThumbnailSize = new Size(1600, 1200); - var formBounds = LayoutHelper.GetPreviewFormBounds( - desktopBounds: desktopBounds, - activatedPosition: activatedPosition, - activatedMonitorBounds: Screen.FromPoint(activatedPosition).Bounds, - maximumThumbnailImageSize: maxThumbnailSize, - thumbnailImagePadding: previewImagePadding); - Logger.LogInfo( - $"form bounds = {formBounds}"); + // initialize the preview image + var preview = new Bitmap( + (int)layoutInfo.PreviewBounds.Width, + (int)layoutInfo.PreviewBounds.Height, + PixelFormat.Format32bppArgb); + this.Thumbnail.Image = preview; - // take a screenshot of the entire desktop - // see https://learn.microsoft.com/en-gb/windows/win32/gdi/the-virtual-screen - var screenshot = new Bitmap(desktopBounds.Width, desktopBounds.Height, PixelFormat.Format32bppArgb); - using (var graphics = Graphics.FromImage(screenshot)) + using var previewGraphics = Graphics.FromImage(preview); + + DrawingHelper.DrawPreviewBackground(previewGraphics, layoutInfo.PreviewBounds, layoutInfo.ScreenBounds); + + var desktopHwnd = HWND.Null; + var desktopHdc = HDC.Null; + var previewHdc = HDC.Null; + try { - // note - it *might* be faster to capture each monitor individually and assemble them into - // a single image ourselves as we *may* not have to transfer all of the blank pixels - // that are outside the desktop bounds - e.g. the *** in the ascii art below - // - // +----------------+******** - // | |******** - // | 1 +-------+ - // | | | - // +----------------+ 0 | - // *****************| | - // *****************+-------+ - // - // for very irregular monitor layouts this *might* be a big percentage of the rectangle - // containing the desktop bounds. - // - // then again, it might not make much difference at all - we'd need to do some perf tests - graphics.CopyFromScreen(desktopBounds.Left, desktopBounds.Top, 0, 0, desktopBounds.Size); + DrawingHelper.EnsureDesktopDeviceContext(ref desktopHwnd, ref desktopHdc); + + // we have to capture the screen where we're going to show the form first + // as the form will obscure the screen as soon as it's visible + var activatedStopwatch = Stopwatch.StartNew(); + DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); + DrawingHelper.DrawPreviewScreen( + desktopHdc, + previewHdc, + layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen], + layoutInfo.ScreenBounds[layoutConfig.ActivatedScreen]); + activatedStopwatch.Stop(); + + // show the placeholder images if it looks like it might take a while + // to capture the remaining screenshot images + if (activatedStopwatch.ElapsedMilliseconds > 250) + { + var activatedArea = layoutConfig.ScreenBounds[layoutConfig.ActivatedScreen].Area; + var totalArea = layoutConfig.ScreenBounds.Sum(screen => screen.Area); + if ((activatedArea / totalArea) < 0.5M) + { + // we need to release the device context handle before we can draw the placeholders + // using the Graphics object otherwise we'll get an error from GDI saying + // "Object is currently in use elsewhere" + DrawingHelper.FreePreviewDeviceContext(previewGraphics, ref previewHdc); + DrawingHelper.DrawPreviewPlaceholders( + previewGraphics, + layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen)); + MainForm.ShowPreview(this); + } + } + + // draw the remaining screen captures (if any) on the preview image + var sourceScreens = layoutConfig.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen).ToList(); + if (sourceScreens.Any()) + { + DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); + DrawingHelper.DrawPreviewScreens( + desktopHdc, + previewHdc, + sourceScreens, + layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreen).ToList()); + MainForm.ShowPreview(this); + } + } + finally + { + DrawingHelper.FreeDesktopDeviceContext(ref desktopHwnd, ref desktopHdc); + DrawingHelper.FreePreviewDeviceContext(previewGraphics, ref previewHdc); } - - // resize and position the form - // note - do this in two steps rather than "this.Bounds = formBounds" as there - // appears to be an issue in WinForms with dpi scaling even when using PerMonitorV2, - // where the form scaling uses either the *primary* screen scaling or the *previous* - // screen's scaling when the form is moved to a different screen. i've got no idea - // *why*, but the exact sequence of calls below seems to be a workaround... - // see https://github.com/mikeclayton/FancyMouse/issues/2 - this.Location = formBounds.Location; - _ = this.PointToScreen(Point.Empty); - this.Size = formBounds.Size; - - // update the preview image - this.Thumbnail.Image = screenshot; - - this.Show(); - Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); // we have to activate the form to make sure the deactivate event fires + MainForm.ShowPreview(this); + Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); this.Activate(); } + + private static void ShowPreview(MainForm form) + { + if (!form.Visible) + { + form.Show(); + } + + form.Thumbnail.Refresh(); + } } diff --git a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj index 0c6b5b90e3..1300f083ab 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj +++ b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj @@ -12,6 +12,7 @@ MouseJumpUI.Program true true + enable diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs new file mode 100644 index 0000000000..5c2c500820 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MouseJumpUI.NativeMethods.Core; + +/// +/// A Boolean variable (should be TRUE or FALSE). +/// This type is declared in WinDef.h as follows: +/// typedef int BOOL; +/// +/// +/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types +/// +internal readonly struct BOOL +{ + public readonly int Value; + + public BOOL(int value) + { + this.Value = value; + } + + public BOOL(bool value) + { + this.Value = value ? 1 : 0; + } + + public static implicit operator bool(BOOL value) => value.Value != 0; + + public static implicit operator BOOL(bool value) => new(value); + + public static implicit operator int(BOOL value) => value.Value; + + public static implicit operator BOOL(int value) => new(value); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs new file mode 100644 index 0000000000..4ebfce933b --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace MouseJumpUI.NativeMethods.Core; + +/// +/// A handle to a device context (DC). +/// This type is declared in WinDef.h as follows: +/// typedef HANDLE HDC; +/// +/// +/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types +/// +internal readonly struct HDC +{ + public static readonly HDC Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HDC(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HDC.Null.Value; +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs new file mode 100644 index 0000000000..2478e0c8bf --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace MouseJumpUI.NativeMethods.Core; + +/// +/// A handle to a window. +/// This type is declared in WinDef.h as follows: +/// typedef HANDLE HWND; +/// +/// +/// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types +/// +internal readonly struct HWND +{ + public static readonly HWND Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HWND(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HWND.Null.Value; +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs new file mode 100644 index 0000000000..f91c6e05f8 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.ROP_CODE.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Gdi32 +{ + /// + /// A raster-operation code. These codes define how the color data for the source + /// rectangle is to be combined with the color data for the destination rectangle + /// to achieve the final color. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt + /// + public enum ROP_CODE : uint + { + BLACKNESS = 0x00000042, + CAPTUREBLT = 0x40000000, + DSTINVERT = 0x00550009, + MERGECOPY = 0x00C000CA, + MERGEPAINT = 0x00BB0226, + NOMIRRORBITMAP = 0x80000000, + NOTSRCCOPY = 0x00330008, + NOTSRCERASE = 0x001100A6, + PATCOPY = 0x00F00021, + PATINVERT = 0x005A0049, + PATPAINT = 0x00FB0A09, + SRCAND = 0x008800C6, + SRCCOPY = 0x00CC0020, + SRCERASE = 0x00440328, + SRCINVERT = 0x00660046, + SRCPAINT = 0x00EE0086, + WHITENESS = 0x00FF0062, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs new file mode 100644 index 0000000000..71214b2204 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.STRETCH_BLT_MODE.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Gdi32 +{ + /// + /// The stretching mode. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode + /// + public enum STRETCH_BLT_MODE : int + { + BLACKONWHITE = 1, + COLORONCOLOR = 3, + HALFTONE = 4, + WHITEONBLACK = 2, + STRETCH_ANDSCANS = STRETCH_BLT_MODE.BLACKONWHITE, + STRETCH_DELETESCANS = STRETCH_BLT_MODE.COLORONCOLOR, + STRETCH_HALFTONE = STRETCH_BLT_MODE.HALFTONE, + STRETCH_ORSCANS = STRETCH_BLT_MODE.WHITEONBLACK, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs new file mode 100644 index 0000000000..085d03295f --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.SetStretchBltMode.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Gdi32 +{ + /// + /// The SetStretchBltMode function sets the bitmap stretching mode in the specified device context. + /// + /// + /// If the function succeeds, the return value is the previous stretching mode. + /// If the function fails, the return value is zero. + /// This function can return the following value. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode + /// + [LibraryImport(Libraries.Gdi32)] + public static partial int SetStretchBltMode( + HDC hdc, + STRETCH_BLT_MODE mode); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs new file mode 100644 index 0000000000..2e7524358e --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Gdi32.StretchBlt.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Gdi32 +{ + /// + /// The StretchBlt function copies a bitmap from a source rectangle into a destination + /// rectangle, stretching or compressing the bitmap to fit the dimensions of the + /// destination rectangle, if necessary. The system stretches or compresses the bitmap + /// according to the stretching mode currently set in the destination device context. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-stretchblt + /// + [LibraryImport(Libraries.Gdi32)] + public static partial BOOL StretchBlt( + HDC hdcDest, + int xDest, + int yDest, + int wDest, + int hDest, + HDC hdcSrc, + int xSrc, + int ySrc, + int wSrc, + int hSrc, + ROP_CODE rop); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Libraries.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Libraries.cs new file mode 100644 index 0000000000..59947db8fd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Libraries.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace MouseJumpUI.NativeMethods; + +internal static class Libraries +{ + public const string Gdi32 = "gdi32"; + public const string User32 = "user32"; +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs new file mode 100644 index 0000000000..bb5ed16514 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetDesktopWindow.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Retrieves a handle to the desktop window. The desktop window covers the entire + /// screen. The desktop window is the area on top of which other windows are painted. + /// + /// + /// The return value is a handle to the desktop window. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdesktopwindow + /// + [LibraryImport(Libraries.User32)] + public static partial HWND GetDesktopWindow(); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs new file mode 100644 index 0000000000..4263aa0b7d --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.GetWindowDC.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The GetWindowDC function retrieves the device context (DC) for the entire window, + /// including title bar, menus, and scroll bars. A window device context permits painting + /// anywhere in a window, because the origin of the device context is the upper-left + /// corner of the window instead of the client area. + /// + /// GetWindowDC assigns default attributes to the window device context each time it + /// retrieves the device context. Previous attributes are lost. + /// + /// + /// If the function succeeds, the return value is a handle to a device context for the specified window. + /// If the function fails, the return value is NULL, indicating an error or an invalid hWnd parameter. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdc + /// + [LibraryImport(Libraries.User32)] + public static partial HDC GetWindowDC( + HWND hWnd); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs new file mode 100644 index 0000000000..f745c13c27 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/User32.ReleaseDC.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The ReleaseDC function releases a device context (DC), freeing it for use by other + /// applications. The effect of the ReleaseDC function depends on the type of DC. It + /// frees only common and window DCs. It has no effect on class or private DCs. + /// + /// + /// The return value indicates whether the DC was released. If the DC was released, the return value is 1. + /// If the DC was not released, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-releasedc + /// + [LibraryImport(Libraries.User32)] + public static partial int ReleaseDC( + HWND hWnd, + HDC hDC); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs new file mode 100644 index 0000000000..7613b8825c --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.SetStretchBltMode.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeWrappers; + +internal static partial class Gdi32 +{ + public static int SetStretchBltMode(HDC hdc, NativeMethods.Gdi32.STRETCH_BLT_MODE mode) + { + var result = NativeMethods.Gdi32.SetStretchBltMode(hdc, mode); + + return result == 0 + ? throw new InvalidOperationException( + $"{nameof(Gdi32.SetStretchBltMode)} returned {result}") + : result; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs new file mode 100644 index 0000000000..115edafee9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/Gdi32/Gdi32.StretchBlt.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeWrappers; + +internal static partial class Gdi32 +{ + public static BOOL StretchBlt( + HDC hdcDest, + int xDest, + int yDest, + int wDest, + int hDest, + HDC hdcSrc, + int xSrc, + int ySrc, + int wSrc, + int hSrc, + NativeMethods.Gdi32.ROP_CODE rop) + { + var result = NativeMethods.Gdi32.StretchBlt( + hdcDest, + xDest, + yDest, + wDest, + hDest, + hdcSrc, + xSrc, + ySrc, + wSrc, + hSrc, + rop); + + return result + ? result + : throw new InvalidOperationException( + $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs new file mode 100644 index 0000000000..8df89f7e39 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetDesktopWindow.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeWrappers; + +internal static partial class User32 +{ + public static HWND GetDesktopWindow() + { + return NativeMethods.User32.GetDesktopWindow(); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs new file mode 100644 index 0000000000..6379e3f73a --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.GetWindowDC.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeWrappers; + +internal static partial class User32 +{ + public static HDC GetWindowDC(HWND hWnd) + { + var hdc = NativeMethods.User32.GetWindowDC(hWnd); + + return hdc.IsNull + ? throw new InvalidOperationException( + $"{nameof(User32.GetWindowDC)} returned null") + : hdc; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs new file mode 100644 index 0000000000..44a8318b85 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeWrappers/User32/User32.ReleaseDC.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeWrappers; + +internal static partial class User32 +{ + public static int ReleaseDC(HWND hWnd, HDC hDC) + { + var result = NativeMethods.User32.ReleaseDC(hWnd, hDC); + + return result == 0 + ? throw new InvalidOperationException( + $"{nameof(User32.ReleaseDC)} returned {result}") + : result; + } +} From 9d7373467d6842aeab38b9ba78acf85e84120f61 Mon Sep 17 00:00:00 2001 From: sosssego Date: Fri, 17 Mar 2023 18:13:16 +0000 Subject: [PATCH 099/163] [Power Rename] Select all while filter is active (#24598) * Check if the result from find item returns a valid item and repopulate ExplorerItems() * Call all the clean and populate methods only when showRenamed is checked. --- .../powerrename/PowerRenameUILib/MainWindow.xaml.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp index 1331d66d1f..e520eb321f 100644 --- a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp +++ b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp @@ -273,6 +273,13 @@ namespace winrt::PowerRenameUI::implementation { ToggleAll(); m_allSelected = !m_allSelected; + if (button_showRenamed().IsChecked()) + { + m_explorerItems.Clear(); + m_explorerItemsMap.clear(); + PopulateExplorerItems(); + UpdateCounts(); + } } } @@ -738,7 +745,8 @@ namespace winrt::PowerRenameUI::implementation int id = 0; spItem->GetId(&id); auto item = FindById(id); - item.Checked(selected); + if (item) + item.Checked(selected); } } UpdateCounts(); From dbe52d3d524f0ab814f53f21ed8b56adc6cc5ae3 Mon Sep 17 00:00:00 2001 From: sosssego Date: Fri, 17 Mar 2023 18:14:23 +0000 Subject: [PATCH 100/163] [Settings] move the DLL imports to NativeMethods file (#24381) * move the DLL imports to NativeMethods file * Change the DLL import from public to internal. --- src/settings-ui/Settings.UI/Helpers/NativeMethods.cs | 7 +++++++ src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs | 11 ++--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs index 8a7bdb29e7..71ff81cf54 100644 --- a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs +++ b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs @@ -38,6 +38,12 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers [DllImport("user32.dll")] internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + [DllImport("shell32.dll")] + internal static extern IntPtr SHBrowseForFolderW(ref ShellGetFolder.BrowseInformation browseInfo); + + [DllImport("shell32.dll")] + internal static extern int SHGetPathFromIDListW(IntPtr pidl, IntPtr pszPath); + #pragma warning disable CA1401 // P/Invokes should not be visible [DllImport("user32.dll")] public static extern bool ShowWindow(System.IntPtr hWnd, int nCmdShow); @@ -59,6 +65,7 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] public static extern bool FreeLibrary(IntPtr hModule); + #pragma warning restore CA1401 // P/Invokes should not be visible [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] diff --git a/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs b/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs index 9b44c88500..32acc06a72 100644 --- a/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs +++ b/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs @@ -5,18 +5,11 @@ using System; using System.Runtime.InteropServices; using System.Text; -using static Microsoft.PowerToys.Settings.UI.Helpers.ShellGetFolder; namespace Microsoft.PowerToys.Settings.UI.Helpers { public class ShellGetFolder { - [DllImport("shell32.dll")] - private static extern IntPtr SHBrowseForFolderW(ref BrowseInformation browseInfo); - - [DllImport("shell32.dll")] - private static extern int SHGetPathFromIDListW(IntPtr pidl, IntPtr pszPath); - public delegate int BrowseCallbackProc(IntPtr hwnd, int msg, IntPtr lp, IntPtr wp); [StructLayout(LayoutKind.Sequential)] @@ -51,8 +44,8 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers try { - pidl = SHBrowseForFolderW(ref browseInfo); - if (SHGetPathFromIDListW(pidl, bufferAddress) == 0) + pidl = NativeMethods.SHBrowseForFolderW(ref browseInfo); + if (NativeMethods.SHGetPathFromIDListW(pidl, bufferAddress) == 0) { return null; } From 41997eb599d2e7e7e88eae4f46411bd5f411f242 Mon Sep 17 00:00:00 2001 From: Tudor Croitoru Date: Fri, 17 Mar 2023 21:25:03 +0000 Subject: [PATCH 101/163] Fix ColorPicker detecting grid (#24413) * Fix ColorPicker sampling colour from the grid Addresses #13831. The sampled pixel is now completely avoided by the grid. Also, this should address the timing issue from the sampling being done every 10ms, which overlaps strangely with monitor refresh rates at 16.6ms, 13.3ms, etc. * Forgot extra library * Revert rounding in GetPixelColor * Ensure EnumDisplaySettingsW returns true Assigning refreshRate only if EnumDisplaySettingsW returns true, otherwise defaulting to 60.0Hz. * Run spellchecker * Spellcheck again --------- Co-authored-by: Stefan Markovic --- .github/actions/spell-check/excludes.txt | 4 +- .github/actions/spell-check/expect.txt | 149 +++--------------- .../ColorPickerUI/Mouse/MouseInfoProvider.cs | 21 ++- .../ColorPickerUI/NativeMethods.cs | 54 +++++++ .../ColorPickerUI/Shaders/GridShader.cso | Bin 2208 -> 2076 bytes .../ColorPickerUI/Shaders/GridShader.fx | 10 +- 6 files changed, 99 insertions(+), 139 deletions(-) diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index 0e66c05d36..fd107c3f77 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -77,10 +77,10 @@ ^\.gitmodules$ ^\Q.github/workflows/spelling2.yml\E$ ^\Q.pipelines/ESRPSigning_core.json\E$ +^\Qinstaller/PowerToysSetup/Settings.wxs\E$ +^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$ ^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$ ^\Qsrc/modules/MouseUtils/MouseJumpUI/MainForm.resx\E$ -^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$ -^\Qinstaller/PowerToysSetup/Settings.wxs\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/CorruptJson/Microsoft/PowerToys/settings.json\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.18.2/Microsoft/PowerToys/PowerRename/power-rename-ui-flags\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.19.2/Microsoft/PowerToys/PowerRename/power-rename-ui-flags\E$ diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 7246134353..155e493848 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -29,8 +29,6 @@ AFFINETRANSFORM AFX AGGREGATABLE AHybrid -Akrotiri -Aktobe ALarger alekhyareddy ALLAPPS @@ -43,13 +41,12 @@ amd AModifier AMPROPERTY AMPROPSETID -Andreanof +ANDSCANS anges angularsen Animatable ansicolor ANull -ANDSCANS AOC aocfnapldcnfbofgmbbllojgocaelgdd APARTMENTTHREADED @@ -72,10 +69,8 @@ appref apps appwindow appwiz -appxpackage APSTUDIO AQS -Aqtobe ARCHITEW arcosh ARemapped @@ -94,7 +89,6 @@ ASingle ASSOCCHANGED ASYNCWINDOWPLACEMENT ASYNCWINDOWPOS -Atikokan atl atlbase atlcom @@ -102,7 +96,6 @@ atleast atlfile atlstr Attribs -Atyrau aumid Aut Authenticode @@ -113,18 +106,14 @@ AUTOMATIONPROPERTIES Autorun AUTOUPDATE AValid -Avanc awakeness awakeversion AYUV azman backtracer bak -Bashkortostan -Bayan bbwe bck -Belarus betadele betsegaw BGR @@ -142,8 +131,8 @@ BITSPIXEL bla BLACKONWHITE Blockquotes -Blt blogs +Blt BLUEGRAY Bluetooth BLURBEHIND @@ -164,7 +153,6 @@ bricelam BRIGHTGREEN Browsable bsd -Bson bstr bthprops bti @@ -176,14 +164,10 @@ bugreport BUILDNUMBER buildtask buildtransitive -Burkina -Buryatia BUTTONUP BValue BYPOSITION bytearray -Cabo -Caiguna CALG callbackptr Cangjie @@ -192,6 +176,10 @@ CAPTUREBLT CAPTURECHANGED CAtl cch +CCHDEVICENAME +CCHDEVICENAME +CCHFORMNAME +CCHFORMNAME CCom CContext cdecl @@ -208,28 +196,24 @@ Changemove chdir CHILDACTIVATE CHILDWINDOW -Choibalsan chrdavis Chrzan cht -Chukotka -Chuuk CIELAB CIEXYZ cim CImage cla clangformat -clayton CLASSDC CLASSNOTAVAILABLE +clayton clickable clickonce CLIENTEDGE clientid clientside CLIPCHILDREN -Clipperton CLIPSIBLINGS Cloneable clrcall @@ -249,7 +233,6 @@ cmpgt cmyk cne cnt -Cocklebiddy coclass CODENAME codeofconduct @@ -274,7 +257,6 @@ cominterop commandline COMMANDTITLE commctrl -Comoros compmgmt COMPOSITIONFULL comsupp @@ -296,7 +278,6 @@ CONTROLL CONTROLPARENT Controlz copiedcolorrepresentation -corewebview cortana cotaskmem COULDNOT @@ -310,7 +291,6 @@ CProj CREATESCHEDULEDTASK CREATESTRUCT CREATEWINDOWFAILED -createcompatibledc critsec Crossdevice CRSEL @@ -327,7 +307,6 @@ CTRLALTDEL Ctrls Ctx CUI -Cunha currentculture CURRENTDIR CURSORINFO @@ -351,12 +330,10 @@ cziplib Dac dacl damienleroy -Danmarkshavn DARKPURPLE DARKTEAL DARKYELLOW datareader -datatemplate Datavalue dataversion DATAW @@ -414,6 +391,8 @@ devblogs devdocs devenum devmgmt +DEVMODEW +DEVMODEW DEVMON devpkey DEVSOURCE @@ -436,7 +415,6 @@ dllmain DNLEN Dns DONOTROUND -DONTRESOLVEDLLREFERENCES DONTVALIDATEPATH dotnet DPICHANGED @@ -500,7 +478,6 @@ endpointvolume endregion ENTERSIZEMOVE enu -enumerationoptions EOAC epicgames epu @@ -514,10 +491,8 @@ ESettings esize esn esrp -Eswatini etl etw -Eucla EUQ eurochange eventlog @@ -554,7 +529,6 @@ fancyzones FANCYZONESDRAWLAYOUTTEST FANCYZONESEDITOR Farbraum -Faroe FARPROC fdw feimage @@ -594,7 +568,6 @@ FSCTL fsmgmt FTYPE Functiondiscoverykeys -Futuna fwlink FZE gabime @@ -612,8 +585,6 @@ gcode gdi gdiplus GDISCALED -getancestor -getasynckeystate GETDESKWALLPAPER GETDLGCODE GETDPISCALEDSIZE @@ -623,7 +594,6 @@ GETSTATE GETTEXT GETTEXTLENGTH GHND -globalassemblycache Globbing GMEM GNumber @@ -642,7 +612,6 @@ gwl GWLP HACCEL handlekeyboardhookevent -handlerroutine hangeul Hankaku hanselman @@ -672,7 +641,6 @@ HEB Heiko Helpline helptext -Heure HGFE hglobal hhk @@ -711,7 +679,6 @@ hotkeycontrol hotkeys hotlight hotspot -Hovd HPAINTBUFFER HPALETTE HRAWINPUT @@ -793,7 +760,6 @@ Inlines inorder INPC inproc -inputdev INPUTHARDWARE INPUTKEYBOARD INPUTLANGCHANGED @@ -822,7 +788,6 @@ ipcmanager IPlugin IPower IPREVIEW -ipreviewhandler ipreviewhandlervisualssetfont IProperty IPublic @@ -837,11 +802,8 @@ iss ITask ith ITHUMBNAIL -Ittoqqortoormiit IUI IUnknown -ivirtualdesktopmanager -Ivoire IWbem IWIC iwr @@ -876,20 +838,11 @@ keynum keyremaps Keytool keyup -Khakassia -Khanty -Khovd KILLFOCUS killrunner -Kitts Knownfolders -Krai KSPROPERTY -Kwango -Kwilu Kybd -Kyrgyzstan -Kyzylorda LAlt Lambson langword @@ -909,8 +862,8 @@ LCtrl Ldone ldx LEFTDOWN -LEFTUP LEFTSCROLLBAR +LEFTUP lego len LError @@ -935,7 +888,6 @@ LMEM LMENU lnk LOADFROMFILE -LOADLIBRARYASDATAFILE LOBYTE LOCALAPPDATA LOCALDISPLAY @@ -988,16 +940,12 @@ lstrcmpi lstrlen LTRB LTRREADING -Luhansk luid lusrmgr LWA lwin LZero lzw -Maarten -Macquarie -Magadan Mainwindow majortype MAJORVERSION @@ -1005,10 +953,7 @@ makecab MAKEINTRESOURCE MAKEINTRESOURCEW makepri -Mangere -Mangystau manifestdependency -Mansi MAPPEDTOSAMEKEY MAPTOSAMESHORTCUT MAPVK @@ -1016,10 +961,8 @@ Markdig markdownpreviewhandler MARKDOWNPREVIEWHANDLERCPP Markovic -Marquesas martinchrzan martinmoene -Mato Maximizable MAXIMIZEBOX MAXSHORTCUTSIZE @@ -1031,8 +974,6 @@ MBUTTON MBUTTONDBLCLK MBUTTONDOWN MBUTTONUP -MCDT -MCST MDICHILD MDL mdtext @@ -1043,7 +984,6 @@ mediatype Melman MENUITEMINFO MENUITEMINFOW -menurc MERGECOPY MERGEPAINT Metadatas @@ -1059,9 +999,9 @@ mftransform Mgmt mic microsoft -Midl MIDDLEDOWN MIDDLEUP +Midl mii MIIM millis @@ -1075,7 +1015,6 @@ miniz minlevel MINORVERSION Miracast -Mishkeegogamang mjpg mkd mkdn @@ -1091,8 +1030,6 @@ mockapi MODECHANGE modernwpf MODESPRUNED -Moldova -Mongala MONITORINFO MONITORINFOEX MONITORINFOEXW @@ -1137,15 +1074,11 @@ MTND Mul MULTIPLEUSE multizone -Mundrabilla mvvm myfile MYICON -MYTZ NAMECHANGE nameof -Navassa -navigatetostring NCACTIVATE ncc NCCALCSIZE @@ -1167,7 +1100,6 @@ NCRBUTTONDBLCLK NCRBUTTONDOWN NCRBUTTONUP NCRENDERING -Ndombe ndp NEEDDISPATCH Nemeth @@ -1211,7 +1143,6 @@ nonclient NONCONVERT NONELEVATED NONINFRINGEMENT -nonpackaged nonstd NOOWNERZORDER NOPARENTNOTIFY @@ -1224,7 +1155,6 @@ NOREPOSITION norestart NORMALDISPLAY NORMALUSER -Noronha NOSEARCH NOSENDCHANGING NOSIZE @@ -1251,8 +1181,6 @@ nullonfailure numberbox NUMLOCK NUMPAD -Nunavut -Nusa nwc Objbase OBJID @@ -1273,8 +1201,6 @@ oldtheme oleaut OLECHAR OLEDB -oledbcommand -oledbconnection OLIVEGREEN onebranch onenote @@ -1339,6 +1265,8 @@ pdtobj pdw PDWORD pedrolamas +Pels +Pels PERCEIVEDFLAG perfmon pesi @@ -1362,7 +1290,6 @@ PINDIR pinfo pinvoke pipename -Pitcairn PKBDLLHOOKSTRUCT PKEY plib @@ -1374,7 +1301,6 @@ plugins pluginsmodel PMSIHANDLE Pnp -Pohnpei Popups POPUPWINDOW posix @@ -1415,7 +1341,6 @@ PREVIOUSVERSIONSINSTALLED prevpane prgms pri -Primorsky PRINTCLIENT printmanagement prm @@ -1464,7 +1389,6 @@ QITAB QITABENT qps QUERYENDSESSION -queryfocus QUERYOPEN QUEUESYNC Quickime @@ -1509,7 +1433,6 @@ regfile REGFILTER REGFILTERPINS REGISTERCLASSFAILED -registerhotkey registrypath regkey REGPINTYPES @@ -1536,7 +1459,6 @@ resizers RESIZETOFIT resmimetype RESOURCEID -resourcemanager RESTORETOMAXIMIZED restrictedcapabilities restrictederrorinfo @@ -1553,21 +1475,19 @@ rhs ricardosantos RIDEV RIGHTDOWN -RIGHTUP RIGHTSCROLLBAR +RIGHTUP riid RKey RLO RMENU RNumber roadmap -Roamable robmensching Roboto rooler rop roslyn -Rothera roundf ROUNDSMALL Rpc @@ -1606,7 +1526,6 @@ SAMESHORTCUTPREVIOUSLYMAPPED SAVEFAILED scancode scanled -Schd schedtasks Scip scipbe @@ -1618,10 +1537,8 @@ sddl SDKDDK sdns searchterm -secauthz secpol Secur -securityoverview Segoe Sekan SENDCHANGE @@ -1657,7 +1574,6 @@ shellex SHELLEXECUTEINFO SHELLEXECUTEINFOW shellscalingapi -shemptyrecyclebina SHFILEINFO SHGFI Shl @@ -1722,13 +1638,12 @@ Soref SOURCECLIENTAREAONLY SOURCEHEADER sourcesdirectory -SPACEBAR spam spdisp spdlog spdo -specialfolder spec'ing +specialfolder spesi splitwstring spsi @@ -1742,7 +1657,6 @@ Srch SRCINVERT SRCPAINT sre -Srednekolymsk SResize srme srre @@ -1794,9 +1708,7 @@ subkey SUBLANG subquery subresource -Sul Superbar -Suri sut SVE SVGIn @@ -1816,7 +1728,6 @@ SYSCOMMAND SYSDEADCHAR sysdm SYSICONINDEX -sysinfo SYSKEY syskeydown SYSKEYUP @@ -1826,7 +1737,6 @@ systemroot SYSTEMTIME sysvol Tadele -Tajikistan talynone TApp TApplication @@ -1854,7 +1764,6 @@ telem telephon Templated templatenamespace -Tenggara testapp testcase testhost @@ -1874,7 +1783,6 @@ timedate timediff timeunion timeutil -timezones Titlecase TKey TLayout @@ -1887,7 +1795,6 @@ toolkitconverters Toolset toolwindow TOPDOWNDIB -tostring TOUCHEVENTF TOUCHINPUT touchpad @@ -1895,15 +1802,12 @@ Towindow tracelogging traies transicc -Transnistria TRAYMOUSEMESSAGE triaging TRK trl TServer -Tshuapa TStr -Tuva TValue TWF tymed @@ -1917,23 +1821,18 @@ TYPESHORTCUT UAC UAL uap -uapmanifestschema udit Udk -Udmurtia Udp uefi UHash UIA -uiauto uid UIEx uipi UIs -Ulaanbaatar ULARGE ULONGLONG -UMsg unassign uncompilable UNCPRIORITY @@ -1954,13 +1853,11 @@ unmute UNORM unregistering unremapped -Unstub unsubscribe unvirtualized Updatelayout UPGRADINGPRODUCTCODE Uptool -Urville Usb USEDEFAULT USEFILEATTRIBUTES @@ -1973,7 +1870,6 @@ USRDLL Utc utf uuidof -Uvs uwp uxtheme UYVY @@ -2014,7 +1910,6 @@ viewbox viewmodel vih VIRTUALDESK -virtualkey visiblecolorformats Visibletrue VKey @@ -2070,7 +1965,6 @@ wikipedia wildcards winapi winappdriver -winauto wincodec Wincodecsdk wincolor @@ -2097,7 +1991,6 @@ winevt winexe winforms winfx -wingdi winget wingetcreate Winhook @@ -2105,7 +1998,6 @@ winkey WINL winmd winmm -winmsg WINNT winres winrt @@ -2116,7 +2008,6 @@ winternl WINTHRESHOLD winui winuiex -winuser winxamlmanager wistd withinrafael @@ -2170,11 +2061,9 @@ wtoi WTS wtsapi WTSAT -wtypes Wubi wuceffectsi WVC -WVk Wwan Wwanpp XAttribute @@ -2184,11 +2073,9 @@ XBUTTON XBUTTONDBLCLK XBUTTONDOWN XBUTTONUP -XControl xcopy XDocument XDOWN -XUP XElement XFile XIncrement @@ -2199,14 +2086,16 @@ XPixel XResource xsi XStr +XUP XVIRTUALSCREEN -Yamalia YAxis YIncrement yinle yinwang yinyue YOffset +YResolution +YResolution YStr YUY yuyoyuppe @@ -2214,7 +2103,6 @@ YUYV YVIRTUALSCREEN YVU YVYU -Zabaykalsky ZEROINIT ZIndex zipfile @@ -2222,6 +2110,5 @@ zipfolder zonable zoneset Zoneszonabletester -Zonev Zykova zzz diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs index 517ef40684..526466dc4a 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs @@ -18,7 +18,7 @@ namespace ColorPicker.Mouse [PartCreationPolicy(CreationPolicy.Shared)] public class MouseInfoProvider : IMouseInfoProvider { - private const int MousePullInfoIntervalInMs = 10; + private readonly double _mousePullInfoIntervalInMs; private readonly DispatcherTimer _timer = new DispatcherTimer(); private readonly MouseHook _mouseHook; private readonly IUserSettings _userSettings; @@ -29,7 +29,8 @@ namespace ColorPicker.Mouse [ImportingConstructor] public MouseInfoProvider(AppStateHandler appStateMonitor, IUserSettings userSettings) { - _timer.Interval = TimeSpan.FromMilliseconds(MousePullInfoIntervalInMs); + _mousePullInfoIntervalInMs = 1000.0 / GetMainDisplayRefreshRate(); + _timer.Interval = TimeSpan.FromMilliseconds(_mousePullInfoIntervalInMs); _timer.Tick += Timer_Tick; if (appStateMonitor != null) @@ -111,6 +112,22 @@ namespace ColorPicker.Mouse return (System.Windows.Point)lpPoint; } + private static double GetMainDisplayRefreshRate() + { + double refreshRate = 60.0; + + foreach (var monitor in MonitorResolutionHelper.AllMonitors) + { + if (monitor.IsPrimary && EnumDisplaySettingsW(monitor.Name, ENUM_CURRENT_SETTINGS, out DEVMODEW lpDevMode)) + { + refreshRate = (double)lpDevMode.dmDisplayFrequency; + break; + } + } + + return refreshRate; + } + private void AppStateMonitor_AppClosed(object sender, EventArgs e) { DisposeHook(); diff --git a/src/modules/colorPicker/ColorPickerUI/NativeMethods.cs b/src/modules/colorPicker/ColorPickerUI/NativeMethods.cs index d02a5e0416..e2f277a8b5 100644 --- a/src/modules/colorPicker/ColorPickerUI/NativeMethods.cs +++ b/src/modules/colorPicker/ColorPickerUI/NativeMethods.cs @@ -22,6 +22,9 @@ namespace ColorPicker public const int KfAltdown = 0x2000; public const int LlkhfAltdown = KfAltdown >> 8; public const int MonitorinfofPrimary = 0x00000001; + public const int CCHDEVICENAME = 32; + public const int CCHFORMNAME = 32; + public const uint ENUM_CURRENT_SETTINGS = 4294967295; public const int VK_SHIFT = 0x10; public const int VK_CONTROL = 0x11; public const int VK_MENU = 0x12; @@ -57,6 +60,13 @@ namespace ColorPicker internal static extern bool EnumDisplayMonitors( HandleRef hdc, IntPtr rcClip, MonitorEnumProc lpfnEnum, IntPtr dwData); + [DllImport("user32.dll", SetLastError = false, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool EnumDisplaySettingsW( + string lpszDeviceName, + uint iModeNum, + out DEVMODEW lpDevMode); + [DllImport("user32.dll")] internal static extern bool GetCursorPos(out PointInter lpPoint); @@ -131,6 +141,50 @@ namespace ColorPicker public char[] szDevice = new char[32]; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct DEVMODEW + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] + public string dmDeviceName; + + public ushort dmSpecVersion; + public ushort dmDriverVersion; + public ushort dmSize; + public ushort dmDriverExtra; + public uint dmFields; + + public int dmPositionX; + public int dmPositionY; + public uint dmDisplayOrientation; + public uint dmDisplayFixedOutput; + + public short dmColor; + public short dmDuplex; + public short dmYResolution; + public short dmTTOption; + public short dmCollate; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)] + public string dmFormName; + + public short dmLogPixels; + public uint dmBitsPerPel; + public uint dmPelsWidth; + public uint dmPelsHeight; + + public uint dmDisplayFlags; + public uint dmDisplayFrequency; + + public uint dmICMMethod; + public uint dmICMIntent; + public uint dmMediaType; + public uint dmDitherType; + public uint dmReserved1; + public uint dmReserved2; + public uint dmPanningWidth; + public uint dmPanningHeight; + } + [StructLayout(LayoutKind.Sequential)] internal struct LowLevelKeyboardInputEvent { diff --git a/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso index db23c058d3adc3cb5e86b4b7c2e1a0d3701e4833..49856b6822be054afe6be9ca19dacad3d944f6e7 100644 GIT binary patch literal 2076 zcmZWqO=}ZT6umP^n;^x4b}5*pRge~cKsD_`p@JF*#7zk{;(%>pK0sVe_Ab(Y@VZSl z{sUdQ(7oH@2tpukL~!GH&b@DD8XfvN@1A$>xgYP|iBz)ePj*jsAKz(S5s_b50&E2@ z%5oy|1FrN0X$!@LRbfKvB176Rt0vcz~bZH zXxM5W^9-H#(LiR5lu_mQ;MJ(z z@3i7K9T|3B4@Z9A8yws^*pmD4vwnBbeLield$ifOyWiSxv|h9iJN?FP_ox>mmd5th z&29L5D1x%AJ5*!GJSOiyf7;(6zM{C?Hr)PSMcOb(5YL2rpcARDJ8V$ON)kwPDw0l~ zh*WV5z344;9Nx~b0GI%a@Q&Q3tdN7)xJi!sI`%0zQ+FL8@+CERQwP851Lq0I-DN#n zgZ^GZZqyF@#hAP>Rey%1z#|8agkwpt_TN8UEnz1f-`&R`p9?C3d-1-_ldTJS3$Tq<|Y zXSseKTkSP3aygMQw-EI+tgKIH^(g9tT0y6*59=?oF|m9Z)571u8sgtu@)3yDZ}|=k zqpsZn?NaqYxx6>g-ZFVk9o09^4ZRcUvo3+gX!@b|iQF`I%E)P{7b|LWR$#`Xdf>I5 z@^(c&xi679;yBZs>6`Tm)xY&Q+aP57Odp-_)-g%Bz_k6z9zjBp% z*AnCau1x1CkSfln;NrJJPHlFr?`gv`!)%TWi;CGn9g5u1KMwPM@>J)aybE2lsp%}5 zFPl5tbG8DhYYaA9;y6p}2i7g0d&(7IP0G2iNR@IzeE|pOxPo}?TV(R`naw=D-q4G@ zT@`jy z@!0A)%;p-uWon0CLtp!M-oFbBvBMX!RIyNM;LcPg-qaY_zp3MQ++rm6WSsXv xUc2)>VBaq73r9ZtX4&_V_sNU(Ttz?{!b`LGh~UR-O8|b?-%1*ZQeu+-M!uZ-h=&v z!MJ~Wba*smF8!O^SFdf~ynbc->W%9+u>2BWd$t0*^06Yn_-&W}@@MHgrpG7JH{bg# zpAIGST8O89f!A1cj%oZccZ($^~DM$^5WMQCe;*uu@7`F(HtA16KtEb;EH}0}zBiU@Qe%GPw zuJN8@w`7Cq`+V^p*D~wFXmp%<{ z6%;f2SX_^}sTdYBcRb3c7PGN!`s^`fU$F_wmt^s(iKoZx$j|8;b4`U+0(B5phMaP3Sk4#b<27pSIAY1KmP{<0m&{{p zp6ej{j`iV>xDOfqR8tjuvPpS|wqCz_qP}Up(Rt3=8}A4E6|uZ00D@*`wa1R_{eMkt z&4kYv?;PUnklaVPCHQlmcMfYRt)7e`C!(&ET9dLz5fxw|bi1DOc4;vbpHM_g?nmbq2}XTu2u0SgoDq z7iXreL380)h1oln@&ALBvKPMQd70c|sb{O1EMmNrxz({Bb&#L#A>oL0_xPNr>P1-9 zqq&xi>{Xx1#6dVuT{r3^lS= topLeftRectangle.x + 1 && pixelPositionX <= topLeftRectangle.x + squareSize) && (pixelPositionY == topLeftRectangle.y + 1 || pixelPositionY == topLeftRectangle.y + squareSize)) || - ((pixelPositionY >= topLeftRectangle.y + 1 && pixelPositionY <= topLeftRectangle.y + squareSize) && (pixelPositionX == topLeftRectangle.x + 1 || pixelPositionX == topLeftRectangle.x + squareSize))) + if (((pixelPositionX >= topLeftRectangle.x + 1 && pixelPositionX <= topLeftRectangle.x + squareSize + 1) && (pixelPositionY == topLeftRectangle.y + 1 || pixelPositionY == topLeftRectangle.y + squareSize + 1)) || + ((pixelPositionY >= topLeftRectangle.y + 1 && pixelPositionY <= topLeftRectangle.y + squareSize + 1) && (pixelPositionX == topLeftRectangle.x + 1 || pixelPositionX == topLeftRectangle.x + squareSize + 1))) { return originalColor; } From 7d4e804cecf1f11b42fc536b4d1b47efe0c27388 Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Fri, 17 Mar 2023 23:25:14 +0100 Subject: [PATCH 102/163] Revert "[Settings / OOBE] Adding Mica (#24327)" (#24875) This reverts commit 4291fa57f9001ed84ce5d6f90f992c77aa8b964f. --- src/settings-ui/Settings.UI/MainWindow.xaml | 11 +++-------- src/settings-ui/Settings.UI/MainWindow.xaml.cs | 2 +- .../Settings.UI/OOBE/Views/OobeShellPage.xaml | 8 +++++++- src/settings-ui/Settings.UI/OobeWindow.xaml | 11 +++-------- src/settings-ui/Settings.UI/OobeWindow.xaml.cs | 3 +-- src/settings-ui/Settings.UI/Views/ShellPage.xaml | 9 ++++++++- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml b/src/settings-ui/Settings.UI/MainWindow.xaml index bd924ed304..715a25863c 100644 --- a/src/settings-ui/Settings.UI/MainWindow.xaml +++ b/src/settings-ui/Settings.UI/MainWindow.xaml @@ -1,19 +1,14 @@ - - - - + - + diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/MainWindow.xaml.cs index 0ccafa42f2..06ce02f20d 100644 --- a/src/settings-ui/Settings.UI/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/MainWindow.xaml.cs @@ -22,7 +22,7 @@ namespace Microsoft.PowerToys.Settings.UI /// /// An empty window that can be used on its own or navigated to within a Frame. /// - public sealed partial class MainWindow : WindowEx + public sealed partial class MainWindow : Window { public MainWindow(bool isDark, bool createHidden = false) { diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml index 425f37e234..73ec67abce 100644 --- a/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeShellPage.xaml @@ -3,11 +3,17 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views" + xmlns:localModels="using:Microsoft.PowerToys.Settings.UI.OOBE.ViewModel" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="using:CommunityToolkit.WinUI.UI" HighContrastAdjustment="None" mc:Ignorable="d"> - + + - - - + - + diff --git a/src/settings-ui/Settings.UI/OobeWindow.xaml.cs b/src/settings-ui/Settings.UI/OobeWindow.xaml.cs index 983f60e641..713e06be3d 100644 --- a/src/settings-ui/Settings.UI/OobeWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/OobeWindow.xaml.cs @@ -13,14 +13,13 @@ using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Windows.ApplicationModel.Resources; using Windows.Graphics; -using WinUIEx; namespace Microsoft.PowerToys.Settings.UI { /// /// An empty window that can be used on its own or navigated to within a Frame. /// - public sealed partial class OobeWindow : WindowEx + public sealed partial class OobeWindow : Window { private PowerToysModules initialModule; diff --git a/src/settings-ui/Settings.UI/Views/ShellPage.xaml b/src/settings-ui/Settings.UI/Views/ShellPage.xaml index 7f17494080..268b25dc15 100644 --- a/src/settings-ui/Settings.UI/Views/ShellPage.xaml +++ b/src/settings-ui/Settings.UI/Views/ShellPage.xaml @@ -11,6 +11,10 @@ xmlns:views="using:Microsoft.PowerToys.Settings.UI.Views" HighContrastAdjustment="None" mc:Ignorable="d"> + @@ -18,7 +22,10 @@ - + + Date: Mon, 20 Mar 2023 13:22:51 +0000 Subject: [PATCH 103/163] [PTRun]Asynchronously load image and thumbnails (#24736) * [PTRun]Asynchronously load image and thumbnails * Bring back check for Adobe PDF generated thumbnails --------- Co-authored-by: Stefan Markovic --- .../ViewModel/ResultViewModel.cs | 56 ++++-- .../Wox.Infrastructure/Image/ImageLoader.cs | 169 +++++++++--------- 2 files changed, 132 insertions(+), 93 deletions(-) diff --git a/src/modules/launcher/PowerLauncher/ViewModel/ResultViewModel.cs b/src/modules/launcher/PowerLauncher/ViewModel/ResultViewModel.cs index 768dcdbe97..d6949c0321 100644 --- a/src/modules/launcher/PowerLauncher/ViewModel/ResultViewModel.cs +++ b/src/modules/launcher/PowerLauncher/ViewModel/ResultViewModel.cs @@ -5,6 +5,7 @@ using System; using System.Collections.ObjectModel; using System.Globalization; +using System.Threading.Tasks; using System.Windows.Input; using System.Windows.Media; using PowerLauncher.Helper; @@ -38,6 +39,9 @@ namespace PowerLauncher.ViewModel private bool _areContextButtonsActive; + private ImageSource _image; + private volatile bool _imageLoaded; + public bool AreContextButtonsActive { get => _areContextButtonsActive; @@ -191,23 +195,49 @@ namespace PowerLauncher.ViewModel { get { - var imagePath = Result.IcoPath; - if (string.IsNullOrEmpty(imagePath) && Result.Icon != null) + if (!_imageLoaded) { - try - { - return Result.Icon(); - } - catch (Exception e) - { - Log.Exception($"IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e, GetType()); - imagePath = ImageLoader.ErrorIconPath; - } + _imageLoaded = true; + _ = LoadImageAsync(); } - // will get here either when icoPath has value\icon delegate is null\when had exception in delegate - return ImageLoader.Load(imagePath, _settings.GenerateThumbnailsFromFiles); + return _image; } + + private set + { + _image = value; + OnPropertyChanged(nameof(Image)); + } + } + + private async Task LoadImageInternalAsync(string imagePath, Result.IconDelegate icon, bool loadFullImage) + { + if (string.IsNullOrEmpty(imagePath) && icon != null) + { + try + { + var image = icon(); + return image; + } + catch (Exception e) + { + Log.Exception( + $"IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", + e, + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + imagePath = ImageLoader.ErrorIconPath; + } + } + + return await ImageLoader.LoadAsync(imagePath, _settings.GenerateThumbnailsFromFiles, loadFullImage).ConfigureAwait(false); + } + + private async Task LoadImageAsync() + { + var imagePath = Result.IcoPath; + var iconDelegate = Result.Icon; + Image = await LoadImageInternalAsync(imagePath, iconDelegate, false).ConfigureAwait(false); } // Returns false if we've already reached the last item. diff --git a/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs b/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs index a52baf3c2c..fab64e67f1 100644 --- a/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs +++ b/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs @@ -64,12 +64,12 @@ namespace Wox.Infrastructure.Image UpdateIconPath(theme); Task.Run(() => { - Stopwatch.Normal("ImageLoader.Initialize - Preload images cost", () => + Stopwatch.Normal("ImageLoader.Initialize - Preload images cost", async () => { - ImageCache.Usage.AsParallel().ForAll(x => + foreach (var (path, _) in ImageCache.Usage) { - Load(x.Key, true); - }); + await LoadAsync(path, true); + } }); Log.Info($"Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}", MethodBase.GetCurrentMethod().DeclaringType); @@ -120,10 +120,9 @@ namespace Wox.Infrastructure.Image Cache, } - private static ImageResult LoadInternal(string path, bool generateThumbnailsFromFiles, bool loadFullImage = false) + private static async ValueTask LoadInternalAsync(string path, bool generateThumbnailsFromFiles, bool loadFullImage = false) { - ImageSource image; - ImageType type = ImageType.Error; + ImageResult imageResult; try { if (string.IsNullOrEmpty(path)) @@ -144,82 +143,92 @@ namespace Wox.Infrastructure.Image return new ImageResult(imageSource, ImageType.Data); } - if (!Path.IsPathRooted(path)) - { - path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path)); - } - - if (Directory.Exists(path)) - { - /* Directories can also have thumbnails instead of shell icons. - * Generating thumbnails for a bunch of folders while scrolling through - * results from Everything makes a big impact on performance and - * Wox responsibility. - * - Solution: just load the icon - */ - type = ImageType.Folder; - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); - } - else if (File.Exists(path)) - { - // Using InvariantCulture since this is internal - var extension = Path.GetExtension(path).ToLower(CultureInfo.InvariantCulture); - if (ImageExtensions.Contains(extension)) - { - type = ImageType.ImageFile; - if (loadFullImage) - { - image = LoadFullImage(path); - } - else - { - // PowerToys Run internal images are png, so we make this exception - if (extension == ".png" || generateThumbnailsFromFiles) - { - /* Although the documentation for GetImage on MSDN indicates that - * if a thumbnail is available it will return one, this has proved to not - * be the case in many situations while testing. - * - Solution: explicitly pass the ThumbnailOnly flag - */ - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.ThumbnailOnly); - } - else - { - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); - } - } - } - else if (!generateThumbnailsFromFiles || (extension == ".pdf" && WindowsThumbnailProvider.DoesPdfUseAcrobatAsProvider())) - { - // The PDF thumbnail provider from Adobe Reader and Acrobat Pro lets crash PT Run with an Dispatcher exception. (https://github.com/microsoft/PowerToys/issues/18166) - // To not run into the crash, we only request the icon of PDF files if the PDF thumbnail handler is set to Adobe Reader/Acrobat Pro. - // Also don't get thumbnail if the GenerateThumbnailsFromFiles option is off. - type = ImageType.File; - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); - } - else - { - type = ImageType.File; - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.RESIZETOFIT); - } - } - else - { - image = ImageCache[ErrorIconPath]; - path = ErrorIconPath; - } - - if (type != ImageType.Error) - { - image.Freeze(); - } + imageResult = await Task.Run(() => GetThumbnailResult(ref path, generateThumbnailsFromFiles, loadFullImage)); } catch (System.Exception e) { Log.Exception($"Failed to get thumbnail for {path}", e, MethodBase.GetCurrentMethod().DeclaringType); - type = ImageType.Error; - image = ImageCache[ErrorIconPath]; + ImageSource image = ImageCache[ErrorIconPath]; ImageCache[path] = image; + imageResult = new ImageResult(image, ImageType.Error); + } + + return imageResult; + } + + private static ImageResult GetThumbnailResult(ref string path, bool generateThumbnailsFromFiles, bool loadFullImage = false) + { + ImageSource image; + ImageType type = ImageType.Error; + + if (!Path.IsPathRooted(path)) + { + path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path)); + } + + if (Directory.Exists(path)) + { + /* Directories can also have thumbnails instead of shell icons. + * Generating thumbnails for a bunch of folders while scrolling through + * results from Everything makes a big impact on performance and + * Wox responsibility. + * - Solution: just load the icon + */ + type = ImageType.Folder; + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); + } + else if (File.Exists(path)) + { + // Using InvariantCulture since this is internal + var extension = Path.GetExtension(path).ToLower(CultureInfo.InvariantCulture); + if (ImageExtensions.Contains(extension)) + { + type = ImageType.ImageFile; + if (loadFullImage) + { + image = LoadFullImage(path); + } + else + { + // PowerToys Run internal images are png, so we make this exception + if (extension == ".png" || generateThumbnailsFromFiles) + { + /* Although the documentation for GetImage on MSDN indicates that + * if a thumbnail is available it will return one, this has proved to not + * be the case in many situations while testing. + * - Solution: explicitly pass the ThumbnailOnly flag + */ + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.ThumbnailOnly); + } + else + { + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); + } + } + } + else if (!generateThumbnailsFromFiles || (extension == ".pdf" && WindowsThumbnailProvider.DoesPdfUseAcrobatAsProvider())) + { + // The PDF thumbnail provider from Adobe Reader and Acrobat Pro lets crash PT Run with an Dispatcher exception. (https://github.com/microsoft/PowerToys/issues/18166) + // To not run into the crash, we only request the icon of PDF files if the PDF thumbnail handler is set to Adobe Reader/Acrobat Pro. + // Also don't get thumbnail if the GenerateThumbnailsFromFiles option is off. + type = ImageType.File; + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); + } + else + { + type = ImageType.File; + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.RESIZETOFIT); + } + } + else + { + image = ImageCache[ErrorIconPath]; + path = ErrorIconPath; + } + + if (type != ImageType.Error) + { + image.Freeze(); } return new ImageResult(image, type); @@ -227,9 +236,9 @@ namespace Wox.Infrastructure.Image private const bool _enableImageHash = true; - public static ImageSource Load(string path, bool generateThumbnailsFromFiles, bool loadFullImage = false) + public static async ValueTask LoadAsync(string path, bool generateThumbnailsFromFiles, bool loadFullImage = false) { - var imageResult = LoadInternal(path, generateThumbnailsFromFiles, loadFullImage); + var imageResult = await LoadInternalAsync(path, generateThumbnailsFromFiles, loadFullImage); var img = imageResult.ImageSource; if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache) From 5baa0b1904706ce618195fc3469c7f679a4bebef Mon Sep 17 00:00:00 2001 From: Niels Laute Date: Mon, 20 Mar 2023 14:27:29 +0100 Subject: [PATCH 104/163] [Settings] Bump settingscontrols version number (#24874) * Bump settingscontrols version number * Replace Experiment bitmap icon with path * Fix build * Update notice.md * Update manifests * Bump WinAppSDK for c++ project --------- Co-authored-by: Stefan Markovic --- Directory.Packages.props | 6 +- NOTICE.md | 18 +- installer/PowerToysSetup/Settings.wxs | 2 +- .../FileLocksmithUI/app.manifest | 21 +++ src/modules/Hosts/Hosts/app.manifest | 6 + .../PowerToys.MeasureToolCore.vcxproj | 8 +- .../MeasureTool/MeasureToolCore/app.manifest | 6 + .../MeasureToolCore/packages.config | 1 + .../MeasureTool/MeasureToolUI/app.manifest | 6 + src/modules/PowerOCR/PowerOCR/app.manifest | 2 +- .../colorPicker/ColorPickerUI/App.manifest | 2 +- .../editor/FancyZonesEditor/app.manifest | 2 +- .../PowerRenameUILib/PowerRenameUI.vcxproj | 8 +- .../powerrename/PowerRenameUILib/app.manifest | 6 + .../PowerRenameUILib/packages.config | 2 +- .../FluentIconsExperimentation.png | Bin 931 -> 0 bytes .../Settings.UI/Views/GeneralPage.xaml | 161 +++++------------- src/settings-ui/Settings.UI/app.manifest | 6 + 18 files changed, 125 insertions(+), 138 deletions(-) create mode 100644 src/modules/FileLocksmith/FileLocksmithUI/app.manifest delete mode 100644 src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsExperimentation.png diff --git a/Directory.Packages.props b/Directory.Packages.props index c56ffd4843..b2db3889f6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ - + @@ -25,9 +25,9 @@ - + - + diff --git a/NOTICE.md b/NOTICE.md index bb2270aae9..d100759d35 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,4 +1,5 @@ # NOTICES AND INFORMATION + This software incorporates material from third parties. - Color Picker @@ -122,7 +123,7 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -### Beta Tadele's Window Walker License +### Beta Tadele's Window Walker License **Source**: https://github.com/betsegaw/windowwalker @@ -167,11 +168,12 @@ SOFTWARE. ## PowerToy: Installer/Runner ### spdlog + **Source**: https://github.com/gabime/spdlog The MIT License (MIT) -Copyright (c) 2016 Gabi Melman. +Copyright (c) 2016 Gabi Melman. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -185,7 +187,7 @@ all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN @@ -195,8 +197,8 @@ THE SOFTWARE. This software depends on the fmt lib (MIT License), and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst - ### expected-lite + **Source**: https://github.com/martinmoene/expected-lite Boost Software License - Version 1.0 - August 17th, 2003 @@ -224,6 +226,7 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ### zip + **Source**: https://github.com/kuba--/zip This is free and unencumbered software released into the public domain. @@ -254,6 +257,7 @@ For more information, please refer to ## Utility: Measure tool ### sse2neon + We adopted some functions from it. **Source**: https://github.com/DLTcollab/sse2neon @@ -277,7 +281,7 @@ SOFTWARE. ## NuGet Packages used by PowerToys -- CommunityToolkit.Labs.WinUI.SettingsControls 0.0.7 +- CommunityToolkit.Labs.WinUI.SettingsControls 0.0.17 - CommunityToolkit.Mvvm 8.0.0 - CommunityToolkit.WinUI.UI 7.1.2 - CommunityToolkit.WinUI.UI.Controls 7.1.2 @@ -296,9 +300,9 @@ SOFTWARE. - Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Web.WebView2 1.0.1343.22 - Microsoft.Windows.CsWin32 0.2.46-beta -- Microsoft.Windows.CsWinRT 2.0.0 +- Microsoft.Windows.CsWinRT 2.0.1 - Microsoft.Windows.SDK.BuildTools 10.0.22621.755 -- Microsoft.WindowsAppSDK 1.2.221116.1 +- Microsoft.WindowsAppSDK 1.2.230217.4 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 - ModernWpfUI 0.9.4 diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs index 013ffe9589..0d2f8a1e8d 100644 --- a/installer/PowerToysSetup/Settings.wxs +++ b/installer/PowerToysSetup/Settings.wxs @@ -7,7 +7,7 @@ - + diff --git a/src/modules/FileLocksmith/FileLocksmithUI/app.manifest b/src/modules/FileLocksmith/FileLocksmithUI/app.manifest new file mode 100644 index 0000000000..575aa4df9d --- /dev/null +++ b/src/modules/FileLocksmith/FileLocksmithUI/app.manifest @@ -0,0 +1,21 @@ + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + + + + + + + diff --git a/src/modules/Hosts/Hosts/app.manifest b/src/modules/Hosts/Hosts/app.manifest index 87a882d863..6417c11e2d 100644 --- a/src/modules/Hosts/Hosts/app.manifest +++ b/src/modules/Hosts/Hosts/app.manifest @@ -12,4 +12,10 @@ PerMonitorV2, PerMonitor + + + + + + diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index 047e418422..93f8a693d9 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -1,8 +1,8 @@  + - true true @@ -143,21 +143,21 @@ - + 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}. - - + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolCore/app.manifest b/src/modules/MeasureTool/MeasureToolCore/app.manifest index 4aedd37014..1292107c7f 100644 --- a/src/modules/MeasureTool/MeasureToolCore/app.manifest +++ b/src/modules/MeasureTool/MeasureToolCore/app.manifest @@ -12,4 +12,10 @@ PerMonitorV2, PerMonitor + + + + + + diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config index 9322bb3315..e52f75ad83 100644 --- a/src/modules/MeasureTool/MeasureToolCore/packages.config +++ b/src/modules/MeasureTool/MeasureToolCore/packages.config @@ -3,4 +3,5 @@ + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolUI/app.manifest b/src/modules/MeasureTool/MeasureToolUI/app.manifest index ba6d50bdd8..908d7af1a8 100644 --- a/src/modules/MeasureTool/MeasureToolUI/app.manifest +++ b/src/modules/MeasureTool/MeasureToolUI/app.manifest @@ -12,4 +12,10 @@ PerMonitorV2, PerMonitor + + + + + + diff --git a/src/modules/PowerOCR/PowerOCR/app.manifest b/src/modules/PowerOCR/PowerOCR/app.manifest index 5102e74135..8bd61651e3 100644 --- a/src/modules/PowerOCR/PowerOCR/app.manifest +++ b/src/modules/PowerOCR/PowerOCR/app.manifest @@ -45,7 +45,7 @@ - + diff --git a/src/modules/colorPicker/ColorPickerUI/App.manifest b/src/modules/colorPicker/ColorPickerUI/App.manifest index 2f83c659bd..94b792d3fe 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.manifest +++ b/src/modules/colorPicker/ColorPickerUI/App.manifest @@ -40,7 +40,7 @@ - + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest b/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest index a52bf87d3a..598c47dd41 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest +++ b/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest @@ -40,7 +40,7 @@ - + diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index 3b8b0c4697..3e6a47e706 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -1,7 +1,7 @@  + - true @@ -185,8 +185,8 @@ - + @@ -197,10 +197,10 @@ - - + + diff --git a/src/modules/powerrename/PowerRenameUILib/app.manifest b/src/modules/powerrename/PowerRenameUILib/app.manifest index 0066894996..8554f867e0 100644 --- a/src/modules/powerrename/PowerRenameUILib/app.manifest +++ b/src/modules/powerrename/PowerRenameUILib/app.manifest @@ -12,4 +12,10 @@ PerMonitorV2, PerMonitor + + + + + + diff --git a/src/modules/powerrename/PowerRenameUILib/packages.config b/src/modules/powerrename/PowerRenameUILib/packages.config index 485d1bab40..78191badcf 100644 --- a/src/modules/powerrename/PowerRenameUILib/packages.config +++ b/src/modules/powerrename/PowerRenameUILib/packages.config @@ -5,5 +5,5 @@ - + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsExperimentation.png b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsExperimentation.png deleted file mode 100644 index dc5e0c3169f8b9a75fdeb1b3d4e76ac46d0a0b40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 931 zcmV;U16=%xP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D12{=UK~#8N?b}(X zO;H@j@q-MB5Dz?15;7H`66T6_F|H~El@q4fc)6oTm z%OQA&rXel+;m1!!p>rJGq4`LQt{i^6z7>T|8~}KS;s78maRA`Q#{od069)j^p*R3Y zOB?|B@o@l9=)?hlcPI`3(h>&%eth8vKnt4QAuW`oC3*ZD6gns19h!%S;rr|}DZ-t!19$#;5r3+f?hJjxZBrzhxyhNlf~pExahr+AE;1 zXx8?^+tQYqk!V=@;S;>5?Z_+IfOQ+*-Zkjw4ar$}F;9{AE)MM!^EPD}>Ymw17E)=o zC#xh^jb1^|4|R)X-^cJGE}>rO1>IwK`v+0C==akwqY)W}=>-JMiRv~2o zlAE+wqULmi-*kL~X5SE_w@5w!T1=@qno)gS?jb_WaQFad=XuT1ZSM;-om8*4LjBW~ zE${(2hfI=u>eKJqDd^pQDD>jRYm_y}Bzhr{JbCDzqboAYR+MWfMDL+Xcjj4&`zY6F z2((0|C38Kn<(3W@1MS>c2rYs%n(hQ=Vi00tVyaE4^aoG(_in&Osw@Bi002ovPDHLk FV1g8RweA1_ diff --git a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml index 33ee9cefe1..5f757a9491 100644 --- a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml +++ b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml @@ -16,18 +16,11 @@ - + - - - + + + @@ -47,9 +40,7 @@ Orientation="Horizontal" Spacing="18" Visibility="{Binding Mode=OneWay, Path=IsNewVersionDownloading, Converter={StaticResource BoolToVisibilityConverter}}"> - + - - + + - + - + - + - + public App() { + Logger.InitializeLogger("\\File Locksmith\\FileLocksmithUI\\Logs"); + this.InitializeComponent(); } diff --git a/src/modules/FileLocksmith/FileLocksmithUI/Helpers/Logger.cs b/src/modules/FileLocksmith/FileLocksmithUI/Helpers/Logger.cs deleted file mode 100644 index 2ec357c3cf..0000000000 --- a/src/modules/FileLocksmith/FileLocksmithUI/Helpers/Logger.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; - -namespace FileLocksmithUI.Helpers -{ - public static class Logger - { - private static readonly string ApplicationLogPath = Path.Combine(interop.Constants.AppDataPath(), "File Locksmith\\FileLocksmithUI\\Logs"); - - static Logger() - { - if (!Directory.Exists(ApplicationLogPath)) - { - Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs b/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs index 6e0adf2dcf..d473a05c58 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs +++ b/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using FileLocksmith.Interop; -using global::FileLocksmithUI.Helpers; +using ManagedCommon; namespace PowerToys.FileLocksmithUI.ViewModels { diff --git a/src/modules/Hosts/Hosts/Helpers/HostsService.cs b/src/modules/Hosts/Hosts/Helpers/HostsService.cs index 32f3ef52cb..7d90e57144 100644 --- a/src/modules/Hosts/Hosts/Helpers/HostsService.cs +++ b/src/modules/Hosts/Hosts/Helpers/HostsService.cs @@ -15,6 +15,7 @@ using System.Threading; using System.Threading.Tasks; using Hosts.Models; using Hosts.Settings; +using ManagedCommon; using Microsoft.Win32; using Settings.UI.Library.Enumerations; diff --git a/src/modules/Hosts/Hosts/Helpers/Logger.cs b/src/modules/Hosts/Hosts/Helpers/Logger.cs deleted file mode 100644 index 8a4cd558ce..0000000000 --- a/src/modules/Hosts/Hosts/Helpers/Logger.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Abstractions; -using interop; - -namespace Hosts.Helpers -{ - // TODO: use centralized logger https://github.com/microsoft/PowerToys/issues/19650 - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "Hosts\\Logs"); - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/Hosts/Hosts/Program.cs b/src/modules/Hosts/Hosts/Program.cs index 32b46a7a44..4b41f3c71a 100644 --- a/src/modules/Hosts/Hosts/Program.cs +++ b/src/modules/Hosts/Hosts/Program.cs @@ -4,7 +4,7 @@ using System; using System.Threading; -using Hosts.Helpers; +using ManagedCommon; using Microsoft.UI.Dispatching; using Microsoft.Windows.AppLifecycle; @@ -15,6 +15,8 @@ namespace Hosts [STAThread] public static void Main(string[] args) { + Logger.InitializeLogger("\\Hosts\\Logs"); + WinRT.ComWrappersSupport.InitializeComWrappers(); if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredHostsFileEditorEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) diff --git a/src/modules/Hosts/Hosts/Settings/UserSettings.cs b/src/modules/Hosts/Hosts/Settings/UserSettings.cs index 89883b510f..f1e47e6019 100644 --- a/src/modules/Hosts/Hosts/Settings/UserSettings.cs +++ b/src/modules/Hosts/Hosts/Settings/UserSettings.cs @@ -5,6 +5,7 @@ using System; using System.IO.Abstractions; using System.Threading; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Settings.UI.Library.Enumerations; diff --git a/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs b/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs index 05fd3c1d16..0896e9c115 100644 --- a/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs +++ b/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs @@ -17,6 +17,7 @@ using CommunityToolkit.WinUI.UI; using Hosts.Helpers; using Hosts.Models; using Hosts.Settings; +using ManagedCommon; using Microsoft.UI.Dispatching; namespace Hosts.ViewModels diff --git a/src/modules/Hosts/Hosts/Views/MainPage.xaml.cs b/src/modules/Hosts/Hosts/Views/MainPage.xaml.cs index 0d1ec6d1c9..37303b358b 100644 --- a/src/modules/Hosts/Hosts/Views/MainPage.xaml.cs +++ b/src/modules/Hosts/Hosts/Views/MainPage.xaml.cs @@ -6,10 +6,10 @@ using System; using System.Threading.Tasks; using System.Windows.Input; using CommunityToolkit.Mvvm.Input; -using Hosts.Helpers; using Hosts.Models; using Hosts.Settings; using Hosts.ViewModels; +using ManagedCommon; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; diff --git a/src/modules/MeasureTool/MeasureToolUI/App.xaml.cs b/src/modules/MeasureTool/MeasureToolUI/App.xaml.cs index f81bc2047f..9cb26f54f5 100644 --- a/src/modules/MeasureTool/MeasureToolUI/App.xaml.cs +++ b/src/modules/MeasureTool/MeasureToolUI/App.xaml.cs @@ -4,7 +4,6 @@ using System; using ManagedCommon; -using MeasureToolUI.Helpers; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; @@ -22,6 +21,8 @@ namespace MeasureToolUI /// public App() { + Logger.InitializeLogger("\\Measure Tool\\MeasureToolUI\\Logs"); + this.InitializeComponent(); } diff --git a/src/modules/MeasureTool/MeasureToolUI/Logger.cs b/src/modules/MeasureTool/MeasureToolUI/Logger.cs deleted file mode 100644 index 30fb1b43a4..0000000000 --- a/src/modules/MeasureTool/MeasureToolUI/Logger.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; - -namespace MeasureToolUI.Helpers -{ - public static class Logger - { - private static readonly string ApplicationLogPath = Path.Combine(interop.Constants.AppDataPath(), "Measure Tool\\MeasureToolUI\\Logs"); - - static Logger() - { - if (!Directory.Exists(ApplicationLogPath)) - { - Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/Logger.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/Logger.cs deleted file mode 100644 index 84e2c29f70..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/Logger.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; - -namespace MouseJumpUI.Helpers -{ - // TODO: use centralized logger https://github.com/microsoft/PowerToys/issues/19650 - public static class Logger - { - private static readonly string ApplicationLogPath = Path.Combine(interop.Constants.AppDataPath(), "MouseJump\\Logs"); - - static Logger() - { - if (!Directory.Exists(ApplicationLogPath)) - { - Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - var stackTrace = new StackTrace(); - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType?.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs index 542046147c..b068989062 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs @@ -8,6 +8,7 @@ using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Windows.Forms; +using ManagedCommon; using MouseJumpUI.Drawing.Models; using MouseJumpUI.Helpers; using MouseJumpUI.NativeMethods.Core; diff --git a/src/modules/MouseUtils/MouseJumpUI/Program.cs b/src/modules/MouseUtils/MouseJumpUI/Program.cs index 6770a0a7be..f5932c13c0 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Program.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Program.cs @@ -4,7 +4,7 @@ using System; using System.Windows.Forms; -using MouseJumpUI.Helpers; +using ManagedCommon; namespace MouseJumpUI; @@ -16,6 +16,8 @@ internal static class Program [STAThread] private static void Main() { + Logger.InitializeLogger("\\MouseJump\\Logs"); + // To customize application configuration such as set high DPI settings or default font, // see https://aka.ms/applicationconfiguration. ApplicationConfiguration.Initialize(); diff --git a/src/modules/PowerOCR/PowerOCR/App.xaml.cs b/src/modules/PowerOCR/PowerOCR/App.xaml.cs index 7a83b93897..60c6c03b81 100644 --- a/src/modules/PowerOCR/PowerOCR/App.xaml.cs +++ b/src/modules/PowerOCR/PowerOCR/App.xaml.cs @@ -26,6 +26,8 @@ public partial class App : Application, IDisposable public App() { + Logger.InitializeLogger("\\TextExtractor\\Logs"); + NativeThreadCTS = new CancellationTokenSource(); } diff --git a/src/modules/PowerOCR/PowerOCR/Helpers/Logger.cs b/src/modules/PowerOCR/PowerOCR/Helpers/Logger.cs deleted file mode 100644 index 447ff6f97b..0000000000 --- a/src/modules/PowerOCR/PowerOCR/Helpers/Logger.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Abstractions; -using interop; - -namespace PowerOCR.Helpers -{ - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "TextExtractor\\Logs"); - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType?.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/PowerOCR/PowerOCR/Helpers/WindowUtilities.cs b/src/modules/PowerOCR/PowerOCR/Helpers/WindowUtilities.cs index d4301defc5..939f987ace 100644 --- a/src/modules/PowerOCR/PowerOCR/Helpers/WindowUtilities.cs +++ b/src/modules/PowerOCR/PowerOCR/Helpers/WindowUtilities.cs @@ -4,6 +4,7 @@ using System.Windows; using System.Windows.Forms; +using ManagedCommon; using Microsoft.PowerToys.Telemetry; using PowerOCR.Helpers; diff --git a/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs b/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs index 4a0f8811c9..66e1374569 100644 --- a/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs +++ b/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs @@ -9,6 +9,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; +using ManagedCommon; using Microsoft.PowerToys.Telemetry; using PowerOCR.Helpers; using PowerOCR.Settings; diff --git a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs index 4019c0f354..d36bf19faa 100644 --- a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs +++ b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs @@ -7,6 +7,7 @@ using System.ComponentModel.Composition; using System.IO; using System.IO.Abstractions; using System.Threading; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/modules/awake/Awake/Program.cs b/src/modules/awake/Awake/Program.cs index 1d016300ff..b2f1244505 100644 --- a/src/modules/awake/Awake/Program.cs +++ b/src/modules/awake/Awake/Program.cs @@ -25,6 +25,7 @@ using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Console; using Windows.Win32.System.Power; +using Logger = NLog.Logger; #pragma warning disable CS8602 // Dereference of a possibly null reference. #pragma warning disable CS8603 // Possible null reference return. diff --git a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs index 2a448c50e2..9097ec9c29 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs +++ b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs @@ -6,7 +6,6 @@ using System; using System.ComponentModel.Composition; using System.Threading; using System.Windows; -using ColorPicker.Helpers; using ColorPicker.Mouse; using Common.UI; using ManagedCommon; diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/ChangeWindowPositionBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/ChangeWindowPositionBehavior.cs index d8e1eb85f9..41f27ea3eb 100644 --- a/src/modules/colorPicker/ColorPickerUI/Behaviors/ChangeWindowPositionBehavior.cs +++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/ChangeWindowPositionBehavior.cs @@ -5,6 +5,7 @@ using System.Windows; using ColorPicker.Helpers; using ColorPicker.Mouse; +using ManagedCommon; using Microsoft.Xaml.Behaviors; namespace ColorPicker.Behaviors diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs index de9847ceb0..fb10b99b75 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs @@ -9,6 +9,7 @@ using System.Windows.Interop; using ColorPicker.Settings; using ColorPicker.ViewModelContracts; using Common.UI; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; namespace ColorPicker.Helpers diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ClipboardHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ClipboardHelper.cs index 627bd382c2..1f87d19fa3 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ClipboardHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ClipboardHelper.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Windows; +using ManagedCommon; using static ColorPicker.NativeMethods; namespace ColorPicker.Helpers diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs deleted file mode 100644 index 93116cbaa7..0000000000 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Abstractions; -using interop; - -namespace ColorPicker.Helpers -{ - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "ColorPicker\\Logs"); - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/SessionEventHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/SessionEventHelper.cs index da21c094de..8a5838d265 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/SessionEventHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/SessionEventHelper.cs @@ -4,6 +4,7 @@ using System; using ColorPicker.Telemetry; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Telemetry; diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs index fbbb416b71..d8c93bf1da 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs @@ -4,7 +4,7 @@ using System; using System.IO.Abstractions; -using ColorPicker.Helpers; +using ManagedCommon; using Microsoft.Win32; namespace ColorPicker.Mouse diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs index d75e57a024..080ec9d35e 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Input; using ColorPicker.Helpers; +using ManagedCommon; using static ColorPicker.NativeMethods; namespace ColorPicker.Mouse diff --git a/src/modules/colorPicker/ColorPickerUI/Program.cs b/src/modules/colorPicker/ColorPickerUI/Program.cs index 9c97e8308b..b3e502171f 100644 --- a/src/modules/colorPicker/ColorPickerUI/Program.cs +++ b/src/modules/colorPicker/ColorPickerUI/Program.cs @@ -6,6 +6,7 @@ using System; using ColorPicker.Helpers; using ColorPicker.Mouse; using ColorPickerUI; +using ManagedCommon; namespace ColorPicker { @@ -16,6 +17,8 @@ namespace ColorPicker [STAThread] public static void Main(string[] args) { + Logger.InitializeLogger("\\ColorPicker\\Logs"); + _args = args; Logger.LogInfo($"Color Picker started with pid={Environment.ProcessId}"); diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index 175413865c..f7f815fd9f 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -10,6 +10,7 @@ using System.IO.Abstractions; using System.Linq; using System.Threading; using ColorPicker.Common; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs index f3ee395100..faec50e2aa 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Windows; using System.Windows.Input; using Common.UI; -using FancyZonesEditor.Logs; using FancyZonesEditor.Utils; using ManagedCommon; @@ -55,6 +54,8 @@ namespace FancyZonesEditor public App() { + Logger.InitializeLogger("\\FancyZones\\Editor\\Logs"); + // DebugModeCheck(); NativeThreadCTS = new CancellationTokenSource(); FancyZonesEditorIO = new FancyZonesEditorIO(); diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs index d6e38930b3..2bbaa067fe 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs @@ -4,8 +4,8 @@ using System.Windows; using System.Windows.Input; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs b/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs index 6bd3f3b652..4a29c81194 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs @@ -4,8 +4,8 @@ using System; using System.Windows; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs index 749ee5a34d..b71cdecb17 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs @@ -6,8 +6,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Controls; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs index 3b5af0ac1a..d49ad9232e 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs @@ -10,8 +10,8 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs index 7cf7257522..61aad3945b 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs @@ -7,8 +7,8 @@ using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs index 9645be9f6d..687895021b 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs @@ -11,9 +11,9 @@ using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Input; using Common.UI; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; using FancyZonesEditor.Utils; +using ManagedCommon; using ModernWpf.Controls; namespace FancyZonesEditor diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs index 0545ce6c55..5677fb565f 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs @@ -6,8 +6,8 @@ using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs index 26b0dfa151..2ddf1cafaf 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs @@ -12,8 +12,8 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; using System.Windows; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor.Utils { diff --git a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs index 999620ac32..01d87d034c 100644 --- a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs +++ b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Text; using System.Unicode; using System.Windows; +using ManagedCommon; using PowerAccent.Core.Services; using PowerAccent.Core.Tools; using PowerToys.PowerAccentKeyboardService; @@ -41,6 +42,8 @@ public class PowerAccent : IDisposable public PowerAccent() { + Logger.InitializeLogger("\\QuickAccent\\Logs"); + LoadUnicodeInfoCache(); _keyboardListener = new KeyboardListener(); diff --git a/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs b/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs index d3d89821c9..50ba68f546 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs @@ -4,6 +4,7 @@ using System.IO.Abstractions; using System.Text.Json; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/modules/poweraccent/PowerAccent.Core/Tools/Logger.cs b/src/modules/poweraccent/PowerAccent.Core/Tools/Logger.cs deleted file mode 100644 index f79e3a42cc..0000000000 --- a/src/modules/poweraccent/PowerAccent.Core/Tools/Logger.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Abstractions; -using interop; - -namespace PowerAccent.Core.Tools -{ - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "QuickAccent\\Logs"); - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - string logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - System.Reflection.MethodBase methodName = stackTrace.GetFrame(3)?.GetMethod(); - string className = methodName?.DeclaringType?.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs b/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs index e4c77b9d0b..af3ff38db9 100644 --- a/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs +++ b/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Threading; using System.Windows; using Common.UI; +using ManagedCommon; using PowerAccent.Core.Tools; namespace PowerAccent.UI diff --git a/src/modules/poweraccent/PowerAccent.UI/Program.cs b/src/modules/poweraccent/PowerAccent.UI/Program.cs index eee06b2038..9ca3de95be 100644 --- a/src/modules/poweraccent/PowerAccent.UI/Program.cs +++ b/src/modules/poweraccent/PowerAccent.UI/Program.cs @@ -22,6 +22,8 @@ internal static class Program [STAThread] public static void Main(string[] args) { + Logger.InitializeLogger("\\QuickAccent\\Logs"); + if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredQuickAccentEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs index 6c8d832b2f..f4aac30d7e 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs @@ -12,8 +12,8 @@ using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows.Forms; using Common; +using ManagedCommon; using Microsoft.PowerToys.PreviewHandler.Monaco.Formatters; -using Microsoft.PowerToys.PreviewHandler.Monaco.Helpers; using Microsoft.PowerToys.PreviewHandler.Monaco.Properties; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; diff --git a/src/modules/previewpane/MonacoPreviewHandler/Program.cs b/src/modules/previewpane/MonacoPreviewHandler/Program.cs index 8589b12c30..b6bcd511f8 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Program.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/Program.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Windows.Threading; using Common.UI; using interop; +using ManagedCommon; namespace Microsoft.PowerToys.PreviewHandler.Monaco { @@ -21,6 +22,8 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco [STAThread] public static void Main(string[] args) { + Logger.InitializeLogger("\\FileExplorer_localLow\\Monaco\\logs", true); + ApplicationConfiguration.Initialize(); if (args != null) { diff --git a/src/modules/previewpane/MonacoPreviewHandler/helpers/Logger.cs b/src/modules/previewpane/MonacoPreviewHandler/helpers/Logger.cs deleted file mode 100644 index 52d8e3f1e1..0000000000 --- a/src/modules/previewpane/MonacoPreviewHandler/helpers/Logger.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO.Abstractions; - -namespace Microsoft.PowerToys.PreviewHandler.Monaco.Helpers -{ - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = System.Environment.GetEnvironmentVariable("USERPROFILE") + "\\AppData\\LocalLow\\Microsoft\\PowerToys\\logs\\FileExplorer_localLow\\Monaco"; - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Monaco-log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - public static void LogTrace() - { - Log(string.Empty, "Trace"); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs index e59e685b76..1de3c04030 100644 --- a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs +++ b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs index 36bed6bf48..1b2dc81fb7 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs @@ -15,6 +15,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Threading; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs index e09687ff77..a77f9e2c62 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs @@ -6,8 +6,8 @@ using System; using System.IO; using System.IO.Abstractions; using System.Text.Json; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.Library { diff --git a/src/settings-ui/Settings.UI.Library/Utilities/Logger.cs b/src/settings-ui/Settings.UI.Library/Utilities/Logger.cs deleted file mode 100644 index fbac2c88fb..0000000000 --- a/src/settings-ui/Settings.UI.Library/Utilities/Logger.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO.Abstractions; - -namespace Microsoft.PowerToys.Settings.UI.Library.Utilities -{ - public static class Logger - { - private static readonly IFileSystem FileSystem = new FileSystem(); - private static readonly IPath Path = FileSystem.Path; - private static readonly IDirectory Directory = FileSystem.Directory; - - private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs"); - - static Logger() - { - if (!Directory.Exists(ApplicationLogPath)) - { - Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - public static void LogError(string message) - { - Log(message, "ERROR"); -#if DEBUG - Debugger.Break(); -#endif - } - - public static void LogError(string message, Exception e) - { - Log( - message + Environment.NewLine + - e?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - e?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - e?.StackTrace, - "ERROR"); -#if DEBUG - Debugger.Break(); -#endif - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/settings-ui/Settings.UI/App.xaml.cs b/src/settings-ui/Settings.UI/App.xaml.cs index 14512c75e3..75cc05883e 100644 --- a/src/settings-ui/Settings.UI/App.xaml.cs +++ b/src/settings-ui/Settings.UI/App.xaml.cs @@ -72,6 +72,8 @@ namespace Microsoft.PowerToys.Settings.UI /// public App() { + Logger.InitializeLogger("\\Settings\\Logs"); + this.InitializeComponent(); } diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs index 258b8625cd..fd35eafc39 100644 --- a/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeWhatsNew.xaml.cs @@ -14,7 +14,7 @@ using System.Text.Json.Serialization; using System.Text.RegularExpressions; using System.Threading.Tasks; using CommunityToolkit.WinUI.UI.Controls; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs index 28dd371fcc..5980e31148 100644 --- a/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs index db3ac314e2..874c20722a 100644 --- a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs @@ -13,6 +13,7 @@ using System.Runtime.CompilerServices; using System.Text.Json; using System.Threading.Tasks; using global::PowerToys.GPOWrapper; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; diff --git a/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs index ed9432c0c5..ad7ba3c679 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs @@ -8,10 +8,10 @@ using System.ComponentModel; using System.IO; using System.Linq; using global::PowerToys.GPOWrapper; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.ViewModels { diff --git a/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs index a12e5cfdfb..771ac564d7 100644 --- a/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs @@ -12,10 +12,10 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using global::PowerToys.GPOWrapper; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands; using Microsoft.PowerToys.Settings.Utilities; using Windows.ApplicationModel.Resources; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs index 039ee3e410..1f1b42635b 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs @@ -6,10 +6,10 @@ using System; using System.IO; using System.Runtime.CompilerServices; using global::PowerToys.GPOWrapper; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.ViewModels { diff --git a/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs b/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs index 35dccfbb50..2079c73eae 100644 --- a/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/AwakePage.xaml.cs @@ -5,10 +5,10 @@ using System; using System.IO; using System.IO.Abstractions; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs index 3a3a68ca11..92c3fd7da1 100644 --- a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs @@ -3,17 +3,15 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Windows.ApplicationModel.Resources; -using Windows.Storage; using Windows.Storage.Pickers; namespace Microsoft.PowerToys.Settings.UI.Views diff --git a/src/settings-ui/Settings.UI/Views/ImageResizerPage.xaml.cs b/src/settings-ui/Settings.UI/Views/ImageResizerPage.xaml.cs index 6e47db0455..2f6de716ef 100644 --- a/src/settings-ui/Settings.UI/Views/ImageResizerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/ImageResizerPage.xaml.cs @@ -4,9 +4,9 @@ using System; using System.Globalization; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/Views/PowerLauncherPage.xaml.cs b/src/settings-ui/Settings.UI/Views/PowerLauncherPage.xaml.cs index 01a79283f8..dcec402c51 100644 --- a/src/settings-ui/Settings.UI/Views/PowerLauncherPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/PowerLauncherPage.xaml.cs @@ -5,9 +5,9 @@ using System; using System.Collections.ObjectModel; using System.IO; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; From f5325f025cfe7b5b35429a83381ed75931326e84 Mon Sep 17 00:00:00 2001 From: ACGNnsj Date: Tue, 21 Mar 2023 17:43:15 +0800 Subject: [PATCH 108/163] Add missing project references (#24925) Signed-off-by: ACGMN --- src/modules/Hosts/Hosts/Hosts.csproj | 1 + src/modules/PowerOCR/PowerOCR/PowerOCR.csproj | 1 + src/modules/awake/Awake/Awake.csproj | 1 + src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj | 1 + src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj | 1 + src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj | 1 + .../previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj | 1 + src/settings-ui/Settings.UI/PowerToys.Settings.csproj | 1 + 8 files changed, 8 insertions(+) diff --git a/src/modules/Hosts/Hosts/Hosts.csproj b/src/modules/Hosts/Hosts/Hosts.csproj index 34f7d3c189..36e0ac4436 100644 --- a/src/modules/Hosts/Hosts/Hosts.csproj +++ b/src/modules/Hosts/Hosts/Hosts.csproj @@ -64,6 +64,7 @@ + diff --git a/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj b/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj index cb56d9cba8..5182b93440 100644 --- a/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj +++ b/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj @@ -55,6 +55,7 @@ + diff --git a/src/modules/awake/Awake/Awake.csproj b/src/modules/awake/Awake/Awake.csproj index e362bc4b84..34c720a427 100644 --- a/src/modules/awake/Awake/Awake.csproj +++ b/src/modules/awake/Awake/Awake.csproj @@ -68,6 +68,7 @@ + diff --git a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj index 33eb5965cb..14bdd57df1 100644 --- a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj +++ b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj @@ -70,6 +70,7 @@ + diff --git a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj index 3de0a079a3..49220b81a6 100644 --- a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj +++ b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj @@ -35,6 +35,7 @@ + diff --git a/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj b/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj index 3225385554..65e002bfd5 100644 --- a/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj +++ b/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj @@ -38,6 +38,7 @@ + diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj index 1656499ec7..18e47055d0 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj @@ -65,6 +65,7 @@ + diff --git a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj index 7a5f19c1c2..47e4712c5d 100644 --- a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj +++ b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj @@ -102,6 +102,7 @@ + From 061d724f44d25b3fed3c76baa27d99263cc25589 Mon Sep 17 00:00:00 2001 From: Jay <65828559+Jay-o-Way@users.noreply.github.com> Date: Tue, 21 Mar 2023 13:02:28 +0100 Subject: [PATCH 109/163] Settings: small tweaks to VCM page (#24735) * Remove `IsVideoConferenceBuild` * small tweaks VCM page * Button: Visibility --> IsEnabled * Revert "Remove `IsVideoConferenceBuild`" This reverts commit acae5a5f3a4a1baedc48aeb7dc48e23116693307. * negate IsOpen bool --- .../Settings.UI/Views/VideoConference.xaml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/settings-ui/Settings.UI/Views/VideoConference.xaml b/src/settings-ui/Settings.UI/Views/VideoConference.xaml index ade28abe6a..7d94dde096 100644 --- a/src/settings-ui/Settings.UI/Views/VideoConference.xaml +++ b/src/settings-ui/Settings.UI/Views/VideoConference.xaml @@ -15,6 +15,10 @@ x:Name="EmptyToCollapsedConverter" EmptyValue="Collapsed" NotEmptyValue="Visible" /> + + Severity="Informational"> + Severity="Informational"/> + IsEnabled="{Binding Path=CameraImageOverlayPath, Mode=OneWay, Converter={StaticResource EmptyToBoolConverter}}" /> - + + CornerRadius="4"> Date: Tue, 21 Mar 2023 22:59:45 +0000 Subject: [PATCH 110/163] [Settings] [VCM] Allow selecting a picture for VCM camera mute when running elevated (#24247) * Fix the issue of not being able to select an image for VCM Camera mute while PowerToys is running elevated. * change the buffer size for Path and filename * move DLL import to native methods file * Adding comment to rember to move back to WinUI3 when it is fixed * making Dll Import methods internal * changes from comments * fix new c# errors * Remove async --- .../Settings.UI/Helpers/NativeMethods.cs | 3 ++ .../Settings.UI/Helpers/OpenFileName.cs | 36 ++++++++++++++++++ .../ViewModels/VideoConferenceViewModel.cs | 8 ++-- .../Settings.UI/Views/VideoConference.xaml.cs | 37 ++++++++++++------- 4 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 src/settings-ui/Settings.UI/Helpers/OpenFileName.cs diff --git a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs index 71ff81cf54..4bb0b1a7f9 100644 --- a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs +++ b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs @@ -44,6 +44,9 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers [DllImport("shell32.dll")] internal static extern int SHGetPathFromIDListW(IntPtr pidl, IntPtr pszPath); + [DllImport("Comdlg32.dll", CharSet = CharSet.Unicode)] + internal static extern bool GetOpenFileName([In, Out] OpenFileName openFileName); + #pragma warning disable CA1401 // P/Invokes should not be visible [DllImport("user32.dll")] public static extern bool ShowWindow(System.IntPtr hWnd, int nCmdShow); diff --git a/src/settings-ui/Settings.UI/Helpers/OpenFileName.cs b/src/settings-ui/Settings.UI/Helpers/OpenFileName.cs new file mode 100644 index 0000000000..3d7c1d8e90 --- /dev/null +++ b/src/settings-ui/Settings.UI/Helpers/OpenFileName.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Microsoft.PowerToys.Settings.UI.Helpers +{ + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Reviewed.")] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class OpenFileName + { + public int StructSize; + public IntPtr Hwnd = IntPtr.Zero; + public IntPtr Hinst = IntPtr.Zero; + public string Filter; + public string CustFilter; + public int CustFilterMax; + public int FilterIndex; + public string File; + public int MaxFile; + public string FileTitle; + public int MaxFileTitle; + public string InitialDir; + public string Title; + public int Flags; + public short FileOffset; + public short FileExtMax; + public string DefExt; + public int CustData; + public IntPtr Hook = IntPtr.Zero; + public string Template; + } +} diff --git a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs index c2bd3eb9af..b746f1962b 100644 --- a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs @@ -30,7 +30,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private Func SendConfigMSG { get; } - private Func> PickFileDialog { get; } + private Func PickFileDialog { get; } private string _settingsConfigFileFolder = string.Empty; @@ -39,7 +39,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels ISettingsRepository settingsRepository, ISettingsRepository videoConferenceSettingsRepository, Func ipcMSGCallBackFunc, - Func> pickFileDialog, + Func pickFileDialog, string configFileSubfolder = "") { PickFileDialog = pickFileDialog; @@ -197,11 +197,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels RaisePropertyChanged(nameof(CameraImageOverlayPath)); } - private async void SelectOverlayImageAction() + private void SelectOverlayImageAction() { try { - string pickedImage = await PickFileDialog().ConfigureAwait(true); + string pickedImage = PickFileDialog(); if (pickedImage != null) { CameraImageOverlayPath = pickedImage; diff --git a/src/settings-ui/Settings.UI/Views/VideoConference.xaml.cs b/src/settings-ui/Settings.UI/Views/VideoConference.xaml.cs index 1a5d80a035..9cf7b71cd7 100644 --- a/src/settings-ui/Settings.UI/Views/VideoConference.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/VideoConference.xaml.cs @@ -3,13 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; -using Windows.Storage; -using Windows.Storage.Pickers; namespace Microsoft.PowerToys.Settings.UI.Views { @@ -17,19 +16,31 @@ namespace Microsoft.PowerToys.Settings.UI.Views { private VideoConferenceViewModel ViewModel { get; set; } - private static async Task PickFileDialog() + private static string PickFileDialog() { - FileOpenPicker openPicker = new FileOpenPicker(); - openPicker.ViewMode = PickerViewMode.Thumbnail; - openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; - openPicker.FileTypeFilter.Add(".jpg"); - openPicker.FileTypeFilter.Add(".jpeg"); - openPicker.FileTypeFilter.Add(".png"); - var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.GetSettingsWindow()); - WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hwnd); + // this code was changed to solve the problem with WinUI3 that prevents to select a file + // while running elevated, when the issue is solved in WinUI3 it should be changed back + OpenFileName openFileName = new OpenFileName(); + openFileName.StructSize = Marshal.SizeOf(openFileName); + openFileName.Filter = "Images(*.jpg,*.jpeg,*.png)\0*.jpg;*.jpeg;*.png\0"; - StorageFile file = await openPicker.PickSingleFileAsync(); - return file?.Path; + // make buffer 65k bytes big as the MAX_PATH can be ~32k chars if long path is enable + // and unicode uses 2 bytes per character + openFileName.File = new string(new char[65000]); + openFileName.MaxFile = openFileName.File.Length; + openFileName.FileTitle = new string(new char[65000]); + openFileName.MaxFileTitle = openFileName.FileTitle.Length; + openFileName.InitialDir = null; + openFileName.Title = string.Empty; + openFileName.DefExt = null; + + bool result = NativeMethods.GetOpenFileName(openFileName); + if (result) + { + return openFileName.File; + } + + return null; } public VideoConferencePage() From dcfc1692404cfead6fe87127c0604fe1c2c000fe Mon Sep 17 00:00:00 2001 From: stevenlele <15964380+stevenlele@users.noreply.github.com> Date: Wed, 22 Mar 2023 17:43:05 +0800 Subject: [PATCH 111/163] Update PowerAccent character list (#24818) --- .../poweraccent/PowerAccent.Core/Languages.cs | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 2ca2ac8d50..6521fc9c73 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -95,7 +95,7 @@ namespace PowerAccent.Core LetterKey.VK_7 => new string[] { "₇", "⁷" }, LetterKey.VK_8 => new string[] { "₈", "⁸" }, LetterKey.VK_9 => new string[] { "₉", "⁹" }, - LetterKey.VK_A => new string[] { "á", "à", "ä", "â", "ă", "å", "α", "ā", "ą", "ȧ", "ã", "æ" }, + LetterKey.VK_A => new string[] { "á", "à", "ä", "â", "ă", "å", "α", "ā", "ą", "ȧ", "ã", "ǎ", "æ" }, LetterKey.VK_B => new string[] { "ḃ", "β" }, LetterKey.VK_C => new string[] { "ç", "ć", "ĉ", "č", "ċ", "¢", "χ" }, LetterKey.VK_D => new string[] { "ď", "ḋ", "đ", "δ", "ð" }, @@ -103,7 +103,7 @@ namespace PowerAccent.Core LetterKey.VK_F => new string[] { "ƒ", "ḟ" }, LetterKey.VK_G => new string[] { "ğ", "ģ", "ǧ", "ġ", "ĝ", "ǥ", "γ" }, LetterKey.VK_H => new string[] { "ḣ", "ĥ", "ħ" }, - LetterKey.VK_I => new string[] { "ï", "î", "í", "ì", "ī", "į", "i", "ı", "İ", "ι", "ǐ" }, + LetterKey.VK_I => new string[] { "ï", "î", "í", "ì", "ī", "į", "ı", "İ", "ι", "ǐ" }, LetterKey.VK_J => new string[] { "ĵ" }, LetterKey.VK_K => new string[] { "ķ", "ǩ", "κ" }, LetterKey.VK_L => new string[] { "ĺ", "ľ", "ļ", "ł", "₺", "λ" }, @@ -114,7 +114,7 @@ namespace PowerAccent.Core LetterKey.VK_R => new string[] { "ŕ", "ř", "ṙ", "₹", "ρ" }, LetterKey.VK_S => new string[] { "ś", "ş", "š", "ș", "ṡ", "ŝ", "ß", "σ", "$" }, LetterKey.VK_T => new string[] { "ţ", "ť", "ț", "ṫ", "ŧ", "θ", "τ", "þ" }, - LetterKey.VK_U => new string[] { "û", "ú", "ü", "ŭ", "ű", "ù", "ů", "ū", "ų", "υ", "ǖ", "ǘ", "ǚ", "ǜ" }, + LetterKey.VK_U => new string[] { "û", "ú", "ü", "ŭ", "ű", "ù", "ů", "ū", "ų", "ǔ", "υ", "ǖ", "ǘ", "ǚ", "ǜ" }, LetterKey.VK_W => new string[] { "ẇ", "ŵ", "₩" }, LetterKey.VK_X => new string[] { "ẋ", "ξ" }, LetterKey.VK_Y => new string[] { "ÿ", "ŷ", "ý", "ẏ" }, @@ -282,23 +282,23 @@ namespace PowerAccent.Core { return letter switch { - LetterKey.VK_1 => new string[] { "̄", "ˉ", "1" }, - LetterKey.VK_2 => new string[] { "́", "ˊ", "2" }, - LetterKey.VK_3 => new string[] { "̌", "ˇ", "3" }, - LetterKey.VK_4 => new string[] { "̀", "ˋ", "4" }, - LetterKey.VK_5 => new string[] { "˙", "5" }, - LetterKey.VK_A => new string[] { "ā", "á", "ǎ", "à", "a", "ɑ", "ɑ̄", "ɑ́", "ɑ̌", "ɑ̀" }, - LetterKey.VK_C => new string[] { "ĉ", "c" }, - LetterKey.VK_E => new string[] { "ē", "é", "ě", "è", "e", "ê", "ê̄", "ế", "ê̌", "ề" }, - LetterKey.VK_I => new string[] { "ī", "í", "ǐ", "ì", "i" }, - LetterKey.VK_M => new string[] { "m̄", "ḿ", "m̌", "m̀", "m" }, - LetterKey.VK_N => new string[] { "n̄", "ń", "ň", "ǹ", "n", "ŋ", "ŋ̄", "ŋ́", "ŋ̌", "ŋ̀" }, - LetterKey.VK_O => new string[] { "ō", "ó", "ǒ", "ò", "o" }, - LetterKey.VK_S => new string[] { "ŝ", "s" }, - LetterKey.VK_U => new string[] { "ū", "ú", "ǔ", "ù", "u", "ü", "ǖ", "ǘ", "ǚ", "ǜ" }, - LetterKey.VK_V => new string[] { "ǖ", "ǘ", "ǚ", "ǜ", "ü" }, - LetterKey.VK_Y => new string[] { "¥", "y" }, - LetterKey.VK_Z => new string[] { "ẑ", "z" }, + LetterKey.VK_1 => new string[] { "\u0304", "ˉ" }, + LetterKey.VK_2 => new string[] { "\u0301", "ˊ" }, + LetterKey.VK_3 => new string[] { "\u030c", "ˇ" }, + LetterKey.VK_4 => new string[] { "\u0300", "ˋ" }, + LetterKey.VK_5 => new string[] { "·" }, + LetterKey.VK_A => new string[] { "ā", "á", "ǎ", "à", "ɑ", "ɑ\u0304", "ɑ\u0301", "ɑ\u030c", "ɑ\u0300" }, + LetterKey.VK_C => new string[] { "ĉ" }, + LetterKey.VK_E => new string[] { "ē", "é", "ě", "è", "ê", "ê\u0304", "ế", "ê\u030c", "ề" }, + LetterKey.VK_I => new string[] { "ī", "í", "ǐ", "ì" }, + LetterKey.VK_M => new string[] { "m\u0304", "ḿ", "m\u030c", "m\u0300" }, + LetterKey.VK_N => new string[] { "n\u0304", "ń", "ň", "ǹ", "ŋ", "ŋ\u0304", "ŋ\u0301", "ŋ\u030c", "ŋ\u0300" }, + LetterKey.VK_O => new string[] { "ō", "ó", "ǒ", "ò" }, + LetterKey.VK_S => new string[] { "ŝ" }, + LetterKey.VK_U => new string[] { "ū", "ú", "ǔ", "ù", "ü", "ǖ", "ǘ", "ǚ", "ǜ" }, + LetterKey.VK_V => new string[] { "ü", "ǖ", "ǘ", "ǚ", "ǜ" }, + LetterKey.VK_Y => new string[] { "¥" }, + LetterKey.VK_Z => new string[] { "ẑ" }, _ => Array.Empty(), }; } @@ -458,20 +458,20 @@ namespace PowerAccent.Core { return letter switch { - LetterKey.VK_A => new string[] { "שׂ", "שׁ", "ְ" }, + LetterKey.VK_A => new string[] { "שׂ", "שׁ", "\u05b0" }, LetterKey.VK_B => new string[] { "׆" }, - LetterKey.VK_E => new string[] { "ָ", "ֳ", "ֻ" }, + LetterKey.VK_E => new string[] { "\u05b8", "\u05b3", "\u05bb" }, LetterKey.VK_G => new string[] { "ױ" }, - LetterKey.VK_H => new string[] { "ײ", "ײַ", "ׯ", "ִ" }, - LetterKey.VK_M => new string[] { "ֵ" }, - LetterKey.VK_P => new string[] { "ַ", "ֲ" }, - LetterKey.VK_S => new string[] { "ּ" }, + LetterKey.VK_H => new string[] { "ײ", "ײַ", "ׯ", "\u05b4" }, + LetterKey.VK_M => new string[] { "\u05b5" }, + LetterKey.VK_P => new string[] { "\u05b7", "\u05b2" }, + LetterKey.VK_S => new string[] { "\u05bc" }, LetterKey.VK_T => new string[] { "ﭏ" }, - LetterKey.VK_U => new string[] { "וֹ", "וּ", "װ", "ֹ" }, - LetterKey.VK_X => new string[] { "ֶ", "ֱ" }, + LetterKey.VK_U => new string[] { "וֹ", "וּ", "װ", "\u05b9" }, + LetterKey.VK_X => new string[] { "\u05b6", "\u05b1" }, LetterKey.VK_Y => new string[] { "ױ" }, LetterKey.VK_COMMA => new string[] { "”", "’", "״", "׳" }, - LetterKey.VK_PERIOD => new string[] { "֫", "ֽ", "ֿ" }, + LetterKey.VK_PERIOD => new string[] { "\u05ab", "\u05bd", "\u05bf" }, LetterKey.VK_MINUS => new string[] { "–", "־" }, _ => Array.Empty(), }; From ac8c0324e2f0cbfb26f3cfee61921f86b78c6601 Mon Sep 17 00:00:00 2001 From: Den Date: Thu, 23 Mar 2023 14:21:05 -0700 Subject: [PATCH 112/163] Update control to make interaction responsive (#24977) --- .../Settings.UI/Strings/en-us/Resources.resw | 12 ++++++--- .../Settings.UI/Views/AwakePage.xaml | 27 ++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 3473d8344e..e882489790 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -1834,8 +1834,8 @@ From there, simply click on one of the supported files in the File Explorer and This setting is only available when keeping the PC awake - - Keep custom awakeness state until a specific date and time + + Keep custom awake state until a specific date and time Mode @@ -1849,6 +1849,12 @@ From there, simply click on one of the supported files in the File Explorer and Minutes + + End date + + + End time + Awake Module name, do not loc @@ -2129,7 +2135,7 @@ From there, simply click on one of the supported files in the File Explorer and Interval before returning to the previous awakeness state - + End date and time diff --git a/src/settings-ui/Settings.UI/Views/AwakePage.xaml b/src/settings-ui/Settings.UI/Views/AwakePage.xaml index 29823d4fe7..749737ab04 100644 --- a/src/settings-ui/Settings.UI/Views/AwakePage.xaml +++ b/src/settings-ui/Settings.UI/Views/AwakePage.xaml @@ -55,21 +55,28 @@ - - - - - - - + Visibility="{x:Bind ViewModel.IsExpirationConfigurationEnabled, Mode=OneWay}" IsExpanded="True"> + + + + + + + + + + - + + Date: Fri, 24 Mar 2023 10:51:03 +0100 Subject: [PATCH 113/163] [File Locksmith] Add context menu icon (#24986) * Add FLS icon * fix icon include * fix * final code fix --- .../FileLocksmithExt/ExplorerCommand.cpp | 47 ++++++++----------- .../FileLocksmithExt/ExplorerCommand.h | 2 + .../FileLocksmithExt/FileLocksmithExt.base.rc | 7 +++ .../FileLocksmithExt/FileLocksmithExt.vcxproj | 3 ++ .../FileLocksmithExt/resource.base.h | 1 + 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp index e61d50da9e..5015d2d9e3 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp @@ -7,6 +7,9 @@ #include "Trace.h" #include "Generated Files/resource.h" +#include +#include + // Implementations of inherited IUnknown methods IFACEMETHODIMP ExplorerCommand::QueryInterface(REFIID riid, void** ppv) @@ -46,9 +49,10 @@ IFACEMETHODIMP ExplorerCommand::GetTitle(IShellItemArray* psiItemArray, LPWSTR* IFACEMETHODIMP ExplorerCommand::GetIcon(IShellItemArray* psiItemArray, LPWSTR* ppszIcon) { - // Path to the icon should be computed relative to the path of this module - ppszIcon = NULL; - return E_NOTIMPL; + std::wstring iconResourcePath = get_module_filename(); + iconResourcePath += L",-"; + iconResourcePath += std::to_wstring(IDI_FILELOCKSMITH); + return SHStrDup(iconResourcePath.c_str(), ppszIcon); } IFACEMETHODIMP ExplorerCommand::GetToolTip(IShellItemArray* psiItemArray, LPWSTR* ppszInfotip) @@ -129,7 +133,18 @@ IFACEMETHODIMP ExplorerCommand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UI mii.fState = MFS_ENABLED; - // TODO icon from file + // icon from file + HICON hIcon = static_cast(LoadImage(globals::instance, MAKEINTRESOURCE(IDI_FILELOCKSMITH), IMAGE_ICON, 16, 16, 0)); + if (hIcon) + { + mii.fMask |= MIIM_BITMAP; + if (m_hbmpIcon == NULL) + { + m_hbmpIcon = CreateBitmapFromIcon(hIcon); + } + mii.hbmpItem = m_hbmpIcon; + DestroyIcon(hIcon); + } if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii)) { @@ -226,30 +241,6 @@ ExplorerCommand::~ExplorerCommand() --globals::ref_count; } -// Implementation taken from src/common/utils -// TODO reference that function -inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool removeFilename = true) -{ - wchar_t buffer[MAX_PATH + 1]; - DWORD actual_length = GetModuleFileNameW(mod, buffer, MAX_PATH + 1); - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) - { - const DWORD long_path_length = 0xFFFF; // should be always enough - std::wstring long_filename(long_path_length, L'\0'); - actual_length = GetModuleFileNameW(mod, long_filename.data(), long_path_length); - PathRemoveFileSpecW(long_filename.data()); - long_filename.resize(std::wcslen(long_filename.data())); - long_filename.shrink_to_fit(); - return long_filename; - } - - if (removeFilename) - { - PathRemoveFileSpecW(buffer); - } - return { buffer, static_cast(lstrlenW(buffer)) }; -} - HRESULT ExplorerCommand::LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer) { // Compute exe path diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h index 5d6e2aad40..9da12236be 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h @@ -42,6 +42,8 @@ public: ~ExplorerCommand(); private: + HBITMAP m_hbmpIcon = nullptr; + // Helpers HRESULT LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer); diff --git a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.base.rc b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.base.rc index b55a2b37ad..ea565d98f9 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.base.rc +++ b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.base.rc @@ -48,3 +48,10 @@ BEGIN VALUE "Translation", 0x409, 1200 END END + +// Non-localizable +////////////////////////////// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_FILELOCKSMITH ICON "..\\FileLocksmithUI\\Assets\\Icon.ico" \ No newline at end of file diff --git a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj index 4e69eba166..d21a9eb775 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj +++ b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj @@ -280,6 +280,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {98537082-0fdb-40de-abd8-0dc5a4269bab} + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} diff --git a/src/modules/FileLocksmith/FileLocksmithExt/resource.base.h b/src/modules/FileLocksmith/FileLocksmithExt/resource.base.h index 1ffbbe0caa..955a04e15b 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/resource.base.h +++ b/src/modules/FileLocksmith/FileLocksmithExt/resource.base.h @@ -8,6 +8,7 @@ #define FILE_DESCRIPTION "PowerToys File Locksmith Static Library" #define INTERNAL_NAME "PowerToys.FileLocksmithLib.lib" #define ORIGINAL_FILENAME "PowerToys.FileLocksmithLib.lib" +#define IDI_FILELOCKSMITH 1001 // Non-localizable ////////////////////////////// From 2a1e11e844e1642d58cd7d920c3c8d4c5eb8e9e1 Mon Sep 17 00:00:00 2001 From: Aaron Junker Date: Fri, 24 Mar 2023 11:04:57 +0100 Subject: [PATCH 114/163] Add info that PowerToys run might get no focus when "Centralized keyboard shortcut" is used. (#24998) * Add info that PowerToys run might get no focus when "Centralized keyboard shortcut" is used * Fix spelling * Update src/settings-ui/Settings.UI/Strings/en-us/Resources.resw --- src/settings-ui/Settings.UI/Strings/en-us/Resources.resw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index e882489790..d203708b84 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -488,7 +488,7 @@ Use centralized keyboard hook - Try this if there are issues with the shortcut + Try this if there are issues with the shortcut (PowerToys Run might not get focus when triggered from an elevated window) Clear the previous query on launch From 95865bfd2483a923e710855df144d0e960433c89 Mon Sep 17 00:00:00 2001 From: Aaron Junker Date: Fri, 24 Mar 2023 11:38:42 +0100 Subject: [PATCH 115/163] Add devdocs for tools (#24903) * first push (associated issues: #17951 #761) * More docs * Fix spelling * More docs * Fix spelling * Push * Fix link * Fix spelling * Update bug-report-tool.md * Update verification-scripts.md * Update doc/devdocs/tools/bug-report-tool.md Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * Update doc/devdocs/tools/bug-report-tool.md * Update doc/devdocs/tools/styles-report-tool.md Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * Update doc/devdocs/tools/build-tools.md * Update doc/devdocs/tools/clean-up-tool.md --------- Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> --- .github/actions/spell-check/expect.txt | 1 + doc/devdocs/tools/bug-report-tool.md | 31 ++++++++++++++++++ doc/devdocs/tools/build-tools.md | 31 ++++++++++++++++++ doc/devdocs/tools/clean-up-tool.md | 5 +++ doc/devdocs/tools/fancyzone-hit-test.md | 5 +++ .../tools/fancyzones-draw-layout-test.md | 4 +-- .../tools/fancyzones-zonable-tester.md | 13 ++++++++ .../monitor-info-report.md} | 9 +++-- doc/devdocs/tools/readme.md | 21 ++++++++++++ doc/devdocs/tools/styles-report-tool.md | 12 +++++++ doc/devdocs/tools/verification-scripts.md | 25 ++++++++++++++ doc/devdocs/tools/webcam-report-tool.md | 6 ++++ doc/images/tools/fancyzone-hit-test.png | Bin 0 -> 31799 bytes .../tools/fancyzones-zonable-tester.png | Bin 0 -> 64678 bytes 14 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 doc/devdocs/tools/bug-report-tool.md create mode 100644 doc/devdocs/tools/build-tools.md create mode 100644 doc/devdocs/tools/clean-up-tool.md create mode 100644 doc/devdocs/tools/fancyzone-hit-test.md rename tools/FancyZones_DrawLayoutTest/README.md => doc/devdocs/tools/fancyzones-draw-layout-test.md (86%) create mode 100644 doc/devdocs/tools/fancyzones-zonable-tester.md rename doc/devdocs/{tools.md => tools/monitor-info-report.md} (89%) create mode 100644 doc/devdocs/tools/readme.md create mode 100644 doc/devdocs/tools/styles-report-tool.md create mode 100644 doc/devdocs/tools/verification-scripts.md create mode 100644 doc/devdocs/tools/webcam-report-tool.md create mode 100644 doc/images/tools/fancyzone-hit-test.png create mode 100644 doc/images/tools/fancyzones-zonable-tester.png diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 1a9d9a16a9..41cbe3a5b4 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -526,6 +526,7 @@ EXTENDEDVERBS eyetracker fabricbot fancymouse +fancyzone fancyzones FANCYZONESDRAWLAYOUTTEST FANCYZONESEDITOR diff --git a/doc/devdocs/tools/bug-report-tool.md b/doc/devdocs/tools/bug-report-tool.md new file mode 100644 index 0000000000..f73bdf3fc3 --- /dev/null +++ b/doc/devdocs/tools/bug-report-tool.md @@ -0,0 +1,31 @@ +# [Bug report tool](/tools/BugReportTool/) + +This tool is used to collect logs and system information for bug reports. The bug report is then saved as a zip file on the desktop. + +## Launching + +It can launch from the PowerToys tray icon by clicking "Report Bug", by clicking the bug report icon in the PowerToys flyout or by running the executable directly. + +## Included files + +The bug report includes the following files: + +* Settings files of the modules. +* Logs of the modules and the runner. +* Update log files. +* `compatibility-tab-info.txt` - Information about [compatibility settings](https://support.microsoft.com/windows/make-older-apps-or-programs-compatible-with-windows-783d6dd7-b439-bdb0-0490-54eea0f45938) set for certain PowerToys executables both in the user and system scope. +* `context-menu-packages.txt` - Information about the packages that are registered for the new Windows 11 context menu. +* `dotnet-installation-info.txt` - Information about the installed .NET versions. +* `EventViewer-*.xml` - These files contain event logs from the Windows Event Viewer for the executable specified in the file name. +* `gpo-configuration-info.txt` - Information about the configured [GPO](/doc/gpo/README.md). +* `installationFolderStructure.txt` - Information about the folder structure of the installation. All lines with files have the following structure: `FileName Version MD5Hash`. +* `last_version_run.json` - Information about the last version of PowerToys that was run. +* `log_settings.json` - Information about the log level settings. +* `monitor-report-info.txt` - Information about the monitors connected to the system. This file is created by the [monitor info report tool](/doc/devdocs/tools/monitor-info-report.md). +* `oobe_settings.json` - Information about the OOBE settings. +* `registry-report-info.txt` - Information about the registry keys that are used by PowerToys. +* `settings_placement.json` - Information about the placement of the settings window. +* `settings-telemetry.json` - Information about the last time telemetry data was sent. +* `UpdateState.json` - Information about the last update check and the current status of the update download. +* `windows-settings.txt` - Information about the Windows language settings. +* `windows-version.txt` - Information about the Windows version. diff --git a/doc/devdocs/tools/build-tools.md b/doc/devdocs/tools/build-tools.md new file mode 100644 index 0000000000..dfa4e251b2 --- /dev/null +++ b/doc/devdocs/tools/build-tools.md @@ -0,0 +1,31 @@ +# [Build tools](/tools/build/) + +These build tools help building PowerToys projects. + +## [build-essentials.ps1](/tools/build/build-essentials.ps1) + +A script that builds certain specified PowerToys projects. You can edit the `$ProjectsToBuild` variable to specify which projects to build. + +## [convert-resx-to-rc.ps1](/tools/build/convert-resx-to-rc.ps1) + +This script converts a .resx file to a .rc file, so it can be used in a C++ project. More information on localization can be found in the [localization guide](/doc/devdocs/localization.md). + +## [convert-stringtable-to-resx.ps1](/tools/build/convert-stringtable-to-resx.ps1) + +This script converts a stringtable to a .resx file, so it can be used in a C# project. More information about this script can be found in the [localization guide](/doc/devdocs/localization.md). + +## [move-and-rename-resx.ps1](/tools/build/move-and-rename-resx.ps1) + +This script is used by the pipeline to move the .resx files to the correct location, so that they can be localized into different languages. + +## [move-uwp-resw.ps1](/tools/build/move-uwp-resw.ps1) + +This script is used by the pipeline to move the .resw files to the correct location, so that they can be localized into different languages. + +## [versionSetting.ps1](/tools/build/versionSetting.ps1) + +Sets `version.props` file with the version number. + +## [video_conference_make_cab.ps1](/tools/build/video_conference_make_cab.ps1) + +This script creates a cab file for the Video Conference Mute driver. diff --git a/doc/devdocs/tools/clean-up-tool.md b/doc/devdocs/tools/clean-up-tool.md new file mode 100644 index 0000000000..f03eacdd32 --- /dev/null +++ b/doc/devdocs/tools/clean-up-tool.md @@ -0,0 +1,5 @@ +# [CleanUp_tool](/tools/CleanUp_tool/) and [CleanUp_tool_powershell_script](/tools/CleanUp_tool_powershell_script/CleanUp_tool.ps1) + +This tool, respective this powershell script, is used to clean up the PowerToys installation. It cleans the `AppData` folder and the registry. + +This tool is currently very outdated and just cleans up the registry keys of some few modules. diff --git a/doc/devdocs/tools/fancyzone-hit-test.md b/doc/devdocs/tools/fancyzone-hit-test.md new file mode 100644 index 0000000000..b49207faa0 --- /dev/null +++ b/doc/devdocs/tools/fancyzone-hit-test.md @@ -0,0 +1,5 @@ +# [FancyZone hit test tool](/tools/FancyZone_HitTest/) + +![Image of the FancyZones hit test tool](/doc/images/tools/fancyzone-hit-test.png) + +This tool tests the FancyZones layout selection logic. It displays a window with 5 zones. By hovering the mouse over the zones, the zone under the mouse cursor is highlighted. The sidebar shows different metrics that are used to determine which zone is under the mouse cursor. diff --git a/tools/FancyZones_DrawLayoutTest/README.md b/doc/devdocs/tools/fancyzones-draw-layout-test.md similarity index 86% rename from tools/FancyZones_DrawLayoutTest/README.md rename to doc/devdocs/tools/fancyzones-draw-layout-test.md index 652124f7c7..9f15a3a2ad 100644 --- a/tools/FancyZones_DrawLayoutTest/README.md +++ b/doc/devdocs/tools/fancyzones-draw-layout-test.md @@ -1,7 +1,7 @@ -## Testing tool for drawing zone layout +# [FancyZones_DrawLayoutTest](/tools/FancyZones_DrawLayoutTest/) This test tool is created in order to debug issues related to the drawing of zone layout on screen. Currently, only column layout is supported with modifiable number of zones. Pressing **w** key toggles zone appearance on primary screen (multi monitor support not yet in place). Pressing **q** key exits application. -Application is DPI unaware which means that application does not scale for DPI changes and it always assumes to have a scale factor of 100% (96 DPI). Scaling will be automatically performed by the system. \ No newline at end of file +Application is DPI unaware which means that application does not scale for DPI changes and it always assumes to have a scale factor of 100% (96 DPI). Scaling will be automatically performed by the system. diff --git a/doc/devdocs/tools/fancyzones-zonable-tester.md b/doc/devdocs/tools/fancyzones-zonable-tester.md new file mode 100644 index 0000000000..0b552f4636 --- /dev/null +++ b/doc/devdocs/tools/fancyzones-zonable-tester.md @@ -0,0 +1,13 @@ +# [FancyZones_zonable_tester](/tools/FancyZones_zonable_tester/) + +![Image of the FancyZones zonable tester](/doc/images/tools/fancyzones-zonable-tester.png) + +This command line application tests if the window where the mouse cursor is located is zonable. It also adds additional information about the window to the console output: + +* The HWND (window handle) of the window +* The process ID of the window +* The HWND of the window in the foreground +* The style of the window +* The exStyle of the window +* The window class +* The path of the process that created the window diff --git a/doc/devdocs/tools.md b/doc/devdocs/tools/monitor-info-report.md similarity index 89% rename from doc/devdocs/tools.md rename to doc/devdocs/tools/monitor-info-report.md index 2e14440c97..489f109a0e 100644 --- a/doc/devdocs/tools.md +++ b/doc/devdocs/tools/monitor-info-report.md @@ -1,10 +1,8 @@ -# Tools - -## [Monitor info report](tools\monitor_info_report) +# [Monitor info report tool](/tools/MonitorReportTool) A small diagnostic tool which helps identifying WinAPI bugs related to the physical monitor detection. When launched, it creates a log file like this: -``` +```text GetSystemMetrics = 2 GetMonitorInfo OK EnumDisplayDevices OK: @@ -20,4 +18,5 @@ EnumDisplayDevices OK: DeviceString = Generic PnP Monitor EnumDisplayMonitors OK ``` -and also duplicates the info to `stdout`. \ No newline at end of file + +and also duplicates the info to `stdout`. diff --git a/doc/devdocs/tools/readme.md b/doc/devdocs/tools/readme.md new file mode 100644 index 0000000000..8794b77e3e --- /dev/null +++ b/doc/devdocs/tools/readme.md @@ -0,0 +1,21 @@ +# PowerToys tools + +Tools in PowerToys are standalone apps and scripts that run outside of the PowerToys runner. They help developers and users to debug and test PowerToys features. + +The source code of the tools can be found in the [tools folder](/tools). The compiled tools are saved under `{PowerToysInstallPath}\tools`. + +## Overview of the tools + +Following tools are currently available: + +* [BugReportTool](bug-report-tool.md) - A tool to collect logs and system information for bug reports. +* [Build tools](build-tools.md) - A set of scripts that help building PowerToys. +* [Clean up tool](clean-up-tool.md) - A tool to clean up the PowerToys installation. +* [FancyZones hit test](fancyzone-hit-test.md) - A tool to test FancyZones layout selection logic. +* [FancyZones draw layout test](fancyzones-draw-layout-test.md) - A tool to test FancyZones layout drawing logic. +* [FancyZones zonable tester](fancyzones-zonable-tester.md) - A tool to test if a window is zonable. +* [Monitor info report](monitor-info-report.md) - A small diagnostic tool which helps identifying WinAPI bugs related to the physical monitor detection. +* [project template](/tools/project_template/README.md) - A Visual Studio project template for a new PowerToys project. +* [StylesReportTool](styles-report-tool.md) - A tool to collect information about an open window. +* [Verification scripts](verification-scripts.md) - A set of scripts that help verifying the PowerToys installation. +* [WebcamReportTool](webcam-report-tool.md) - A tool to collect information about the connected webcams. diff --git a/doc/devdocs/tools/styles-report-tool.md b/doc/devdocs/tools/styles-report-tool.md new file mode 100644 index 0000000000..bb31c9c98d --- /dev/null +++ b/doc/devdocs/tools/styles-report-tool.md @@ -0,0 +1,12 @@ +# [Styles Report Tool](/tools/StylesReportTool/) + +The Styles Report Tool is a tool to collect information about an open window. Run the tool, bring the window to the foreground and press Ctrl+Alt+S. The tool will create a file on your desktop called `window_styles.txt` with the information about the window. + +## Collected information + +* Process name +* Window class +* Window style parameters +* Window exStyle parameters +* DWM attributes +* Infos about the Virtual Desktop the window is on diff --git a/doc/devdocs/tools/verification-scripts.md b/doc/devdocs/tools/verification-scripts.md new file mode 100644 index 0000000000..cff58f478f --- /dev/null +++ b/doc/devdocs/tools/verification-scripts.md @@ -0,0 +1,25 @@ +# [Verification Scripts](/tools/Verification%20scripts/) + +This folder contains powershell scripts that help verifying the PowerToys installation. + +## [Check preview handler registration](/tools/Verification%20scripts/Check%20preview%20handler%20registration.ps1) + +This script checks the preview handler registration for the following file types: + +* .markdown +* .mdtext +* .mdtxt +* .mdown +* .mkdn +* .mdwn +* .mkd +* .md +* .svg +* .svgz +* .pdf +* .gcode +* .stl +* .txt +* .ini + +The tool shows the user handler and the machine handler for each file type and displays the App GUID of the corresponding handler. diff --git a/doc/devdocs/tools/webcam-report-tool.md b/doc/devdocs/tools/webcam-report-tool.md new file mode 100644 index 0000000000..efa6d47da3 --- /dev/null +++ b/doc/devdocs/tools/webcam-report-tool.md @@ -0,0 +1,6 @@ +# [WebcamReportTool](/tools/WebcamReportTool/) + +This command line application generates a report about the connected webcams on the desktop called "WebcamReport.txt". The report contains the following information about every webcam: + +* Name +* Supported formats diff --git a/doc/images/tools/fancyzone-hit-test.png b/doc/images/tools/fancyzone-hit-test.png new file mode 100644 index 0000000000000000000000000000000000000000..8886b2be5fa814fb1f31124ad4f0c7f3b0146b18 GIT binary patch literal 31799 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 literal 0 HcmV?d00001 diff --git a/doc/images/tools/fancyzones-zonable-tester.png b/doc/images/tools/fancyzones-zonable-tester.png new file mode 100644 index 0000000000000000000000000000000000000000..4a10315456e23815e472104b7d374598686297b9 GIT binary patch literal 64678 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 literal 0 HcmV?d00001 From 144721ad078d33f455d3abc0bcad137ab1edd425 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Mon, 27 Mar 2023 15:21:06 +0200 Subject: [PATCH 116/163] Fix folders not being deleted on uninstall (#25021) Add missing MouseJumpUI dlls --- installer/PowerToysSetup/MouseUtils.wxs | 2 +- installer/PowerToysSetup/Product.wxs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/installer/PowerToysSetup/MouseUtils.wxs b/installer/PowerToysSetup/MouseUtils.wxs index 913ac7b504..44260d14b4 100644 --- a/installer/PowerToysSetup/MouseUtils.wxs +++ b/installer/PowerToysSetup/MouseUtils.wxs @@ -4,7 +4,7 @@ - + diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 090aa98c8c..daed766bcc 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -178,7 +178,8 @@ Installed AND (REMOVE="ALL") - + + Installed AND (REMOVE="ALL") From 81c09685e9e3df81c2acba18e507af4684dca08a Mon Sep 17 00:00:00 2001 From: Randy Date: Mon, 27 Mar 2023 06:21:46 -0700 Subject: [PATCH 117/163] Registry Preview: complete feature with integration with Settings and the Launcher UX (#23709) * Initial src for Registry Preview Initial collection of files * Update MainWindow.Utilities.cs fixing a few spelling items * Update expect.txt * Update App.xaml.cs * Update MainWindow.Events.cs * Update MainWindow.Utilities.cs * Update MainWindow.xaml.cs * Update expect.txt * Update MainWindow.Events.cs * Rename AddPreviewtoRegfile.reg to AddPreviewToRegfile.reg * Rename RemovePreviewtoRegfile.reg to RemovePreviewToRegfile.reg * Update Resources.resw * Update MainWindow.xaml * Turns on self-contained mode Updates the csproj file to compile the app as self-contained . Includes fixes for code that expected the app to be in an ApplicationContainer. Updates WindowsAppSDK from 1.1.5 to 1.2.230118.102. * Updated to align with StyleCop errors Multiple changes across the codebase that now aligns with StyleCop guidelines. Tested again after the changes, to make sure no new bugs crept in. * Added comments for spell-checker Unclear if this side step should be done or not, but the kxz comes from a GUID and the other three names are constants. * Adding code-custom.dic Comments didn't work; trying a dic file * Added four new terms to expects.txt file Cleaning up attempt to update the spell-checker with dic file and moved it to expects.txt file * Adding one more string Adding one more string for Spell-Check * Adding back egfile Seems this is needed. * Correcting a variable name Seems one of the variable names that changed globally got missed in a XAML file. * Update project to be more PowerToys friendly Tweaking names and output file name to fit better with PowerToys. * First pass at integration into Settings and Launcher This PR is not as large as it seems: - RegistryPreview's source moved around to a "better" directory that makes it look like the whole app changed. It didn't. In fact, I opened it in Beyond Compare and there's not much difference in the RegistryPreview app. - Added RegistryPreviewExt that produces a DLL that the Launcher can run the EXE - Changes to Runner calls the Ext DLL rather than the app - Settings UI got a bunch of changes to enable the Settings page for enable/disable across ViewModels, Views, and string tables. Still todo: - Add "Preview" to .REG files, when the app is enable (and remove it when disabled) - Update the thumbnail-screenshot in the Settings page - Add support for OOBE * Update expect.txt Added REGISTRYPREVIEWEXT for recent changes and corrected an alphabetic sorting error. * Updating project file for Release mode Build failed due to a bad Includes path in Release mode. * Adds REG registration but breaks settings This update will update the HKCU branch of the Registry for REG files: if the app in PowerToys is enabled, it adds a Preview item to the context menu of REG files and disabling in PowerToys removes the menu item. While working on this, I noticed that the application settings were broken, after moving to a self-contained EXE: there must have been old settings from past builds, when it was still using containers and family names. Added TODO's to add a new way to save settings, likely as JSON. * Re-enabled app settings Moved from using ApplicationDataContainer for app settings and now use simple JSON. Also cleaned up handling the scenario where the Launcher send in the PID from PowerToys' main thread. Fixed past spelling errors as well. * Update RegistryPreview.png Captured new screenshot. * Integration into OOBE Integrates new page for Registry Preview into OOBE process. * Removing old comment and unneeded calls Two bits of source removed as they aren't needed any longer Removing a chunk of old commented out code that doesn't make sense anymore. * Merging file from upstream Updating some files due to three merge conflicts from upstream changes and a couple of other changes to keep up. * Update .gitignore Adding two vcxproj files that have local updates for atls.lib locations. * Update Resources.resw Fixing a typo that involved a missing closing tag. * Fix analyzer warnings * Fix CI build * Fix ARM64 build Project file cleanup * Add to installer * expect.txt * Remove unneeded dll * Update MainWindow.xaml.cs Added check for current Theme and adjust TextBox Foreground accordingly. * Update expect.txt Cleaning up merge cruft. * Revert wrong .gitignore changes * Fix ARM installer * Update Brushes for textBox to use Theme based versions Finally figured out how to use the built-in, Theme-aware Brushes for the font colors in the onscreen textBox. Also have it aligning the font color for the hover state. * Align configuration in PowerToys.sln * [installer] Add missing files * Fix bad merge * Fixes for stefansjfw's review Includes: - two new strings for UX localization - adds compatibility section for Windows 10 - fix to only track successful activations - Removes two REG files that were there for examples * Fixes from review from htcfreek Updates: - Fixed an issue where TextBox_TextChanged was firing when you simply opened a file. - Added clamp to prevent files larger than 10MB from being opened. - Added support in the UX to show Keys and Values that are deleted via the file - Added support to specially handle Keys that start with - and Values that have =- in them (delete scenario) - Changed AppBarButton icon for Edit from Rename to NewWindow * Create deleted-folder32.png * Added Registry Preview to GPO * Update expect.txt Updating spellchecker works * Updating Size/Move code for better results - Moved the size/move to the MainWindow layer - Cleaned up the JSON settings handling to avoid an access denied on first run * Improving text handling Changed how special characters are parse, which helps with live entry. * Updates to parsing and other fixes - Renamed the value PNG for parity - Added new error image - Added check that values have " at start and finish. - Added support for a new "ERROR" type for Values - Fixed support for @ versus "@" in values - Fixed bug where Save wasn't activating in all scernarios * Fix signing and versioning * Update src/settings-ui/Settings.UI/ViewModels/RegistryPreviewViewModel.cs Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * Apply suggestions from code review Adds Launch button to the settings page. Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * Update Resources.resw Adding strings for new launch button * Adding new version for GPO Moving to 0.69.0 * More parsing bug fixes - Changes to look for [- instead of -[ for syntax deleting keys (fix for developer's mental hiccup) - Moved [- to top of the decision making stack, as it needs to come before [ - Added new StripEscapedCharacters function for both sides of a Value line - Fixed crashing bug for scenario where no Keys are parsed before a Value * Bug fixes from most recent review. - Dictionary will now be case insensitive when searching for keys - Added tool tips (and strings) to the images of the Keys and Values - Updated delete handling for Keys, so that only the leaf-most node gets marked as deleted; also stops the top most roots from being marked deleted. * Tweaking for @=- Forces the UX to take @=- and treat it as @="" since that's what Registry Editor would do. * Removing unused usings * Updates app description * Update src/gpo/assets/PowerToys.admx Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * cleanup proj file launch process only if module is enabled add process to bugreport process list * Add context menu icon * Update src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * Use modulesRegistry.h to apply/unapply registry changes * Tweaked window settings Moved the loading of the settings a little later in the initialization code, which gives more time for things to initialize. * Update registry.h Moving the definition out of the detail namespace to the registry name space to fix a compilation issue in RegistryPreviewExt. * Unapply on creation If module is disabled in settings.json, on startup reg entries should be unnaplied. TODO: read m_enabled from settings file on creation * Removing size/move main window Added a TODO comment that responds to the size/position settings that are being saved out in the JSON blob on close as it doesn't always work on every PC, as the MainWindow initializes at different times. * Change to always keep Save As active No reason for this to be disabled, honestly. --------- Co-authored-by: Clint Rutkas Co-authored-by: Stefan Markovic Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> --- .github/actions/spell-check/expect.txt | 14 + .pipelines/ESRPSigning_core.json | 6 + .pipelines/versionAndSignCheck.ps1 | 6 +- PowerToys.sln | 33 + installer/PowerToysSetup/Common.wxi | 1 + .../PowerToysSetup/PowerToysInstaller.wixproj | 1 + installer/PowerToysSetup/Product.wxs | 6 + installer/PowerToysSetup/RegistryPreview.wxs | 44 + installer/PowerToysSetup/Settings.wxs | 6 +- installer/PowerToysSetup/WinAppSDK.wxs | 9 +- installer/PowerToysSetup/publish.cmd | 2 + .../CustomAction.cpp | 17 +- src/common/Common.UI/SettingsDeepLink.cs | 3 + src/common/GPOWrapper/GPOWrapper.cpp | 4 + src/common/GPOWrapper/GPOWrapper.h | 1 + src/common/GPOWrapper/GPOWrapper.idl | 1 + src/common/interop/interop.cpp | 10 +- src/common/interop/shared_constants.h | 3 + src/common/logger/logger_settings.h | 2 + src/common/utils/gpo.h | 5 + src/common/utils/modulesRegistry.h | 24 +- src/common/utils/registry.h | 6 +- src/gpo/assets/PowerToys.admx | 12 +- src/gpo/assets/en-US/PowerToys.adml | 2 + .../RegistryPreviewExt/Constants.h | 7 + .../RegistryPreviewExt/RegistryPreviewExt.rc | 108 +++ .../RegistryPreviewExt.vcxproj | 134 +++ .../RegistryPreviewExt.vcxproj.filters | 53 ++ .../RegistryPreviewExt/Trace.cpp | 40 + .../RegistryPreviewExt/Trace.h | 14 + .../RegistryPreviewExt/dllmain.cpp | 265 ++++++ .../RegistryPreviewExt/packages.config | 5 + .../RegistryPreviewExt/pch.cpp | 1 + .../registrypreview/RegistryPreviewExt/pch.h | 16 + .../RegistryPreviewExt/resource.h | 21 + .../RegistryPreviewUI/App.xaml | 15 + .../RegistryPreviewUI/App.xaml.cs | 66 ++ .../RegistryPreviewUI/Assets/data32.png | Bin 0 -> 367 bytes .../Assets/deleted-folder32.png | Bin 0 -> 1281 bytes .../Assets/deleted-value32.png | Bin 0 -> 1131 bytes .../RegistryPreviewUI/Assets/error32.png | Bin 0 -> 678 bytes .../RegistryPreviewUI/Assets/folder32.png | Bin 0 -> 832 bytes .../RegistryPreviewUI/Assets/string32.png | Bin 0 -> 356 bytes .../RegistryPreviewUI/MainWindow.Events.cs | 385 ++++++++ .../RegistryPreviewUI/MainWindow.Utilities.cs | 896 ++++++++++++++++++ .../RegistryPreviewUI/MainWindow.xaml | 212 +++++ .../RegistryPreviewUI/MainWindow.xaml.cs | 93 ++ .../RegistryPreviewUI/RegistryKey.xaml.cs | 32 + .../RegistryPreviewUI.csproj | 67 ++ .../RegistryPreviewUI/RegistryValue.xaml.cs | 57 ++ .../Strings/en-US/Resources.resw | 231 +++++ .../registrypreview/RegistryPreviewUI/app.ico | Bin 0 -> 410598 bytes .../RegistryPreviewUI/app.manifest | 21 + src/runner/main.cpp | 1 + src/runner/settings_window.cpp | 6 + src/runner/settings_window.h | 3 +- .../Settings.UI.Library/EnabledModules.cs | 16 + .../RegistryPreviewSettings.cs | 27 + src/settings-ui/Settings.UI/App.xaml.cs | 1 + .../FluentIconsRegistryPreview.png | Bin 0 -> 2695 bytes .../Assets/Modules/OOBE/RegistryPreview.png | Bin 0 -> 435357 bytes .../Assets/Modules/RegistryPreview.png | Bin 0 -> 68058 bytes .../Settings.UI/Flyout/LaunchPage.xaml.cs | 7 + .../Settings.UI/MainWindow.xaml.cs | 3 + .../OOBE/Enums/PowerToysModules.cs | 1 + .../OOBE/Views/OobeRegistryPreview.xaml | 56 ++ .../OOBE/Views/OobeRegistryPreview.xaml.cs | 53 ++ .../Settings.UI/OOBE/Views/OobeShellPage.xaml | 4 + .../OOBE/Views/OobeShellPage.xaml.cs | 7 + .../Settings.UI/PowerToys.Settings.csproj | 3 + .../Settings.UI/Strings/en-us/Resources.resw | 44 + .../ViewModels/Flyout/AllAppsViewModel.cs | 6 + .../ViewModels/Flyout/LauncherViewModel.cs | 13 + .../ViewModels/RegistryPreviewViewModel.cs | 98 ++ .../Views/RegistryPreviewPage.xaml | 54 ++ .../Views/RegistryPreviewPage.xaml.cs | 29 + .../Settings.UI/Views/ShellPage.xaml | 5 + .../BugReportTool/ProcessesList.cpp | 3 +- 78 files changed, 3376 insertions(+), 21 deletions(-) create mode 100644 installer/PowerToysSetup/RegistryPreview.wxs create mode 100644 src/modules/registrypreview/RegistryPreviewExt/Constants.h create mode 100644 src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.rc create mode 100644 src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj create mode 100644 src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj.filters create mode 100644 src/modules/registrypreview/RegistryPreviewExt/Trace.cpp create mode 100644 src/modules/registrypreview/RegistryPreviewExt/Trace.h create mode 100644 src/modules/registrypreview/RegistryPreviewExt/dllmain.cpp create mode 100644 src/modules/registrypreview/RegistryPreviewExt/packages.config create mode 100644 src/modules/registrypreview/RegistryPreviewExt/pch.cpp create mode 100644 src/modules/registrypreview/RegistryPreviewExt/pch.h create mode 100644 src/modules/registrypreview/RegistryPreviewExt/resource.h create mode 100644 src/modules/registrypreview/RegistryPreviewUI/App.xaml create mode 100644 src/modules/registrypreview/RegistryPreviewUI/App.xaml.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUI/Assets/data32.png create mode 100644 src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-folder32.png create mode 100644 src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-value32.png create mode 100644 src/modules/registrypreview/RegistryPreviewUI/Assets/error32.png create mode 100644 src/modules/registrypreview/RegistryPreviewUI/Assets/folder32.png create mode 100644 src/modules/registrypreview/RegistryPreviewUI/Assets/string32.png create mode 100644 src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml create mode 100644 src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUI/RegistryKey.xaml.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj create mode 100644 src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw create mode 100644 src/modules/registrypreview/RegistryPreviewUI/app.ico create mode 100644 src/modules/registrypreview/RegistryPreviewUI/app.manifest create mode 100644 src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs create mode 100644 src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsRegistryPreview.png create mode 100644 src/settings-ui/Settings.UI/Assets/Modules/OOBE/RegistryPreview.png create mode 100644 src/settings-ui/Settings.UI/Assets/Modules/RegistryPreview.png create mode 100644 src/settings-ui/Settings.UI/OOBE/Views/OobeRegistryPreview.xaml create mode 100644 src/settings-ui/Settings.UI/OOBE/Views/OobeRegistryPreview.xaml.cs create mode 100644 src/settings-ui/Settings.UI/ViewModels/RegistryPreviewViewModel.cs create mode 100644 src/settings-ui/Settings.UI/Views/RegistryPreviewPage.xaml create mode 100644 src/settings-ui/Settings.UI/Views/RegistryPreviewPage.xaml.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 41cbe3a5b4..69df4fe631 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -65,6 +65,7 @@ applicationframehost Applist applog appmanifest +APPNAME appref apps appwindow @@ -217,6 +218,7 @@ CLIPCHILDREN CLIPSIBLINGS Cloneable clrcall +clrcompression Cls CLSCTX clsid @@ -350,6 +352,7 @@ DCOM dcommon dcomp dcompi +DCompiler DComposition DCR DCs @@ -373,6 +376,7 @@ DEFERERASE DEFPUSHBUTTON deinitialization DELA +DELETEDKEYIMAGE DELETESCANS deletethis Delimarsky @@ -469,6 +473,7 @@ editkeyboardwindow EDITSHORTCUTS editshortcutswindow EFile +egfile ekus emmintrin Emoji @@ -508,6 +513,7 @@ EWXREBOOT EWXSHUTDOWN examplehandler examplepowertoy +EXAND EXCLUDEFROMCAPTURE exdisp executionpolicy @@ -748,6 +754,7 @@ IMAGERESIZEREXT imageresizerinput imageresizersettings imagingdevices +Imc ime imeutil inetcpl @@ -836,6 +843,7 @@ keydown keydropdowncontrol keyevent KEYEVENTF +KEYIMAGE keynum keyremaps Keytool @@ -1293,6 +1301,7 @@ pinfo pinvoke pipename PKBDLLHOOKSTRUCT +Pkcs PKEY plib PLK @@ -1435,7 +1444,10 @@ regfile REGFILTER REGFILTERPINS REGISTERCLASSFAILED +REGISTRYHEADER registrypath +registrypreview +REGISTRYPREVIEWEXT regkey REGPINTYPES regsvr @@ -2043,6 +2055,7 @@ wox wparam wpf wpfdepsjsonpath +wpfgfx wpftmp wpr wprp @@ -2091,6 +2104,7 @@ XStr XUP XVIRTUALSCREEN YAxis +Yeet YIncrement yinle yinwang diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 24f0d42852..8ebf519c13 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -147,6 +147,10 @@ "modules\\PowerRename\\PowerToys.PowerRenameContextMenu.dll", "modules\\PowerRename\\PowerRenameContextMenuPackage.msix", + "modules\\RegistryPreview\\PowerToys.RegistryPreviewExt.dll", + "modules\\RegistryPreview\\PowerToys.RegistryPreview.dll", + "modules\\RegistryPreview\\PowerToys.RegistryPreview.exe", + "modules\\ShortcutGuide\\ShortcutGuide\\PowerToys.ShortcutGuide.exe", "modules\\ShortcutGuide\\ShortcutGuideModuleInterface\\PowerToys.ShortcutGuideModuleInterface.dll", @@ -240,6 +244,8 @@ "modules\\PowerAccent\\Vanara.PInvoke.Shell32.dll", "modules\\PowerAccent\\Vanara.PInvoke.ShlwApi.dll", "modules\\PowerAccent\\Vanara.PInvoke.User32.dll", + "modules\\RegistryPreview\\clrcompression.dll", + "modules\\RegistryPreview\\Microsoft.Graphics.Canvas.Interop.dll", "modules\\FileExplorerPreview\\Microsoft.Web.WebView2.Core.dll", "modules\\FileExplorerPreview\\Microsoft.Web.WebView2.WinForms.dll", "modules\\FileExplorerPreview\\Microsoft.Web.WebView2.Wpf.dll", diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 753ab98897..b0089f17fa 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -18,7 +18,8 @@ $versionExceptions = @( "Microsoft.Xaml.Interactions.dll", "Microsoft.Xaml.Interactivity.dll", "hyjiacan.py4n.dll", - "Microsoft.WindowsAppRuntime.Release.Net.dll") -join '|'; + "Microsoft.WindowsAppRuntime.Release.Net.dll", + "Microsoft.Windows.Widgets.Projection.dll") -join '|'; $nullVersionExceptions = @( "codicon.ttf", "e_sqlite3.dll", @@ -32,7 +33,8 @@ $nullVersionExceptions = @( "MRM.dll", "PushNotificationsLongRunningTask.ProxyStub.dll", "WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll", - "System.Diagnostics.EventLog.Messages.dll") -join '|'; + "System.Diagnostics.EventLog.Messages.dll", + "Microsoft.Windows.Widgets.dll") -join '|'; $totalFailure = 0; Write-Host $DirPath; diff --git a/PowerToys.sln b/PowerToys.sln index 8f3f5d3ed5..ec3bdfde46 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -494,6 +494,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PastePlainModuleInterface", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AllExperiments", "src\common\AllExperiments\AllExperiments.csproj", "{9CE59ED5-7087-4353-88EB-788038A73CEC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RegistryPreviewUI", "src\modules\registrypreview\RegistryPreviewUI\RegistryPreviewUI.csproj", "{FD86C06A-FB54-4D5E-9831-1CDADF60D45F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RegistryPreviewExt", "src\modules\registrypreview\RegistryPreviewExt\RegistryPreviewExt.vcxproj", "{697C6AF9-0A48-49A9-866C-67DA12384015}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RegistryPreview", "RegistryPreview", "{929C1324-22E8-4412-A9A8-80E85F3985A5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -2066,6 +2072,30 @@ Global {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x64.Build.0 = Release|x64 {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x86.ActiveCfg = Release|x64 {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x86.Build.0 = Release|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|ARM64.Build.0 = Debug|ARM64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|x64.ActiveCfg = Debug|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|x64.Build.0 = Debug|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|x86.ActiveCfg = Debug|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|x86.Build.0 = Debug|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|ARM64.ActiveCfg = Release|ARM64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|ARM64.Build.0 = Release|ARM64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|x64.ActiveCfg = Release|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|x64.Build.0 = Release|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|x86.ActiveCfg = Release|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|x86.Build.0 = Release|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|ARM64.Build.0 = Debug|ARM64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|x64.ActiveCfg = Debug|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|x64.Build.0 = Debug|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|x86.ActiveCfg = Debug|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|x86.Build.0 = Debug|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|ARM64.ActiveCfg = Release|ARM64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|ARM64.Build.0 = Release|ARM64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|x64.ActiveCfg = Release|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|x64.Build.0 = Release|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|x86.ActiveCfg = Release|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2240,6 +2270,9 @@ Global {9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B} {9CE59ED5-7087-4353-88EB-788038A73CEC} = {1AFB6476-670D-4E80-A464-657E01DFF482} + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F} = {929C1324-22E8-4412-A9A8-80E85F3985A5} + {697C6AF9-0A48-49A9-866C-67DA12384015} = {929C1324-22E8-4412-A9A8-80E85F3985A5} + {929C1324-22E8-4412-A9A8-80E85F3985A5} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/installer/PowerToysSetup/Common.wxi b/installer/PowerToysSetup/Common.wxi index 9e5b9ea5bc..c0956226fc 100644 --- a/installer/PowerToysSetup/Common.wxi +++ b/installer/PowerToysSetup/Common.wxi @@ -15,6 +15,7 @@ + diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj index 256ba27132..131d2dceda 100644 --- a/installer/PowerToysSetup/PowerToysInstaller.wixproj +++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj @@ -77,6 +77,7 @@ call "..\..\publish.cmd" arm64 + diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index daed766bcc..88c47e351a 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -68,6 +68,7 @@ + @@ -485,6 +486,11 @@ + + + + + diff --git a/installer/PowerToysSetup/RegistryPreview.wxs b/installer/PowerToysSetup/RegistryPreview.wxs new file mode 100644 index 0000000000..393ca839d2 --- /dev/null +++ b/installer/PowerToysSetup/RegistryPreview.wxs @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs index 0d2f8a1e8d..0686ccfab6 100644 --- a/installer/PowerToysSetup/Settings.wxs +++ b/installer/PowerToysSetup/Settings.wxs @@ -5,9 +5,9 @@ - - - + + + diff --git a/installer/PowerToysSetup/WinAppSDK.wxs b/installer/PowerToysSetup/WinAppSDK.wxs index b1b7203bc9..998a28dfbf 100644 --- a/installer/PowerToysSetup/WinAppSDK.wxs +++ b/installer/PowerToysSetup/WinAppSDK.wxs @@ -18,7 +18,7 @@ - + @@ -351,6 +351,13 @@ + + + + diff --git a/installer/PowerToysSetup/publish.cmd b/installer/PowerToysSetup/publish.cmd index 58c6ef48aa..ec3658f294 100644 --- a/installer/PowerToysSetup/publish.cmd +++ b/installer/PowerToysSetup/publish.cmd @@ -22,3 +22,5 @@ msbuild !PTRoot!\src\modules\previewpane\SvgThumbnailProvider\SvgThumbnailProvid msbuild !PTRoot!\src\modules\MeasureTool\MeasureToolUI\MeasureToolUI.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml msbuild !PTRoot!\src\modules\FileLocksmith\FileLocksmithUI\FileLocksmithUI.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml + +msbuild !PTRoot!\src\modules\registrypreview\RegistryPreviewUI\RegistryPreviewUI.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp index ed17d9a34c..f68c969c28 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp @@ -984,6 +984,7 @@ const std::wstring WinAppSDKConsumers[] = L"modules\\MeasureTool", L"modules\\FileLocksmith", L"modules\\Hosts", + L"modules\\RegistryPreview", }; UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall) @@ -1037,6 +1038,7 @@ const std::wstring PTInteropConsumers[] = L"modules\\Hosts", L"modules\\FileExplorerPreview", L"modules\\MouseUtils\\MouseJumpUI", + L"modules\\RegistryPreview", }; UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall) @@ -1081,7 +1083,7 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) UINT er = ERROR_SUCCESS; std::wstring installationFolder, dotnetRuntimeFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir, imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, hostsDir, fileLocksmithDir, - mouseJumpDir; + mouseJumpDir, registryPreviewDir; hr = WcaInitialize(hInstall, "CreateDotnetRuntimeHardlinksCA"); ExitOnFailure(hr, "Failed to initialize"); @@ -1103,6 +1105,7 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) hostsDir = installationFolder + L"modules\\Hosts\\"; fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\"; mouseJumpDir = installationFolder + L"modules\\MouseUtils\\MouseJumpUI\\"; + registryPreviewDir = installationFolder + L"modules\\RegistryPreview\\"; for (auto file : dotnetRuntimeFiles) { @@ -1120,6 +1123,7 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileLocksmithDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (mouseJumpDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (registryPreviewDir + file).c_str(), ec); if (ec.value() != S_OK) { @@ -1144,6 +1148,7 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileExplorerAddOnsDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (mouseJumpDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (registryPreviewDir + file).c_str(), ec); if (ec.value() != S_OK) { @@ -1238,7 +1243,7 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) UINT er = ERROR_SUCCESS; std::wstring installationFolder, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir, imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, - hostsDir, fileLocksmithDir, mouseJumpDir; + hostsDir, fileLocksmithDir, mouseJumpDir, registryPreviewDir; hr = WcaInitialize(hInstall, "DeleteDotnetRuntimeHardlinksCA"); ExitOnFailure(hr, "Failed to initialize"); @@ -1259,6 +1264,7 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) hostsDir = installationFolder + L"modules\\Hosts\\"; fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\"; mouseJumpDir = installationFolder + L"modules\\MouseUtils\\MouseJumpUI\\"; + registryPreviewDir = installationFolder + L"modules\\RegistryPreview\\"; try { @@ -1277,6 +1283,7 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) DeleteFile((hostsDir + file).c_str()); DeleteFile((fileLocksmithDir + file).c_str()); DeleteFile((mouseJumpDir + file).c_str()); + DeleteFile((registryPreviewDir + file).c_str()); } for (auto file : dotnetRuntimeWPFFiles) @@ -1291,6 +1298,7 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) DeleteFile((fileExplorerAddOnsDir + file).c_str()); DeleteFile((hostsDir + file).c_str()); DeleteFile((mouseJumpDir + file).c_str()); + DeleteFile((registryPreviewDir + file).c_str()); } } catch (std::exception e) @@ -1324,7 +1332,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall) } processes.resize(bytes / sizeof(processes[0])); - std::array processesToTerminate = { + std::array processesToTerminate = { L"PowerToys.PowerLauncher.exe", L"PowerToys.Settings.exe", L"PowerToys.Awake.exe", @@ -1334,7 +1342,8 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall) L"PowerToys.MouseJumpUI.exe", L"PowerToys.ColorPickerUI.exe", L"PowerToys.AlwaysOnTop.exe", - L"PowerToys.exe" + L"PowerToys.exe", + L"PowerToys.RegistryPreview.exe", }; for (const auto procID : processes) diff --git a/src/common/Common.UI/SettingsDeepLink.cs b/src/common/Common.UI/SettingsDeepLink.cs index 42da58b8a0..c63f3fc1ad 100644 --- a/src/common/Common.UI/SettingsDeepLink.cs +++ b/src/common/Common.UI/SettingsDeepLink.cs @@ -24,6 +24,7 @@ namespace Common.UI ShortcutGuide, VideoConference, Hosts, + RegistryPreview, } private static string SettingsWindowNameToString(SettingsWindow value) @@ -56,6 +57,8 @@ namespace Common.UI return "VideoConference"; case SettingsWindow.Hosts: return "Hosts"; + case SettingsWindow.RegistryPreview: + return "RegistryPreview"; default: { return string.Empty; diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index 95e228ec3e..9e4a6ef347 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -100,6 +100,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation { return static_cast(powertoys_gpo::getConfiguredQuickAccentEnabledValue()); } + GpoRuleConfigured GPOWrapper::GetConfiguredRegistryPreviewEnabledValue() + { + return static_cast(powertoys_gpo::getConfiguredRegistryPreviewEnabledValue()); + } GpoRuleConfigured GPOWrapper::GetConfiguredScreenRulerEnabledValue() { return static_cast(powertoys_gpo::getConfiguredScreenRulerEnabledValue()); diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h index 3a0ca75ace..77d6ba72f9 100644 --- a/src/common/GPOWrapper/GPOWrapper.h +++ b/src/common/GPOWrapper/GPOWrapper.h @@ -31,6 +31,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation static GpoRuleConfigured GetConfiguredPowerRenameEnabledValue(); static GpoRuleConfigured GetConfiguredPowerLauncherEnabledValue(); static GpoRuleConfigured GetConfiguredQuickAccentEnabledValue(); + static GpoRuleConfigured GetConfiguredRegistryPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredScreenRulerEnabledValue(); static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue(); static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue(); diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl index 5ab4744702..b0f1633dfc 100644 --- a/src/common/GPOWrapper/GPOWrapper.idl +++ b/src/common/GPOWrapper/GPOWrapper.idl @@ -35,6 +35,7 @@ namespace PowerToys static GpoRuleConfigured GetConfiguredPowerRenameEnabledValue(); static GpoRuleConfigured GetConfiguredPowerLauncherEnabledValue(); static GpoRuleConfigured GetConfiguredQuickAccentEnabledValue(); + static GpoRuleConfigured GetConfiguredRegistryPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredScreenRulerEnabledValue(); static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue(); static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue(); diff --git a/src/common/interop/interop.cpp b/src/common/interop/interop.cpp index a7ec3b47dd..ae63efa64b 100644 --- a/src/common/interop/interop.cpp +++ b/src/common/interop/interop.cpp @@ -215,10 +215,14 @@ public return gcnew String(CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT); } - static String - ^ MeasureToolTriggerEvent() { + static String ^ RegistryPreviewTriggerEvent() { + return gcnew String(CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT); + } + + static String ^ MeasureToolTriggerEvent() { return gcnew String(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT); - } + } + static String ^ GcodePreviewResizeEvent() { return gcnew String(CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT); } diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index 43fc2bad20..7f8c0bbd7e 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -46,6 +46,9 @@ namespace CommonSharedConstants // Path to the event used by PowerOCR const wchar_t SHOW_POWEROCR_SHARED_EVENT[] = L"Local\\PowerOCREvent-dc864e06-e1af-4ecc-9078-f98bee745e3a"; + // Path to the event used by RegistryPreview + const wchar_t REGISTRY_PREVIEW_TRIGGER_EVENT[] = L"Local\\RegistryPreviewEvent-4C559468-F75A-4E7F-BC4F-9C9688316687"; + // Path to the event used by MeasureTool const wchar_t MEASURE_TOOL_TRIGGER_EVENT[] = L"Local\\MeasureToolEvent-3d46745f-09b3-4671-a577-236be7abd199"; diff --git a/src/common/logger/logger_settings.h b/src/common/logger/logger_settings.h index bfe35315d8..fb64a11eae 100644 --- a/src/common/logger/logger_settings.h +++ b/src/common/logger/logger_settings.h @@ -57,6 +57,8 @@ struct LogSettings inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt"; inline const static std::string hostsLoggerName = "hosts"; inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.txt"; + inline const static std::string registryPreviewLoggerName = "registrypreview"; + inline const static std::wstring registryPreviewLogPath = L"Logs\\registryPreview-log.txt"; inline const static int retention = 30; std::wstring logLevel; LogSettings(); diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index 4e54aeda3d..31563a2075 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -48,6 +48,7 @@ namespace powertoys_gpo { const std::wstring POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR = L"ConfigureEnabledUtilityTextExtractor"; const std::wstring POLICY_CONFIGURE_ENABLED_PASTE_PLAIN = L"ConfigureEnabledUtilityPastePlain"; const std::wstring POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE = L"ConfigureEnabledUtilityVideoConferenceMute"; + const std::wstring POLICY_CONFIGURE_ENABLED_REGISTRY_PREVIEW = L"ConfigureEnabledUtilityRegistryPreview"; // The registry value names for PowerToys installer and update policies. const std::wstring POLICY_DISABLE_AUTOMATIC_UPDATE_DOWNLOAD = L"AutomaticUpdateDownloadDisabled"; @@ -255,6 +256,10 @@ namespace powertoys_gpo { return getConfiguredValue(POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE); } + inline gpo_rule_configured_t getConfiguredRegistryPreviewEnabledValue() + { + return getConfiguredValue(POLICY_CONFIGURE_ENABLED_REGISTRY_PREVIEW); + } inline gpo_rule_configured_t getDisableAutomaticUpdateDownloadValue() { return getConfiguredValue(POLICY_DISABLE_AUTOMATIC_UPDATE_DOWNLOAD); diff --git a/src/common/utils/modulesRegistry.h b/src/common/utils/modulesRegistry.h index 62d0d35cf2..1f58ab55ca 100644 --- a/src/common/utils/modulesRegistry.h +++ b/src/common/utils/modulesRegistry.h @@ -192,6 +192,24 @@ inline registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring in NonLocalizable::ExtSTL); } +inline registry::ChangeSet getRegistryPreviewChangeSet(const std::wstring installationDir,const bool perUser) +{ + const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + + using vec_t = std::vector; + vec_t changes; + + std::wstring command = installationDir; + command.append(L"\\modules\\RegistryPreview\\PowerToys.RegistryPreview.exe \"%1\""); + changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview\\command", std::nullopt, command }); + + std::wstring icon_path = installationDir; + icon_path.append(L"\\modules\\RegistryPreview\\app.ico"); + changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview", L"icon", icon_path }); + + return { changes }; +} + inline std::vector getAllOnByDefaultModulesChangeSets(const std::wstring installationDir) { constexpr bool PER_USER = true; @@ -201,7 +219,8 @@ inline std::vector getAllOnByDefaultModulesChangeSets(const getGcodePreviewHandlerChangeSet(installationDir, PER_USER), getSvgThumbnailHandlerChangeSet(installationDir, PER_USER), getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER), - getStlThumbnailHandlerChangeSet(installationDir, PER_USER) }; + getStlThumbnailHandlerChangeSet(installationDir, PER_USER), + getRegistryPreviewChangeSet(installationDir, PER_USER) }; } inline std::vector getAllModulesChangeSets(const std::wstring installationDir) @@ -215,5 +234,6 @@ inline std::vector getAllModulesChangeSets(const std::wstri getSvgThumbnailHandlerChangeSet(installationDir, PER_USER), getPdfThumbnailHandlerChangeSet(installationDir, PER_USER), getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER), - getStlThumbnailHandlerChangeSet(installationDir, PER_USER) }; + getStlThumbnailHandlerChangeSet(installationDir, PER_USER), + getRegistryPreviewChangeSet(installationDir, PER_USER) }; } diff --git a/src/common/utils/registry.h b/src/common/utils/registry.h index fbb44a23f5..cd4a3fb715 100644 --- a/src/common/utils/registry.h +++ b/src/common/utils/registry.h @@ -16,6 +16,9 @@ namespace registry { + template + inline constexpr bool always_false_v = false; + namespace detail { struct on_exit @@ -27,9 +30,6 @@ namespace registry ~on_exit() { f(); } }; - template - inline constexpr bool always_false_v = false; - template struct overloaded : Ts... { diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx index ef5b2c015d..e3f44a5118 100644 --- a/src/gpo/assets/PowerToys.admx +++ b/src/gpo/assets/PowerToys.admx @@ -269,7 +269,17 @@ - + + + + + + + + + + + diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml index d86fbf9a95..915d61f6c9 100644 --- a/src/gpo/assets/en-US/PowerToys.adml +++ b/src/gpo/assets/en-US/PowerToys.adml @@ -11,6 +11,7 @@ PowerToys version 0.64.0 or later PowerToys version 0.68.0 or later + PowerToys version 0.69.0 or later This policy configures the enabled state for a PowerToys utility. @@ -81,6 +82,7 @@ If this setting is disabled, experimentation is not allowed. Power Rename: Configure enabled state PowerToys Run: Configure enabled state Quick Accent: Configure enabled state + Registry Preview: Configure enabled state Screen Ruler: Configure enabled state Shortcut Guide: Configure enabled state Text Extractor: Configure enabled state diff --git a/src/modules/registrypreview/RegistryPreviewExt/Constants.h b/src/modules/registrypreview/RegistryPreviewExt/Constants.h new file mode 100644 index 0000000000..5c81392657 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/Constants.h @@ -0,0 +1,7 @@ +#include + +namespace RegistryPreviewConstants +{ + // Name of the powertoy module. + inline const std::wstring ModuleKey = L"RegistryPreview"; +} \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.rc b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.rc new file mode 100644 index 0000000000..e13a322c4f --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.rc @@ -0,0 +1,108 @@ +// Microsoft Visual C++ generated resource script. +// +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END + + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#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 + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_REGISTRYPREVIEW_NAME "Registry Preview" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj new file mode 100644 index 0000000000..5f9b2fac10 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj @@ -0,0 +1,134 @@ + + + + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {697C6AF9-0A48-49A9-866C-67DA12384015} + Win32Proj + RegistryPreviewExt + 10.0.19041.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\RegistryPreview\ + PowerToys.RegistryPreviewExt + + + + Level3 + true + _DEBUG;REGISTRYPREVIEWEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) + + + Windows + true + false + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;REGISTRYPREVIEWEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + Create + pch.h + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + + + + + 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}. + + + + + + \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj.filters b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj.filters new file mode 100644 index 0000000000..bca37b946b --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj.filters @@ -0,0 +1,53 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + + + + + \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp b/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp new file mode 100644 index 0000000000..7dda85e43e --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp @@ -0,0 +1,40 @@ +#include "pch.h" +#include "trace.h" + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} + +// Log if the user has enabled or disabled the app +void Trace::EnableRegistryPreview(_In_ bool enabled) noexcept +{ + TraceLoggingWrite( + g_hProvider, + "RegistryPreview_EnableRegistryPreview", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingBoolean(enabled, "Enabled")); +} + +// Log that the user tried to activate the app +void Trace::ActivateEditor() noexcept +{ + TraceLoggingWrite( + g_hProvider, + "RegistryPreview_Activate", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} diff --git a/src/modules/registrypreview/RegistryPreviewExt/Trace.h b/src/modules/registrypreview/RegistryPreviewExt/Trace.h new file mode 100644 index 0000000000..d2cda345d8 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/Trace.h @@ -0,0 +1,14 @@ +#pragma once + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); + + // Log if the user has enabled or disabled the app + static void EnableRegistryPreview(const bool enabled) noexcept; + + // Log that the user tried to activate the app + static void ActivateEditor() noexcept; +}; diff --git a/src/modules/registrypreview/RegistryPreviewExt/dllmain.cpp b/src/modules/registrypreview/RegistryPreviewExt/dllmain.cpp new file mode 100644 index 0000000000..4ddac84299 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/dllmain.cpp @@ -0,0 +1,265 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include +#include +#include "trace.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "resource.h" +#include "Constants.h" + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + return TRUE; +} + +const static wchar_t* MODULE_NAME = L"RegistryPreview"; +const static wchar_t* MODULE_DESC = L"A quick little utility to visualize and edit complex Windows Registry files."; + +class RegistryPreviewModule : public PowertoyModuleIface +{ + +private: + bool m_enabled = false; + + //Hotkey m_hotkey; + HANDLE m_hProcess; + + HANDLE triggerEvent; + EventWaiter triggerEventWaiter; + + bool is_process_running() + { + return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT; + } + + void launch_process() + { + if (m_enabled) + { + Logger::trace(L"Starting Registry Preview process"); + unsigned long powertoys_pid = GetCurrentProcessId(); + + std::wstring executable_args = L""; + executable_args.append(std::to_wstring(powertoys_pid)); + + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = L"modules\\RegistryPreview\\PowerToys.RegistryPreview.exe"; + sei.nShow = SW_SHOWNORMAL; + sei.lpParameters = executable_args.data(); + if (ShellExecuteExW(&sei)) + { + Logger::trace("Successfully started the Registry Preview process"); + } + else + { + Logger::error(L"Registry Preview failed to start. {}", get_last_error_or_default(GetLastError())); + } + + m_hProcess = sei.hProcess; + } + } + + void terminate_process() + { + TerminateProcess(m_hProcess, 1); + } + +public: + RegistryPreviewModule() + { + LoggerHelpers::init_logger(GET_RESOURCE_STRING(IDS_REGISTRYPREVIEW_NAME), L"ModuleInterface", "RegistryPreview"); + Logger::info("Registry Preview object is constructing"); + + if (!m_enabled) + { + const std::wstring installationDir = get_module_folderpath(); + + auto regChanges = getRegistryPreviewChangeSet(installationDir, true); + + if (!regChanges.unApply()) + { + Logger::error(L"Unapplying registry changes failed"); + } + } + + triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT); + triggerEventWaiter = EventWaiter(CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT, [this](int) { + on_hotkey(0); + }); + } + + ~RegistryPreviewModule() + { + if (m_enabled) + { + terminate_process(); + } + m_enabled = false; + } + + // Destroy the powertoy and free memory + virtual void destroy() override + { + delete this; + } + + // Return the localized display name of the powertoy + virtual const wchar_t* get_name() override + { + return MODULE_NAME; + } + + // Return the non localized key of the powertoy, this will be cached by the runner + virtual const wchar_t* get_key() override + { + return MODULE_NAME; + } + + // Return the configured status for the gpo policy for the module + virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override + { + return powertoys_gpo::getConfiguredRegistryPreviewEnabledValue(); + } + + // Return JSON with the configuration options. + virtual bool get_config(wchar_t* buffer, int* buffer_size) override + { + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + + // Create a Settings object. + PowerToysSettings::Settings settings(hinstance, get_name()); + settings.set_description(MODULE_DESC); + + return settings.serialize_to_buffer(buffer, buffer_size); + } + + // Pop open the app, if the OOBE page asks it to + virtual void call_custom_action(const wchar_t* action) override + { + try + { + PowerToysSettings::CustomActionObject action_object = + PowerToysSettings::CustomActionObject::from_json_string(action); + + if (action_object.get_name() == L"Launch") + { + launch_process(); + Trace::ActivateEditor(); + } + } + catch (std::exception&) + { + Logger::error(L"Failed to parse action. {}", action); + } + } + + // Called by the runner to pass the updated settings values as a serialized JSON. + virtual void set_config(const wchar_t* config) override + { + try + { + // Parse the input JSON string. + PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); + + // If you don't need to do any custom processing of the settings, proceed + // to persists the values. + values.save_to_settings_file(); + } + catch (std::exception&) + { + // Improper JSON. + } + } + + // Enable the powertoy + virtual void enable() + { + const std::wstring installationDir = get_module_folderpath(); + + if (!getRegistryPreviewChangeSet(installationDir, true).apply()) + { + Logger::error(L"Applying registry changes failed"); + } + + // let the DLL enable the app + m_enabled = true; + Trace::EnableRegistryPreview(true); + }; + + virtual void disable() + { + if (m_enabled) + { + // let the DLL disable the app + terminate_process(); + + Trace::EnableRegistryPreview(false); + Logger::trace(L"Disabling Registry Preview..."); + + // Yeet the Registry setting so preview doesn't work anymore + const std::wstring installationDir = get_module_folderpath(); + + if (!getRegistryPreviewChangeSet(installationDir, true).unApply()) + { + Logger::error(L"Unapplying registry changes failed"); + } + } + + m_enabled = false; + } + + // Returns if the powertoys is enabled + virtual bool is_enabled() override + { + return m_enabled; + } + + // Respond to a "click" from the launcher + virtual bool on_hotkey(size_t /*hotkeyId*/) override + { + if (m_enabled) + { + Logger::trace(L"Registry Preview hotkey pressed"); + if (is_process_running()) + { + terminate_process(); + } + else + { + launch_process(); + } + + return true; + } + + return false; + } +}; + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new RegistryPreviewModule(); +} diff --git a/src/modules/registrypreview/RegistryPreviewExt/packages.config b/src/modules/registrypreview/RegistryPreviewExt/packages.config new file mode 100644 index 0000000000..c92dd4bf0c --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewExt/pch.cpp b/src/modules/registrypreview/RegistryPreviewExt/pch.cpp new file mode 100644 index 0000000000..1d9f38c57d --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/modules/registrypreview/RegistryPreviewExt/pch.h b/src/modules/registrypreview/RegistryPreviewExt/pch.h new file mode 100644 index 0000000000..be72eb015e --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/pch.h @@ -0,0 +1,16 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +//#include +#include +#include +#include + +#include + +#include +#include +//#include +#include +#include diff --git a/src/modules/registrypreview/RegistryPreviewExt/resource.h b/src/modules/registrypreview/RegistryPreviewExt/resource.h new file mode 100644 index 0000000000..a62c618221 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/resource.h @@ -0,0 +1,21 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Awake.rc +// +#define IDS_REGISTRYPREVIEW_NAME 101 + + +#define FILE_DESCRIPTION "PowerToys Registry Preview Module" +#define INTERNAL_NAME "PowerToys.RegistryPreview" +#define ORIGINAL_FILENAME "PowerToys.RegistryPreview.dll" + +// 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 101 +#endif +#endif diff --git a/src/modules/registrypreview/RegistryPreviewUI/App.xaml b/src/modules/registrypreview/RegistryPreviewUI/App.xaml new file mode 100644 index 0000000000..ffd4f5f5ae --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/App.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUI/App.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/App.xaml.cs new file mode 100644 index 0000000000..749a5bfcbe --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/App.xaml.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.UI.Xaml; +using Windows.ApplicationModel.Activation; +using LaunchActivatedEventArgs = Windows.ApplicationModel.Activation.LaunchActivatedEventArgs; + +namespace RegistryPreview +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : Application + { + /// + /// Initializes a new instance of the class. + /// + public App() + { + this.InitializeComponent(); + } + + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + { + // Grab the command line parameters directly from the Environment since this is expected to be run + // via Context Menu of a REG file. + string[] cmdArgs = Environment.GetCommandLineArgs(); + if (cmdArgs == null) + { + // Covers the double click exe scenario and treated as no file loaded + AppFilename = string.Empty; + } + else if (cmdArgs.Length == 2) + { + // GetCommandLineArgs() send in the called EXE as 0 and the selected filename as 1 + AppFilename = cmdArgs[1]; + } + else + { + // Anything else should be treated as no file loaded + AppFilename = string.Empty; + } + + // Start the application + appWindow = new MainWindow(); + appWindow.Activate(); + } + + private Window appWindow; + +#pragma warning disable SA1401 // Fields should be private +#pragma warning disable CA2211 // Non-constant fields should not be visible. TODO: consider making it a property + public static string AppFilename; +#pragma warning restore CA2211 // Non-constant fields should not be visible +#pragma warning restore SA1401 // Fields should be private + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/data32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/data32.png new file mode 100644 index 0000000000000000000000000000000000000000..de5afb66e92397445c2218263a65f6824b4412f4 GIT binary patch literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil&0(?STuUxsZY15_&6DR!n^LysZnIGQ2*9*Q?(DkIhzaONZ^ZI8X#Z?mI z7yKUv7&e{JIS&-&EbxddW?N45=Yjw`pW3#r%qfo-vhtE@IE!#NegLz9OgZz;?}mt2~XSagDl+k2m%wN;*v8e_-J}?X|XF zddH#ciq>U+cz1;Glx$GUVE>)SZ)G#}p|SCz)V!ApLTrET-%q@8XmRUeHiZqdw=HsE z|Mq|N{EIVlB6MzFa}>_sj!y85}Sb4q9e01-ly Ag#Z8m literal 0 HcmV?d00001 diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-folder32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-folder32.png new file mode 100644 index 0000000000000000000000000000000000000000..ccd53343a6f4262c2c979feb4d16c9d246a7a913 GIT binary patch literal 1281 zcmV+c1^)VpP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T&KM}d(ph6?=v0)oOxmWn(ROyu-nZ+1KHl@3?Zxe|{i|!c&hzo!&vRe*{oK!c z4*th~9-e6tTqq6##Mn{)=EDOg+0|x&FOxCky*(7JTPWyJBM7u}-519hDp5XLk7J5S z{k?bxRy8+9gnaV{caYR-?vG4b_cp9-3@$q%)%? zRbOU(H85jl%9jbrn6h)%XqQjFcUyHr0jc(qzL~3kdHK$ zd4qVuAApw{0UkU6F5d>uCxHHYKp!dTi*ehRRsf|%z?&}s8=eK8%I6j$OhowbN{?g< znCSg9$S--z7?~&}Dse0kNMA)V!E`+W&RhW+E(2HYYNtDQ0-HDUSqfOPgrd#^O-)+N z`V!!SwLry-dOwQrRv%Tq!y?Qt-T+K;GsQt5Dg49qhsZyf^1p!locrPjU@zHyM>`iJ zlfc**@QC81HOHl%|H}2Bp98+^VAkZB9EV1jfnexs=3J*o6e_B*R;>CD$AQo2xRBAT zS)-%1n3gk_bp|oU7LS_&Az8V0jeA@L4!j2M)$g*8AQ(JKPmZ%tfb``N+kK~j#%`dw z8Q8K#mgR=iTwhrUY%7J=bbY3PBXU>R6Q}m<#kmpDO!a%YL5k06YJjpbV4NA@4Ve?N z@6x3#(mvqC31)-YN&D4Y*%;P9SY%gS@^wY6#Ig0uvN zk0`4!lYzTm=^B`M1k#@C2R>#+yEt|X*t5qB=7HnKf!({!AjDQD15V`j#iB?7o!7OE zi?pfaix+{qI-TCDEU<$If!0v`Gywbe z1MTg=sZ%V}eLzc#W^JvW6Sy}dgO{@fWJPX7$Xwzv0^F-6SI@S#IPG((h1pm)BLjw^ z5F^6AY@#rqTOf$eKs;K2r4m9B@;Y58?3*zg(~ba%); zUUmmlWd+94zzT>y+xg%ZvMUr&wDqR<47t2=z$+3~-BfMy`!~FzUAMfv#6&gCrnEI- z1-Q4sig86nUq9LB8?GG9)W95|aU+|PdPGu_I5t^&VFT z_8YUlehQtP-*TpwGyFBX0MMOBen@taY0^d)MR_iEVm~Y3oHO8p8%io)C*LLcI}I!Z r$je_Uk6(xQojgl+(a|W|836tTxv9>b-Uyh^00000NkvXXu0mjfNX1|4 literal 0 HcmV?d00001 diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-value32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-value32.png new file mode 100644 index 0000000000000000000000000000000000000000..6bf283e6c9978ce763f890b730bf455afdfa926b GIT binary patch literal 1131 zcmV-x1eE)UP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!2kdb!2!6DYwZ941OQ1yK~z{ry_Q`_ z6j2n%|FdfpBm{;p)k9KIs9D;FWK^%!R-i#2_#l>OrBS^J)mtC*WKj!kFI(`$vx))nvzl!)T|Bl!Ym>t*EH5UFfg}mMvQx%Z%PXPfV)-hCRW7sXQ@hLFjZQ7(E~y4$0OW zgjQ5gPP+%J{GSKDeFK`FLwoWBe`@bx=8r*ejtK{?D>D;f`ErPD+klN5fTSe(%0T~s zY^!cum+iy^2#V&rccFQ%0L{(NA`zgV09d~s2nK-!+K$FXpt@QYlam8cQVg+Y56!5T zflF?;Y@==g@2Q>r@>N4SR0?h1erPXV=#w);LqLB&(ATH8B$DChavYDR_wefk3c3B^Ltw`ad2d|Q)Bt&Th!hv&Vp^)a6;l9Dtoo5#H+47is*8$%RQkXG1(dO; zG2!hQ$hwPM@4zKbG;h`g%Dz!{147 zX~E2&KLPdtTPPG9HY=g~_jwH?&Fwax#>wT&qrUNI|2`4Z0}OA2=JOd>CPI2#!0!i6 zozmU=`ZaLo4A9;Vymf<;|ETqsoV+!DH(AwIJD;sX#22P#?8XABH4}jyx z0V+(=PMpy1G3E?0NdELW&Qkp9mC>=moNAnUs#U?Mo^vc%Nbc#e7ogHk5i`!?$pXg4 z0}hIWs2*Su%49kHUpVY6L`ZymU#5d)D(GFXAU**iBg5#KuyN^l>773h4nL(4TVhi3tf%kP!Z>%jvP5 xjEe)f6nos^_t!K90@Sqv`~{Qf5`A#x0U7`R002ovPDHLkV1iJX26q4e literal 0 HcmV?d00001 diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/error32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/error32.png new file mode 100644 index 0000000000000000000000000000000000000000..100bfed8408caa210626d8ad546f5b4716474bdf GIT binary patch literal 678 zcmV;X0$KfuP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!2kdb!2!6DYwZ940y{}WK~z{ry_e5R z6;Tw&uO);Mh6rx!FObYaa251#B)V)LX=(pL(7zC7wP?}0NNk@dQna%t?Lvv#h)US! zJ9FM^-u2F%JHtG`@ZtVA=bm%8Z|=-}L#~F-oSUb)2{(-EAa#s=*e2*pO|~MEUV~-m zz*!=DumockrkM!v2#z6?B!we*XyMYuq7hiPAZ@S)S1f#7IL^WsTi7PMaL3Ywg~KR( zwFQGfMU?LZ#x}RTj5SNwFAREWLkk8$@4?L815Aw5{DfhRFF?mIN%B9J^09jWRmpF` zX5Niz18(QtQt3HR&Rp!keqnn;Oq;8GH$7XCkLD)78PDdk*L7U*3?bfU&L+m8B6`Us zjXisa`*M8lO_mWWqDgY`OwRAT$rzy`nkE--wP4$utfH@oqOjFTEco2Y;94YxoT0xj#pI`eZP58*kVUC#FlnIH5u1-LoR*taD!KLli=B8=*rGwP zZt2bo<5l=paBdsjwnr>oSUAkVAd1j!d)Cs#g`;-jwJmIeS1@Yf)5W64@YjMg;S`oE zTr&}10#=~vWmQCX%qO7Rc2wDWAXNlhq$&G*uD|AL_4oXRCadM#KfPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0@X=GK~z{r?N&`{ zR6!7~o`4uc!Ht-J3q?>bAYMSXp1_?K5Kj_t;St>FN(BXXZd~fhfE#fknh*^*GtT6- z^;Lh>ulmhQHgT7!q^7IB{_3yaOQvNlr(P0Q$?q=ZEF(4Smmf(KRI|`>2XG{3?tQCq zCl#$&BsV(@?LRjDcnY?aZOCC)EMKi5V}(l#&#WU{Cfw%jTUIZ3BN^Z{^wT4>IjG z^#rt#D#~TE!NA8{&Zn<(uG^I~+f!(2ypovec2d0aW}wK^@249(Z3E+vkMr5>yCg9X zQ(2&P58EagYafTi`h*Kb>=_I&%USGn^GVx4{{Ae>{1*utGze(dWl4ddVfc)X`_3np zZauZy^{+tY2fBeNIcZhLqAG5u6!(|d_6VqzPo?w?;3cWVsKkd_`Aldw_hK>Q1$Cd& zH_#LYiMA(OXpHL5y20E=91F&rh8O7@@QsET)$!qejG-M*Wr0VWGHBqK0Gmo}n-(j1 z^<>DaG1P9xmA*Ge2@POlm=ua5j#%Ku#4D9Nk6-x?)a0s03IVkUvC55h;M90st+1}c z9F(?!W^$67>0M(&SH+tmaizu>%JO%h76e&W55)8HXX=wVr`k&t@sV$Skz-y$RS)up zY9iQZg!^?e`i6y<4>9gT;Zp`LArwhGk=p_DV&2@)2G5(U7;#rLLs>k5n#@;MKYIFg zYG|0w_nLn|16_6kll@)YTvgmLS7O$L`mvd62Y$0Zli&LjRt?{VKb~L5tx&(n!sP=O z5!?mo`ZdiiDDXjazNN}~m+1KgLk`?6A}(_E!KVVOWi6*%ME(Ir>C*71+};oX0000< KMNUMnLSTYTQ-&n~ literal 0 HcmV?d00001 diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/string32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/string32.png new file mode 100644 index 0000000000000000000000000000000000000000..499f28daea071b9279858ad6619c66736ddfee6a GIT binary patch literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil&0(?STuUxsZapT4b6DRcd_y76x`&O{t$AeowynjD)=1iaxVS`s7W4KC! z{DS|(0K=v;I_H6+oCO|{#S9F5he4R}c>anMpx{nV7sn6_|FxGqg$^t5usCdH{qvtc z-K66v%S0I?cAiCRgLc+P7T&wxGtd6f9Dd#-u`(>xHx#PASu{v2InKG^xS-3Kjf|R! z2PX(I@2O+(ITzp1bJJnc6>gi~3z_dJe~vrC@MUYq?tOa3CI(-O4l};k%lYgUqm9*- zQifhDw%e&}HS;-7lrd=6Gq!FFVV}k@q5l7}m8y9YtFjuJE^q16vO4g8{ntaMT9e(J pUM%3$T`}XJkl + /// Event handler to grab the main window's size and position before it closes + /// + private void AppWindow_Closing(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowClosingEventArgs args) + { + jsonSettings.SetNamedValue("appWindow.Position.X", JsonValue.CreateNumberValue(appWindow.Position.X)); + jsonSettings.SetNamedValue("appWindow.Position.Y", JsonValue.CreateNumberValue(appWindow.Position.Y)); + jsonSettings.SetNamedValue("appWindow.Size.Width", JsonValue.CreateNumberValue(appWindow.Size.Width)); + jsonSettings.SetNamedValue("appWindow.Size.Height", JsonValue.CreateNumberValue(appWindow.Size.Height)); + } + + /// + /// Event that is will prevent the app from closing if the "save file" flag is active + /// + public void Window_Closed(object sender, WindowEventArgs args) + { + // Only block closing if the REG file has been edited but not yet saved + if (saveButton.IsEnabled) + { + // if true, the app will not close + args.Handled = true; + + // ask the user if they want to save, discard or cancel the close; strings must be loaded here and passed to avoid timing issues + HandleDirtyClosing( + resourceLoader.GetString("YesNoCancelDialogTitle"), + resourceLoader.GetString("YesNoCancelDialogContent"), + resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"), + resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"), + resourceLoader.GetString("YesNoCancelDialogCloseButtonText")); + } + + // Save app settings + jsonSettings.SetNamedValue("checkBoxTextBox.Checked", JsonValue.CreateBooleanValue(checkBoxTextBox.IsChecked.Value)); + SaveSettingsFile(settingsFolder, settingsFile); + } + + /// + /// Event that gets fired after the visual tree has been fully loaded; the app opens the reg file from here so it can show a message box successfully + /// + private void GridPreview_Loaded(object sender, RoutedEventArgs e) + { + // static flag to track whether the Visual Tree is ready - if the main Grid has been loaded, the tree is ready. + visualTreeReady = true; + + // Load and restore app settings + if (jsonSettings.ContainsKey("checkBoxTextBox.Checked")) + { + checkBoxTextBox.IsChecked = jsonSettings.GetNamedBoolean("checkBoxTextBox.Checked"); + } + + // Check to see if the REG file was opened and parsed successfully + if (OpenRegistryFile(App.AppFilename) == false) + { + if (File.Exists(App.AppFilename)) + { + // Allow Refresh and Edit to be enabled because a broken Reg file might be fixable + UpdateToolBarAndUI(false, true, true); + UpdateWindowTitle(resourceLoader.GetString("InvalidRegistryFileTitle")); + textBox.TextChanged += TextBox_TextChanged; + return; + } + else + { + UpdateToolBarAndUI(false, false, false); + UpdateWindowTitle(); + } + } + else + { + textBox.TextChanged += TextBox_TextChanged; + } + + textBox.Focus(FocusState.Programmatic); + } + + /// + /// Uses a picker to select a new file to open + /// + private async void OpenButton_Click(object sender, RoutedEventArgs e) + { + // Check to see if the current file has been saved + if (saveButton.IsEnabled) + { + ContentDialog contentDialog = new ContentDialog() + { + Title = resourceLoader.GetString("YesNoCancelDialogTitle"), + Content = resourceLoader.GetString("YesNoCancelDialogContent"), + PrimaryButtonText = resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"), + SecondaryButtonText = resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"), + CloseButtonText = resourceLoader.GetString("YesNoCancelDialogCloseButtonText"), + DefaultButton = ContentDialogButton.Primary, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + ContentDialogResult contentDialogResult = await contentDialog.ShowAsync(); + switch (contentDialogResult) + { + case ContentDialogResult.Primary: + // Save, then continue the file open + SaveFile(); + break; + case ContentDialogResult.Secondary: + // Don't save and continue the file open! + saveButton.IsEnabled = false; + break; + default: + // Don't open the new file! + return; + } + } + + // Pull in a new REG file + FileOpenPicker fileOpenPicker = new FileOpenPicker(); + fileOpenPicker.ViewMode = PickerViewMode.List; + fileOpenPicker.CommitButtonText = resourceLoader.GetString("OpenButtonText"); + fileOpenPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; + fileOpenPicker.FileTypeFilter.Add(".reg"); + + // Get the HWND so we an open the modal + IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this); + InitializeWithWindow.Initialize(fileOpenPicker, hWnd); + + StorageFile storageFile = await fileOpenPicker.PickSingleFileAsync(); + + if (storageFile != null) + { + // mute the TextChanged handler to make for clean UI + textBox.TextChanged -= TextBox_TextChanged; + + App.AppFilename = storageFile.Path; + UpdateToolBarAndUI(OpenRegistryFile(App.AppFilename)); + + // disable the Save button as it's a new file + saveButton.IsEnabled = false; + + // Restore the event handler as we're loaded + textBox.TextChanged += TextBox_TextChanged; + } + } + + /// + /// Saves the currently opened file in place + /// + private void SaveButton_Click(object sender, RoutedEventArgs e) + { + SaveFile(); + } + + /// + /// Uses a picker to save out a copy of the current reg file + /// + private async void SaveAsButton_Click(object sender, RoutedEventArgs e) + { + // Save out a new REG file and then open it + FileSavePicker fileSavePicker = new FileSavePicker(); + fileSavePicker.CommitButtonText = resourceLoader.GetString("SaveButtonText"); + fileSavePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; + fileSavePicker.FileTypeChoices.Add("Registry file", new List() { ".reg" }); + fileSavePicker.SuggestedFileName = resourceLoader.GetString("SuggestFileName"); + + // Get the HWND so we an save the modal + IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this); + InitializeWithWindow.Initialize(fileSavePicker, hWnd); + + StorageFile storageFile = await fileSavePicker.PickSaveFileAsync(); + + if (storageFile != null) + { + App.AppFilename = storageFile.Path; + SaveFile(); + UpdateToolBarAndUI(OpenRegistryFile(App.AppFilename)); + } + } + + /// + /// Reloads the current REG file from storage + /// + private void RefreshButton_Click(object sender, RoutedEventArgs e) + { + // mute the TextChanged handler to make for clean UI + textBox.TextChanged -= TextBox_TextChanged; + + // reload the current Registry file and update the toolbar accordingly. + UpdateToolBarAndUI(OpenRegistryFile(App.AppFilename), true, true); + + saveButton.IsEnabled = false; + + // restore the TextChanged handler + textBox.TextChanged += TextBox_TextChanged; + } + + /// + /// Opens the Registry Editor; UAC is handled by the request to open + /// + private void RegistryButton_Click(object sender, RoutedEventArgs e) + { + // pass in an empty string as we have no file to open + OpenRegistryEditor(string.Empty); + } + + /// + /// Merges the currently saved file into the Registry Editor; UAC is handled by the request to open + /// + private async void WriteButton_Click(object sender, RoutedEventArgs e) + { + // Check to see if the current file has been saved + if (saveButton.IsEnabled) + { + ContentDialog contentDialog = new ContentDialog() + { + Title = resourceLoader.GetString("YesNoCancelDialogTitle"), + Content = resourceLoader.GetString("YesNoCancelDialogContent"), + PrimaryButtonText = resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"), + SecondaryButtonText = resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"), + CloseButtonText = resourceLoader.GetString("YesNoCancelDialogCloseButtonText"), + DefaultButton = ContentDialogButton.Primary, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + ContentDialogResult contentDialogResult = await contentDialog.ShowAsync(); + switch (contentDialogResult) + { + case ContentDialogResult.Primary: + // Save, then continue the file open + SaveFile(); + break; + case ContentDialogResult.Secondary: + // Don't save and continue the file open! + saveButton.IsEnabled = false; + break; + default: + // Don't open the new file! + return; + } + } + + // pass in the filename so we can edit the current file + OpenRegistryEditor(App.AppFilename); + } + + /// + /// Opens the currently saved file in the PC's default REG file editor (often Notepad) + /// + private void EditButton_Click(object sender, RoutedEventArgs e) + { + // use the REG file's filename and verb so we can respect the selected editor + Process process = new Process(); + process.StartInfo.FileName = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", App.AppFilename); + process.StartInfo.Verb = "Edit"; + process.StartInfo.UseShellExecute = true; + + try + { + process.Start(); + } + catch + { + ShowMessageBox( + resourceLoader.GetString("ErrorDialogTitle"), + resourceLoader.GetString("FileEditorError"), + resourceLoader.GetString("OkButtonText")); + } + } + + /// + /// Trigger that fires when a node in treeView is clicked and which populates dataGrid + /// Can also be fired from elsewhere in the code + /// + private void TreeView_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args) + { + TreeViewItemInvokedEventArgs localArgs = args as TreeViewItemInvokedEventArgs; + TreeViewNode treeViewNode = null; + + // if there are no args, the mouse didn't get clicked but we want to believe it did + if (args != null) + { + treeViewNode = args.InvokedItem as TreeViewNode; + } + else + { + treeViewNode = treeView.SelectedNode; + } + + // Grab the object that has Registry data in it from the currently selected treeView node + RegistryKey registryKey = (RegistryKey)treeViewNode.Content; + + // no matter what happens, clear the ListView of items on each click + ClearTable(); + + // if there's no ListView items stored for the selected node, dataGrid is clear so get out now + if (registryKey.Tag == null) + { + return; + } + + // if there WAS something in the Tag property, cast it to a list and Populate the ListView + ArrayList arrayList = (ArrayList)registryKey.Tag; + listRegistryValues = new List(); + + for (int i = 0; i < arrayList.Count; i++) + { + RegistryValue listViewItem = (RegistryValue)arrayList[i]; + listRegistryValues.Add(listViewItem); + } + + // create a new binding for dataGrid and reattach it, updating the rows + Binding listRegistryValuesBinding = new Binding { Source = listRegistryValues }; + dataGrid.SetBinding(DataGrid.ItemsSourceProperty, listRegistryValuesBinding); + } + + /// + /// When the text in textBox changes, reload treeView and possibly dataGrid and reset the save button + /// + private void TextBox_TextChanged(object sender, TextChangedEventArgs e) + { + RefreshRegistryFile(); + saveButton.IsEnabled = true; + } + + /// + /// Readonly checkbox is checked, set textBox to read only; also update the font color so it has a hint of being "disabled" (also the hover state!) + /// + private void CheckBoxTextBox_Checked(object sender, RoutedEventArgs e) + { + textBox.IsReadOnly = true; + SolidColorBrush brush = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 120, 120, 120)); // (SolidColorBrush)Application.Current.Resources["TextBoxDisabledForegroundThemeBrush"]; + if (brush != null) + { + textBox.Foreground = brush; + textBox.Resources["TextControlForegroundPointerOver"] = brush; + } + } + + /// + /// Readonly checkbox is unchecked, set textBox to be editable; also update the font color back to a theme friendly foreground (also the hover state!) + /// + private void CheckBoxTextBox_Unchecked(object sender, RoutedEventArgs e) + { + textBox.IsReadOnly = false; + SolidColorBrush brush = (SolidColorBrush)Application.Current.Resources["TextControlForeground"]; + if (brush != null) + { + textBox.Foreground = brush; + textBox.Resources["TextControlForegroundPointerOver"] = brush; + } + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs new file mode 100644 index 0000000000..62de8f22d2 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs @@ -0,0 +1,896 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation.Metadata; +using Windows.Storage; + +namespace RegistryPreview +{ + public sealed partial class MainWindow : Window + { + /// + /// Method that opens and processes the passed in file name; expected to be an absolute path and a first time open + /// + private bool OpenRegistryFile(string filename) + { + // clamp to prevent attempts to open a file larger than 10MB + try + { + long fileLength = new System.IO.FileInfo(filename).Length; + if (fileLength > 1048576) + { + ShowMessageBox(resourceLoader.GetString("LargeRegistryFileTitle"), App.AppFilename + resourceLoader.GetString("LargeRegistryFile"), resourceLoader.GetString("OkButtonText")); + ChangeCursor(gridPreview, false); + return false; + } + } + catch + { + // Do nothing here - a missing or invalid file will be caught below + } + + // Disable parts of the UI that can cause trouble when loading + ChangeCursor(gridPreview, true); + textBox.Text = string.Empty; + + // clear the treeView and dataGrid no matter what + treeView.RootNodes.Clear(); + ClearTable(); + + // update the current window's title with the current filename + UpdateWindowTitle(filename); + + // Load in the whole file in one call and plop it all into textBox + FileStream fileStream = null; + try + { + FileStreamOptions fileStreamOptions = new FileStreamOptions(); + fileStreamOptions.Access = FileAccess.Read; + fileStreamOptions.Share = FileShare.ReadWrite; + fileStreamOptions.Mode = FileMode.Open; + + fileStream = new FileStream(filename, fileStreamOptions); + StreamReader streamReader = new StreamReader(fileStream); + + string filenameText = streamReader.ReadToEnd(); + textBox.Text = filenameText; + streamReader.Close(); + } + catch + { + // restore TextChanged handler to make for clean UI + textBox.TextChanged += TextBox_TextChanged; + + // Reset the cursor but leave textBox disabled as no content got loaded + ChangeCursor(gridPreview, false); + return false; + } + finally + { + // clean up no matter what + if (fileStream != null) + { + fileStream.Dispose(); + } + } + + // now that the file is loaded and in textBox, parse the data + ParseRegistryFile(textBox.Text); + + // Getting here means that the entire REG file was parsed without incident + // so select the root of the tree and celebrate + if (treeView.RootNodes.Count > 0) + { + treeView.SelectedNode = treeView.RootNodes[0]; + treeView.Focus(FocusState.Programmatic); + } + + // reset the cursor + ChangeCursor(gridPreview, false); + return true; + } + + /// + /// Method that re-opens and processes the filename the app already knows about; expected to not be a first time open + /// + private void RefreshRegistryFile() + { + // Disable parts of the UI that can cause trouble when loading + ChangeCursor(gridPreview, true); + + // Get the current selected node so we can return focus to an existing node + TreeViewNode currentNode = treeView.SelectedNode; + + // clear the treeView and dataGrid no matter what + treeView.RootNodes.Clear(); + ClearTable(); + + // the existing text is still in textBox so parse the data again + ParseRegistryFile(textBox.Text); + + // check to see if there was a key in treeView before the refresh happened + if (currentNode != null) + { + // since there is a valid node, get the FullPath of the key that was selected + string selectedFullPath = ((RegistryKey)currentNode.Content).FullPath; + + // check to see if we still have the key in the new Dictionary of keys + if (mapRegistryKeys.ContainsKey(selectedFullPath)) + { + // we found it! select it in the tree and pretend it was selected + TreeViewNode treeViewNode; + mapRegistryKeys.TryGetValue(selectedFullPath, out treeViewNode); + treeView.SelectedNode = treeViewNode; + TreeView_ItemInvoked(treeView, null); + } + else + { + // we failed to find an existing node; it could have been deleted in the edit + if (treeView.RootNodes.Count > 0) + { + treeView.SelectedNode = treeView.RootNodes[0]; + } + } + } + else + { + // no node was previously selected so check for a RootNode and select it + if (treeView.RootNodes.Count > 0) + { + treeView.SelectedNode = treeView.RootNodes[0]; + } + } + + // enable the UI + ChangeCursor(gridPreview, false); + } + + /// + /// Parses the text that is passed in, which should be the same text that's in textBox + /// + private bool ParseRegistryFile(string filenameText) + { + // if this is a not-first open, clear out the Dictionary of nodes + if (mapRegistryKeys != null) + { + mapRegistryKeys.Clear(); + mapRegistryKeys = null; + } + + // set up a new dictionary + mapRegistryKeys = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + // As we'll be processing the text one line at a time, this string will be the current line + string registryLine; + + // Brute force editing: for textBox to show Cr-Lf corrected, we need to strip out the \n's + filenameText = filenameText.Replace("\r\n", "\r"); + + // split apart all of the text in textBox, where one element in the array represents one line + string[] registryLines = filenameText.Split("\r"); + if (registryLines.Length <= 1) + { + // after the split, we have no lines so get out + ChangeCursor(gridPreview, false); + return false; + } + + // REG files have to start with one of two headers and it's case insensitive + registryLine = registryLines[0]; + registryLine = registryLine.ToLowerInvariant(); + + // make sure that this is a valid REG file, based on the first line of the file + switch (registryLine) + { + case REGISTRYHEADER4: + case REGISTRYHEADER5: + break; + default: + ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText")); + ChangeCursor(gridPreview, false); + return false; + } + + // these are used for populating the tree as we read in one line at a time + TreeViewNode treeViewNode = null; + RegistryValue registryValue = null; + + // start with the first element of the array + int index = 1; + registryLine = registryLines[index]; + + while (index < registryLines.Length) + { + // special case for when the registryLine begins with a @ - make some tweaks and + // let the regular processing handle the rest. + if (registryLine.StartsWith("@=-", StringComparison.InvariantCulture)) + { + // REG file has a callout to delete the @ Value which won't work *but* the Registry Editor will + // clear the value of the @ Value instead, so it's still a valid line. + registryLine = registryLine.Replace("@=-", "\"(Default)\"=\"\""); + } + else if (registryLine.StartsWith("@=", StringComparison.InvariantCulture)) + { + // This is the a Value called "(Default)" so we tweak the line for the UX + registryLine = registryLine.Replace("@=", "\"(Default)\"="); + } + + // continue until we have nothing left to read + // switch logic, based off what the current line we're reading is + if (registryLine.StartsWith("[-", StringComparison.InvariantCulture)) + { + // remove the - as we won't need it but it will get special treatment in the UI + registryLine = registryLine.Remove(1, 1); + + // this is a key, so remove the first [ and last ] + registryLine = StripFirstAndLast(registryLine); + + // do not track the result of this node, since it should have no children + AddTextToTree(registryLine, DELETEDKEYIMAGE); + } + else if (registryLine.StartsWith("[", StringComparison.InvariantCulture)) + { + // this is a key, so remove the first [ and last ] + registryLine = StripFirstAndLast(registryLine); + + treeViewNode = AddTextToTree(registryLine, KEYIMAGE); + } + else if (registryLine.StartsWith("\"", StringComparison.InvariantCulture) && registryLine.EndsWith("=-", StringComparison.InvariantCulture)) + { + // this line deletes this value so it gets special treatment for the UI + registryLine = registryLine.Replace("=-", string.Empty); + + // remove the "'s without removing all of them + registryLine = StripFirstAndLast(registryLine); + + // Create a new listview item that will be used to display the delete value and store it + registryValue = new RegistryValue(registryLine, string.Empty, string.Empty); + SetValueToolTip(registryValue); + + // store the ListViewItem, if we have a valid Key to attach to + if (treeViewNode != null) + { + StoreTheListValue((RegistryKey)treeViewNode.Content, registryValue); + } + } + else if (registryLine.StartsWith("\"", StringComparison.InvariantCulture)) + { + // this is a named value + + // split up the name from the value by looking for the first found = + int equal = registryLine.IndexOf('='); + if ((equal < 0) || (equal > registryLine.Length - 1)) + { + // something is very wrong + Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "SOMETHING WENT WRONG: {0}", registryLine)); + break; + } + + // set the name and the value + string name = registryLine.Substring(0, equal); + name = StripFirstAndLast(name); + + // Clean out any escaped characters in the value, only for the preview + name = StripEscapedCharacters(name); + + string value = registryLine.Substring(equal + 1); + + // Create a new listview item that will be used to display the value + registryValue = new RegistryValue(name, "REG_SZ", string.Empty); + + // if the first and last character is a " then this is a string value; get rid of the first and last " + if (value.StartsWith("\"", StringComparison.InvariantCulture) && value.EndsWith("\"", StringComparison.InvariantCulture)) + { + value = StripFirstAndLast(value); + } + else + { + // this is an invalid value as there are no "s in the right side of the = + registryValue.Type = "ERROR"; + } + + if (value.StartsWith("dword:", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_DWORD"; + value = value.Replace("dword:", string.Empty); + } + else if (value.StartsWith("hex(b):", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_QWORD"; + value = value.Replace("hex(b):", string.Empty); + } + else if (value.StartsWith("hex:", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_BINARY"; + value = value.Replace("hex:", string.Empty); + } + else if (value.StartsWith("hex(2):", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_EXAND_SZ"; + value = value.Replace("hex(2):", string.Empty); + } + else if (value.StartsWith("hex(7):", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_MULTI_SZ"; + value = value.Replace("hex(7):", string.Empty); + } + + // If the end of a decimal line ends in a \ then you have to keep + // reading the block as a single value! + while (value.EndsWith(@",\", StringComparison.InvariantCulture)) + { + value = value.TrimEnd('\\'); + index++; + if (index >= registryLines.Length) + { + ChangeCursor(gridPreview, false); + return false; + } + + registryLine = registryLines[index]; + registryLine = registryLine.TrimStart(); + value += registryLine; + } + + // Clean out any escaped characters in the value, only for the preview + value = StripEscapedCharacters(value); + + // update the ListViewItem with this information + if (registryValue.Type != "ERROR") + { + registryValue.Value = value; + } + + // update the ToolTip + SetValueToolTip(registryValue); + + // store the ListViewItem, if we have a valid Key to attach to + if (treeViewNode != null) + { + StoreTheListValue((RegistryKey)treeViewNode.Content, registryValue); + } + } + + // if we get here, it's not a Key (starts with [) or Value (starts with ") so it's likely waste (comments that start with ; fall out here) + + // read the next line from the REG file + index++; + + // if we've gone too far, escape the proc! + if (index >= registryLines.Length) + { + // check to see if anything got parsed! + if (treeView.RootNodes.Count <= 0) + { + ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText")); + } + + ChangeCursor(gridPreview, false); + return false; + } + + // carry on with the next line + registryLine = registryLines[index]; + } + + // last check, to see if anything got parsed! + if (treeView.RootNodes.Count <= 0) + { + ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText")); + ChangeCursor(gridPreview, false); + return false; + } + + return true; + } + + /// + /// We're going to store this ListViewItem in an ArrayList which will then + /// be attached to the most recently returned TreeNode that came back from + /// AddTextToTree. If there's already a list there, we will use that list and + /// add our new node to it. + /// + private void StoreTheListValue(RegistryKey registryKey, RegistryValue registryValue) + { + ArrayList arrayList = null; + if (registryKey.Tag == null) + { + arrayList = new ArrayList(); + } + else + { + arrayList = (ArrayList)registryKey.Tag; + } + + arrayList.Add(registryValue); + + // shove the updated array into the Tag property + registryKey.Tag = arrayList; + } + + /// + /// Adds the REG file that's being currently being viewed to the app's title bar + /// + private void UpdateWindowTitle(string filename) + { + string[] file = filename.Split('\\'); + if (file.Length > 0) + { + appWindow.Title = file[file.Length - 1] + " - " + APPNAME; + } + else + { + appWindow.Title = filename + " - " + APPNAME; + } + } + + /// + /// No REG file was opened, so leave the app's title bar alone + /// + private void UpdateWindowTitle() + { + appWindow.Title = APPNAME; + } + + /// + /// Helper method that assumes everything is enabled/disabled together + /// + private void UpdateToolBarAndUI(bool enable) + { + UpdateToolBarAndUI(enable, enable, enable); + } + + /// + /// Enable command bar buttons and textBox. + /// Note that writeButton and textBox all update with the same value on purpose + /// + private void UpdateToolBarAndUI(bool enableWrite, bool enableRefresh, bool enableEdit) + { + refreshButton.IsEnabled = enableRefresh; + editButton.IsEnabled = enableEdit; + writeButton.IsEnabled = enableWrite; + } + + /// + /// Helper method that creates a new TreeView node, attaches it to a parent if any, and then passes the new node back to the caller + /// mapRegistryKeys is a collection of all of the [] lines in the file + /// keys comes from the REG file and represents a bunch of nodes + /// + private TreeViewNode AddTextToTree(string keys, string image) + { + string[] individualKeys = keys.Split('\\'); + string fullPath = keys; + TreeViewNode returnNewNode = null, newNode = null, previousNode = null; + + // Walk the list of keys backwards + for (int i = individualKeys.Length - 1; i >= 0; i--) + { + // when a Key is marked for deletion, make sure it only sets the icon for the bottom most leaf + if (image == DELETEDKEYIMAGE) + { + if (i < individualKeys.Length - 1) + { + image = KEYIMAGE; + } + else + { + // special casing for Registry roots + switch (individualKeys[i]) + { + case "HKEY_CLASSES_ROOT": + case "HKEY_CURRENT_USER": + case "HKEY_LOCAL_MACHINE": + case "HKEY_USERS": + case "HKEY_CURRENT_CONFIG": + image = KEYIMAGE; + break; + } + } + } + + // First check the dictionary, and return the current node if it already exists + if (mapRegistryKeys.ContainsKey(fullPath)) + { + // was a new node created? + if (returnNewNode == null) + { + // if no new nodes have been created, send out the node we should have already + mapRegistryKeys.TryGetValue(fullPath, out returnNewNode); + } + else + { + // as a new node was created, hook it up to this found parent + mapRegistryKeys.TryGetValue(fullPath, out newNode); + newNode.Children.Add(previousNode); + } + + // return the new node no matter what + return returnNewNode; + } + + // Since the path is not in the tree, create a new node and add it to the dictionary + RegistryKey registryKey = new RegistryKey(individualKeys[i], fullPath, image, GetFolderToolTip(image)); + + newNode = new TreeViewNode() { Content = registryKey, IsExpanded = true }; + mapRegistryKeys.Add(fullPath, newNode); + + // if this is the first new node we're creating, we need to return it to the caller + if (previousNode == null) + { + // capture the first node so it can be returned + returnNewNode = newNode; + } + else + { + // The newly created node is a parent to the previously created node, as add it here. + newNode.Children.Add(previousNode); + } + + // before moving onto the next node, tag the previous node and update the path + previousNode = newNode; + fullPath = fullPath.Replace(string.Format(CultureInfo.InvariantCulture, @"\{0}", individualKeys[i]), string.Empty); + + // One last check: if we get here, the parent of this node is not yet in the tree, so we need to add it as a RootNode + if (i == 0) + { + treeView.RootNodes.Add(newNode); + treeView.UpdateLayout(); + } + } + + return returnNewNode; + } + + /// + /// Wrapper method that shows a simple one-button message box, parented by the main application window + /// + private async void ShowMessageBox(string title, string content, string closeButtonText) + { + ContentDialog contentDialog = new ContentDialog() + { + Title = title, + Content = content, + CloseButtonText = closeButtonText, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + await contentDialog.ShowAsync(); + } + + /// + /// Wrapper method that shows a Save/Don't Save/Cancel message box, parented by the main application window and shown when closing the app + /// + private async void HandleDirtyClosing(string title, string content, string primaryButtonText, string secondaryButtonText, string closeButtonText) + { + ContentDialog contentDialog = new ContentDialog() + { + Title = title, + Content = content, + PrimaryButtonText = primaryButtonText, + SecondaryButtonText = secondaryButtonText, + CloseButtonText = closeButtonText, + DefaultButton = ContentDialogButton.Primary, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + ContentDialogResult contentDialogResult = await contentDialog.ShowAsync(); + + switch (contentDialogResult) + { + case ContentDialogResult.Primary: + // Save, then close + SaveFile(); + break; + case ContentDialogResult.Secondary: + // Don't save, and then close! + saveButton.IsEnabled = false; + break; + default: + // Cancel closing! + return; + } + + // if we got here, we should try to close again + App.Current.Exit(); + } + + /// + /// Method will open the Registry Editor or merge the current REG file into the Registry via the Editor + /// Process will prompt for elevation if it needs it. + /// + private void OpenRegistryEditor(string fileMerge) + { + Process process = new Process(); + process.StartInfo.FileName = "regedit.exe"; + process.StartInfo.UseShellExecute = true; + if (File.Exists(fileMerge)) + { + // If Merge was called, pass in the filename as a param to the Editor + process.StartInfo.Arguments = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", fileMerge); + } + + try + { + process.Start(); + } + catch + { + ShowMessageBox( + resourceLoader.GetString("UACDialogTitle"), + resourceLoader.GetString("UACDialogError"), + resourceLoader.GetString("OkButtonText")); + } + } + + /// + /// Utility method that clears out the GridView as there's no other way to do it. + /// + private void ClearTable() + { + if (listRegistryValues != null) + { + listRegistryValues.Clear(); + } + + dataGrid.ItemsSource = null; + } + + /// + /// Change the current app cursor at the grid level to be a wait cursor. Sort of works, sort of doesn't, but it's a nice attempt. + /// + public void ChangeCursor(UIElement uiElement, bool wait) + { + // You can only change the Cursor if the visual tree is loaded + if (!visualTreeReady) + { + return; + } + + InputCursor cursor = InputSystemCursor.Create(wait ? InputSystemCursorShape.Wait : InputSystemCursorShape.Arrow); + System.Type type = typeof(UIElement); + type.InvokeMember("ProtectedCursor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, uiElement, new object[] { cursor }, CultureInfo.InvariantCulture); + } + + /// + /// Wrapper method that saves the current file in place, using the current text in textBox. + /// + private void SaveFile() + { + ChangeCursor(gridPreview, true); + + // set up the FileStream for all writing + FileStream fileStream = null; + + try + { + // attempt to open the existing file for writing + FileStreamOptions fileStreamOptions = new FileStreamOptions(); + fileStreamOptions.Access = FileAccess.Write; + fileStreamOptions.Share = FileShare.Write; + fileStreamOptions.Mode = FileMode.OpenOrCreate; + + fileStream = new FileStream(App.AppFilename, fileStreamOptions); + StreamWriter streamWriter = new StreamWriter(fileStream, System.Text.Encoding.Unicode); + + // if we get here, the file is open and writable so dump the whole contents of textBox + string filenameText = textBox.Text; + streamWriter.Write(filenameText); + streamWriter.Flush(); + streamWriter.Close(); + + // only change when the save is successful + saveButton.IsEnabled = false; + } + catch (UnauthorizedAccessException ex) + { + // this exception is thrown if the file is there but marked as read only + ShowMessageBox( + resourceLoader.GetString("ErrorDialogTitle"), + ex.Message, + resourceLoader.GetString("OkButtonText")); + } + catch + { + // this catch handles all other exceptions thrown when trying to write the file out + ShowMessageBox( + resourceLoader.GetString("ErrorDialogTitle"), + resourceLoader.GetString("FileSaveError"), + resourceLoader.GetString("OkButtonText")); + } + finally + { + // clean up no matter what + if (fileStream != null) + { + fileStream.Dispose(); + } + } + + // restore the cursor + ChangeCursor(gridPreview, false); + } + + private async void OpenSettingsFile(string path, string filename) + { + StorageFolder storageFolder = null; + StorageFile storageFile = null; + string fileContents = string.Empty; + + try + { + storageFolder = await StorageFolder.GetFolderFromPathAsync(path); + } + catch + { + } + + try + { + if (storageFolder != null) + { + storageFile = await storageFolder.GetFileAsync(filename); + } + } + catch + { + } + + try + { + if (storageFile != null) + { + fileContents = await Windows.Storage.FileIO.ReadTextAsync(storageFile); + } + } + catch + { + } + + try + { + jsonSettings = Windows.Data.Json.JsonObject.Parse(fileContents); + } + catch + { + // set up default JSON blob + fileContents = "{ }"; + jsonSettings = Windows.Data.Json.JsonObject.Parse(fileContents); + } + } + + /// + /// Save the settings JSON blob out to a local file + /// + private async void SaveSettingsFile(string path, string filename) + { + StorageFolder storageFolder = null; + StorageFile storageFile = null; + string fileContents = string.Empty; + + try + { + storageFolder = await StorageFolder.GetFolderFromPathAsync(path); + } + catch (FileNotFoundException ex) + { + Debug.WriteLine(ex.Message); + Directory.CreateDirectory(path); + storageFolder = await StorageFolder.GetFolderFromPathAsync(path); + } + + try + { + storageFile = await storageFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists); + } + catch (FileNotFoundException ex) + { + Debug.WriteLine(ex.Message); + storageFile = await storageFolder.CreateFileAsync(filename); + } + + try + { + fileContents = jsonSettings.Stringify(); + await Windows.Storage.FileIO.WriteTextAsync(storageFile, fileContents); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + } + + /// + /// Rip the first and last character off a string, + /// checking that the string is at least 2 characters long to avoid errors + /// + private string StripFirstAndLast(string line) + { + if (line.Length > 1) + { + line = line.Remove(line.Length - 1, 1); + line = line.Remove(0, 1); + } + + return line; + } + + /// + /// Replace any escaped characters in the REG file with their counterparts, for the UX + /// + private string StripEscapedCharacters(string value) + { + value = value.Replace("\\\\", "\\"); // Replace \\ with \ in the UI + value = value.Replace("\\\"", "\""); // Replace \" with " in the UI + return value; + } + + /// + /// Loads and returns a string for a given Key's image in the tree, based off the current set image + /// + private string GetFolderToolTip(string key) + { + string value = string.Empty; + switch (key) + { + case DELETEDKEYIMAGE: + value = resourceLoader.GetString("ToolTipDeletedKey"); + break; + case KEYIMAGE: + value = resourceLoader.GetString("ToolTipAddedKey"); + break; + } + + return value; + } + + /// + /// Loads a string for a given Value's image in the grid, based off the current type and updates the RegistryValue that's passed in + /// + private void SetValueToolTip(RegistryValue registryValue) + { + string value = string.Empty; + switch (registryValue.Type) + { + case "REG_SZ": + case "REG_EXAND_SZ": + case "REG_MULTI_SZ": + value = resourceLoader.GetString("ToolTipStringValue"); + break; + case "ERROR": + value = resourceLoader.GetString("ToolTipErrorValue"); + break; + case "": + value = resourceLoader.GetString("ToolTipDeletedValue"); + break; + default: + value = resourceLoader.GetString("ToolTipBinaryValue"); + break; + } + + registryValue.ToolTipText = value; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml new file mode 100644 index 0000000000..a0157581f5 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml.cs new file mode 100644 index 0000000000..8866ca2112 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.UI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Windows.ApplicationModel.Resources; +using Windows.Data.Json; +using Windows.Graphics; + +namespace RegistryPreview +{ + public sealed partial class MainWindow : Window + { + // Const values + private const string REGISTRYHEADER4 = "regedit4"; + private const string REGISTRYHEADER5 = "windows registry editor version 5.00"; + private const string APPNAME = "Registry Preview"; + private const string KEYIMAGE = "ms-appx:///Assets/folder32.png"; + private const string DELETEDKEYIMAGE = "ms-appx:///Assets/deleted-folder32.png"; + + // private members + private Microsoft.UI.Windowing.AppWindow appWindow; + private ResourceLoader resourceLoader; + private bool visualTreeReady; + private Dictionary mapRegistryKeys; + private List listRegistryValues; + private JsonObject jsonSettings; + private string settingsFolder = string.Empty; + private string settingsFile = string.Empty; + + internal MainWindow() + { + this.InitializeComponent(); + + // Initialize the string table + resourceLoader = ResourceLoader.GetForViewIndependentUse(); + + // Removed this on 2/15/23 as it doesn't seem to be doing anything any more + // Attempts to force the visual tree to load faster + // this.Activate(); + + // Update the Win32 looking window with the correct icon (and grab the appWindow handle for later) + IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this); + WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle); + appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId); + appWindow.SetIcon("app.ico"); + appWindow.Closing += AppWindow_Closing; + + // Open settings file; this moved to after the window tweak because it gives the window time to start up + settingsFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerToys\" + APPNAME; + settingsFile = APPNAME + "_settings.json"; + OpenSettingsFile(settingsFolder, settingsFile); + + // TODO: figure out a way to only call this once after MainWindow is initialized but before it shows itself + // Calling it from here only successfully resizes/moves the window and it seems to be based off timing, which is horrible. + // Calling it from GridPreview_Loaded() works 100% of the time, but the initial state of the window flashes before sizing/moving it + // + // // if have settings, update the location of the window + // if (jsonSettings != null) + // { + // // resize the window + // if (jsonSettings.ContainsKey("appWindow.Size.Width") && jsonSettings.ContainsKey("appWindow.Size.Height")) + // { + // SizeInt32 size; + // size.Width = (int)jsonSettings.GetNamedNumber("appWindow.Size.Width"); + // size.Height = (int)jsonSettings.GetNamedNumber("appWindow.Size.Height"); + // appWindow.Resize(size); + // } + // + // // reposition the window + // if (jsonSettings.ContainsKey("appWindow.Position.X") && jsonSettings.ContainsKey("appWindow.Position.Y")) + // { + // PointInt32 point; + // point.X = (int)jsonSettings.GetNamedNumber("appWindow.Position.X"); + // point.Y = (int)jsonSettings.GetNamedNumber("appWindow.Position.Y"); + // appWindow.Move(point); + // } + // } + + // Update Toolbar + if ((App.AppFilename == null) || (File.Exists(App.AppFilename) != true)) + { + UpdateToolBarAndUI(false); + UpdateWindowTitle(resourceLoader.GetString("FileNotFound")); + } + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryKey.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/RegistryKey.xaml.cs new file mode 100644 index 0000000000..343966fa3d --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryKey.xaml.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace RegistryPreview +{ + /// + /// Class representing an each item in the tree view, each one a Registry Key; + /// FullPath is so we can re-select the node after a live update + /// Tag is an Array of ListViewItems that stores all the children for the current object + /// + public class RegistryKey + { + public string Name { get; set; } + + public string FullPath { get; set; } + + public string Image { get; set; } + + public string ToolTipText { get; set; } + + public object Tag { get; set; } + + public RegistryKey(string name, string fullPath, string image, string toolTipText) + { + this.Name = name; + this.FullPath = fullPath; + this.Image = image; + this.ToolTipText = toolTipText; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj b/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj new file mode 100644 index 0000000000..35c36b74c6 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj @@ -0,0 +1,67 @@ + + + + WinExe + net7.0-windows10.0.19041.0 + 10.0.19041.0 + x86;x64;arm64 + $(SolutionDir)$(Platform)\$(Configuration)\modules\RegistryPreview + true + False + app.ico + app.manifest + true + false + false + true + None + true + 10.0.19041.0 + true + $(AssemblyName) + PowerToys.RegistryPreview + PowerToys.RegistryPreview + PowerToys RegistryPreview + RegistryPreview + true + + + + + win10-x64 + + + win10-arm64 + + + + + + + + + https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json + + + + + + + + + + + + + + + + + Never + + + Never + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs new file mode 100644 index 0000000000..ce372281b3 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace RegistryPreview +{ + /// + /// Class representing an each item in the list view, each one a Registry Value. + /// + public class RegistryValue + { + // Static members + private static Uri uriStringValue = new Uri("ms-appx:///Assets/string32.png"); + private static Uri uriBinaryValue = new Uri("ms-appx:///Assets/data32.png"); + private static Uri uriDeleteValue = new Uri("ms-appx:///Assets/deleted-value32.png"); + private static Uri uriErrorValue = new Uri("ms-appx:///Assets/error32.png"); + + public string Name { get; set; } + + public string Type { get; set; } + + public string Value { get; set; } + + public string ToolTipText { get; set; } + + public Uri ImageUri + { + // Based off the Type of the item, pass back the correct image Uri used by the Binding of the DataGrid + get + { + switch (Type) + { + case "REG_SZ": + case "REG_EXAND_SZ": + case "REG_MULTI_SZ": + return uriStringValue; + case "ERROR": + return uriErrorValue; + case "": + return uriDeleteValue; + } + + return uriBinaryValue; + } + } + + public RegistryValue(string name, string type, string value) + { + this.Name = name; + this.Type = type; + this.Value = value; + this.ToolTipText = string.Empty; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw new file mode 100644 index 0000000000..dc89b61703 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Read only + + + Edit file... + + + Error + + + The REG file editor could not be opened. + + + File was not found! + + + The REG file cannot be written to. + + + doesn't appear to be a valid registry file! + + + File was not a Registry file! + + + is larger than 10MB which is too large for this application. + + + File is too large! + + + Name + + + OK + + + Open file... + + + Open + + + Reload from file + + + Open Registry Editor... + + + Save file as... + + + Save file + + + Save + + + New Registry File + + + Registry file text will appear here... + + + Key will be added, if needed + + + Binary value will be updated + + + Key will be deleted + + + Value will be deleted + + + Value has a syntax error + + + String value will be updated + + + Type + + + You must click Yes on the previous popup if you want to run the Registry application. + + + User Account Control + + + Value + + + Write to Registry... + + + Cancel + + + Changes were made to the text file. Do you want to save your changes? + + + Save + + + Don't save + + + Registry Preview + + \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewUI/app.ico b/src/modules/registrypreview/RegistryPreviewUI/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..77b76593e318758392a41d851283be57d0e6d11b GIT binary patch literal 410598 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�!Ry5)sZ1LKw!WN>*X+rl`tSc(%jN$qybAIY3@rx2NDd2uF*BuH#3@hMsp9E z`#!$8=bP`yeXput_p9v8s>-UYs;=%g5jk01-CdnuX8!)~W#+3a-17%DV)ew=1#5E0 zfBHMYec2Zm|A9L2{r{-e-3j|Ox%dms_NRC;Xcyk*aPRQYnm<>^fA$`=efqBl>xrmM zxb`>e|MsbgaDRBv4y-R=e~9uA?GJN5I0jT(VNc2pe`?o%)B@~p;QK!D?SG^0h1f^@ zotggt&y)CKxPQn)uzvOUjeQEW?+38%`~z6i*u7#_u#RM#57yg0Xn(W?mOfB?`$NxS z|8D(5^{Z1q{3P^+Pel5{<|$!+z}O0C^HE%f#Wu%%+0MTB`*45%J7B+!f7y)b3ic0y z&)Q1Y=kvs-R|SM6O0e=_m{&OCH^M*Jk8Y5!C zy+fTlwxz*6^sh^;cmQhOG`UFws%(GW7pA-qo}bjGe(9-i|LE4ucmJ{4J@wzJ9iR!D zfCsD>y-lpIBmTlKstxynzp&(w)!AjRzYN&XDWCpj^|M=Eua-i+uLYk1_xM471l!np zO?nTmjWJ-aKwm^AH-J8Dn0}o)wDq~5ZGyHBVtK9vePDDa`0F?aOzn^MX4l8SxA>6S zIQ30I+nfAfEC>EhKeYY!@Bdja?+cQW+UJYw3wj?2+J9-0_Mf+My5np6)ElS%c{YC` zh~a^?eY71HVGpo#FTPLhU2rw{8NZ+~_8aZ#REQJ9{a!Ez9OoPGJnDQHAEfnzMw_pM z7$c1D31Y_PT&E89PgWOB{2Z&Vp~)8m?WaC~Hel%3Q{gyZBdh`8{SMIfXI}X4!QND8 zXKei~h#!OB+WW%SgLs0WQ9o^u{X(|`o!I`omynHU^B2HALYs`PfOc7paa+*l$Dqw| z-#6R~Y%lEbiTk?-v3|fR_ZKi`5BCJcy_s--xk<1O6!nEws2>o|wF2}Z(&lra%~woT z=b_D!MU3UacDVxaJ!tcz32gwZ1za2e9}|2$So6nNgF{dLc60y0wzkLLg|rfW_my$4 zS4TI_3w|Sr69wO`6;qJT=TEBvVf!yB(EsPHneUkT!b{)$_{%^23i#>s-=hvK{C#!q z`FBGs&wF4mIgIIwwz)}PoAV36JoN?KSMIx@3%C9S#(ck5?fKzfs#j;f4>-R=oj=Li z94hPzti^}E9<(#+eOvoue;5qLf@jx(Ein+4`|QV0)PEe-=p?!U4oZZ5P})^!KT)^WU#_|M&(-5DyP&-wR(;`(OT++V|oufjv3A`hIoc^xoh%FXK4k zGO!N)D;(?j?E`gh|2Yd5d;{AEBp6s6Q3Lx{1@*b<{&xhizCrvo*jV(VBJB=sjOPMv z2mY|HygK7rbzsSD>H_%K^bglrf;NYCxB~0$7=uIqh9M~P;`xCvJ~tTi*>&JDI0Bb1 zj|6=QWFxR0Xg|n?VEHlOJyngv;UR$z5sl$g#Lj20^59Zqs`A9-4ev(2iJoCh9Ec}86O%7WA*Vo z{=+%fL$7U6FFkgfy8ovCs_ys{+_!e>aPZ#XAhf@=AE0Jn86klnU`#(HnlFgj59&){ z9_a&-_D4O)?+f4`P{;P4L+w9;wis+JCb(|`HJADV9-ld|TJ3%QW|%jAo7x1=_1(Yt zR(1Bs7KJP#Z}{0EaO9A2O#B~mxO>AQ_0-HSs_%aBKLyY8!?k#f&&B6>%^Uki!N*Y> zg0{5w0rC%{ZA)W?q}<*I{IvhFrAuq!|8f53%nA7)h4!bmi{y9@4KyycabP2`Z$P6{ zn_q!84}3K8-LU=P^8+W3!yNnfA?E6nYVzN|If%i;HvcBv2k;%3i>EaJjLW#`k6;|} z<2!>sfSOTMABeO++JcA<~^#V1c(f+6d z!>3ljdhD+#yhj(?{Fc9j@c_&j1@YOi4yUgJVr!{ZDTHn0|rnj|uz5#S<_;^HH_uNAC)r;hVIsOnm|G*ZAH)!k)w+GJ(#JFMxfZ z*$+tndG2ltB7GpD0oi?lp#7KCQUCL{f1_W(JPqpp=+3XfTph&i?EN@A|LwPfy#+|? zsjZDUtO24g@RMnu4Kx7##-<%8wGU*o16b(gHE#bA(0^Xvb~|9w0k8#N7e)@jnmXQ} zf%CMmSB%>EH)`gM|2kD{9`*_J3-JD}Ei0c0`T#Fa^BC+l*_vqXrUMZT@Zby7xBVOA zzfti2(f2YX^dH(EeQ#T8>lf4(FlTV( z@;Qh>{zPMq4%X(V{W0JBG4<*rzZYl##+fDd0q75S&*t1QaIX*i!#Toe`(s;E`%=48 zPUq9*k@nB20kv=c!2b`&?yd13YJHtXPxc8b^MR*Z%U{Y;HVED`*F+WQ=98K z>;Z+EFy%A99P9<3)CbUC#66>M53${wUc{>H_&$JT?5Xz0_{@Qg5S#O7VSJA1{w&@P zUi+vz`0V=wKR}N)$JkuF$G9062y+FvSJAz2e%<46&oBJLJ{aMjv>?y{(tn<(vjJ`E z0~s{{%eZ{mvKr3+(fH3Y|7T(!=iXtgH+=j#rPlsI;~uSs&&S)GA7ji8_yRO0#CtU` zQG6k`Kkhq(`vN_Od;UN?0`BuPe0d<4b86arYImL+`#}2k_t6J>tNqa@fOS3S10#E< z*lj;=e>`rC31{8(X7yEwAw@fY&l-Ld;zt(4niAc&vL0hNAg<)>!BxRr5RNyR?T>BE z+dlFh=U@9y(_b+6i-<3nK?CaE{tD**hrp&|GTQq#9RPn2eSZwn-1=$o{b3C!!3Kn|!<(o=7jpMVEjkzZ_ADS0>}Pmwt^1;_va_wtA(+9us_h=1#eaBAvP0v*JDyK zc4yne9}L&+tYEg?K-Fr z)V=*j6Xt*P_HVvMy8tEt@7EgL`Tw8~;61wrUl6#zwBnD$Jp{)^+a3D=#u8yX->F?s zhwDGIo*cvth5AqJe}$OU+uv#jiq`;a|Nc7a|GatO`d>o(^SPgPoq+ZMtsc4nbGFF8 ze!mv(PXPOaZT{Yy8uy|_+aF^~(dO@c{zj}u=ueHch(;f1wmY;h9ha{G@pHmh?p|tt z>JLpFp!uK^&w~cMzcnVrao^65!9J0{8Q!Bqnh$Lr#1DezFS{9H2WP@wlu(Bl1B7iH z%=yJ>K$CUbc%WQApjX>J(hndo1Y`L}_dqNUt^=YS0Goe$(R;(a2|(j{%qZ^nigBiU z7u}>Tojn}1f6L!%#1zEzfub}Z%UCdM|K+ut|CyIj|Eb-1K6K@B7@rII=oWaEz?Q#I zSKyiP*v`w~KEL3;A9$YpR=fvf={SWkqu5^p?jgSdae}1pVIJ)R(f1O@gn^IXTLbJi zK-wSuz%T_GaDJN_UGs-(6Wp7=V(jbHj>rC59a-~$x_J6v(B4>I*zOolHrR;uXm8(W z`{R1Q{uwt7D9#V;p7w{iU2y2BEwklUg0?-k`v=hG)8IMn5aSOO1c(Dp-(6!wEG{UK~q7!m~kFs2XpU2V-7 zXmgAW-M~K3-u?|=5c`2;JWxdY<2B8Fz-$A8_V2H+{m1sFb~okO`U3T(q)QSc8vb8W`}020>I*HKA7>{bW5hu7HU9BjtOkUiC;r|2 z0N(!XK7>hg@La1OMZe$F8=8lL9RIE8@2{QzzhJ?FVE)IY{i!d6{h-x$)P|;<`he92 zH2MI=nYFh`)DO_#Z?;KWyAkKu?x;i2XTc=h`xV^hAKCW@_W)|&BR=4NdHwW1Py1tC z`t1WPJD|63YClkKf4q+XZ8<(WV0c(vyl_FCK6OeBoW^uIF`b6b@DzTBzTe8xyIr!l zv-bbs_j%cl^R~ZBA0Q3D+^PfcUfQvM-bdmz0QuaxbEjIkaG`qo>8Eq1r{Me1v~lRP}26d+h@;8jxx( zpv?wc26-M)n>KAy`}gk;^!((>lj_8Y6G1wD{J5fY?AS4N^ytwb9XWDDVLE*HusU?; zP>>EDJg5#FIM7T-jvgJw>nwWiIrWWi-26wK$aq|tn*4+P8XAJ}8c%F40NXcBO5+cZ z9k6NujRl|`b;h%5K+yghHwEltdtp0M!uF=NHnlgkIrbe)`}XZqd-v{DaDMdY(PQeF zXP;JI`qJk=5LeHg`d)1NXY32U`ha;pDBCz7pu1YU~7Nm-p~hdwQg;`L1mH^!yj2 z0g3vLetx45L^VFz{#W$4u$cBgeE5hufBt-618nV&z975@ZGTq1(8X=<*8Y}0P$>WD z)&pJIAIEhUU=0v`08Dxxz_y3;qu57Yc;SVv<=&+K!{Gm6GW&s5w0{^|V4VkSi~+N> zKeBCVe>gvS0owk>7rVdhb^fXS?R~)A2go%BpmhMuBVz#4|8Ne-rTx*TNB(*HqXwW4 z0Dj@Rro_lZn@u4X8u=qYnsr4{d)@z4TJ=v_1BLN7(q^&OO-#1OG8? zkNu*U_Q&~v)*L_{{?D8lKs^|}1owOQTH6Qh|Hvba`M z|C9cc?Z>vqXGFaG@=HCV`K0~jw14QsnPNimTwL3u%}>+*Y1RN~{s;Yk8vlXbx7z;3 z`mW*f_H;ddBo+S#-A}=O$bWMCTl)a?kD&b{zF$=Pjcb6|Z!GhFxR!(Sf5iRbmtR&l z-gske*Y921TmO7=`&-&RMf>Nn{VWbJ)BL~Q{uuuSv41%K3wl3_*Ic}~x7!~3LVWwj zw0()~A07L(^RK_JvyU1=`j6{)IKQ!Y@#3E5o^;^x#~&YyvC+No4`Xz3tha30GHLrp z2Y~heZ(%I-=FOY0Ky372TiUQ;a6{xgyvB2STF=j|hxZ|^Uq84$lGed%Ev;KOxGs{` z!s}LAyB3buu7QO4+Ch9BrZtd5TGO7^tXVy%r}gj~ixw>!{n^ics&4+q%{^=HiF?W) zee}^^gyVk!3GZ9z_Jn^Y{=M=~YCvj0YCvj0YCvj0YCvj0YCvj0YCvj0YCvj0YCvj0 zYCvj0YCvj0YM>)(;J2_8zX0iVn7s{O_4U2&^)*~g05(8`6HwT`fSX{ z@z3=@v_LwM)IhyzAPw_r`4=sySKCWzd$R^yna{$%XhCoGE%~cEvj*}opO=5pg3jy{ zQmXE*0bk7f?x*nbOMF51^;h|e^4EaQyp2zO-q$hb*4HsFn|@8w zGSPzk{YI`(-x`Qx-o_6<@9S9S)5mLGwz%tw7Sy-@O3B@8AT9GYe)xG`$8mgSeUFzd z?KMOT-20P!qw{MZ3-dO9_<3K)Y58>d950*8_vo{t1)blorG)kx$jiKqAAa7~F)`;e z=Vi-#UC{!2Ka+Pls|NVIJ!c@#G3O`Gb!^||*Y|bIf6Lc3@j9Xfoz>r^G|?L1{odCx z=f~GI`E~g9eH|Bl{R!Xt-)hpN32M?rQyLHF!!!}{W|{!Uku(lohiL-jK^hNvB#njF z(KH@j2WcGS%`^s%+tWBWH*PGY+u^u9jfHbT8UuMV-3G_)X$+hjb33J5;nMHfb_dc#RF8#i`1kWp#HeK61aw(wtX{zVIVi+|3pk7MD# zd-&&CAnOKpU8uAD_rW~BhVU=^_nKA+|9{(nzonCI81JO9ILuUB{8U}l()hh2|(9l3rt&F8V+={M4R-t{xR#TUP0U@{OY?c*-#$O*!g7|+3jg)v-^4tzujf9r zK=`kY&n!&4d?(-^?i=h>A3v^KykGh_?qvVpg?U{A=yg^t;B}kEp1XcV?0 z#c`Z<>U+Fj`Z(@1{(Ue{?CZIU7Vx@tWjoF1g@55ci^lMN>EpN)_%D)qT?6QKk{0l~ zO~bS6XM}&@znA&1f_Y+J&rMn&{8z`P%UC|kXT|@oZvIc7x7S&$`ZZ~R@GtylvCl<+ zhxbb#$3^@9gRA~T-Sc;?^?5vg{HuLx$?f5~UDdqv-`_a(jcVT`Z&w$VeW1BEkA6xK z@de`lSEnyx|BJQ%$o$cT*QhP`UDaHlN8XY9PSXM#^SYlxet|D75dMY#EWUydo6oP| z%l?~~=eCs}SFHtUndfEZ$3C<`_!s``&c6%uoF9H%mKNk?o|l;)XVn7XziR$(mG%D& z`+xdip7X`F|U{Zr(f?=GykDa`O<c`--j0T3d>pk&R*g_j(Oy4(*tj@#^aW$1zDKK_(Gho zK)(h37c8&NR9RY(g?TD}+;}Ui7G&X~SH9Ej{3kKbW8C@pKQ1=E2rbCNJg!gB94H_6 zmZ=4Km^YO_X}sm41--&@mcKLP|F*7L|8rrUxWSzB&p9ee3w$xp{{~)eeq4kW_+mb( z{CTw?3m3ieoo?YjEAzbX+4$!iWzhm2Kfrx(`t79kdGd|9ouu#TIWMd2*i{R7d;#}; z=(o!(e-S_OdFi(9L^ECW(rqXHw{f2&hecqdo*Ik&W>toK#m*zN) z7TDJ3t1f?9Ey%(}uY3pjA6rxYKmS{^)xY2VSIv1YlRwSbNIKT}Ov4X-M%Mz9Z>(Qm z@4s1@=YJQKCri$?z*Lv$e_)yE+WOyIW%>2G+;HF3>b0jouTJmnSG~e=mcO&k{A0}E z&wuMc<9-VFpYgg6eCevlXI;u``mAeN zZJ&SS+iy|p7hJ2(EdD^-{sk1bhs;s=&-b_HAWh{;d(KuKm-AEp{wnp;8J|~sw+Z`6 znowu{-}U-2&3W&%{VVQszWC>T6}5o-_53)^cs{PYruX=7`#QG8Kh~lAQ$K&T*#C?6 z`wQyKzp4M{IadV#$RIM0{R;dme8@^I}9|0Y`*@|@?t z>+3kl2KMs%!oPk0FJJwS_R!<{f__0BPHf-xRc3x2EI0igoyXW*I_EQ|>-)@mneU{( zUHC7af8vAlkNum@`^#iNbzu12h^ZcCfFZ^fOpQGsS__UjNE!{qI2J#$JIrZz)d7pWna@xKt z{C7G3W5xd${#$W9x(*2c!hg}WJj?Zc+D+lVoB6*8F#wplm*cE|8;<|SiTw}e;&uOA zw}V+v`2WN{<-O_)Aphfqe~JHrb$;FM z)9Y@x|D*w!_nI#t{EPojoZIqV;=ic@s0GF`L3hmy2>-&rje9!ZYy6Wx&m_CMkqIpWu%^I7w_^5fsY_vz-p>v~)i|8eEl_epmxGyi-R z?)C53eSvz92YQYFxIXvF9dAhe++5zjl8%XQAN%yag1KDiFyaaJv$-_Wm5=P8dXe_EdNe9oo?Y5A|R{Bc^4mv!6syFmj2{>RtX z|Bvf)X_zPWFxUCi`RA`)w19p?zdoLiD}NrA^M03WK^*^`Sbkj#Z2bB-ZyyiTV_vY% z{Qu^jziFGdcV(W~bmc#-7T`0#=r?r!pZwYzg878JtQPfsdWJE7&L97~fBX&ed}&eT zalP)kyRTB)p8R-l->(nWAM46vPZu2zqKwNL5s1^61zkyqp$aWXVqPyhy~gK&c7hgE$vo#DkL#uZb>^S!lds%2|D*-P z3yw=M(QC@9(1I+?Z@KTPAf|}Q&GWLgpp%%dvjzzN#mE1@Hvj5io{#h;&R;H43$ikQ zbm29M<^WLBFt!+DjB$>TYXBV=r3IbLyio&;_Yif~yrA%3JpbgM5dY*CU|zHq_+XyO zk2x=U8Cp;$=If{dA^+pLI{uIM-?(EFpL&1K@|u1#>H1lne{yG@6YNN>|S_-Le8l?A@euD9Nwd)&EXy0zg6vA_e@*bwCoY}{KMajqTCm$?4I%i;6*%*n$}J^<=e zCu)H3-}(IWev4WV7hhny_y6IqrSt7M=eA>BmMq7l1)HC`MP0rOTvKZJ0O_^BpaU09 zsNuc$G~*VwJ^tRp_+S0pgU3dT@f3^DAj4!k_0JXqL2U;31cy6B>-SL&Szx9q!zM&8exc{bW zGV$MO8X)}F%l?}-N4veIju2gzGT z+xIa(AT8^*&sC`bA^+ng{-=iXf7BNt&EA$7hcGQ;PEig0a43o-e8FKN^2^VH%Jn7Qo~SRG|UF zzwn>L4u9V#_jD~{pXa&`4DWx?$>#Gi<8hI(084*#hZi593O_*j7yflt;$9Q8K6B1} z+wsMdYUH)?x!9+_=ewVKV+jY{eXb~i8B7jKBAAGef4qbe`Q~ve%EL2t4{cJ z+L&*Q2d>~)-w@N2_3tS%7QpqMAQSieTv=lQ;a}{(3s0O|U&ki?hv5DlTTG8jdHH8C z7SdOl_3Pw~1>o${aTyvQ{0sk_k)&h2-}`vo#y|M{N#)4;eo?UicwZrL>njiVFL)0T z?(`SfvZyoT-iblzv~tIYg5CjOJ+db9pbb%+HhQv-UPe~g>g z_s`+azTo{d>oN6J;oq#SCjJKt90SxL7N9H*2>GATQ}KT$=II_fJ@=sn!oQh&(o-Y< zFa{W5_wrL6`pD}N3s8mz^a}qj%D=?zzUKqFnSUS56Z?AZq6O4%g?~5xQ5Rfe{i&{#@|W*@r}udt@@IU<0&xEExLpIX z?46H=k0YCtA0&d zK>b$izgY+3_z%VayS`b71{4_!kad4ho%6WYJ~W`t{6F@8_Nb5jyL;5|)9-FRJECgl z>Gw_hZ0}w8-csWWNOyeY_&gEZTa4_suRMwWB4YuH+8@-{z5sFj<9m5E0QtY8ruHA7 z|4sioK?_XG<8$-yxjOhBX#@EMzG97tZ{5G3^LcV)9-m)`&k>{Y)ANOVX+aYIK`cPV z`~H*u_B^ow_^cIE!adj@{&Ju4wJ$)N1_=M80Ij@CElA5ev7_fcw1E1!FaAx;>*c1` zKC~c_{~}`n=D#yw^@lh2sb_C#PuM>6x2DbUxhBUp=ie7Bj(h%I9u4Rg``@BJdQ^lK z9q$j`1qXk7VPyI&EU9=!8|2~-4%T2Fcv>*-t_!(cZ0Qg))9Me!)edSx{ zU#Bh%xIY&xEA#sI+BG0~O|X~v=L(TV3(98R)(?}kz=i)JnYWccNej~Q?;{of=VP#4 zZDq}Re)mH6XM*bd=6&7He{x(vukvrx0$fYLH3ifM;>t9Km$jed{hmn+sGPR(e-+Hz z{!Wt?q~$+vEWp{9u8YiL+REs9{>Yka&jQTLyXpI`8X)|0C5RsD^Y*U&qUg_)7TEaT zIVU=AUvzm~uTNSqc=>X3|KB7n7|j?9aN?~N;5`q?eo4qZyKAJ_Aj7kfgx+Ye%r@&c0BfBb!6={b@9wmU?2HUrvX`F z0g4#wZ-!@zTo^b4?9($#vp)ySh3zz-ZRg)U4iNs6bpSun30h!c-W2;xet{leP_-7A znJ4yn-g+K@$sb6&FCfnI6*JbqL1VwZ8X)|qw6=FF4J4m~tHJ^LRyDeYl**vHr>tH;87RK{~z%wOSw~zYpaje_k)30HV1;Bg4Y=3js^ElQY*bno3 z@V7)#7id7p|D>LX|A~dy^P6e3pls%C{XadbekLn|h$ zi+g|A?7yZtytMt(_vcJnfcE?NtB0GNP>l=^t1H05<>M>c_8s=!fAe0Cw4jY! zoom!Xi@@w=3ctbH(|SKX%pp${PZ7X-WEvYda^fHMbH zsXfnpQQe8{5YX3{O7}F`3t23pG z;LAt;hPE<9#~J*ywby`)X!m)#poD(|s0E!u{)fT-n|$6g`>WT2fO#03oIk!j@KI2s zC~*$jj&-hGUbpdQv-zg;c2?WU*!DZwzFTJvFpUF*|AJsQTz1;DX;%KZCX}fKam*9@ zJdfonhWoRpk19;fbpig_Ha1PH+sft*+9PAl}xsiX;f;R4K%$v?f8I3Jp+wZu2Hyi)B zUQoRT=sv*E(2%-Q`1j7b>4I7K_oW4!VLX4Z-|X8H(^+zzv;8|(s$~oBRQKQXF5^Cd zs0X3})#?D(0O8+6YBA@U{0|NS11XbbeJsrwftWvcY`byJAq(%O?`&TFsQTHAFDQKW zP12sAlRoup3jKmu4M2Y&Vxvv=-geH-mPdcb?YnjL19bi;-+70`{}h9KI z*-!I1&ww*f%_7_>}~S-WdFga2`RMT+~Jj}#_~GPuCH;vz`7<#YXX!< z<^wv<56Gqg)sF*&|H5!@yRcvWZ5m?Mh16Pb@sz^-HHv&bF4u{~H9_JZ^SWw42af}U z|29kuJ28Fw^k)2jvVFJl?Rvgl58||7bk{A-`;v-!J|^cMj}zAfxdyc9Kx@nrKQ~a$ ze1O#t=uiz9#`(X=GXGx~^z6db@Xz%kUJHhgwC+j7eKqhI*+o4ck842cH9@We5nrHl zG{DL~l-GX0aCPGV;hzCo@L~1*+qGcm?2e}0=X>gGd;Go5>wBE-w8!_~bWOUsz+4(2 zK7byBDEt=;J-hsjO8kG$yyf`Psn}=hH$5&F>ey#`QtqHRIx7gp8p8vElLdE(4Rv(~4)&qq9c20{r z6O8}C{@b{BIUlPLj2D|8j2xKe%s%Qsd251KsbdGWx%&a)132w7Ap93ad;4`~%$T7@ zJn?VR3Zo_%v|#w;(oF1=A5iL=Am^F=E$RJ$G_e7_>jMb??VJ{MCg8uZ|F6w1$DWTF zGw6C?(}F9o)|dAAdBnRZFLh0@n>D~X7g)|bfbd@w?d{iX@!uYYXS=V@e7?1I7wAO_ zT_8Oe-Q9}sHF2HxT9(Kw(&A3*r`&$;c2#qzIfLPHN4S}=6Z9N()m?fSZsH9^#X zwEW}eNHbF0``r3$F`qa_w5F6NJ z*+B#NcPiEZcm9jrHyHV!a@S;u|Mic1+ZAWdEYkmHylZ=l;d8QdW_`XcVoh+uNBh(R zpY2mWytz;HkLwGbk#y;-;n_WD{QxHoD8}}acDZXnadUyfe;cNSov4KWOQ*fe_b2Iv zk2S$NZ|GAq|DjL)^y{!Ez{F_6XQk2F2yxWRYo-Che_@Dq^o1+oAAEt~lgkRV|D+9B z)&%eQyFNAd3vln>ccQ#={_%Oq#6IOs_Q|e0Ye2Pe0iB`&V*ksNjH>v@HN=s2cVmm2cclWQUOJ|SzfPDA128jJHPaS5?oLS)dzkU9XVu);e{E+|d zE5|j#xUqZ^_w@eh-OpOtCto0w4-nCSB>wUJV*G$k@Bsq;r^x(&87jc}AA)uHxJ1nQ z&PNXUjR7)g#@UyyYn#6#rqky8U%Dlc{ahLlVLC|zis2u1rD_cj{>vag{EdKrxc?`P z`!w(6)dXKLz$W&G*IuvI-SviC{Npps$S)_xlk$j-cAg8YZX6)|m&ae#zcdT|e-HfY z8sVx3L+1|^wkEiJ+M9E+k7IuQd8JA0l9k2P(7E+3#q8c?MVApDmAe&Z)}{`Gry@ik{T?{ShA_>2p@ zuudBKE%w6(Igi+`F91q6ZT{7{ z*hjxVjPHegQ6W|-uLk6s3+ON(py2xgh5!2V&-Ea!7H}QNvnF`rscUny--_=IV;JHz zKzsoH9I^c40O7w({$~}s{~y-@JWitpqyt&@0v=rTC%M=9c0BfBZJpnb2Gr39sA3Et z{C6+^qz6elfEtixO>oP7SLL$(=J?)Z{&7w)jUV8w0UfX&=%xW;|I4E{|1Zs&Rj&Of z{wYt=fwa#8!nmJ(kGwq>`#9#e$M+^{KpOt@%>`C758%ds{=Gp0`+v90|L3PW`3hC@ zpGF5pQ|uLzH@+9`erwEcv*ms?z}W|g#1fUS0oCw7MdE*w@y-2-PT@aR2QI^YAu;y} zW{vONc;D4(&$C}_%=6or*UuNQ9^kYdSU&%#S8f`RZwwIHf8jqj+$k91i);F5=g*(m5j^vV_8`aD+;skXMLs~q^8jxA=NkhE|0%%r^pV-Ks~!Jy z?)76`2SyBgg&%t2orz<9oX-Fj zdVRCg`PVgI_(FJ}pdQ~FjOCx9v3zKsqs;5)I#2^T+y`*uKl>O!`1gu-FjdVMqG ze+2yDD=Aa^_}P`?Jf9~G;JF*~c0Zsa#sOXB17sTm2>)IYkG+^X|0bO%R}(n1NLp9O?gNy+7U(tx$ie?TGXEC~uBSJN{~@sdF3IjMl%oa2JKoYm% z`=LLP_x_-W1|<0a6^;Si_|G;65ZHg=-xJ~Sm&wY%t`%lIC{_zN`*iF>1F9GYIPs7A z(A0nq@c}%}0fhYDE%ATx=z4kA7yn!j+O?oK9U$&`o=pStj04062>b%02Dpy_gnut+ zr@F|TVE%u^$@k~1Cmlz%pcozC?9;I~4FFr~JPt6-0i?M^k0sOB66g>tI z{!^jr$RnGaL$;{ z!uj;+GWZc<|H~1OD)=wb7vSvEvAYIT=L58OZrJQ34M^bs?3pv_^r@5H(N!hd)3-|h?en;RtVDR<`|?RZNAI@$+l^4~rNKo*Z2K3rHj4A)Md4#8*B zrQ^qr3IFBb-&d9WC;p?pfFBJY?s@L60WJPBh|FNS- z)xLfESlSE6C8WLZ8*OP1oJ*e$9ylQU_YnVe(16T7fMH(FiT@1i0K>z>YVzdCwN6u~ z%KU#B63_|upL~JVykIeYKzTj@jvuP<0m3x^+4fF#k`yn{uBT!2N$ZcGdvHT41`p z!m8ju90L&lM_~-0rzA~?(*e#u9Vcl(R`zeb?N;Hx2l@9s79j2^cjn*6JYdOVfH?lS z9>i$@*8%Pa;4!gJxi1YsyT2Ih{%yj33FL=AF?VkH>;ERZub=bf2N3r>ch&&i2S_;w zSk)MS_&)~o=z8M(^J9}1l&Jy2erd=@ekj8K5ZnW7OOE#k>pW+9U6ckm^Pj^9$a7zC zwsnBZDvp1y2b_OA&Z7b317ObQ0`QpT18ie~fc?c{_ak7I^rFeXjeFbq67--b{?Qk3 z(}1jNfovWy?-;;g9WX8bqy>3&fW`orlLol(AF}_ly6tuu@0Wyph zzCbM~N(0>ZkFH0gj|E`-8}_ySoqpvC@eGS?TOvb+~w z_$Q4hNC$jq03ZLm+kfP^1LC$|# zKVT`$^(|S9c3;>pkM{Oo^2&cfI^ctUjN!|t0onO)&I6V-2GIE@9iUv-1CthT9ngJ& zv>IR=2P|D0vVXg<-wtHSXA0pTb-=SP;6nrQ@$X>_&>{SrbilU;*yaM3E?pYfePO?3 z&@De#82^Q7K$Js%uYS_8&cFLOpxp;Z8pG2(gKZ3u^!+Sr0K>yWLHr+ao|JQcK-U4A zA3#0;=C-kbt^v!IEmKRDELOLVxn0Kl79Pt!T`2#aHK2MQpq>A`v4BqeC*2FsxEGN4 zKMD8$@f4>6zBGW(1#td_{j$kU_j~hv%>UE4-SPpi7DcJonV)xTQS>mTk;oqYlkk<#$#{lWL z$NB$8${Pz3&;<^+@u30KOku?K%WQb???8Rh~0OEvc%8Nn>UZe{|rI=uPw#I z2J4*1z4oR7)sF!v4#0{3ym3G&;sDA1pFVv$@c)Tj2E}aJd)276ah) zY#;~wkNoqLM+4IM04r9k2-qJhKEES{)qOla|J7(fr9MDL{&60#j5$EE|7TzyZA@u3 zfcpTZxd5&K{L6m$&!7Q0eSk{&H{1)9ixi10ep1d-4G& zCtaZN0I^Rw*?!7V2TXH;=mV@;1$JN9ubATOKVKRDIB(!Z13F_3Ac@~9?El%bX9NGA zbO5>M8bJK(bAoAo0AW8ncb)Rh`SWT0AMLk~|<{70f_%s0``{)`4gMVV5a+?o8^8lQG@&U;A z$9(l_VLuaO9sDJof8t)x-E^Sa`H#*yri%e8-~J=_#6IPu0cpno#6Pm%FLu9!NzPU@ z&cDt)e=Yon{5Ho1&M`pc^MC&Q`G9|}1H?a{4@506`2aKruzL0Cfc^2}^JjysgTE#G zXBq$3!~S#rk$Xxy`*HlQS%d5^SL4Tzm+^iFbDXtk!hi4ZPh)_z{0HpAJ^bS*2>V(2 z>h$k%{?W(hK0h71`Tpgt0aiEvS5N!z!vC5zYv4X!VZYP)&0D?&hV_5q9&>m0dx8IY z%>Q%$pV&_`{$DHX=f$cv-#77}jd{|Fa_0V9_W)js2kgV%JrgDh`!&KhcTxTF@8J8({eOku|C`qK3;Ve- zs_g~*@m~e|*~b3pBlx!eN%!xOe^KWCKeXN-=l8E$w=USTPwake5zbX`;Xl*@;xk>| zKK}1y{!Q!s==UT0D`C&Ri4!Nvct00K_5HFR{vB<W@_ z6W9KA{*ie~fzL1O7s63pFZ$3!53%R}A=ebgr{_7CC%f-q+~4BABHy3cf8syMen-as z_zVE;8K6nu&tw0QeZ039*i^SVuJgI@!mn)KOjEMQ}~RL_%v6|n;WLN zkekySINwNfAcr)^nr5pxv%@qSa&wvm=NoC(EH%rRX2N+b&77%bM$!y;t)&_8x{+o; z-b&NqSWDBVtLc$64PI+$8oX|#X^>Bw_MkmI2=6r#KA%I_uQ{Mne5nDc0jUA00jUA0 z0jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA0 z0jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA0 z0jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA00jUA0 b0jUA00jUA00jUA00jUA00jYr=tAYOy=mXEu literal 0 HcmV?d00001 diff --git a/src/modules/registrypreview/RegistryPreviewUI/app.manifest b/src/modules/registrypreview/RegistryPreviewUI/app.manifest new file mode 100644 index 0000000000..0871bb63b2 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/app.manifest @@ -0,0 +1,21 @@ + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + + + + + + + diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 3e0ed200cc..cc07f71126 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -165,6 +165,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow L"modules/PowerOCR/PowerToys.PowerOCRModuleInterface.dll", L"modules/PastePlain/PowerToys.PastePlainModuleInterface.dll", L"modules/FileLocksmith/PowerToys.FileLocksmithExt.dll", + L"modules/RegistryPreview/PowerToys.RegistryPreviewExt.dll", L"modules/MeasureTool/PowerToys.MeasureToolModuleInterface.dll", L"modules/Hosts/PowerToys.HostsModuleInterface.dll", }; diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 06f853713d..99dc0a4ae6 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -663,6 +663,8 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value) return "VideoConference"; case ESettingsWindowNames::Hosts: return "Hosts"; + case ESettingsWindowNames::RegistryPreview: + return "RegistryPreview"; default: { Logger::error(L"Can't convert ESettingsWindowNames value={} to string", static_cast(value)); @@ -726,6 +728,10 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value) { return ESettingsWindowNames::Hosts; } + else if (value == "RegistryPreview") + { + return ESettingsWindowNames::RegistryPreview; + } else { Logger::error(L"Can't convert string value={} to ESettingsWindowNames", winrt::to_hstring(value)); diff --git a/src/runner/settings_window.h b/src/runner/settings_window.h index e03a7c0ef0..1497c2382e 100644 --- a/src/runner/settings_window.h +++ b/src/runner/settings_window.h @@ -16,7 +16,8 @@ enum class ESettingsWindowNames FileExplorer, ShortcutGuide, VideoConference, - Hosts + Hosts, + RegistryPreview, }; std::string ESettingsWindowNames_to_string(ESettingsWindowNames value); diff --git a/src/settings-ui/Settings.UI.Library/EnabledModules.cs b/src/settings-ui/Settings.UI.Library/EnabledModules.cs index c501ada3f3..7db56bc0ec 100644 --- a/src/settings-ui/Settings.UI.Library/EnabledModules.cs +++ b/src/settings-ui/Settings.UI.Library/EnabledModules.cs @@ -362,6 +362,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + private bool registryPreview = true; + + [JsonPropertyName("RegistryPreview")] + public bool RegistryPreview + { + get => registryPreview; + set + { + if (registryPreview != value) + { + LogTelemetryEvent(value); + registryPreview = value; + } + } + } + private void NotifyChange() { notifyEnabledChangedAction?.Invoke(); diff --git a/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs b/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs new file mode 100644 index 0000000000..92718d2e91 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class RegistryPreviewSettings : BasePTModuleSettings, ISettingsConfig + { + public const string ModuleName = "RegistryPreview"; + + public RegistryPreviewSettings() + { + Version = "1"; + Name = ModuleName; + } + + public string GetModuleName() + => Name; + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + => false; + } +} diff --git a/src/settings-ui/Settings.UI/App.xaml.cs b/src/settings-ui/Settings.UI/App.xaml.cs index 75cc05883e..007482ee92 100644 --- a/src/settings-ui/Settings.UI/App.xaml.cs +++ b/src/settings-ui/Settings.UI/App.xaml.cs @@ -153,6 +153,7 @@ namespace Microsoft.PowerToys.Settings.UI case "VideoConference": StartupPage = typeof(Views.VideoConferencePage); break; case "MeasureTool": StartupPage = typeof(Views.MeasureToolPage); break; case "Hosts": StartupPage = typeof(Views.HostsPage); break; + case "RegistryPreview": StartupPage = typeof(Views.RegistryPreviewPage); break; case "PastePlain": StartupPage = typeof(Views.PastePlainPage); break; default: Debug.Assert(false, "Unexpected SettingsWindow argument value"); break; } diff --git a/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsRegistryPreview.png b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsRegistryPreview.png new file mode 100644 index 0000000000000000000000000000000000000000..57e7549189dac0d810497d7f63f95aade2c71cb4 GIT binary patch literal 2695 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 )F2{_JU2IjBe3nS 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

FWN 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 SvR-Ha0000pcK!M^`+@ZL;L-B9l??=c9=OkyJ z-I+UACQ?~R8X18A;lqay$g(mLDjz;TB>*o3IB4KMjbtfZz#Ei{inQ2=nhByK;KN5t zQAN=YAL`=~UyMHipWz*4v|T=YK8oanzIqF-Cj*7q zB^6mrg2=it^{)prnh|Oyuf}slmyQz+w>wRJ)D67XdqEH3dptp@Me&WAnyQoQ{c2aL z1kaCfw86i`hsmhOgQ-GT#b~h5Hyo`i!{e5t~-b{lk__wgpl>edC~|t@7u+B zqC06@e*e|dCAQ>sM$0MExjsL#QdYsA)V0Tz+*%lNqKhAF|Mt~Ke%w;b;#z#-o;Dr0r<&Wa4vWV%>WoJ`ZNGvU0wx_T{O0_~0U);MAs5ld2BblMrs4zW%2&Uwa? z;}0lSHqvZG_QFGkR~@dBD81`%%4fUbA`4B$#V$KOXI+myd2ljGw3<{=n8olb71m=< zLCX1Tnrv@Sdu>bZKWEb;hFKTpNhk^Dn&iz{ak+T|!V9gu`R*5^|AJ zyzk`$x(*Vn5!EfaZb+3NYj`zgWminpk=zmG{E88(_I1As+7DA)(pXd&6ru!rQY92b zi4eP9Ldz!M*Q+#yDLVWWR(Km?0%y|~J61~|xB3L3P-F4IN+DqEEUM{Z!7H0eWJUE1 zDg+gguLd4-B2i`RZddX-x57*Mqv1imNFdNhCHKt|Z}eE)e}pbcgFdD#f6EH zC(4wXm*_7gF*W$UTQk~5BoRa<#{Kyqqbr|gUrSd(y8WZ~8aU=`2!${i!gLS3IJv$! zSSNpi+YOw9$xLmD+|pPWF9d>og^oh8(N~-GbNZ$}6!uLfsL;xhET@}O-Vl8W$Ia~~ zwX_OK{Jb)ZVgnVS05~t-s4|0}`LbA7fnA%8cl48}ex(jSgM7p(Zahm$-L}w+B9Eu)UEVXAUfY$_ytC8K2+49-CA83?gGB`iH6xm{ zer$7)239&jl#nD5FejFksmk@QVmbyI=C+zzDXcZ5`l7;drwwbVqZ_wZ5sS}>Ib#>#=iBH)NB1gf9Zrmu_FA9*M;SzhZkb}pf} z(s{Dn|1M}|Wzuh_616w%Z}aZ&o!pOcG^%9%Psx*|X~HZQEnr+>LrVu4BIE)-Wh`X$ zX;w}(^kzfs+vYZhk$Uo5k;Ds9sws`@<`guW5_rWf&5Z4r`IG$6;~r2gP){UCKM_2;$%mFy|5ck5=A&3@C$WqS!>rS#dbcUE?v$yM@G25i-%?10#Vqqd5VFxsjns&hs(dsC%wP%eRSdB6AH3oumC`i4 zVIO%UDVIpIp}=coXc$DE{BOE;s#6PO_9ZKtjO!SZM{t343m9UoQ%3EFL%6)HpWrGC8GHOm&&(+UgBDY0JZd zdIVI2--f<6B()Cz>^7L>qko#XbE+v3<)+oRXdk(z(2g%y;Ry6q8+hf$#hiTzHggb$ z5g4Y`Grt(`5Q67DlJ5N(KNCbX>Z+RZK%8ReyN5GQh+=Q{`J9uGi&v7%7<7#d8Z|t= z&Ox=uKy>56DuF3Pt1bW$!;x#qv@|~y*S7dp`#X;Z1l(ob4E#JP0^@$Y6GOM>~WkQ%Tvh$Kk$zfk25lYW*Hd{vRkMvlfb$@v4% z_JN>X2yBzJT`cU%QeC?)70{ZJcP>G1$N?9QiS=k;#(t8At&$Z(v4JFw4Hu%750ZQ3 zz9*@Sk+`d*L(s>UoSsHY61RaS&%^cd(DivC`8vK&Ud|_Fmc*CqZ=*Pt7^_Eh9b4?` zpR~*Dax{%Du2KjR1dyg^gw`d=kgT?;{Rhp9%wj1D2V5vCUOhyCOF#UZb|MKLSH+jX z@u0Fo%7oaHk--t_sw8jf(c2Fo>9SvUUl3k7i5zd^`F*W_eCFvNADB_ujUI`*m4uYz z!A;Zshxgx{o5D)C=4+p-Q@_8tmG^vu@w=m)QqCoor|=eNUm3+s{v2zf;d5<)08aJ@+SR4dp?6Vl?{vN$tu@%C0f1iwt56#IJk^?B+fqDD(EB1d z5uP)d(cxFC;jkbN!Vnn`@iE3UniBojR9?CeE-I=67ba);3aX-E&BS9H!||zK>d3E< z>Y?d=S7KWuqxujuycnwgu3l)$lB9~$Y()GzIp>=!y+Wkg8d8O1a_u%g_z{~Pwuu~# zU3oViUNQ4vov`{kRr1Df(eKl!M7Io6PQ(qGd2Ab1rEQq2DHoRHYf%EBGP}(yOE-HG z-_DMbTXBbMu;qJqIHd^+FUK0~7e0VDvRBS&T z{5Syp3t2$deyommk)G_%!!{Tc#G#7dQ6#FgG$*PCEt`YsnF-X12$ZH9;iK$GneX~`}aJt?TL99puiawYwpZaVPP zVt{5&@2eq_N)`U7`Cg^SEu!R`6)3baQZHFqF%ON1#aJB~p|E=9>;suvgYU&7K;0zPgl>%2eYMC8)^(IE%AehrqO;|u(i`I5?5E|@7h0A3Bm zD@dTnabyyM$8sJDhd^Y+#kp7Y$RI$6w&It-Za_RV^Tl-8$hw}yXVl>KX#HNZG(eM} zC|C*dm0HI~DxpdH*<3fx-1B|a|gE}M4_QhLtEf9Mr z5sY=$^Ff_jtTeuuB)YccWsDO>b#RKGKhqIiA@xd47>=n1T~UJ74Qu|cJdK zstXG?#R6%bgi^C#+wSO5lOXS@R{0=vW{>rtr#AHFC_}TP1|HTLyFTdCtZTICQ+nim z@DcOFeC?75!6;!ZYBNS=Dl$AZAClEt{{Jtlw;oT$R)wAp?aE7-*G4*ad*FjQ z?MGVRD+TGp8nv&YcJTOPo1n4Ud-1^p`D%JlqxOc#Ywz@@h++>tXV zsQ3E!G$)l=kiVJD1L^uk9({=9Tn}zwwtcuPOie%y7#bGiuPa| z?{dc-@lKDN`jwG-Q>~{cyq304sk7egvv0*Vz>$>IEU_g05~}H6O6XwiN$^B00wHf` zirY0i(y-!UHr@)DY6EO<*NwSFldKd6PnjFf;Hq<`K|%eKut;c0E-@2$$B4KY^ zF(5J+C5p&t$k6IXoFBX9>8#4%d(mDsy#I{UvC2KmAQ1=avc38x)u|XR=Vmg=vd*{- zuVKk$DuD;$zNx znKjMo+Sb~AAJk!t;6h4*OFQWqpTihc;iRmWrCI>+iTC#dXZ`R8pS9C9e19{$7D`-7 z`R%ln@b-W$^QOnC=s_IX$%=(+E`3%G)miwuZq$9qVgI`Rh8tZ6>G>h#!9zugs-}Zf zVwaqYrS?6#d|x_IF496~e%uziEzAESR+%6jpY7;zhm!~_AIWQ!q}MT(WtND(ewj9F zWZ8_>{ON*xx{^FO1IP|kVVW@bIZR5cM*R#~DX(&d8e`<;7tNT!UC#X7LSi{fd4W|* z*Zc}?O7EWXZS-GF;tY$BFc9)Ag*ID})$~#Wlpp6Jn%r|6i*oHm^N+sl6UF(OPbv@| zO-XH?Gdctxu3ZPG3Pbh*IEnU28+sk_f5znLwSKeeDNLVnPCoY&n%fr5Mc1|H*0T*%XqiEwa_EvEzT6Ak9)nTD=hOyoO#GSKd(7Ir_9Nz zXsIi9N0e=mU_Nbx1e;T!Q~Tw&v^J0y=>7GP9UdnWl>2kD{&kj0W1|5zD$}3S3<9}Z z_$*gc_)b`rPoW}DQmsCBbC7Z=93yKDqid~~EMJa=8{vGYt1fGh@jE6c;WEHHlBAni5Hjh%jJuC(xxbY+1gE^ACKf~R z7>!f=vF;_gl}`lxhBTzx<6^7?*so@8lnD6>C#!EobykrN=l`Oryx&EV=kLqGa5FMI zw@y6Rn3v!^aaW30_`_Kp)p@9SP>rGM~)O}CMwQypEG=9E3y&KKN=M@ zK$0~#+*)6?*TLnLBSmKDx`;)Mf#OF&JLGB%gQ6Jn?&An1IsbCK9v6>7WKAmME8)|l z9jfM+&+(Bcr%sN)>K=@v5m?pU(ca#HLi+RIX=GS=K_3b5rN&XB2f}C}XU#vwO z*OQ5(!|R5o^gck=?URMEwzirtgYR@knE`~ZjKRg7n^U2Gy zF>|@PxzT|Qc5TD%n!7`BbgX`Z)8T`@3cucN_5`Ob@pN6q9*fz2$l#dZ-T55^qv00; zUR#^Jxvd<#_n@H{fqUEAhBm*Tmg7+4;;Pt@7Jf#RYzUj0$*-(E_TK&7Q6?QRKfNGm z*Go;wzao5L9bmH~ojf5>BshC^f3_+eN3^`W?Dg+*OFSHf&MADZkQ{zb?*3wf)B};! z?{<2jTGxCzHg(ua((~bbO%Bv!jUdO*JbkcAp_NQ<&gQd&$&*AN1k#$MR z^cUtyXbjV8E-;yB8UQT~Uf-WeN0V3Jta^@$O#k-u>%g*GyTRn)?MA9a-)UM~y1jjk zyJoq=gGJ2A;rafI<0CIzO6BWmTxp!H@ynojg*0;V<*^@1WC}5#DhjL?R1RO`H8Kwy9D~>2= z*{yNV8eIEZKC^5rw;lwqy&pZ*7ftfVB0!hrxUcXXWCxq;9dW8y=J~bs@L&A8kx;Sv zG>{$e_6RK>`cX`QEb47K1FTvDFPLJ~8qM4;b6f1I1}m$nE@h~TP!Y9nZ2aH-?zI`c zb=Jr>jhgY+GVJ}d+C9&IsnS3)6iaxZAZ~Y&QCeo%w~xx-fuco{

3~mjA{^K~j=)u-uKLR?g8VctGK1Hm4`Il;a(?_T;DO_6Aea$I#)8kX0I(dbBDl?i z-5fs?*cIFn`P6@iX(O39I&x0H4~&DFQ(##S^J-#_P?ZJKQ4^R3o1=>7FMtlW#R&>o zRx9kGpWMvh9B(*Z*CI@M4C(kbyn=^+_tXQGb5CjlM!Pta;e4st+2KNMYyTDc-b7q= zqe#Ac{Xy9W%Jfh5`%%|nx>r`f3bs@jh-FL6TO#eFXfoL>zD%&O}`i1m_N z-$ll+DM!a0G3rSCEuEO(^I&mSA4@fUINb)MJpVIQ|Jwfc3sxc2zutl@Yqb{PyQ_;E zLCQDOThUj=LaI=o-3R4>T}itdm~M>CUqjvd)7kbtYC|`%6x{@mE8^5rt!!AW(B}5% zV`UJX^(%-W+*E)Ef(Mi82(Vg?hW|wjx_C&91JN|;_(7@Oa8y5%H?*PPviL~)`W=Wg zmVRh*kKhmOe7rvqm2e9Je+-~m#a-`igXTr}8#Q;Z1x@M|UsikO`2MTs>Ry@lVK@Ix z2@zZeb3l-nuyZndRjGwa2q~zq+iT^0ub2IZ2@kXRASsOa88XsSCDQUkp_QfUhLe8{ z#r5_#OeERfwqmQtti`o~VkG*|11g6v2b%4@z zobIL-m>zU(j&2YAw9ZUUDl_%a*R8m3NNH1)Y9Y^Ka|nek5%GpCZUY%@EMAMIh{YStkL-TM_R?g!DU|h{4`kixI%q1t?*Dd; z9YYyjFVA@|MpRvlJ1&8C8DMA|sa_Dq*hJM~nt@Ojt)hNrc$?U(WSCV^m(eUX;|`I; zMd~3+BrGy(ZbgmX6#B+X9c`?0MxfUlJ&{#8A>wh$mAqZGz+xi~2cQnlWr{nz#Q5{ApU2 zDC=~pcku0qh!^cLnY`?>Na$4pOOXhRs4tFnl*7IxaN^)0v*I&B`0!!Vr&jlM=nO_g z6qpco{Y~;RLmtjQ3UCmx@${tTmG0Z0*u~)D(J-rX!08(u`pyz*^il_Ict+Fcx{s61 zLP<)11*G5?(jpJybVKymVQBXrjxt1lWH&%$EaB}dO{=_bH@*Lb?kqd7zCyO;@TnuC zr~0FcP zjLy%XpXbXEor50pc*UvNv&lqQw#Fi}%oAHw6-JSkzZg)aq2V6jMF8j6oJ+8qXB4}F zN`UpDWdWm92$aksPoH|4(>PrrFbdr7l?;2P%^PRw{C0O*Mt)>L^LA~g{xhO|9Wb$ zl)GsSEs}Efz9;iEcNDEZ;owsfDV_`(krnruq*`P1XRiEA8r)a>e zW6+zC;mUGVjiM@YY}TcCdUJUv0sMGrg8{&fD*G*X6`aKekF!_6U~J!bf#$mi)`W8F zi0lw@?c`Y!2nSgpE!^;Ig0k5brH3QbFurAeA0O~7a_~Qs{8WZWGo~z2huCkjrTRxg zt46%QJ<;)}(iuv|f=y8n#}iFZvIE|dlB<@;@9LAc3V>)|{7n!hXfsB6g}c@7gtW1E zS6fE3OGr>ygdrTH+7;z%FnCR5;0K5K_eJ zdN~#J^KbQ2a}+7wDF_`SJ-fV7FcmM4k!C9C5g18(j1}p=i07swNJM{7X9fD5@K*b2 z5W!5e|74hE#2z2HABP`<<2B-4(3*4eK*cYo(YKMGE%`|OATth(j%p84a|w9={-}I} z7^JOpnlP-D)U5G<>KnCCyX)=XicU$qF?1=DTxpS{*G zY@5}Hu2HlLeul({=*%vkM|aJo{=v4cXvIPO`E?BGXW2-@%MIllU~g1#oB`Btf@rij z;1}nHLv+pQ&wB)lDI3vRWo^I}fQPlw15Q{(UM`GEi zZct2Bfq3ZGCrD>_QZA^d@7WnOesYS?6Td%8anL71FGt2S8Ydqd)yr(`sTy zUIxzlO-62{i7UUlMuGm4Wth)$Yv^ny>>pdCgXvay)Gmh)ThzJ46y>jX@45%7^=M7; zX9oZlhrUAKqckTjpQtuI@xER9)&2nHX=H&MeQp)|#OI+FpQN!n%5twty8BQa{xiTE z5vj(z`3=f-hPhfty08)>t-$5Z0&_+B3|yRf?aiY(Rem@e~2@N7g9U%YQ&N zRtRwZl0}AgxZ1uXs#a6bEH%PAL8nG^@sRWQ`>{)5N&xf(QUEeR=94H$e;+WO#XVJ* z_%j2BrwQ1_4){lMyf)GPdzgE?HANI1DNkff0;QRy6u-5UK3Ab<-(&DSFtx}u|HEJ7 z?aoZWJu7vKA=;LMAuJu-AY2$I@pyMphun29c9B#Vu6w+lVeW z%pW8}bL5!s)vptEKQDeiA$!>S-7UHD{b+=Lx_?7Fbx9F3*qc!a^X5sq>7>BB?!6sI zWEo!`(eFgum<; ztPcrx0UvdFBJGJ$CvWG-*=54%KnNIpgjZE5E@H ziuk*AoDXrdB?}$90Ua#ELcpeuOolq(;UZ_aY>LFx6C!YcdXV$5mqSP|dHX_P$~Kw= zY=5Jf0Pd9E?H4*8hM5?47i0D+c#wk`Q+s!!*jrYH++6B*aM+ynm|RcUR(?yPU#Do_{I|JOA6 zZ~S$!@5!qFO)x*L4*^svM)l5k%~ro1B)eZ@{fB{Rp6QiM)!U{>`W<*uGOY=>lt)qk zP20x1iiye^^NHU5!*wUi`?#Aj{AmS>@LvtJuQxE-FH|4UUi}6Kr|hR5)$dq+FJFO^ zyI)m*c`5r`;O#$F8jHx*k4@aI)p+TW{$g&3bU@&iFL)w2Q<|c`EEb9b&k)B59{$!} z9@~oZ*FyiiT?ObuOXcby`ayRg{g-YsmK=D*5Br&k44f|z9Z!w1#;Rd^yvxk10=evG z*eH$9mu3s`o|0@-lfv#&*tCsbXjsR+B0He`tePnh8F(cwkH}e8xiESN}fF zf2o22gJ5L53WPoX(cwgh>;lhCK1`zIWkAd*#Gm&A;6O&mi<)Z6iIQ7Mwc!;g5RO=_ z6mW@%C5s4~{?>BE?-55|*#V$Tr=^)jJyr8gU)OjYsPMNppp#Idr3sh|$fP!Mo?;f2 zrtpwERbHIt#lQaBW-~t!FDdD9TT?o{#_l8 z9_BvGY!o^lCkRN+GR;7|7FY+WdX$COB=!Z^r(LV8QF z4*2OS#x{E=Z}6a1=$09<+s-><#!Rq339evdVaFqG`1weu%eGdi{f!H*OtVgcm&QJ= zn#Vq!o4SDwqv2pY*|&zc!zc`&@?xc`eAd)1VH`>3DJ3tPicL9Hr0;yG22p5L9o84=c>8%=RDRmBXXMWsZ}Wp3YZ(QQ-VDKc7%1j%2)8(} zzMIPH9}l+&HhG?a!dQhk(t!0Jze$XF7T4F=K9jxXCCn$aFc;^Y#Q$aC>_X(zqe{lH zLAw9_wJ6hq;Y3ug>N|JsT<)dRV|ZA>Gwt~*n6nT+MdT0VwC$Zx>u-C8B}^*ym7$D2 zf(o3qsq}=J<7YHza_YAbCDMsB%417akFts5a&BRJ_jd4Qj%8ouqm|pzrpAN%uV$E1 zHcIr!c-|$ZLB5o^{9EL&NU6G```KZ!#0`~RDXj^r@f((~D$GIp{9Y0y`CmZEu02=1 z`zc)*Tx0*{E=!N|32h>rqw&9*D7OJ_#T~ha0gu#Oc~vS*O<9EF+h#%0DX7sd~n0r#OG{i^V2c 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 literal 0 HcmV?d00001 diff --git a/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs index c921aeb4e0..e16a48461b 100644 --- a/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs @@ -65,6 +65,13 @@ namespace Microsoft.PowerToys.Settings.UI.Flyout break; + case "RegistryPreview": // Launch Registry Preview + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RegistryPreviewTriggerEvent())) + { + eventHandle.Set(); + } + + break; case "MeasureTool": // Launch Screen Ruler using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.MeasureToolTriggerEvent())) { diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/MainWindow.xaml.cs index 06ce02f20d..b214590e65 100644 --- a/src/settings-ui/Settings.UI/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/MainWindow.xaml.cs @@ -138,6 +138,9 @@ namespace Microsoft.PowerToys.Settings.UI case "PowerAccent": needToUpdate = generalSettingsConfig.Enabled.PowerAccent != isEnabled; generalSettingsConfig.Enabled.PowerAccent = isEnabled; break; + case "RegistryPreview": + needToUpdate = generalSettingsConfig.Enabled.RegistryPreview != isEnabled; + generalSettingsConfig.Enabled.RegistryPreview = isEnabled; break; case "MeasureTool": needToUpdate = generalSettingsConfig.Enabled.MeasureTool != isEnabled; generalSettingsConfig.Enabled.MeasureTool = isEnabled; break; diff --git a/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs b/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs index b7ceb31266..bdfb3e54f6 100644 --- a/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs +++ b/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs @@ -26,5 +26,6 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Enums Hosts, PastePlain, WhatsNew, + RegistryPreview, } } diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeRegistryPreview.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobeRegistryPreview.xaml new file mode 100644 index 0000000000..0709030bea --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeRegistryPreview.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + +

vzJ`x?*xsL~iJ@=-;m3av`AO81qoBW_p4h}8 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