Merged powerToys master into Launcher master
5
.gitignore
vendored
@ -301,6 +301,8 @@ __pycache__/
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
ImageResizer/tools/**
|
||||
!ImageResizer/tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
@ -332,3 +334,6 @@ ASALocalRun/
|
||||
# Temp build files
|
||||
src/settings/settings-html/200.html
|
||||
src/settings/settings-html/404.html
|
||||
|
||||
# Temp telemetry files.
|
||||
src/common/Telemetry/*.etl
|
@ -33,3 +33,16 @@ steps:
|
||||
msbuildArgs: ${{ parameters.additionalBuildArguments }}
|
||||
clean: true
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: VSTest@2
|
||||
inputs:
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
testSelector: 'testAssemblies'
|
||||
testAssemblyVer2: |
|
||||
**\PreviewPaneUnitTests.dll
|
||||
**\UnitTests-SvgPreviewHandler.dll
|
||||
**\UnitTests-PreviewHandlerCommon.dll
|
||||
**\powerpreviewTest.dll
|
||||
!**\*TestAdapter.dll
|
||||
!**\obj\**
|
||||
|
@ -50,6 +50,14 @@ build:
|
||||
- 'modules\fancyzones.dll'
|
||||
- 'modules\shortcut_guide.dll'
|
||||
- 'modules\PowerRenameExt.dll'
|
||||
- 'modules\WindowWalker.exe'
|
||||
- 'modules\WindowWalker.dll'
|
||||
- 'modules\ImageResizerExt.dll'
|
||||
- 'modules\ImageResizer.exe'
|
||||
- 'modules\powerpreview.dll'
|
||||
- 'modules\PreviewHandlerCommon.dll'
|
||||
- 'modules\MarkdownPreviewHandler.dll'
|
||||
- 'modules\SvgPreviewHandler.dll'
|
||||
signing_options:
|
||||
sign_inline: true # This does signing a soon as this command completes
|
||||
- !!buildcommand
|
||||
|
46
NOTICE.md
Normal file
@ -0,0 +1,46 @@
|
||||
# NOTICES AND INFORMATION
|
||||
Do Not Translate or Localize
|
||||
|
||||
This software incorporates material from third parties. Microsoft makes certain
|
||||
open source code available at http://3rdpartysource.microsoft.com, or you may
|
||||
send a check or money order for US $5.00, including the product name, the open
|
||||
source component name, and version number, to:
|
||||
|
||||
```
|
||||
Source Code Compliance Team
|
||||
Microsoft Corporation
|
||||
One Microsoft Way
|
||||
Redmond, WA 98052
|
||||
USA
|
||||
```
|
||||
|
||||
Notwithstanding any other terms, you may reverse engineer this software to the
|
||||
extent required to debug changes to any libraries licensed under the GNU Lesser
|
||||
General Public License.
|
||||
|
||||
## ImageResizer
|
||||
|
||||
**Source**: https://github.com/bricelam/ImageResizer/
|
||||
|
||||
### License
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Brice Lambson. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
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
|
||||
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
|
||||
THE SOFTWARE.
|
107
PowerToys.sln
@ -5,13 +5,28 @@ VisualStudioVersion = 16.0.28803.452
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner.vcxproj", "{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{217DF501-135C-4E38-BFC8-99D4821032EA} = {217DF501-135C-4E38-BFC8-99D4821032EA}
|
||||
{0E072714-D127-460B-AFAD-B4C40B412798} = {0E072714-D127-460B-AFAD-B4C40B412798}
|
||||
{48804216-2A0E-4168-A6D8-9CD068D14227} = {48804216-2A0E-4168-A6D8-9CD068D14227}
|
||||
{51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2}
|
||||
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} = {51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}
|
||||
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} = {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}
|
||||
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73} = {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}
|
||||
{74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3}
|
||||
{0485F45C-EA7A-4BB5-804B-3E8D14699387} = {0485F45C-EA7A-4BB5-804B-3E8D14699387}
|
||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D} = {D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}
|
||||
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}
|
||||
{0B593A6C-4143-4337-860E-DB5710FB87DB} = {0B593A6C-4143-4337-860E-DB5710FB87DB}
|
||||
{E364F67B-BB12-4E91-B639-355866EBCD8B} = {E364F67B-BB12-4E91-B639-355866EBCD8B}
|
||||
{DA425894-6E13-404F-8DCB-78584EC0557A} = {DA425894-6E13-404F-8DCB-78584EC0557A}
|
||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}
|
||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}
|
||||
{B25AC7A5-FB9F-4789-B392-D5C85E948670} = {B25AC7A5-FB9F-4789-B392-D5C85E948670}
|
||||
{AF2349B8-E5B6-4004-9502-687C1C7730B1} = {AF2349B8-E5B6-4004-9502-687C1C7730B1}
|
||||
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} = {B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB}
|
||||
{A46629C4-1A6C-40FA-A8B6-10E5102BB0BA} = {A46629C4-1A6C-40FA-A8B6-10E5102BB0BA}
|
||||
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
|
||||
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}
|
||||
{07C389E3-6BC8-41CF-923E-307B1265FA2D} = {07C389E3-6BC8-41CF-923E-307B1265FA2D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
@ -124,6 +139,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowWalker", "src\modules
|
||||
{74485049-C722-400F-ABE5-86AC52D929B3} = {74485049-C722-400F-ABE5-86AC52D929B3}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "imageresizer", "imageresizer", "{6C7F47CC-2151-44A3-A546-41C70025132C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageResizerUI", "src\modules\imageresizer\ui\ImageResizerUI.csproj", "{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageResizerExt", "src\modules\imageresizer\dll\ImageResizerExt.vcxproj", "{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageResizerUITest", "src\modules\imageresizer\tests\ImageResizerUITest.csproj", "{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "action_runner", "src\action_runner\action_runner.vcxproj", "{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
|
||||
@ -171,6 +194,28 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher", "src\module
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerLauncher.UI", "src\modules\launcher\PowerLauncher.UI\PowerLauncher.UI.csproj", "{4A3DE70C-684C-410D-B851-C23B6DAEDF16}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E775CC2C-24CB-48D6-9C3A-BE4CCE0DB17A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "win-app-driver", "src\tests\win-app-driver\win-app-driver.csproj", "{880ED251-9E16-4713-9A70-D35FE0C01669}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "previewpane", "previewpane", "{2F305555-C296-497E-AC20-5FA1B237996A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PreviewHandlerCommon", "src\modules\previewpane\Common\PreviewHandlerCommon.csproj", "{AF2349B8-E5B6-4004-9502-687C1C7730B1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownPreviewHandler", "src\modules\previewpane\MarkDownPreviewHandler\MarkdownPreviewHandler.csproj", "{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-MarkdownPreviewHandler", "src\modules\previewpane\PreviewPaneUnitTests\UnitTests-MarkdownPreviewHandler.csproj", "{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgPreviewHandler", "src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csproj", "{DA425894-6E13-404F-8DCB-78584EC0557A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-SvgPreviewHandler", "src\modules\previewpane\UnitTests-SvgPreviewHandler\UnitTests-SvgPreviewHandler.csproj", "{060D75DA-2D1C-48E6-A4A1-6F0718B64661}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests-PreviewHandlerCommon", "src\modules\previewpane\UnitTests-PreviewHandlerCommon\UnitTests-PreviewHandlerCommon.csproj", "{748417CA-F17E-487F-9411-CAFB6D3F4877}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "powerpreview", "src\modules\previewpane\powerpreview\powerpreview.vcxproj", "{217DF501-135C-4E38-BFC8-99D4821032EA}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "powerpreviewTest", "src\modules\previewpane\powerpreviewTest\powerpreviewTest.vcxproj", "{47310AB4-9034-4BD1-8D8B-E88AD21A171B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@ -261,6 +306,18 @@ Global
|
||||
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Debug|x64.Build.0 = Debug|x64
|
||||
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.ActiveCfg = Release|x64
|
||||
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6}.Release|x64.Build.0 = Release|x64
|
||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x64.Build.0 = Debug|x64
|
||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x64.ActiveCfg = Release|x64
|
||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x64.Build.0 = Release|x64
|
||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x64.Build.0 = Debug|x64
|
||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x64.ActiveCfg = Release|x64
|
||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x64.Build.0 = Release|x64
|
||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x64.Build.0 = Debug|x64
|
||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.ActiveCfg = Release|x64
|
||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.Build.0 = Release|x64
|
||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.Build.0 = Debug|x64
|
||||
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.ActiveCfg = Release|x64
|
||||
@ -323,6 +380,42 @@ Global
|
||||
{4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Release|x64.ActiveCfg = Release|x64
|
||||
{4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Release|x64.Build.0 = Release|x64
|
||||
{4A3DE70C-684C-410D-B851-C23B6DAEDF16}.Release|x64.Deploy.0 = Release|x64
|
||||
{880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{880ED251-9E16-4713-9A70-D35FE0C01669}.Debug|x64.Build.0 = Debug|x64
|
||||
{880ED251-9E16-4713-9A70-D35FE0C01669}.Release|x64.ActiveCfg = Release|x64
|
||||
{880ED251-9E16-4713-9A70-D35FE0C01669}.Release|x64.Build.0 = Release|x64
|
||||
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.Build.0 = Debug|x64
|
||||
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.ActiveCfg = Release|x64
|
||||
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.Build.0 = Release|x64
|
||||
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.Build.0 = Debug|x64
|
||||
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.ActiveCfg = Release|x64
|
||||
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.Build.0 = Release|x64
|
||||
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.Build.0 = Debug|x64
|
||||
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.ActiveCfg = Release|x64
|
||||
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.Build.0 = Release|x64
|
||||
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.Build.0 = Debug|x64
|
||||
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.ActiveCfg = Release|x64
|
||||
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.Build.0 = Release|x64
|
||||
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.Build.0 = Debug|x64
|
||||
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.ActiveCfg = Release|x64
|
||||
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.Build.0 = Release|x64
|
||||
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.Build.0 = Debug|x64
|
||||
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.ActiveCfg = Release|x64
|
||||
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.Build.0 = Release|x64
|
||||
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.Build.0 = Debug|x64
|
||||
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.ActiveCfg = Release|x64
|
||||
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.Build.0 = Release|x64
|
||||
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.Build.0 = Debug|x64
|
||||
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.ActiveCfg = Release|x64
|
||||
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -351,6 +444,10 @@ Global
|
||||
{8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{B9BDF8BE-FED7-49B5-A7AE-DD4D1CA2D9EB} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03}
|
||||
{51D3BD1F-07A8-48EB-B2A0-0A249CD4E1A6} = {8DC78AF7-DC3E-4C57-A8FB-7E347DE74A03}
|
||||
{6C7F47CC-2151-44A3-A546-41C70025132C} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {6C7F47CC-2151-44A3-A546-41C70025132C}
|
||||
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {6C7F47CC-2151-44A3-A546-41C70025132C}
|
||||
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8} = {6C7F47CC-2151-44A3-A546-41C70025132C}
|
||||
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{DB90F671-D861-46BB-93A3-F1304F5BA1C5} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
|
||||
@ -367,6 +464,16 @@ Global
|
||||
{F8B870EB-D5F5-45BA-9CF7-A5C459818820} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
{F97E5003-F263-4D4A-A964-0F1F3C82DEF2} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
|
||||
{4A3DE70C-684C-410D-B851-C23B6DAEDF16} = {C140A3EF-6DBF-4084-9D4C-4EB5A99FEE68}
|
||||
{880ED251-9E16-4713-9A70-D35FE0C01669} = {E775CC2C-24CB-48D6-9C3A-BE4CCE0DB17A}
|
||||
{2F305555-C296-497E-AC20-5FA1B237996A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{AF2349B8-E5B6-4004-9502-687C1C7730B1} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{DA425894-6E13-404F-8DCB-78584EC0557A} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{060D75DA-2D1C-48E6-A4A1-6F0718B64661} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{748417CA-F17E-487F-9411-CAFB6D3F4877} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{217DF501-135C-4E38-BFC8-99D4821032EA} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
{47310AB4-9034-4BD1-8D8B-E88AD21A171B} = {2F305555-C296-497E-AC20-5FA1B237996A}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
103
README.md
@ -32,31 +32,22 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
|
||||
|
||||
## Build Status
|
||||
|
||||
[![Build Status](https://dev.azure.com/ms/PowerToys/_apis/build/status/microsoft.PowerToys?branchName=master)](https://dev.azure.com/ms/PowerToys/_build?definitionId=35096)
|
||||
[![Build Status](https://dev.azure.com/ms/PowerToys/_apis/build/status/microsoft.PowerToys?branchName=master)](https://dev.azure.com/ms/PowerToys/_build?definitionId=219)
|
||||
|
||||
## Installing and running Microsoft PowerToys
|
||||
|
||||
> 👉 **Note:** Microsoft PowerToys requires Windows 10 1803 (build 17134) or later.
|
||||
|
||||
> 👉 **Upgrading to 0.15:** You need to reapply your zone layout for FancyZones. Don't worry, your custom zone sets are preserved.
|
||||
## Installing and running Microsoft PowerToys 0.16
|
||||
👉 **Note:** Microsoft PowerToys requires Windows 10 1803 (build 17134) or later.
|
||||
|
||||
### Via Github with MSI [Recommended]
|
||||
|
||||
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.15.0-x64.msi` to download the PowerToys installer.
|
||||
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.16.0-x64.msi` to download the PowerToys installer.
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
### Other install methods
|
||||
|
||||
#### Via GitHub with MSIX - ⚠ Experimental ⚠
|
||||
##### MSIX / Store Build Update
|
||||
|
||||
The experimental version of PowerToys using MSIX is available. It can be installed from the [PowerToys GitHub releases page][github-release-link].
|
||||
|
||||
Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-MSIX-0.15.0.zip` to download the PowerToys installer zip. From there, please read the ReadMe and you can double click to install the MSIX file.
|
||||
|
||||
##### Known issues with MSIX Build
|
||||
|
||||
- For PowerRename, you may need to restart your machine to get this to work for the first time.
|
||||
- We put in a lot of effort here but currently our plan of record is to make the MSI our only installer option and built-in auto-upgrade. MSIX is a great installer / container tech but there are few spots we are working with the team to improve so we can adopt.
|
||||
|
||||
#### Via Chocolatey - ⚠ Unofficial ⚠
|
||||
|
||||
@ -74,17 +65,13 @@ To upgrade PowerToys, run the following command from the command line / PowerShe
|
||||
choco upgrade powertoys
|
||||
```
|
||||
|
||||
### Microsoft Store
|
||||
|
||||
On backlog, [Issue #413](https://github.com/microsoft/PowerToys/issues/413)
|
||||
|
||||
### Processor support
|
||||
|
||||
We currently support the matrix below. Adding MSIX support will make supporting x86 and ARM much easier.
|
||||
We currently support the matrix below.
|
||||
|
||||
| x64 | x86 | ARM |
|
||||
|:---:|:---:|:---:|
|
||||
| [Install][github-release-link] | [Issue #602](https://github.com/microsoft/PowerToys/issues/602) | [Issue #490](https://github.com/microsoft/PowerToys/issues/490)|
|
||||
| [Supported][github-release-link] | [Issue #602](https://github.com/microsoft/PowerToys/issues/602) | [Issue #490](https://github.com/microsoft/PowerToys/issues/490) |
|
||||
|
||||
## Current PowerToy Utilities
|
||||
|
||||
@ -92,7 +79,7 @@ We currently support the matrix below. Adding MSIX support will make supporting
|
||||
|
||||
[FancyZones](/src/modules/fancyzones/) - FancyZones is a window manager that makes it easy to create complex window layouts and quickly position windows into those layouts.
|
||||
|
||||
### Shortcut
|
||||
### Shortcut Guide
|
||||
|
||||
[Windows key shortcut guide](/src/modules/shortcut_guide) - The shortcut guide appears when a user holds the Windows key down for more than one second and shows the available shortcuts for the current state of the desktop.
|
||||
|
||||
@ -100,36 +87,66 @@ We currently support the matrix below. Adding MSIX support will make supporting
|
||||
|
||||
[PowerRename](/src/modules/powerrename) - PowerRename is a Windows Shell Extension for advanced bulk renaming using search and replace or regular expressions. PowerRename allows simple search and replace or more advanced regular expression matching. While you type in the search and replace input fields, the preview area will show what the items will be renamed to. PowerRename then calls into the Windows Explorer file operations engine to perform the rename. This has the benefit of allowing the rename operation to be undone after PowerRename exits.
|
||||
|
||||
This code is based on [Chris Davis's SmartRename](https://github.com/chrdavis/SmartRename).
|
||||
|
||||
### File Explorer (Preview Panes)
|
||||
|
||||
[File Explorer](/src/modules/previewpane) add-ons right now are just limited to Preview Pane additions for File Explorer. Preview Pane is an existing feature in the File Explorer. To enable it, you just click the View tab in the ribbon and then click "Preview Pane".
|
||||
|
||||
PowerToys will now enable two types of files to be previewed:
|
||||
|
||||
- Markdown files (.md)
|
||||
- SVG (.svg)
|
||||
|
||||
### Image Resizer
|
||||
|
||||
[Image Resizer](/src/modules/imageresizer) is a Windows Shell Extension for quickly resizing images. With a simple right click from File Explorer, resize one or many images instantly.
|
||||
|
||||
This code is based on [Brice Lambson's Image Resizer](https://github.com/bricelam/ImageResizer).
|
||||
|
||||
### Window Walker (Text based alt-tab alternative)
|
||||
|
||||
[Window Walker](src/modules/windowwalker/) is an app that lets you search and switch between windows that you have open, all from the comfort of your keyboard. As you are searching for an app, you can use the keyboard up and down arrows to see an Alt-Tab style preview of the windows. In the future, this will be merged into the Launcher project.
|
||||
|
||||
This code is based on [Beta Tadele's Window Walker](https://github.com/betsegaw/windowwalker).
|
||||
|
||||
### Version 1.0 plan
|
||||
|
||||
Our plan for all the [goals and utilities for v1.0 detailed over here in the wiki][v1].
|
||||
|
||||
## What's Happening
|
||||
|
||||
### February 2020 Update
|
||||
### March 2020 Update
|
||||
|
||||
Our mantra for the 0.15 was infrastructure, quality, stability and work toward getting a way to auto-update PowerToys. While it took a bit longer to get here, we feel it was worth the extra time to fix bugs that really impacted your experience with PowerToys.
|
||||
Our mantra for the 0.16 was adding in new features along with a continual push for quality and stability. We are working toward getting a way to auto-update PowerToys and have a good plan for this. We want to proactively thank the community for quickly identifying a few bugs inside 0.15 and allowing us to quickly release 0.15.1 and 0.15.2.
|
||||
|
||||
Below are just a few of the bullet items from this release.
|
||||
|
||||
- We shipped [v0.15][github-release-link]!
|
||||
- Make you aware there is a new version from within PowerToys
|
||||
- Removed requirement to always 'run as admin'
|
||||
- Added almost 300 unit tests to increase stability and prevent regressions.
|
||||
- Resolved almost 100 issues
|
||||
- Made .NET Framework parts of the source run faster with NGEN
|
||||
- Improved for how we store data locally
|
||||
- Increased FancyZones compatibility with applications
|
||||
- Initial work for 4 new PowerToys added for 0.16!
|
||||
- Created the [v1.0 strategy][v1], the [launcher](https://github.com/microsoft/PowerToys/wiki/Launcher), the [keyboard manager](https://github.com/microsoft/PowerToys/wiki/Keyboard-Manager) specs
|
||||
- Work on cleaning up our issue backlog and labels
|
||||
- We shipped [v0.16][github-release-link]!
|
||||
- FancyZone improvement:
|
||||
- Multi-Monitor improvement: Zone flipping switching now works between monitors!
|
||||
- Simplified UX: Removed layout hot-swap and flashing feature due to need to improve multi-monitor support
|
||||
- New Utilities!
|
||||
- Markdown Preview pane extension
|
||||
- SVG Preview pane extension
|
||||
- Image Resizer Window Shell extension
|
||||
- Window Walker, an alt-tab alternative
|
||||
- Fixed over 100 issues!
|
||||
- Testing improvements
|
||||
- 54 UX Functional tests
|
||||
- 161 new Unit tests
|
||||
|
||||
For 0.16, we have some fun things planned and hopefully will be able to ship pretty quickly. Here are the new utilities we'll enable:
|
||||
For [0.17](https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F3), we are proactively working on:
|
||||
|
||||
- An alternative to Alt-Tab PowerToy
|
||||
- SVG preview pane for support Explorer
|
||||
- Markdown preview pane support for Explorer
|
||||
- Image Resizer PowerToy
|
||||
- Auto-updating
|
||||
- Win+R replacement (Launcher)
|
||||
- Keyboard remapping
|
||||
- Performance improvements with FancyZones
|
||||
- A testing utility for FancyZones to be sure we can test different window configurations.
|
||||
|
||||
Future release work, we are proactively working on:
|
||||
|
||||
- Settings v2 / Fix bug #243
|
||||
|
||||
## Developer Guidance
|
||||
|
||||
@ -151,16 +168,14 @@ PowerToys is still a very fluidic project and the team is actively working out o
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct-code]. For more information see the [Code of Conduct FAQ][oss-conduct-FAQ] or contact [opencode@microsoft.com][oss-conduct-email] with any additional questions or comments.
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct-code].
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has the trends from the telemetry. Please read the [Microsoft privacy statement][privacyLink] for more information.
|
||||
|
||||
[oss-CLA]: https://cla.opensource.microsoft.com
|
||||
[oss-conduct-code]: https://opensource.microsoft.com/codeofconduct/
|
||||
[oss-conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
|
||||
[oss-conduct-email]: mailto:opencode@microsoft.com
|
||||
[oss-conduct-code]: CODE_OF_CONDUCT.md
|
||||
[github-release-link]: https://github.com/microsoft/PowerToys/releases/
|
||||
[v1]: https://github.com/microsoft/PowerToys/wiki/Version-1.0-Strategy
|
||||
[privacyLink]: http://go.microsoft.com/fwlink/?LinkId=521839
|
||||
|
71
doc/devdocs/guidance.md
Normal file
@ -0,0 +1,71 @@
|
||||
# Coding Guidance
|
||||
|
||||
## Working With Strings
|
||||
|
||||
In order to support localization **YOU SHOULD NOT** have hardcoded UI display strings in your code. Instead, use resource files to consume strings.
|
||||
|
||||
### For CPP
|
||||
Use [`StringTable` resource][String Table] to store the strings and resource header file(`resource.h`) to store Id's linked to the UI display string. Add the strings with Id's referenced from the header file to the resource-definition script file. You can use [Visual Studio Resource Editor][VS Resource Editor] to create and manage resource files.
|
||||
|
||||
- `resource.h`:
|
||||
|
||||
XXX must be a unique int in the list (mostly the int ID of the last string id plus one):
|
||||
|
||||
```cpp
|
||||
#define IDS_MODULE_DISPLAYNAME XXX
|
||||
```
|
||||
|
||||
- `StringTable` in resource-definition script file `validmodulename.rc`:
|
||||
|
||||
```
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_MODULE_DISPLAYNAME L"Module Name"
|
||||
END
|
||||
```
|
||||
|
||||
- Use the `GET_RESOURCE_STRING(UINT resource_id)` method to consume strings in your code.
|
||||
```cpp
|
||||
#include <common.h>
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
std::wstring GET_RESOURCE_STRING(IDS_MODULE_DISPLAYNAME)
|
||||
```
|
||||
|
||||
### For C#
|
||||
Use [XML resource file(.resx)][Resx Files] to store the UI display strings and [`Resource Manager`][Resource Manager] to consume those strings in the code. You can use [Visual Studio][Resx Files VS] to create and manage XML resources files.
|
||||
|
||||
- `Resources.resx`
|
||||
|
||||
```xml
|
||||
<data name="ValidUIDisplayString" xml:space="preserve">
|
||||
<value>Description to be displayed on UI.</value>
|
||||
<comment>This text is displayed when XYZ button clicked.</comment>
|
||||
</data>
|
||||
```
|
||||
|
||||
- Use [`Resource Manager`][Resource Manager] to consume strings in code.
|
||||
```csharp
|
||||
System.Resources.ResourceManager manager = new System.Resources.ResourceManager(baseName, assembly);
|
||||
string validUIDisplayString = manager.GetString("ValidUIDisplayString", resourceCulture);
|
||||
```
|
||||
|
||||
In case of Visual Studio is used to create the resource file. Simply use the `Resources` class in auto-generated `Resources.Designer.cs` file to access the strings which encapsulate the [`Resource Manager`][Resource Manager] logic.
|
||||
|
||||
```csharp
|
||||
string validUIDisplayString = Resources.ValidUIDisplayString;
|
||||
```
|
||||
|
||||
## More On Coding Guidance
|
||||
Please review these brief docs below relating to our coding standards etc.
|
||||
|
||||
* [Coding Style](./style.md)
|
||||
* [Code Organization](./readme.md)
|
||||
|
||||
|
||||
[VS Resource Editor]: https://docs.microsoft.com/en-us/cpp/windows/resource-editors?view=vs-2019
|
||||
[String Table]: https://docs.microsoft.com/en-us/windows/win32/menurc/stringtable-resource
|
||||
[Resx Files VS]: https://docs.microsoft.com/en-us/dotnet/framework/resources/creating-resource-files-for-desktop-apps#resource-files-in-visual-studio
|
||||
[Resx Files]: https://docs.microsoft.com/en-us/dotnet/framework/resources/creating-resource-files-for-desktop-apps#resources-in-resx-files
|
||||
[Resource Manager]: https://docs.microsoft.com/en-us/dotnet/api/system.resources.resourcemanager?view=netframework-4.8
|
@ -10,12 +10,17 @@
|
||||
|
||||
## Github Workflow
|
||||
|
||||
- Follow the PR template, in particular make sure there is open issue for the new PR.
|
||||
- When the PR is approved, let the owner of the PR merge it.
|
||||
- Before starting to work on a fix/feature, make sure there is an open issue to track the work.
|
||||
- Add the `In progress` label to the issue, if not already present also add a `Cost-Small/Medium/Large` estimate and make sure all appropriate labels are set.
|
||||
- If you are a community contributor, you will not be able to add labels to the issue, in that case just add a comment saying that you started to work on the issue and try to give an estimate for the delivery date.
|
||||
- If the work item has a medium/large cost, using the markdown task list, list each sub item and update the list with a check mark after completing each sub item.
|
||||
- When opening a PR, follow the PR template.
|
||||
- When the PR is approved, let the owner of the PR merge it. For community contributions the reviewer that approved the PR can also merge it.
|
||||
- Use the `Squash and merge` option to merge a PR, if you don't want to squash it because there are logically different commits, use `Rebase and merge`.
|
||||
- We don't close issues automatically when referenced in a PR, so after the OR is merged:
|
||||
- mark the issue(s) fixed by the PR with the `resolved` label.
|
||||
- don't close the issue if it's a bug in the current release since users tend to not search for closed issues, we will close the resolved issues when a new released is published.
|
||||
- We don't close issues automatically when referenced in a PR, so after the PR is merged:
|
||||
- mark the issue(s), that the PR solved, with the `Resolution-Fix-Committed` label, remove the `In progress` label and if the issue is assigned to a project, move the item to the `Done` status.
|
||||
- don't close the issue if it's a bug in the current released version since users tend to not search for closed issues, we will close the resolved issues when a new version is released.
|
||||
- if it's not a code fix that effects the end user, the issue can be closed (for example a fix in the build or a code refactoring and so on).
|
||||
|
||||
## Repository Overview
|
||||
|
||||
|
50
doc/devdocs/run-as-admin-detection.md
Normal file
@ -0,0 +1,50 @@
|
||||
# PowerToys and running as Administrator
|
||||
|
||||
## Too long, Didn't Read 😁
|
||||
|
||||
If you're running any application as an administrator (aka elevated) and PowerToys is not, a few things may not work correctly when the elevated applications are in focus or trying to interact with a PowerToys feature like FancyZones.
|
||||
|
||||
## Having PowerToys keep functioning properly
|
||||
|
||||
We understand users will run applications elevated. We do as well. We have two options for you when this scenario happens:
|
||||
|
||||
1. **Recommended:** PowerToys will prompt when we detect a process that is elevated. Go to PowerToys settings inside the General Tab and click "Relaunch as adminstrator".
|
||||
2. Enable "Always run as administrator" in the PowerToys settings.
|
||||
|
||||
## What is "Run as Administrator" / Elevated processes
|
||||
|
||||
This is when a process runs with "elevated" privileges. Typically this would be associated with the administrator accounts on a system.
|
||||
|
||||
Basically it runs with additional access to the operating system. Most things do not need run elevated. A common scenario would be needing to run certain PowerShell commands or edit the registry.
|
||||
|
||||
How do i know my application is "elevated"? If you see this prompt (User Access Control prompt), the application is requesting it:
|
||||
|
||||
![alt text][uac]
|
||||
|
||||
At times also, elevated terminals for instance, they will typically have the phrase "Administrator" appended to the title bar. Be warned, this isn't always the case it will be appended.
|
||||
|
||||
![alt text][elevatedWindow]
|
||||
|
||||
## When does PowerToys need this
|
||||
|
||||
PowerToys in itself does not. It only needs to be elevated when it has to interact with other applications that are running elevated. If those applications are in focus, PowerToys may not function unless it is elevated as well.
|
||||
|
||||
These are the two scenarios we will not work in:
|
||||
|
||||
1. Intercepting certain types of keyboard strokes
|
||||
2. Resizing / Moving windows
|
||||
|
||||
### PowerToys affected
|
||||
|
||||
1. FancyZones
|
||||
- Snapping a window into a zone
|
||||
- Moving the window to a different zone
|
||||
2. Shortcut guide
|
||||
- Display shortcut
|
||||
3. Keyboard remapper
|
||||
- key to key remapping
|
||||
- Global level shortcuts remapping
|
||||
- App-targeted shortcuts remapping
|
||||
|
||||
[uac]: ../images/runAsAdmin/uac.png "User access control (UAC)"
|
||||
[elevatedWindow]: ../images/runAsAdmin/elevatedWindows.png "Run as admin"
|
BIN
doc/images/imageresizer/resizeDragAndDrop.gif
Normal file
After Width: | Height: | Size: 762 KiB |
BIN
doc/images/imageresizer/resizeNormal.gif
Normal file
After Width: | Height: | Size: 405 KiB |
BIN
doc/images/imageresizer/resizeSettings.gif
Normal file
After Width: | Height: | Size: 588 KiB |
BIN
doc/images/preview_pane/demo.gif
Normal file
After Width: | Height: | Size: 641 KiB |
BIN
doc/images/preview_pane/general-settings.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
doc/images/preview_pane/settings-ui.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
doc/images/runAsAdmin/elevatedWindows.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
doc/images/runAsAdmin/uac.png
Normal file
After Width: | Height: | Size: 15 KiB |
@ -3,7 +3,7 @@
|
||||
|
||||
<Package ID="PowerToys-x64" ProcessorArchitecture="x64">
|
||||
<Files>
|
||||
<File DestinationPath="License.rtf" SourcePath="..\..\License.rtf"/>
|
||||
<File DestinationPath="License.rtf" SourcePath="..\License.rtf"/>
|
||||
|
||||
<File DestinationPath="action_runner.exe" SourcePath="..\..\x64\Release\action_runner.exe"/>
|
||||
<File DestinationPath="PowerToys.exe" SourcePath="..\..\x64\Release\PowerToys.exe"/>
|
||||
@ -16,7 +16,14 @@
|
||||
<File DestinationPath="modules\Microsoft.Xaml.Behaviors.dll" SourcePath="..\..\x64\Release\modules\Microsoft.Xaml.Behaviors.dll"/>
|
||||
<File DestinationPath="modules\PowerRenameExt.dll" SourcePath="..\..\x64\Release\modules\PowerRenameExt.dll"/>
|
||||
<File DestinationPath="modules\shortcut_guide.dll" SourcePath="..\..\x64\Release\modules\shortcut_guide.dll"/>
|
||||
<File DestinationPath="modules\PowerRenameUWPUI.exe" SourcePath="..\..\x64\Release\PowerRenameUWPUI.exe"/>
|
||||
<File DestinationPath="modules\PowerRenameUWPUI.exe" SourcePath="..\..\x64\Release\modules\PowerRenameUWPUI.exe"/>
|
||||
<File DestinationPath="modules\ImageResizer.exe" SourcePath="..\..\x64\Release\modules\ImageResizer.exe"/>
|
||||
<File DestinationPath="modules\ImageResizerExt.dll" SourcePath="..\..\x64\Release\modules\ImageResizerExt.dll"/>
|
||||
<File DestinationPath="modules\GalaSoft.MvvmLight.dll" SourcePath="..\..\x64\Release\modules\GalaSoft.MvvmLight.dll"/>
|
||||
<File DestinationPath="modules\GalaSoft.MvvmLight.Platform.dll" SourcePath="..\..\x64\Release\modules\GalaSoft.MvvmLight.Platform.dll"/>
|
||||
<File DestinationPath="modules\GalaSoft.MvvmLight.Extras.dll" SourcePath="..\..\x64\Release\modules\GalaSoft.MvvmLight.Extras.dll"/>
|
||||
<File DestinationPath="modules\System.Windows.Interactivity.dll" SourcePath="..\..\x64\Release\modules\System.Windows.Interactivity.dll"/>
|
||||
<File DestinationPath="modules\Newtonsoft.Json.dll" SourcePath="..\..\x64\Release\modules\Newtonsoft.Json.dll"/>
|
||||
<File DestinationPath="modules\System.Text.Json.dll" SourcePath="..\..\x64\Release\modules\System.Text.Json.dll"/>
|
||||
<File DestinationPath="modules\System.Memory.dll" SourcePath="..\..\x64\Release\modules\System.Memory.dll"/>
|
||||
<File DestinationPath="modules\System.Buffers.dll" SourcePath="..\..\x64\Release\modules\System.Buffers.dll"/>
|
||||
@ -26,6 +33,13 @@
|
||||
<File DestinationPath="modules\System.ValueTuple.dll" SourcePath="..\..\x64\Release\modules\System.ValueTuple.dll"/>
|
||||
<File DestinationPath="modules\System.Numerics.Vectors.dll" SourcePath="..\..\x64\Release\modules\System.Numerics.Vectors.dll"/>
|
||||
<File DestinationPath="modules\Microsoft.Bcl.AsyncInterfaces.dll" SourcePath="..\..\x64\Release\modules\Microsoft.Bcl.AsyncInterfaces.dll"/>
|
||||
<File DestinationPath="modules\powerpreview.dll" SourcePath="..\..\x64\Release\modules\powerpreview.dll"/>
|
||||
<File DestinationPath="modules\PreviewHandlerCommon.dll" SourcePath="..\..\x64\Release\modules\PreviewHandlerCommon.dll"/>
|
||||
<File DestinationPath="modules\SvgPreviewHandler.dll" SourcePath="..\..\x64\Release\modules\SvgPreviewHandler.dll"/>
|
||||
<File DestinationPath="modules\MarkdownPreviewHandler.dll" SourcePath="..\..\x64\Release\modules\MarkdownPreviewHandler.dll"/>
|
||||
<File DestinationPath="modules\Markdig.Signed.dll" SourcePath="..\..\x64\Release\modules\Markdig.Signed.dll"/>
|
||||
<File DestinationPath="modules\HtmlAgilityPack.dll" SourcePath="..\..\x64\Release\modules\HtmlAgilityPack.dll"/>
|
||||
<File DestinationPath="registry.dat" SourcePath="registry.dat"/>
|
||||
|
||||
<File DestinationPath="modules\FancyZonesEditor.exe.config" SourcePath="..\..\x64\Release\modules\FancyZonesEditor.exe.config"/>
|
||||
|
||||
@ -34,6 +48,27 @@
|
||||
<File DestinationPath="svgs\*" SourcePath="..\..\x64\Release\svgs\*"/>
|
||||
<File DestinationPath="settings-html\**" SourcePath="..\..\x64\Release\settings-html\**"/>
|
||||
<File DestinationPath="Images\*.png" SourcePath="Images\*.png"/>
|
||||
|
||||
<!-- Resource files for ar,bg,ca,cs,de,es,eu-ES,fr,he,hu,it,nb-NO,nl,pl,pt-BR,ru,sk,tr,zh-Hans -->
|
||||
<File DestinationPath="modules\ar\**" SourcePath="..\..\x64\Release\modules\ar\**"/>
|
||||
<File DestinationPath="modules\bg\**" SourcePath="..\..\x64\Release\modules\bg\**"/>
|
||||
<File DestinationPath="modules\ca\**" SourcePath="..\..\x64\Release\modules\ca\**"/>
|
||||
<File DestinationPath="modules\cs\**" SourcePath="..\..\x64\Release\modules\cs\**"/>
|
||||
<File DestinationPath="modules\de\**" SourcePath="..\..\x64\Release\modules\de\**"/>
|
||||
<File DestinationPath="modules\es\**" SourcePath="..\..\x64\Release\modules\es\**"/>
|
||||
<File DestinationPath="modules\eu-ES\**" SourcePath="..\..\x64\Release\modules\eu-ES\**"/>
|
||||
<File DestinationPath="modules\fr\**" SourcePath="..\..\x64\Release\modules\fr\**"/>
|
||||
<File DestinationPath="modules\he\**" SourcePath="..\..\x64\Release\modules\he\**"/>
|
||||
<File DestinationPath="modules\hu\**" SourcePath="..\..\x64\Release\modules\hu\**"/>
|
||||
<File DestinationPath="modules\it\**" SourcePath="..\..\x64\Release\modules\it\**"/>
|
||||
<File DestinationPath="modules\nb-NO\**" SourcePath="..\..\x64\Release\modules\nb-NO\**"/>
|
||||
<File DestinationPath="modules\nl\**" SourcePath="..\..\x64\Release\modules\nl\**"/>
|
||||
<File DestinationPath="modules\pl\**" SourcePath="..\..\x64\Release\modules\pl\**"/>
|
||||
<File DestinationPath="modules\pt-BR\**" SourcePath="..\..\x64\Release\modules\pt-BR\**"/>
|
||||
<File DestinationPath="modules\ru\**" SourcePath="..\..\x64\Release\modules\ru\**"/>
|
||||
<File DestinationPath="modules\sk\**" SourcePath="..\..\x64\Release\modules\sk\**"/>
|
||||
<File DestinationPath="modules\tr\**" SourcePath="..\..\x64\Release\modules\tr\**"/>
|
||||
<File DestinationPath="modules\zh-Hans\**" SourcePath="..\..\x64\Release\modules\zh-Hans\**"/>
|
||||
</Files>
|
||||
</Package>
|
||||
</PackageFamily>
|
||||
|
@ -2,9 +2,12 @@
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:desktop2="http://schemas.microsoft.com/appx/manifest/desktop/windows10/2"
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5" IgnorableNamespaces="desktop4">
|
||||
<Identity Name="Microsoft.PowerToys" Version="0.15.2.0" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" ProcessorArchitecture="x64" />
|
||||
@ -28,6 +31,12 @@
|
||||
<Application Id="PowerToys" Executable="PowerToys.exe" EntryPoint="Windows.FullTrustApplication">
|
||||
<uap:VisualElements DisplayName="PowerToys (Experimental)" Description="Windows system utilities to maximize productivity" Square150x150Logo="Images\logo150.png" Square44x44Logo="Images\logo44.png" BackgroundColor="transparent" />
|
||||
<Extensions>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="powertoys">
|
||||
<uap:Logo>images\logo.png</uap:Logo>
|
||||
<uap:DisplayName>Powertoys custom protocol</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
<uap5:Extension Category="windows.startupTask" Executable="PowerToys.exe" EntryPoint="Windows.FullTrustApplication">
|
||||
<uap5:StartupTask TaskId="PowerToysStartupTaskID" Enabled="true" DisplayName="PowerToys" />
|
||||
</uap5:Extension>
|
||||
@ -36,6 +45,9 @@
|
||||
<com:ExeServer Executable="modules\PowerRenameUWPUI.exe" DisplayName="PowerRenameUWPUI">
|
||||
<com:Class Id="0440049F-D1DC-4E46-B27B-98393D79486B"/>
|
||||
</com:ExeServer>
|
||||
<com:SurrogateServer DisplayName="ImageResizerExt">
|
||||
<com:Class Id="51B4D7E5-7568-4234-B4BB-47FB3C016A69" Path="modules\ImageResizerExt.dll" ThreadingModel="STA"/>
|
||||
</com:SurrogateServer>
|
||||
</com:ComServer>
|
||||
</com:Extension>
|
||||
<desktop4:Extension Category="windows.fileExplorerContextMenus">
|
||||
@ -46,8 +58,35 @@
|
||||
<desktop5:ItemType Type="Directory">
|
||||
<desktop5:Verb Id="DirectoryPowerRename" Clsid="0440049F-D1DC-4E46-B27B-98393D79486B" />
|
||||
</desktop5:ItemType>
|
||||
<desktop4:ItemType Type="*">
|
||||
<desktop4:Verb Id="ImageResizer" Clsid="51B4D7E5-7568-4234-B4BB-47FB3C016A69" />
|
||||
</desktop4:ItemType>
|
||||
</desktop4:FileExplorerContextMenus>
|
||||
</desktop4:Extension>
|
||||
<uap:Extension Category="windows.fileTypeAssociation">
|
||||
<uap3:FileTypeAssociation Name="mdpreviewhandler" desktop2:AllowSilentDefaultTakeOver="true">
|
||||
<uap:SupportedFileTypes>
|
||||
<uap:FileType>.md</uap:FileType>
|
||||
</uap:SupportedFileTypes>
|
||||
<desktop2:DesktopPreviewHandler Clsid="E0907A95-6F9A-4D1B-A97A-7D9D2648881E"/>
|
||||
</uap3:FileTypeAssociation>
|
||||
</uap:Extension>
|
||||
<uap:Extension Category="windows.fileTypeAssociation">
|
||||
<uap3:FileTypeAssociation Name="svgpreviewhandler" desktop2:AllowSilentDefaultTakeOver="true">
|
||||
<uap:SupportedFileTypes>
|
||||
<uap:FileType>.svg</uap:FileType>
|
||||
</uap:SupportedFileTypes>
|
||||
<desktop2:DesktopPreviewHandler Clsid="74619BDA-A66B-451D-864C-A7726F5FE650"/>
|
||||
</uap3:FileTypeAssociation>
|
||||
</uap:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:SurrogateServer DisplayName="Preview Handler" AppId="E39A92FE-D89A-417B-9B9D-F0B6BD564B36" SystemSurrogate="PreviewHost">
|
||||
<com:Class Id="74619BDA-A66B-451D-864C-A7726F5FE650" Path="modules\powerpreview.dll" ThreadingModel="Both"/>
|
||||
<com:Class Id="E0907A95-6F9A-4D1B-A97A-7D9D2648881E" Path="modules\powerpreview.dll" ThreadingModel="Both"/>
|
||||
</com:SurrogateServer>
|
||||
</com:ComServer>
|
||||
</com:Extension>
|
||||
<Extension Category="windows.backgroundTasks" EntryPoint="PowerToysNotifications.BackgroundHandler">
|
||||
<BackgroundTasks>
|
||||
<Task Type="general" />
|
||||
|
@ -1 +1,13 @@
|
||||
makeappx build /v /overwrite /f PackagingLayout.xml /id "PowerToys-x64" /op bin\
|
||||
param (
|
||||
[bool]$debug = 0
|
||||
)
|
||||
|
||||
$PackagingLayoutFile = "PackagingLayout.xml"
|
||||
|
||||
if ($debug) {
|
||||
(Get-Content $PackagingLayoutFile) `
|
||||
-replace 'x64\\Release\\', 'x64\Debug\' `
|
||||
| Out-File -Encoding utf8 "$env:temp\$PackagingLayoutFile"
|
||||
$PackagingLayoutFile = "$env:temp\$PackagingLayoutFile"
|
||||
}
|
||||
makeappx build /v /overwrite /f $PackagingLayoutFile /id "PowerToys-x64" /op bin\
|
||||
|
BIN
installer/MSIX/registry.dat
Normal file
BIN
installer/MSIX/registry.reg
Normal file
@ -15,7 +15,6 @@
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||
<DefineConstants>Debug</DefineConstants>
|
||||
<OutputPath>$(Platform)\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
|
||||
</PropertyGroup>
|
||||
|
@ -22,7 +22,7 @@
|
||||
Property="PREVIOUSVERSIONSINSTALLED"
|
||||
IncludeMinimum="yes" IncludeMaximum="no" />
|
||||
</Upgrade>
|
||||
|
||||
|
||||
<MediaTemplate EmbedCab="yes" />
|
||||
|
||||
<Property Id="WINDOWSBUILDNUMBER" Secure="yes">
|
||||
@ -32,11 +32,12 @@
|
||||
<![CDATA[(WINDOWSBUILDNUMBER >= 17134)]]>
|
||||
</Condition>
|
||||
|
||||
<Icon Id="powertoys.ico" SourceFile="$(var.BinX64Dir)\svgs\icon.ico"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="powertoys.ico" />
|
||||
<Icon Id="powertoys.exe" SourceFile="$(var.BinX64Dir)\svgs\icon.ico"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="powertoys.exe" />
|
||||
<Feature Id="CoreFeature" Title="PowerToys" AllowAdvertise="no" Absent="disallow" TypicalDefault="install"
|
||||
Description="Contains the Shortcut Guide and Fancy Zones features.">
|
||||
<ComponentGroupRef Id="CoreComponents" />
|
||||
<ComponentGroupRef Id="ResourcesComponents" />
|
||||
</Feature>
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
|
||||
<UI>
|
||||
@ -55,7 +56,7 @@
|
||||
</UI>
|
||||
<WixVariable Id="WixUIBannerBmp" Value="$(var.ProjectDir)\Bitmaps\banner.bmp" />
|
||||
<WixVariable Id="WixUIDialogBmp" Value="$(var.ProjectDir)\Bitmaps\dialog.bmp" />
|
||||
<WixVariable Id="WixUILicenseRtf" Value="$(var.RepoDir)\License.rtf" />
|
||||
<WixVariable Id="WixUILicenseRtf" Value="$(var.RepoDir)\installer\License.rtf" />
|
||||
<Property Id="INSTALLSTARTMENUSHORTCUT" Value="1"/>
|
||||
<Property Id="CREATESCHEDULEDTASK" Value="1"/>
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
|
||||
@ -66,6 +67,9 @@
|
||||
<Property Id ="EXISTINGPOWERRENAMEEXTPATH">
|
||||
<RegistrySearch Id="ExistingExtPath" Root="HKCR" Key="CLSID\{0440049F-D1DC-4E46-B27B-98393D79486B}\InprocServer32" Type="raw"/>
|
||||
</Property>
|
||||
<Property Id ="EXISTINGIMAGERESIZERPATH">
|
||||
<RegistrySearch Id="ExistingImageResizerPath" Root="HKCU" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32" Type="raw"/>
|
||||
</Property>
|
||||
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="SetRegisterPowerToysSchTaskParam" Before="RegisterPowerToysSchTask" />
|
||||
@ -144,7 +148,7 @@
|
||||
BinaryKey="PTCustomActions"
|
||||
DllEntry="TelemetryLogUninstallFailCA"
|
||||
/>
|
||||
|
||||
|
||||
<CustomAction Id="TelemetryLogRepairCancel"
|
||||
Return="ignore"
|
||||
Impersonate="yes"
|
||||
@ -161,9 +165,9 @@
|
||||
|
||||
<!-- Close 'PowerToys.exe' before uninstall-->
|
||||
<Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable" />
|
||||
<!-- Restart explorer.exe if we detect existing powerrenameext.dll installation -->
|
||||
<!-- Restart explorer.exe if we detect existing PowerRenameExt.dll or ImageResizerExt.dll installation -->
|
||||
<util:CloseApplication Target="explorer.exe" RebootPrompt="no" TerminateProcess="0">
|
||||
EXISTINGPOWERRENAMEEXTPATH
|
||||
EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH
|
||||
</util:CloseApplication>
|
||||
<util:CloseApplication CloseMessage="yes" Target="PowerToys.exe" ElevatedCloseMessage="yes" RebootPrompt="no" TerminateProcess="0" />
|
||||
</Product>
|
||||
@ -177,7 +181,25 @@
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="PowerToys">
|
||||
<Directory Id="SvgsInstallFolder" Name="svgs"/>
|
||||
<Directory Id="ModulesInstallFolder" Name="modules"/>
|
||||
<Directory Id="ModulesInstallFolder" Name="modules">
|
||||
<!-- Resource file directories -->
|
||||
<?foreach Language in ar;bg;ca;cs;de;es;eu-ES;fr;he;hu;it;nb-NO;nl;pl;pt-BR;ru;sk;tr;zh-Hans?>
|
||||
<!--NB: Ids can't contain hyphens-->
|
||||
<?if $(var.Language) = eu-ES?>
|
||||
<?define IdSafeLanguage = eu_ES?>
|
||||
<?elseif $(var.Language) = nb-NO?>
|
||||
<?define IdSafeLanguage = nb_NO?>
|
||||
<?elseif $(var.Language) = pt-BR?>
|
||||
<?define IdSafeLanguage = pt_BR?>
|
||||
<?elseif $(var.Language) = zh-Hans?>
|
||||
<?define IdSafeLanguage = zh_Hans?>
|
||||
<?else?>
|
||||
<?define IdSafeLanguage = $(var.Language)?>
|
||||
<?endif?>
|
||||
<Directory Id="Resources$(var.IdSafeLanguage)Folder" Name="$(var.Language)" />
|
||||
<?undef IdSafeLanguage?>
|
||||
<?endforeach?>
|
||||
</Directory>
|
||||
<Directory Id="SettingsHtmlInstallFolder" Name="settings-html">
|
||||
<Directory Id="SettingsHtmlDistInstallFolder" Name="dist"/>
|
||||
</Directory>
|
||||
@ -206,7 +228,7 @@
|
||||
Description="PowerToys - Windows system utilities to maximize productivity"
|
||||
Directory="ApplicationProgramsFolder"
|
||||
WorkingDirectory="INSTALLFOLDER"
|
||||
Icon="powertoys.ico"
|
||||
Icon="powertoys.exe"
|
||||
IconIndex="0"
|
||||
Advertise="yes">
|
||||
<ShortcutProperty Key="System.AppUserModel.ID" Value="Microsoft.PowerToysWin32"/>
|
||||
@ -214,8 +236,20 @@
|
||||
</Shortcut>
|
||||
</File>
|
||||
|
||||
<RegistryKey Root="HKCR" Key="powertoys" Action="createAndRemoveOnUninstall">
|
||||
<RegistryValue Type="string" Name="URL Protocol" Value=""/>
|
||||
<RegistryValue Type="string" Value="URL:PowerToys custom internal URI protocol"/>
|
||||
<RegistryKey Key="DefaultIcon">
|
||||
<RegistryValue Type="string" Value="PowerToys.exe" />
|
||||
</RegistryKey>
|
||||
<RegistryKey Key="shell\open\command">
|
||||
<RegistryValue Type="string" Value=""[INSTALLFOLDER]PowerToys.exe" "%1"" />
|
||||
</RegistryKey>
|
||||
</RegistryKey>
|
||||
|
||||
<RemoveFolder Id="DeleteShortcutFolder" Directory="ApplicationProgramsFolder" On="uninstall" />
|
||||
</Component>
|
||||
|
||||
<Component Id="settings_exe" Guid="A5A461A9-7097-4CBA-9D39-3DBBB6B7B80C" Win64="yes">
|
||||
<File Id="PowerToysSettings.exe" KeyPath="yes" Checksum="yes" />
|
||||
</Component>
|
||||
@ -223,7 +257,7 @@
|
||||
<File Id="Notifications.dll" KeyPath="yes" Checksum="yes" />
|
||||
</Component>
|
||||
<Component Id="License_rtf" Guid="3E5AE43B-CFB4-449B-A346-94CAAFF3312E" Win64="yes">
|
||||
<File Source="$(var.RepoDir)\License.rtf" Id="License.rtf" KeyPath="yes" />
|
||||
<File Source="$(var.RepoDir)\installer\License.rtf" Id="License.rtf" KeyPath="yes" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="SvgsInstallFolder" FileSource="$(var.BinX64Dir)\svgs\">
|
||||
@ -274,7 +308,167 @@
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
|
||||
</RegistryKey>
|
||||
<RegistryKey Root="HKCR" Key="AllFileSystemObjects\ShellEx\ContextMenuHandlers\PowerRenameExt">
|
||||
<RegistryValue Type="string" Value="{0440049F-D1DC-4E46-B27B-98393D79486B}"/>
|
||||
<RegistryValue Type="string" Value="{0440049F-D1DC-4E46-B27B-98393D79486B}"/>
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
|
||||
<Component Id="Module_WindowWalker" Guid="0F96981C-5D36-4467-9515-71FB0CE72F6F" Win64="yes">
|
||||
<File Source="$(var.BinX64Dir)\modules\WindowWalker.exe" />
|
||||
<File Source="$(var.BinX64Dir)\modules\WindowWalker.dll" />
|
||||
<File Source="$(var.BinX64Dir)\modules\MaterialDesignColors.dll" />
|
||||
<File Source="$(var.BinX64Dir)\modules\MaterialDesignThemes.Wpf.dll" />
|
||||
</Component>
|
||||
|
||||
<Component Id="Module_ImageResizer" Guid="96E63289-759C-4A73-A56B-EE7429932F72" Win64="yes">
|
||||
<File Source="$(var.BinX64Dir)\modules\ImageResizer.exe">
|
||||
<netfx:NativeImage Id="ImageResizer.exe" Platform="all" Priority="0" />
|
||||
</File>
|
||||
<File Source="$(var.BinX64Dir)\modules\GalaSoft.MvvmLight.dll" />
|
||||
<File Source="$(var.BinX64Dir)\modules\GalaSoft.MvvmLight.Platform.dll" />
|
||||
<File Source="$(var.BinX64Dir)\modules\GalaSoft.MvvmLight.Extras.dll" />
|
||||
<File Source="$(var.BinX64Dir)\modules\System.Windows.Interactivity.dll">
|
||||
<!-- NB: Needed since it's only referenced in XAML. -->
|
||||
<netfx:NativeImage Id="Interactivity" Platform="all" Priority="0"/>
|
||||
</File>
|
||||
<File Source="$(var.BinX64Dir)\modules\Newtonsoft.Json.dll" />
|
||||
<File Source="$(var.BinX64Dir)\modules\ImageResizerExt.dll" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="Module_ImageResizer_Registry" Guid="8B593E2C-2D9B-4EBC-93F7-A2B69707DAC9" Win64="yes">
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32">
|
||||
<RegistryValue Value="[ModulesInstallFolder]ImageResizerExt.dll" Type="string" />
|
||||
<RegistryValue Name="ThreadingModel" Value="Apartment" Type="string" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for the drag and drop handler -->
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\Directory\ShellEx\DragDropHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<!-- Registry Keys for the context menu handler for each of the following image formats: bmp, dib, gif, jfif, jpe, jpeg, jpg, jxr, png, rle, tif, tiff, wdp -->
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.bmp\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.dib\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.gif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jfif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jpe\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jpeg\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jpg\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jxr\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.png\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.rle\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.tif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.tiff\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.wdp\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
</Component>
|
||||
<Component Id="Module_PowerPreview" Guid="FF1700D5-1B07-4E07-9A62-4D206645EEA9" Win64="yes">
|
||||
<!-- Component to include PowerPreview Module Source dll's -->
|
||||
<!-- File to include PowerPreview Module native dll -->
|
||||
<File Source="$(var.BinX64Dir)\modules\powerpreview.dll" KeyPath="yes" />
|
||||
<!-- File to include common library used by preview handlers -->
|
||||
<File Source="$(var.BinX64Dir)\modules\PreviewHandlerCommon.dll" />
|
||||
<!-- File to include dll for Svg Preview Handler -->
|
||||
<File Source="$(var.BinX64Dir)\modules\SvgPreviewHandler.dll" />
|
||||
<!-- Files to include dll's for Markdown Preview Handler and it's dependencies -->
|
||||
<File Source="$(var.BinX64Dir)\modules\MarkdownPreviewHandler.dll" />
|
||||
<File Source="$(var.BinX64Dir)\modules\Markdig.Signed.dll" />
|
||||
<File Source="$(var.BinX64Dir)\modules\HtmlAgilityPack.dll" />
|
||||
</Component>
|
||||
<Component Id="Module_PowerPreview_PerUserRegistry" Guid="CD90ADC0-7CD5-4A62-B0AF-23545C1E6DD3" Win64="yes">
|
||||
<!-- Added a separate component for Per-User registry changes -->
|
||||
<!-- Registry Key for Class Registration of Svg Preview Handler -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{ddee2b8a-6807-48a6-bb20-2338174ff779}">
|
||||
<RegistryValue Type="string" Value="SvgPreviewHandler.SvgPreviewHandler" />
|
||||
<RegistryValue Type="string" Name="DisplayName" Value="Svg Preview Handler" />
|
||||
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
|
||||
<RegistryValue Type="string" Key="Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value=""/>
|
||||
<RegistryValue Type="string" Key="InprocServer32" Value="mscoree.dll" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="Assembly" Value="SvgPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="Class" Value="SvgPreviewHandler.SvgPreviewHandler" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Both" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="CodeBase" Value="file:///[ModulesInstallFolder]SvgPreviewHandler.dll" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Assembly" Value="SvgPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Class" Value="SvgPreviewHandler.SvgPreviewHandler" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="RuntimeVersion" Value="v4.0.30319" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[ModulesInstallFolder]SvgPreviewHandler.dll" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for Class Registration of Markdown Preview Handler -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{45769bcc-e8fd-42d0-947e-02beef77a1f5}">
|
||||
<RegistryValue Type="string" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
|
||||
<RegistryValue Type="string" Name="DisplayName" Value="Markdown Preview Handler" />
|
||||
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
|
||||
<RegistryValue Type="string" Key="Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Value="mscoree.dll" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="Assembly" Value="MarkdownPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="Class" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Both" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="CodeBase" Value="file:///[ModulesInstallFolder]MarkdownPreviewHandler.dll" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Assembly" Value="MarkdownPreviewHandler, Version=$(var.Version).0, Culture=neutral" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Class" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="RuntimeVersion" Value="v4.0.30319" />
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[ModulesInstallFolder]MarkdownPreviewHandler.dll" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for AppID registration -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\AppID\{CF142243-F059-45AF-8842-DBBE9783DB14}">
|
||||
<RegistryValue Type="expandable" Name="DllSurrogate" Value="%SystemRoot%\system32\prevhost.exe" />
|
||||
</RegistryKey>
|
||||
<!-- Add Svg preview handler to preview handlers list -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
|
||||
<RegistryValue Type="string" Name="{ddee2b8a-6807-48a6-bb20-2338174ff779}" Value="Svg Preview Handler" />
|
||||
</RegistryKey>
|
||||
<!-- Add Markdown preview handler to preview handlers list -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
|
||||
<RegistryValue Type="string" Name="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" Value="Markdown Preview Handler" />
|
||||
</RegistryKey>
|
||||
<!-- Add file type association for Svg Preview Handler -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\.svg\shellex">
|
||||
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{ddee2b8a-6807-48a6-bb20-2338174ff779}" />
|
||||
</RegistryKey>
|
||||
<!-- Add file type association for Markdown Preview Handler -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\.md\shellex">
|
||||
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" />
|
||||
</RegistryKey>
|
||||
<!-- Update Key to use IE11 for prevhost.exe -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
|
||||
<RegistryValue Type="integer" Name="prevhost.exe" Value="11000" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
@ -306,7 +500,7 @@
|
||||
Description="PowerToys - Windows system utilities to maximize productivity"
|
||||
Target="[!PowerToys.exe]"
|
||||
WorkingDirectory="INSTALLFOLDER"
|
||||
Icon="powertoys.ico"
|
||||
Icon="powertoys.exe"
|
||||
Directory="DesktopFolder"/>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
@ -323,10 +517,38 @@
|
||||
<ComponentRef Id="Module_FancyZones" />
|
||||
<ComponentRef Id="DesktopShortcut" />
|
||||
<ComponentRef Id="Module_PowerRename" />
|
||||
<ComponentRef Id="Module_ImageResizer" />
|
||||
<ComponentRef Id="Module_ImageResizer_Registry" />
|
||||
<ComponentRef Id="Module_PowerPreview" />
|
||||
<ComponentRef Id="Module_PowerPreview_PerUserRegistry" />
|
||||
<ComponentRef Id="Module_WindowWalker" />
|
||||
<ComponentRef Id="settings_exe" />
|
||||
<ComponentRef Id="settings_html" />
|
||||
<ComponentRef Id="settings_dark_html" />
|
||||
<ComponentRef Id="settings_js_bundle" />
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
<Fragment>
|
||||
<ComponentGroup Id="ResourcesComponents">
|
||||
<!-- Components for adding resource files -->
|
||||
<?foreach Language in ar;bg;ca;cs;de;es;eu-ES;fr;he;hu;it;nb-NO;nl;pl;pt-BR;ru;sk;tr;zh-Hans?>
|
||||
<!--NB: Ids can't contain hyphens-->
|
||||
<?if $(var.Language) = eu-ES?>
|
||||
<?define IdSafeLanguage = eu_ES?>
|
||||
<?elseif $(var.Language) = nb-NO?>
|
||||
<?define IdSafeLanguage = nb_NO?>
|
||||
<?elseif $(var.Language) = pt-BR?>
|
||||
<?define IdSafeLanguage = pt_BR?>
|
||||
<?elseif $(var.Language) = zh-Hans?>
|
||||
<?define IdSafeLanguage = zh_Hans?>
|
||||
<?else?>
|
||||
<?define IdSafeLanguage = $(var.Language)?>
|
||||
<?endif?>
|
||||
<Component Id="Resources_$(var.IdSafeLanguage)_Component" Directory="Resources$(var.IdSafeLanguage)Folder">
|
||||
<File Id="Resources_ImageResizer_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)\modules\$(var.Language)\ImageResizer.resources.dll" />
|
||||
</Component>
|
||||
<?undef IdSafeLanguage?>
|
||||
<?endforeach?>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
@ -221,22 +221,25 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
|
||||
}
|
||||
|
||||
// Run the task with the highest available privileges.
|
||||
hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
|
||||
hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA);
|
||||
pPrincipal->Release();
|
||||
ExitOnFailure(hr, "Cannot put principal run level: %x", hr);
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Save the task in the PowerToys folder.
|
||||
hr = pTaskFolder->RegisterTaskDefinition(
|
||||
_bstr_t(wstrTaskName.c_str()),
|
||||
pTask,
|
||||
TASK_CREATE_OR_UPDATE,
|
||||
_variant_t(username_domain),
|
||||
_variant_t(),
|
||||
TASK_LOGON_INTERACTIVE_TOKEN,
|
||||
_variant_t(L""),
|
||||
&pRegisteredTask);
|
||||
ExitOnFailure(hr, "Error saving the Task : %x", hr);
|
||||
{
|
||||
_variant_t SDDL_FULL_ACCESS_FOR_EVERYONE = L"D:(A;;FA;;;WD)";
|
||||
hr = pTaskFolder->RegisterTaskDefinition(
|
||||
_bstr_t(wstrTaskName.c_str()),
|
||||
pTask,
|
||||
TASK_CREATE_OR_UPDATE,
|
||||
_variant_t(username_domain),
|
||||
_variant_t(),
|
||||
TASK_LOGON_INTERACTIVE_TOKEN,
|
||||
SDDL_FULL_ACCESS_FOR_EVERYONE,
|
||||
&pRegisteredTask);
|
||||
ExitOnFailure(hr, "Error saving the Task : %x", hr);
|
||||
}
|
||||
|
||||
WcaLog(LOGMSG_STANDARD, "Scheduled task created for the current user.");
|
||||
|
||||
|
@ -20,7 +20,7 @@ For the first-time installation, you'll need to generate a self-signed certifica
|
||||
**Note:** if you delete the folder, you will have to regenerate the key
|
||||
|
||||
#### Elevate `Developer PowerShell for VS` permissions due to unsigned file
|
||||
`msix_reinstall.ps1` is unsigned, you'll need to elevate your prompt.
|
||||
`reinstall_msix.ps1` is unsigned, you'll need to elevate your prompt.
|
||||
1. Open `Developer PowerShell for VS` as admin
|
||||
2. Run `Set-ExecutionPolicy -executionPolicy Unrestricted`
|
||||
|
||||
@ -31,10 +31,10 @@ In order to install the MSIX package without using the Microsoft Store, sideload
|
||||
1. Make sure you've built the `Release` configuration of `powertoys.sln`
|
||||
2. Open `Developer PowerShell for VS`
|
||||
3. Navigate to your repo's `installer\MSIX`
|
||||
4. Run `.\msix_reinstall.ps1` from the devenv powershell
|
||||
4. Run `.\reinstall_msix.ps1` from the devenv powershell
|
||||
|
||||
### What msix_reinstall.ps1 does
|
||||
`msix_reinstall.ps1` removes the current PowerToys installation, restarts explorer.exe (to update PowerRename shell extension), builds `PowerToys-x64.msix` package, signs it with a PowerToys_TemporaryKey.pfx, and finally installs it.
|
||||
### What reinstall_msix.ps1 does
|
||||
`reinstall_msix.ps1` removes the current PowerToys installation, restarts explorer.exe (to update PowerRename and ImageResizer shell extension), builds `PowerToys-x64.msix` package, signs it with a PowerToys_TemporaryKey.pfx, and finally installs it.
|
||||
|
||||
## Cleanup - Removing all .msi/.msix PowerToys installations
|
||||
```ps
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Version>0.15.3</Version>
|
||||
<Version>0.16.2</Version>
|
||||
<DefineConstants>Version=$(Version);</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -73,6 +73,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>../;$(IncludePath)</IncludePath>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
@ -80,6 +81,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>../;$(IncludePath)</IncludePath>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
|
@ -433,7 +433,24 @@ namespace UnitTestsCommonLib
|
||||
compareJsons(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD (SettingsAddStringMultiline)
|
||||
TEST_METHOD(SettingsAddLargeHeader)
|
||||
{
|
||||
const auto value = L"large header sample text ";
|
||||
|
||||
Settings settings(nullptr, m_moduleName);
|
||||
settings.add_header_szLarge(m_defaultSettingsName, m_defaultSettingsDescription, value);
|
||||
|
||||
auto expected = m_defaultSettingsJson;
|
||||
auto expectedProperties = createSettingsProperties(L"header_large");
|
||||
expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
|
||||
expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
|
||||
|
||||
const auto actual = json::JsonObject::Parse(settings.serialize());
|
||||
|
||||
compareJsons(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(SettingsAddStringMultiline)
|
||||
{
|
||||
const auto value = L"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident,\nsunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||
|
||||
|
@ -48,9 +48,11 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
@ -91,10 +93,11 @@
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>RuntimeObject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>RuntimeObject.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="UnitTestsCommon.cpp" />
|
||||
<ClCompile Include="UnitTestsVersionHelper.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
|
@ -24,6 +24,9 @@
|
||||
<ClCompile Include="UnitTestsVersionHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UnitTestsCommon.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
|
57
src/common/UnitTests-CommonLib/UnitTestsCommon.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "pch.h"
|
||||
#include "common.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace UnitTestsCommon
|
||||
{
|
||||
TEST_CLASS (CommonUtils)
|
||||
{
|
||||
std::vector<std::wstring> what_global{
|
||||
L"TELEGRAM",
|
||||
L"SUBLIME TEXT",
|
||||
L"PROGRAM",
|
||||
L"TEXT",
|
||||
};
|
||||
|
||||
TEST_METHOD (FindAppNameInPathTest1)
|
||||
{
|
||||
std::wstring where(L"C:\\USERS\\GUEST\\APPDATA\\ROAMING\\TELEGRAM DESKTOP\\TELEGRAM.EXE");
|
||||
bool ans = find_app_name_in_path(where, what_global);
|
||||
Assert::IsTrue(ans);
|
||||
}
|
||||
TEST_METHOD (FindAppNameInPathTest2)
|
||||
{
|
||||
std::vector<std::wstring> what{
|
||||
L"NOTEPAD",
|
||||
};
|
||||
std::wstring where(L"C:\\PROGRAM FILES\\NOTEPAD++\\NOTEPAD++.EXE");
|
||||
bool ans = find_app_name_in_path(where, what);
|
||||
Assert::IsTrue(ans);
|
||||
}
|
||||
TEST_METHOD (FindAppNameInPathTest3)
|
||||
{
|
||||
std::vector<std::wstring> what{
|
||||
L"NOTEPAD++.EXE",
|
||||
};
|
||||
std::wstring where(L"C:\\PROGRAM FILES\\NOTEPAD++\\NOTEPAD++.EXE");
|
||||
bool ans = find_app_name_in_path(where, what);
|
||||
Assert::IsTrue(ans);
|
||||
}
|
||||
TEST_METHOD (FindAppNameInPathTest4)
|
||||
{
|
||||
std::wstring where(L"C:\\PROGRAM FILES\\SUBLIME TEXT 3\\SUBLIME_TEXT.EXE");
|
||||
bool ans = find_app_name_in_path(where, what_global);
|
||||
Assert::IsFalse(ans);
|
||||
}
|
||||
TEST_METHOD (FindAppNameInPathTest5)
|
||||
{
|
||||
std::vector<std::wstring> what{
|
||||
L"NOTEPAD.EXE",
|
||||
};
|
||||
std::wstring where(L"C:\\PROGRAM FILES\\NOTEPAD++\\NOTEPAD++.EXE");
|
||||
bool ans = find_app_name_in_path(where, what);
|
||||
Assert::IsFalse(ans);
|
||||
}
|
||||
};
|
||||
}
|
@ -50,45 +50,51 @@ namespace UnitTestsVersionHelper
|
||||
}
|
||||
TEST_METHOD (whenMajorVersionIsGreaterComparationOperatorShouldReturnProperValue)
|
||||
{
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper lhs(MAJOR_VERSION_0 + 1, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
|
||||
Assert::IsTrue(lhs > rhs);
|
||||
}
|
||||
TEST_METHOD (whenMajorVersionIsLesserComparationOperatorShouldReturnProperValue)
|
||||
{
|
||||
VersionHelper rhs(MAJOR_VERSION_0 + 1, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper lhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper rhs(MAJOR_VERSION_0 + 1, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
|
||||
Assert::IsFalse(lhs > rhs);
|
||||
}
|
||||
TEST_METHOD (whenMajorVersionIsEqualComparationOperatorShouldCompareMinorVersionValue)
|
||||
{
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12 - 1, REVISION_VERSION_0);
|
||||
|
||||
VersionHelper lhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12 - 1, REVISION_VERSION_0);
|
||||
|
||||
Assert::IsTrue(lhs > rhs);
|
||||
}
|
||||
TEST_METHOD (whenMajorVersionIsEqualComparationOperatorShouldCompareMinorVersionValue2)
|
||||
{
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper lhs(MAJOR_VERSION_0, MINOR_VERSION_12 - 1, REVISION_VERSION_0);
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
|
||||
Assert::IsFalse(lhs > rhs);
|
||||
}
|
||||
|
||||
TEST_METHOD (whenMajorAndMinorVersionIsEqualComparationOperatorShouldCompareRevisionValue)
|
||||
{
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper lhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0 + 1);
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
|
||||
Assert::IsTrue(lhs > rhs);
|
||||
}
|
||||
TEST_METHOD (whenMajorAndMinorVersionIsEqualComparationOperatorShouldCompareRevisionValue2)
|
||||
{
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0 + 1);
|
||||
VersionHelper lhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0 + 1);
|
||||
|
||||
Assert::IsFalse(lhs > rhs);
|
||||
}
|
||||
TEST_METHOD (whenMajorMinorAndRevisionIsEqualGreaterThanOperatorShouldReturnFalse)
|
||||
{
|
||||
VersionHelper lhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
VersionHelper rhs(MAJOR_VERSION_0, MINOR_VERSION_12, REVISION_VERSION_0);
|
||||
|
||||
Assert::IsFalse(lhs > rhs);
|
||||
}
|
||||
|
@ -27,37 +27,3 @@ VersionHelper::VersionHelper(int major, int minor, int revision) :
|
||||
revision(revision)
|
||||
{
|
||||
}
|
||||
|
||||
bool VersionHelper::operator>(const VersionHelper& rhs)
|
||||
{
|
||||
if (major < rhs.major)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (major > rhs.major)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (minor < rhs.minor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (minor > rhs.minor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (revision < rhs.revision)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <compare>
|
||||
|
||||
struct VersionHelper
|
||||
{
|
||||
VersionHelper(std::string str);
|
||||
VersionHelper(int major, int minor, int revision);
|
||||
|
||||
bool operator>(const VersionHelper& rhs);
|
||||
auto operator<=>(const VersionHelper&) const = default;
|
||||
|
||||
int major;
|
||||
int minor;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "version.h"
|
||||
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
#pragma comment(lib, "shlwapi.lib")
|
||||
|
||||
namespace localized_strings
|
||||
{
|
||||
@ -442,6 +443,7 @@ bool run_elevated(const std::wstring& file, const std::wstring& params)
|
||||
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||
exec_info.lpDirectory = 0;
|
||||
exec_info.hInstApp = 0;
|
||||
exec_info.nShow = SW_SHOWDEFAULT;
|
||||
|
||||
if (ShellExecuteExW(&exec_info))
|
||||
{
|
||||
@ -714,3 +716,18 @@ bool check_user_is_admin()
|
||||
freeMemory(pSID, pGroupInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what)
|
||||
{
|
||||
for (const auto& row : what)
|
||||
{
|
||||
const auto pos = where.rfind(row);
|
||||
const auto last_slash = where.rfind('\\');
|
||||
//Check that row occurs in where, and its last occurrence contains in itself the first character after the last backslash.
|
||||
if (pos != std::wstring::npos && pos <= last_slash + 1 && pos + row.length() > last_slash)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// Returns RECT with positions of the minmize/maximize buttons of the given window.
|
||||
// Does not always work, since some apps draw custom toolbars.
|
||||
@ -77,6 +78,9 @@ bool run_same_elevation(const std::wstring& file, const std::wstring& params);
|
||||
// Returns true if the current process is running from administrator account
|
||||
bool check_user_is_admin();
|
||||
|
||||
//Returns true when one or more strings from vector found in string
|
||||
bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what);
|
||||
|
||||
// Get the executable path or module name for modern apps
|
||||
std::wstring get_process_path(DWORD pid) noexcept;
|
||||
// Get the executable path or module name for modern apps
|
||||
|
@ -8,12 +8,7 @@
|
||||
<HeaderLines Include="#define VERSION_MINOR $(Version.Split('.')[1])" />
|
||||
<HeaderLines Include="#define VERSION_REVISION $(Version.Split('.')[2])" />
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile
|
||||
File="Generated Files\version_gen.h"
|
||||
Lines="@(HeaderLines)"
|
||||
Overwrite="true"
|
||||
Encoding="Unicode"
|
||||
WriteOnlyWhenDifferent="true" />
|
||||
<WriteLinesToFile File="Generated Files\version_gen.h" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
|
||||
</Target>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
@ -34,6 +29,9 @@
|
||||
<ProjectName>common</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ImportGroup Label="Shared">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
@ -61,9 +59,11 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
@ -167,7 +167,16 @@
|
||||
<ClCompile Include="window_helpers.cpp" />
|
||||
<ClCompile Include="winstore.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.190716.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
@ -9,9 +9,14 @@ namespace json
|
||||
{
|
||||
try
|
||||
{
|
||||
std::wifstream file(file_name.data(), std::ios::binary);
|
||||
using isbi = std::istreambuf_iterator<wchar_t>;
|
||||
return JsonValue::Parse(std::wstring{ isbi{ file }, isbi{} }).GetObjectW();
|
||||
std::ifstream file(file_name.data(), std::ios::binary);
|
||||
if (file.is_open())
|
||||
{
|
||||
using isbi = std::istreambuf_iterator<char>;
|
||||
std::string obj_str{ isbi{ file }, isbi{} };
|
||||
return JsonValue::Parse(winrt::to_hstring(obj_str)).GetObjectW();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -21,6 +26,7 @@ namespace json
|
||||
|
||||
void to_file(std::wstring_view file_name, const JsonObject& obj)
|
||||
{
|
||||
std::wofstream{ file_name.data(), std::ios::binary } << obj.Stringify().c_str();
|
||||
std::wstring obj_str{ obj.Stringify().c_str() };
|
||||
std::ofstream{ file_name.data(), std::ios::binary } << winrt::to_string(obj_str);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include <winrt/Windows.Web.Http.h>
|
||||
#include <winrt/Windows.Web.Http.Headers.h>
|
||||
#include <winrt/Windows.Management.Deployment.h>
|
||||
|
||||
#include "VersionHelper.h"
|
||||
|
||||
@ -23,6 +24,8 @@ namespace
|
||||
const wchar_t* DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH = L"delete_previous_powertoys_confirm";
|
||||
const wchar_t* USER_AGENT = L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)";
|
||||
const wchar_t* LATEST_RELEASE_ENDPOINT = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
|
||||
const wchar_t* MSIX_PACKAGE_NAME = L"Microsoft.PowerToys";
|
||||
const wchar_t* MSIX_PACKAGE_PUBLISHER = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
|
||||
}
|
||||
|
||||
namespace localized_strings
|
||||
@ -110,20 +113,45 @@ std::future<std::optional<new_version_download_info>> check_for_new_github_relea
|
||||
winrt::Windows::Foundation::Uri release_page_uri{ json_body.GetNamedString(L"html_url") };
|
||||
|
||||
VersionHelper github_version(winrt::to_string(new_version));
|
||||
|
||||
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
|
||||
if (current_version > github_version)
|
||||
if (github_version > current_version)
|
||||
{
|
||||
co_return std::nullopt;
|
||||
co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str() };
|
||||
}
|
||||
else
|
||||
{
|
||||
co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str() };
|
||||
co_return std::nullopt;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
co_return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::future<bool> uninstall_previous_msix_version_async()
|
||||
{
|
||||
winrt::Windows::Management::Deployment::PackageManager package_manager;
|
||||
|
||||
try
|
||||
{
|
||||
auto packages = package_manager.FindPackagesForUser({}, MSIX_PACKAGE_NAME, MSIX_PACKAGE_PUBLISHER);
|
||||
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
|
||||
for (auto package : packages)
|
||||
{
|
||||
VersionHelper msix_version(package.Id().Version().Major, package.Id().Version().Minor, package.Id().Version().Revision);
|
||||
|
||||
if (msix_version < current_version)
|
||||
{
|
||||
co_await package_manager.RemovePackageAsync(package.Id().FullName());
|
||||
co_return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
co_return false;
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ std::wstring get_msi_package_path();
|
||||
bool uninstall_msi_version(const std::wstring& package_path);
|
||||
bool offer_msi_uninstallation();
|
||||
|
||||
std::future<bool> uninstall_previous_msix_version_async();
|
||||
|
||||
struct new_version_download_info
|
||||
{
|
||||
winrt::Windows::Foundation::Uri release_page_uri;
|
||||
|
@ -73,6 +73,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>../../;$(IncludePath)</IncludePath>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
@ -80,6 +81,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>../../;$(IncludePath)</IncludePath>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
|
@ -3,7 +3,9 @@
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
#pragma warning (disable: 5205)
|
||||
#include <winrt/base.h>
|
||||
#pragma warning (default: 5205)
|
||||
#include <Windows.h>
|
||||
#include <MsiQuery.h>
|
||||
#include <Shlwapi.h>
|
||||
|
@ -145,10 +145,10 @@ void notifications::register_background_toast_handler()
|
||||
}
|
||||
}
|
||||
|
||||
void notifications::show_toast(std::wstring_view message)
|
||||
void notifications::show_toast(std::wstring message, toast_params params)
|
||||
{
|
||||
// The toast won't be actually activated in the background, since it doesn't have any buttons
|
||||
show_toast_with_activations(message, {}, {});
|
||||
show_toast_with_activations(std::move(message), {}, {}, std::move(params));
|
||||
}
|
||||
|
||||
inline void xml_escape(std::wstring data)
|
||||
@ -182,13 +182,13 @@ inline void xml_escape(std::wstring data)
|
||||
data.swap(buffer);
|
||||
}
|
||||
|
||||
void notifications::show_toast_with_activations(std::wstring_view message, std::wstring_view background_handler_id, std::vector<button_t> buttons)
|
||||
void notifications::show_toast_with_activations(std::wstring message, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params)
|
||||
{
|
||||
// DO NOT LOCALIZE any string in this function, because they're XML tags and a subject to
|
||||
// https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/toast-xml-schema
|
||||
|
||||
std::wstring toast_xml;
|
||||
toast_xml.reserve(1024);
|
||||
toast_xml.reserve(2048);
|
||||
std::wstring title{ L"PowerToys" };
|
||||
if (winstore::running_as_packaged())
|
||||
{
|
||||
@ -200,28 +200,78 @@ void notifications::show_toast_with_activations(std::wstring_view message, std::
|
||||
toast_xml += L"</text><text>";
|
||||
toast_xml += message;
|
||||
toast_xml += L"</text></binding></visual><actions>";
|
||||
for (size_t i = 0; i < size(actions); ++i)
|
||||
{
|
||||
std::visit(overloaded{
|
||||
[&](const snooze_button& b) {
|
||||
const bool has_durations = !b.durations.empty() && size(b.durations) <= 5;
|
||||
std::wstring selection_id = L"snoozeTime";
|
||||
selection_id += static_cast<wchar_t>(L'0' + i);
|
||||
if (has_durations)
|
||||
{
|
||||
toast_xml += LR"(<input id=")";
|
||||
toast_xml += selection_id;
|
||||
toast_xml += LR"(" type="selection" defaultInput=")";
|
||||
toast_xml += std::to_wstring(b.durations[0].minutes);
|
||||
toast_xml += LR"(">)";
|
||||
for (const auto& duration : b.durations)
|
||||
{
|
||||
toast_xml += LR"(<selection id=")";
|
||||
toast_xml += std::to_wstring(duration.minutes);
|
||||
toast_xml += LR"(" content=")";
|
||||
toast_xml += duration.label;
|
||||
toast_xml += LR"("/>)";
|
||||
}
|
||||
toast_xml += LR"(</input>)";
|
||||
}
|
||||
},
|
||||
[](const auto&) {} },
|
||||
actions[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size(buttons); ++i)
|
||||
for (size_t i = 0; i < size(actions); ++i)
|
||||
{
|
||||
std::visit(overloaded{
|
||||
[&](const link_button& b) {
|
||||
toast_xml += LR"(<action activationType="protocol" arguments=")";
|
||||
toast_xml += LR"(<action activationType="protocol" )";
|
||||
if (b.context_menu)
|
||||
{
|
||||
toast_xml += LR"(placement="contextMenu" )";
|
||||
}
|
||||
toast_xml += LR"(arguments=")";
|
||||
toast_xml += b.url;
|
||||
toast_xml += LR"(" content=")";
|
||||
toast_xml += b.label;
|
||||
toast_xml += LR"("/>)";
|
||||
toast_xml += LR"(" />)";
|
||||
},
|
||||
[&](const background_activated_button& b) {
|
||||
toast_xml += LR"(<action activationType="background" arguments=")";
|
||||
toast_xml += LR"(<action activationType="background" )";
|
||||
if (b.context_menu)
|
||||
{
|
||||
toast_xml += LR"(placement="contextMenu" )";
|
||||
}
|
||||
toast_xml += LR"(arguments=")";
|
||||
toast_xml += L"button_id=" + std::to_wstring(i); // pass the button ID
|
||||
toast_xml += L"&handler=";
|
||||
toast_xml += background_handler_id;
|
||||
toast_xml += LR"(" content=")";
|
||||
toast_xml += b.label;
|
||||
toast_xml += LR"("/>)";
|
||||
toast_xml += LR"(" />)";
|
||||
},
|
||||
},
|
||||
buttons[i]);
|
||||
[&](const snooze_button& b) {
|
||||
const bool has_durations = !b.durations.empty() && size(b.durations) <= 5;
|
||||
std::wstring selection_id = L"snoozeTime";
|
||||
selection_id += static_cast<wchar_t>(L'0' + i);
|
||||
toast_xml += LR"(<action activationType="system" arguments="snooze" )";
|
||||
if (has_durations)
|
||||
{
|
||||
toast_xml += LR"(hint-inputId=")";
|
||||
toast_xml += selection_id;
|
||||
toast_xml += '"';
|
||||
}
|
||||
toast_xml += LR"( content="" />)";
|
||||
} },
|
||||
actions[i]);
|
||||
}
|
||||
toast_xml += L"</actions></toast>";
|
||||
|
||||
@ -232,5 +282,22 @@ void notifications::show_toast_with_activations(std::wstring_view message, std::
|
||||
|
||||
const auto notifier = winstore::running_as_packaged() ? ToastNotificationManager::ToastNotificationManager::CreateToastNotifier() :
|
||||
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(WIN32_AUMID);
|
||||
|
||||
// Set a tag-related params if it has a valid length
|
||||
if (params.tag.has_value() && params.tag->length() < 64)
|
||||
{
|
||||
notification.Tag(*params.tag);
|
||||
if (!params.resend_if_scheduled)
|
||||
{
|
||||
for (const auto& scheduled_toast : notifier.GetScheduledToastNotifications())
|
||||
{
|
||||
if (scheduled_toast.Tag() == *params.tag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifier.Show(notification);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
|
||||
namespace notifications
|
||||
{
|
||||
@ -12,19 +14,38 @@ namespace notifications
|
||||
|
||||
void run_desktop_app_activator_loop();
|
||||
|
||||
struct snooze_duration
|
||||
{
|
||||
std::wstring label;
|
||||
int minutes;
|
||||
};
|
||||
|
||||
struct snooze_button
|
||||
{
|
||||
std::vector<snooze_duration> durations;
|
||||
};
|
||||
|
||||
struct link_button
|
||||
{
|
||||
std::wstring_view label;
|
||||
std::wstring_view url;
|
||||
std::wstring label;
|
||||
std::wstring url;
|
||||
bool context_menu = false;
|
||||
};
|
||||
|
||||
struct background_activated_button
|
||||
{
|
||||
std::wstring_view label;
|
||||
std::wstring label;
|
||||
bool context_menu = false;
|
||||
};
|
||||
|
||||
using button_t = std::variant<link_button, background_activated_button>;
|
||||
struct toast_params
|
||||
{
|
||||
std::optional<std::wstring_view> tag;
|
||||
bool resend_if_scheduled = true;
|
||||
};
|
||||
|
||||
void show_toast(std::wstring_view plaintext_message);
|
||||
void show_toast_with_activations(std::wstring_view plaintext_message, std::wstring_view background_handler_id, std::vector<button_t> buttons);
|
||||
using action_t = std::variant<link_button, background_activated_button, snooze_button>;
|
||||
|
||||
void show_toast(std::wstring plaintext_message, toast_params params = {});
|
||||
void show_toast_with_activations(std::wstring plaintext_message, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params = {});
|
||||
}
|
||||
|
71
src/common/notifications/fancyzones_notifications.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#include <limits>
|
||||
|
||||
#include "../timeutil.h"
|
||||
namespace
|
||||
{
|
||||
const inline wchar_t CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{e16ea82f-6d94-4f30-bb02-d6d911588afd})";
|
||||
const inline int64_t disable_interval_in_days = 30;
|
||||
}
|
||||
|
||||
inline bool disable_cant_drag_elevated_warning()
|
||||
{
|
||||
HKEY key{};
|
||||
if (RegCreateKeyExW(HKEY_CURRENT_USER,
|
||||
CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH,
|
||||
0,
|
||||
nullptr,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
KEY_ALL_ACCESS,
|
||||
nullptr,
|
||||
&key,
|
||||
nullptr) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto now = timeutil::now();
|
||||
const size_t buf_size = sizeof(now);
|
||||
if (RegSetValueExW(key, nullptr, 0, REG_QWORD, reinterpret_cast<const BYTE*>(&now), sizeof(now)) != ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(key);
|
||||
return false;
|
||||
}
|
||||
RegCloseKey(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool is_cant_drag_elevated_warning_disabled()
|
||||
{
|
||||
HKEY key{};
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER,
|
||||
CANT_DRAG_ELEVATED_DONT_SHOW_AGAIN_REGISTRY_PATH,
|
||||
0,
|
||||
KEY_READ,
|
||||
&key) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::wstring buffer(std::numeric_limits<time_t>::digits10 + 2, L'\0');
|
||||
time_t last_disabled_time{};
|
||||
DWORD time_size = static_cast<DWORD>(sizeof(last_disabled_time));
|
||||
if (RegGetValueW(
|
||||
key,
|
||||
nullptr,
|
||||
nullptr,
|
||||
RRF_RT_REG_QWORD,
|
||||
nullptr,
|
||||
&last_disabled_time,
|
||||
&time_size) != ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(key);
|
||||
return false;
|
||||
}
|
||||
RegCloseKey(key);
|
||||
return timeutil::diff::in_days(timeutil::now(), last_disabled_time) < disable_interval_in_days;
|
||||
return false;
|
||||
}
|
@ -73,6 +73,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetName>Notifications</TargetName>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetName>NOTIFICATIONSDLL</TargetName>
|
||||
@ -81,6 +82,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetName>Notifications</TargetName>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetName>NOTIFICATIONSDLL</TargetName>
|
||||
|
@ -1,11 +1,10 @@
|
||||
namespace PowerToysNotifications
|
||||
{
|
||||
[version(1)]
|
||||
[default_interface]
|
||||
runtimeclass BackgroundHandler : Windows.ApplicationModel.Background.IBackgroundTask
|
||||
runtimeclass BackgroundHandler
|
||||
{
|
||||
BackgroundHandler();
|
||||
|
||||
|
||||
void Run(Windows.ApplicationModel.Background.IBackgroundTaskInstance taskInstance);
|
||||
}
|
||||
}
|
||||
|
@ -75,9 +75,11 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetName>notifications</TargetName>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetName>notifications</TargetName>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
|
4
src/common/packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.190716.2" targetFramework="native" />
|
||||
</packages>
|
@ -109,6 +109,17 @@ namespace PowerToysSettings
|
||||
m_json.GetNamedObject(L"properties").SetNamedValue(name, ml_string);
|
||||
}
|
||||
|
||||
void Settings::add_header_szLarge(std::wstring_view name, std::wstring_view description, std::wstring_view value)
|
||||
{
|
||||
json::JsonObject string;
|
||||
string.SetNamedValue(L"display_name", json::value(description));
|
||||
string.SetNamedValue(L"editor_type", json::value(L"header_large"));
|
||||
string.SetNamedValue(L"value", json::value(value));
|
||||
string.SetNamedValue(L"order", json::value(++m_curr_priority));
|
||||
|
||||
m_json.GetNamedObject(L"properties").SetNamedValue(name, string);
|
||||
}
|
||||
|
||||
// add_color_picker overloads.
|
||||
void Settings::add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value)
|
||||
{
|
||||
|
@ -50,6 +50,7 @@ namespace PowerToysSettings
|
||||
void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value);
|
||||
void add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value);
|
||||
|
||||
void add_header_szLarge(std::wstring_view name, std::wstring_view description, std::wstring_view value);
|
||||
// Serialize the internal json to a string.
|
||||
std::wstring serialize();
|
||||
// Serialize the internal json to the input buffer.
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "window_helpers.h"
|
||||
#include "pch.h"
|
||||
#include <wil/Resource.h>
|
||||
|
||||
|
||||
HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p)
|
||||
{
|
||||
@ -27,4 +29,32 @@ HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p
|
||||
}
|
||||
|
||||
return hwnd;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsProcessOfWindowElevated(HWND window)
|
||||
{
|
||||
DWORD pid = 0;
|
||||
GetWindowThreadProcessId(window, &pid);
|
||||
if (!pid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
|
||||
FALSE,
|
||||
pid) };
|
||||
|
||||
wil::unique_handle token;
|
||||
bool elevated = false;
|
||||
|
||||
if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token))
|
||||
{
|
||||
TOKEN_ELEVATION elevation;
|
||||
DWORD size;
|
||||
if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size))
|
||||
{
|
||||
return elevation.TokenIsElevated != 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p);
|
||||
HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p);
|
||||
|
||||
// If HWND is already dead, we assume it wasn't elevated
|
||||
bool IsProcessOfWindowElevated(HWND window);
|
@ -46,10 +46,12 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
|
@ -48,10 +48,12 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
|
@ -12,6 +12,10 @@
|
||||
<!-- Accent and AppTheme setting -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF333333"/>
|
||||
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#FF1a1a1a"/>
|
||||
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
@ -55,6 +55,12 @@ namespace FancyZonesEditor
|
||||
previewChildrenCount++;
|
||||
}
|
||||
|
||||
while (previewChildrenCount > _model.Zones.Count)
|
||||
{
|
||||
Preview.Children.RemoveAt(previewChildrenCount - 1);
|
||||
previewChildrenCount--;
|
||||
}
|
||||
|
||||
for (int i = 0; i < previewChildrenCount; i++)
|
||||
{
|
||||
Int32Rect rect = _model.Zones[i];
|
||||
@ -65,6 +71,7 @@ namespace FancyZonesEditor
|
||||
Canvas.SetTop(zone, rect.Y);
|
||||
zone.Height = rect.Height;
|
||||
zone.Width = rect.Width;
|
||||
zone.LabelID.Content = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ namespace FancyZonesEditor
|
||||
{
|
||||
InitializeComponent();
|
||||
_model = EditorOverlay.Current.DataContext as CanvasLayoutModel;
|
||||
_stashedModel = (CanvasLayoutModel)_model.Clone();
|
||||
}
|
||||
|
||||
private void OnAddZone(object sender, RoutedEventArgs e)
|
||||
@ -24,7 +25,14 @@ namespace FancyZonesEditor
|
||||
_offset += 100;
|
||||
}
|
||||
|
||||
protected new void OnCancel(object sender, RoutedEventArgs e)
|
||||
{
|
||||
base.OnCancel(sender, e);
|
||||
_stashedModel.RestoreTo(_model);
|
||||
}
|
||||
|
||||
private int _offset = 100;
|
||||
private CanvasLayoutModel _model;
|
||||
private CanvasLayoutModel _stashedModel;
|
||||
}
|
||||
}
|
||||
|
@ -5,40 +5,113 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:FancyZonesEditor"
|
||||
mc:Ignorable="d"
|
||||
Background="LightGray"
|
||||
Opacity="0.75"
|
||||
Background="Transparent"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid x:Name="Frame">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="8"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="8"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Thumb x:Name="NWResize" Cursor="SizeNWSE" Background="Black" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted"/>
|
||||
<Thumb x:Name="NEResize" Cursor="SizeNESW" Background="Black" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted"/>
|
||||
<Thumb x:Name="SWResize" Cursor="SizeNESW" Background="Black" Grid.Row="4" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted"/>
|
||||
<Thumb x:Name="SEResize" Cursor="SizeNWSE" Background="Black" Grid.Row="4" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted"/>
|
||||
<Thumb x:Name="NResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="0" Grid.Column="2" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted"/>
|
||||
<Thumb x:Name="SResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="5" Grid.Column="2" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted"/>
|
||||
<Thumb x:Name="WResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="0" Grid.RowSpan="2" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted"/>
|
||||
<Thumb x:Name="EResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="4" Grid.RowSpan="2" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted"/>
|
||||
<DockPanel Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3">
|
||||
<Button DockPanel.Dock="Right" Padding="8,0" Click="OnClose">
|
||||
<Image Source="images/ChromeClose.png" Height="24" Width="24" />
|
||||
</Button>
|
||||
<Thumb x:Name="Caption" Cursor="SizeAll" Background="DarkGray" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted"/>
|
||||
</DockPanel>
|
||||
<Rectangle Fill="LightGray" Grid.Row="3" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3"/>
|
||||
<Canvas x:Name="Body" />
|
||||
</Grid>
|
||||
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="CanvasZoneThumbStyle" TargetType="{x:Type Thumb}">
|
||||
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Border x:Name="ThumbBorder" Opacity="0" BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates" >
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition GeneratedDuration="0:0:0.15">
|
||||
<VisualTransition.GeneratedEasingFunction>
|
||||
<ExponentialEase EasingMode="EaseInOut"/>
|
||||
</VisualTransition.GeneratedEasingFunction>
|
||||
</VisualTransition>
|
||||
</VisualStateGroup.Transitions>
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="MouseOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="ThumbBorder" Duration="0:0:0.15" Storyboard.TargetProperty="Opacity" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Padding" Value="1"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
|
||||
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsDefaulted" Value="true">
|
||||
<Setter Property="BorderBrush" TargetName="border" Value="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="true">
|
||||
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.6"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="true">
|
||||
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.4"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Border BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" Background="{StaticResource CanvasZoneBackgroundBrush}" BorderThickness="1">
|
||||
<Grid x:Name="Frame">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="8"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="8"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Name="LabelID"
|
||||
Content="ID"
|
||||
Canvas.Left="10"
|
||||
Canvas.Bottom="10"
|
||||
FontSize="64"
|
||||
FontFamily="Segoe UI Light"
|
||||
Foreground="White"
|
||||
Grid.Column="2"
|
||||
Grid.Row="2"
|
||||
VerticalContentAlignment="Center"
|
||||
HorizontalContentAlignment="Center" />
|
||||
|
||||
<Thumb x:Name="Caption" Cursor="SizeAll" Background="Transparent" BorderThickness="3" Padding="4" Grid.Column="0" Grid.ColumnSpan="5" Grid.Row="0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
|
||||
<Thumb x:Name="NResize" Cursor="SizeNS" BorderThickness="0,3,0,0" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="SResize" Cursor="SizeNS" BorderThickness="0,0,0,3" Grid.Row="4" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="WResize" Cursor="SizeWE" BorderThickness="3,0,0,0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="EResize" Cursor="SizeWE" BorderThickness="0,0,3,0" Grid.Column="4" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
|
||||
<Thumb x:Name="NWResize" Cursor="SizeNWSE" BorderThickness="3,3,0,0" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="NEResize" Cursor="SizeNESW" BorderThickness="0,3,3,0" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="SWResize" Cursor="SizeNESW" BorderThickness="3,0,0,3" Grid.Row="3" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="SEResize" Cursor="SizeNWSE" BorderThickness="0,0,3,3" Grid.Row="3" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
|
||||
<Button Content="" BorderThickness="0" ToolTip="Delete zone" Background="Transparent" Foreground="White" FontSize="16" Padding="4" Click="OnClose" Grid.Row="2" Grid.Column="2" FontFamily="Segoe MDL2 Assets" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{DynamicResource CloseButtonStyle}"/>
|
||||
|
||||
<Canvas x:Name="Body" />
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
|
@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
using FancyZonesEditor.Utils;
|
||||
|
||||
namespace FancyZonesEditor
|
||||
{
|
||||
public class DashCaseNamingPolicy : JsonNamingPolicy
|
||||
{
|
||||
public static DashCaseNamingPolicy Instance { get; } = new DashCaseNamingPolicy();
|
||||
|
||||
public override string ConvertName(string name)
|
||||
{
|
||||
return name.UpperCamelCaseToDashCase();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,14 +21,14 @@ namespace FancyZonesEditor
|
||||
|
||||
LayoutModel.SerializeDeletedCustomZoneSets();
|
||||
|
||||
_choosing = true;
|
||||
_backToLayoutPicker = false;
|
||||
Close();
|
||||
EditorOverlay.Current.Close();
|
||||
}
|
||||
|
||||
protected void OnClosed(object sender, EventArgs e)
|
||||
{
|
||||
if (!_choosing)
|
||||
if (_backToLayoutPicker)
|
||||
{
|
||||
EditorOverlay.Current.ShowLayoutPicker();
|
||||
}
|
||||
@ -36,11 +36,10 @@ namespace FancyZonesEditor
|
||||
|
||||
protected void OnCancel(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_choosing = true;
|
||||
_backToLayoutPicker = true;
|
||||
Close();
|
||||
EditorOverlay.Current.ShowLayoutPicker();
|
||||
}
|
||||
|
||||
private bool _choosing = false;
|
||||
private bool _backToLayoutPicker = true;
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,7 @@
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<IntermediateOutputPath>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(AssemblyName)\</IntermediateOutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
@ -109,6 +110,8 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="DashCaseNamingPolicy.cs" />
|
||||
<Compile Include="StringUtils.cs" />
|
||||
<Compile Include="Converters\BooleanToBrushConverter.xaml.cs" />
|
||||
<Compile Include="Converters\BooleanToIntConverter.xaml.cs" />
|
||||
<Compile Include="CanvasEditor.xaml.cs">
|
||||
|
@ -1,32 +1,48 @@
|
||||
<UserControl x:Class="FancyZonesEditor.GridEditor"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:FancyZonesEditor"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl
|
||||
x:Class="FancyZonesEditor.GridEditor"
|
||||
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="clr-namespace:FancyZonesEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontFamily" Value="Segoe UI" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Foreground" Value="Black"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Background" Value="#F2F2F2"/>
|
||||
<Setter Property="Width" Value="150"/>
|
||||
<Setter Property="Foreground" Value="Black" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Background" Value="#F2F2F2" />
|
||||
<Setter Property="Width" Value="150" />
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Canvas x:Name="Preview"/>
|
||||
<Canvas x:Name="AdornerLayer"/>
|
||||
<Canvas x:Name="MergePanel" Visibility="Collapsed" MouseUp="MergePanelMouseUp">
|
||||
<Canvas x:Name="Preview" />
|
||||
<Canvas x:Name="AdornerLayer" />
|
||||
<Canvas
|
||||
x:Name="MergePanel"
|
||||
MouseUp="MergePanelMouseUp"
|
||||
Visibility="Collapsed">
|
||||
|
||||
<StackPanel x:Name="MergeButtons" Background="Gray" Orientation="Horizontal">
|
||||
<Button Click="MergeClick" Margin="0" Height="36" Width="134">
|
||||
<StackPanel
|
||||
x:Name="MergeButtons"
|
||||
Background="Gray"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Width="134"
|
||||
Height="36"
|
||||
Margin="0"
|
||||
Click="MergeClick">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="images/Merge.png" Margin="0,0,12,0" Height="16" HorizontalAlignment="Left" />
|
||||
<TextBlock Text="Merge zones"/>
|
||||
<Image
|
||||
Height="16"
|
||||
Margin="0,0,12,0"
|
||||
HorizontalAlignment="Left"
|
||||
Source="images/Merge.png" />
|
||||
<TextBlock Text="Merge zones" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
@ -18,11 +18,16 @@ namespace FancyZonesEditor
|
||||
{
|
||||
public static readonly DependencyProperty ModelProperty = DependencyProperty.Register("Model", typeof(GridLayoutModel), typeof(GridEditor), new PropertyMetadata(null, OnGridDimensionsChanged));
|
||||
|
||||
private static int gridEditorUniqueIdCounter = 0;
|
||||
|
||||
private int gridEditorUniqueId;
|
||||
|
||||
public GridEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += GridEditor_Loaded;
|
||||
((App)Application.Current).ZoneSettings.PropertyChanged += ZoneSettings_PropertyChanged;
|
||||
gridEditorUniqueId = ++gridEditorUniqueIdCounter;
|
||||
}
|
||||
|
||||
private void GridEditor_Loaded(object sender, RoutedEventArgs e)
|
||||
@ -73,7 +78,9 @@ namespace FancyZonesEditor
|
||||
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
Size actualSize = new Size(ActualWidth, ActualHeight);
|
||||
if (actualSize.Width > 0)
|
||||
|
||||
// Only enter if this is the newest instance
|
||||
if (actualSize.Width > 0 && gridEditorUniqueId == gridEditorUniqueIdCounter)
|
||||
{
|
||||
ArrangeGridRects(actualSize);
|
||||
}
|
||||
@ -134,83 +141,50 @@ namespace FancyZonesEditor
|
||||
|
||||
private void ExtendRangeToHaveEvenCellEdges()
|
||||
{
|
||||
// extend each edge of the [(_startCol, _startRow) - (_endCol, _endRow)] range based on merged cells until you have 4 straight edges with no "straddling cells"
|
||||
// As long as there is an edge of the 2D range such that some zone crosses its boundary, extend
|
||||
// that boundary. A single pass is not enough, a while loop is needed. This results in the unique
|
||||
// smallest rectangle containing the initial range such that no zone is "broken", meaning that
|
||||
// some part of it is inside the 2D range, and some part is outside.
|
||||
GridLayoutModel model = Model;
|
||||
bool possiblyBroken = true;
|
||||
|
||||
while (_startRow > 0)
|
||||
while (possiblyBroken)
|
||||
{
|
||||
bool dirty = false;
|
||||
possiblyBroken = false;
|
||||
|
||||
for (int col = _startCol; col <= _endCol; col++)
|
||||
{
|
||||
if (model.CellChildMap[_startRow - 1, col] == model.CellChildMap[_startRow, col])
|
||||
if (_startRow > 0 && model.CellChildMap[_startRow - 1, col] == model.CellChildMap[_startRow, col])
|
||||
{
|
||||
_startRow--;
|
||||
dirty = true;
|
||||
possiblyBroken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dirty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (_endRow < model.Rows - 1)
|
||||
{
|
||||
bool dirty = false;
|
||||
for (int col = _startCol; col <= _endCol; col++)
|
||||
{
|
||||
if (model.CellChildMap[_endRow + 1, col] == model.CellChildMap[_endRow, col])
|
||||
if (_endRow < model.Rows - 1 && model.CellChildMap[_endRow + 1, col] == model.CellChildMap[_endRow, col])
|
||||
{
|
||||
_endRow++;
|
||||
dirty = true;
|
||||
possiblyBroken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dirty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (_startCol > 0)
|
||||
{
|
||||
bool dirty = false;
|
||||
for (int row = _startRow; row <= _endRow; row++)
|
||||
{
|
||||
if (model.CellChildMap[row, _startCol - 1] == model.CellChildMap[row, _startCol])
|
||||
if (_startCol > 0 && model.CellChildMap[row, _startCol - 1] == model.CellChildMap[row, _startCol])
|
||||
{
|
||||
_startCol--;
|
||||
dirty = true;
|
||||
possiblyBroken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dirty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (_endCol < model.Columns - 1)
|
||||
{
|
||||
bool dirty = false;
|
||||
for (int row = _startRow; row <= _endRow; row++)
|
||||
{
|
||||
if (model.CellChildMap[row, _endCol + 1] == model.CellChildMap[row, _endCol])
|
||||
if (_endCol < model.Columns - 1 && model.CellChildMap[row, _endCol + 1] == model.CellChildMap[row, _endCol])
|
||||
{
|
||||
_endCol++;
|
||||
dirty = true;
|
||||
possiblyBroken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dirty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -528,7 +502,8 @@ namespace FancyZonesEditor
|
||||
|
||||
private void OnGridDimensionsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if ((e.PropertyName == "Rows") || (e.PropertyName == "Columns"))
|
||||
// Only enter if this is the newest instance
|
||||
if (((e.PropertyName == "Rows") || (e.PropertyName == "Columns")) && gridEditorUniqueId == gridEditorUniqueIdCounter)
|
||||
{
|
||||
OnGridDimensionsChanged();
|
||||
}
|
||||
@ -594,6 +569,7 @@ namespace FancyZonesEditor
|
||||
top = _rowInfo[row].Start;
|
||||
Canvas.SetLeft(zone, left);
|
||||
Canvas.SetTop(zone, top);
|
||||
zone.LabelID.Content = i + 1;
|
||||
|
||||
int maxRow = row;
|
||||
while (((maxRow + 1) < rows) && (model.CellChildMap[maxRow + 1, col] == i))
|
||||
|
@ -2,6 +2,9 @@
|
||||
// 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;
|
||||
using FancyZonesEditor.Models;
|
||||
|
||||
namespace FancyZonesEditor
|
||||
{
|
||||
/// <summary>
|
||||
@ -12,6 +15,16 @@ namespace FancyZonesEditor
|
||||
public GridEditorWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
_stashedModel = (GridLayoutModel)(EditorOverlay.Current.DataContext as GridLayoutModel).Clone();
|
||||
}
|
||||
|
||||
protected new void OnCancel(object sender, RoutedEventArgs e)
|
||||
{
|
||||
base.OnCancel(sender, e);
|
||||
GridLayoutModel model = EditorOverlay.Current.DataContext as GridLayoutModel;
|
||||
_stashedModel.RestoreTo(model);
|
||||
}
|
||||
|
||||
private GridLayoutModel _stashedModel;
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,16 @@
|
||||
<Thumb.Template>
|
||||
<ControlTemplate>
|
||||
<StackPanel x:Name="Body" Grid.Column="0" Width="48" Height="48">
|
||||
<Ellipse Height="48" Width="48" Fill="#0078D7" />
|
||||
|
||||
<Ellipse x:Name="BackgroundEllipse" Height="48" Width="48" Fill="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" />
|
||||
<Rectangle Height="20" Width="2" Fill="White" Margin="5,-48,0,0"/>
|
||||
<Rectangle Height="20" Width="2" Fill="White" Margin="-5,-48,0,0"/>
|
||||
</StackPanel>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="true">
|
||||
<Setter Property="Opacity" TargetName="BackgroundEllipse" Value="0.6"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
|
@ -1,17 +1,31 @@
|
||||
<UserControl x:Class="FancyZonesEditor.GridZone"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:FancyZonesEditor"
|
||||
mc:Ignorable="d"
|
||||
Background="LightGray"
|
||||
BorderThickness="1"
|
||||
BorderBrush="DarkGray"
|
||||
Opacity="0.5"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid x:Name="Frame" Visibility="Collapsed">
|
||||
<UserControl
|
||||
x:Class="FancyZonesEditor.GridZone"
|
||||
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="clr-namespace:FancyZonesEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Background="{StaticResource GridZoneBackgroundBrush}"
|
||||
BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"
|
||||
BorderThickness="1"
|
||||
Opacity="0.8"
|
||||
mc:Ignorable="d">
|
||||
<Grid x:Name="Frame">
|
||||
<Canvas x:Name="Body" />
|
||||
<Label
|
||||
Name="LabelID"
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Canvas.Left="10"
|
||||
Canvas.Bottom="10"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Content="ID"
|
||||
FontFamily="Segoe UI Light"
|
||||
FontSize="64"
|
||||
Foreground="White" />
|
||||
<!--<TextBlock Margin="2" Text="Shift Key switches direction Ctrl Key repeats"/>-->
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
@ -45,7 +45,7 @@ namespace FancyZonesEditor
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
Background = IsSelected ? Brushes.SteelBlue : Brushes.LightGray;
|
||||
Background = IsSelected ? SystemParameters.WindowGlassBrush : App.Current.Resources["GridZoneBackgroundBrush"] as SolidColorBrush;
|
||||
}
|
||||
|
||||
public bool IsSelected
|
||||
@ -60,7 +60,7 @@ namespace FancyZonesEditor
|
||||
OnSelectionChanged();
|
||||
_splitter = new Rectangle
|
||||
{
|
||||
Fill = Brushes.DarkGray,
|
||||
Fill = SystemParameters.WindowGlassBrush,
|
||||
};
|
||||
Body.Children.Add(_splitter);
|
||||
|
||||
@ -146,13 +146,13 @@ namespace FancyZonesEditor
|
||||
|
||||
protected override void OnMouseEnter(MouseEventArgs e)
|
||||
{
|
||||
Frame.Visibility = Visibility.Visible;
|
||||
_splitter.Fill = SystemParameters.WindowGlassBrush; // Active Accent color
|
||||
base.OnMouseEnter(e);
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(MouseEventArgs e)
|
||||
{
|
||||
Frame.Visibility = Visibility.Collapsed;
|
||||
_splitter.Fill = Brushes.Transparent;
|
||||
base.OnMouseLeave(e);
|
||||
}
|
||||
|
||||
|
@ -113,48 +113,86 @@ namespace FancyZonesEditor.Models
|
||||
return layout;
|
||||
}
|
||||
|
||||
public void RestoreTo(CanvasLayoutModel other)
|
||||
{
|
||||
other.Zones.Clear();
|
||||
foreach (Int32Rect zone in Zones)
|
||||
{
|
||||
other.Zones.Add(zone);
|
||||
}
|
||||
}
|
||||
|
||||
private struct Zone
|
||||
{
|
||||
public int X { get; set; }
|
||||
|
||||
public int Y { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
private struct CanvasLayoutInfo
|
||||
{
|
||||
public int RefWidth { get; set; }
|
||||
|
||||
public int RefHeight { get; set; }
|
||||
|
||||
public Zone[] Zones { get; set; }
|
||||
}
|
||||
|
||||
private struct CanvasLayoutJson
|
||||
{
|
||||
public string Uuid { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public CanvasLayoutInfo Info { get; set; }
|
||||
}
|
||||
|
||||
// PersistData
|
||||
// Implements the LayoutModel.PersistData abstract method
|
||||
protected override void PersistData()
|
||||
{
|
||||
CanvasLayoutInfo layoutInfo = new CanvasLayoutInfo
|
||||
{
|
||||
RefWidth = _referenceWidth,
|
||||
RefHeight = _referenceHeight,
|
||||
Zones = new Zone[Zones.Count],
|
||||
};
|
||||
for (int i = 0; i < Zones.Count; ++i)
|
||||
{
|
||||
Zone zone = new Zone
|
||||
{
|
||||
X = Zones[i].X,
|
||||
Y = Zones[i].Y,
|
||||
Width = Zones[i].Width,
|
||||
Height = Zones[i].Height,
|
||||
};
|
||||
|
||||
layoutInfo.Zones[i] = zone;
|
||||
}
|
||||
|
||||
CanvasLayoutJson jsonObj = new CanvasLayoutJson
|
||||
{
|
||||
Uuid = "{" + Guid.ToString().ToUpper() + "}",
|
||||
Name = Name,
|
||||
Type = "canvas",
|
||||
Info = layoutInfo,
|
||||
};
|
||||
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
FileStream outputStream = File.Open(Settings.AppliedZoneSetTmpFile, FileMode.Create);
|
||||
using (var writer = new Utf8JsonWriter(outputStream, options: default))
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
|
||||
writer.WriteString("name", Name);
|
||||
|
||||
writer.WriteString("type", "canvas");
|
||||
|
||||
writer.WriteStartObject("info");
|
||||
|
||||
writer.WriteNumber("ref-width", _referenceWidth);
|
||||
writer.WriteNumber("ref-height", _referenceHeight);
|
||||
|
||||
writer.WriteStartArray("zones");
|
||||
foreach (Int32Rect rect in Zones)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteNumber("X", rect.X);
|
||||
writer.WriteNumber("Y", rect.Y);
|
||||
writer.WriteNumber("width", rect.Width);
|
||||
writer.WriteNumber("height", rect.Height);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
|
||||
// end info object
|
||||
writer.WriteEndObject();
|
||||
|
||||
// end root object
|
||||
writer.WriteEndObject();
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
outputStream.Close();
|
||||
string jsonString = JsonSerializer.Serialize(jsonObj, options);
|
||||
File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -131,6 +131,12 @@ namespace FancyZonesEditor.Models
|
||||
public override LayoutModel Clone()
|
||||
{
|
||||
GridLayoutModel layout = new GridLayoutModel(Name);
|
||||
RestoreTo(layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
public void RestoreTo(GridLayoutModel layout)
|
||||
{
|
||||
int rows = Rows;
|
||||
int cols = Columns;
|
||||
|
||||
@ -163,69 +169,69 @@ namespace FancyZonesEditor.Models
|
||||
}
|
||||
|
||||
layout.ColumnPercents = colPercents;
|
||||
}
|
||||
|
||||
return layout;
|
||||
private struct GridLayoutInfo
|
||||
{
|
||||
public int Rows { get; set; }
|
||||
|
||||
public int Columns { get; set; }
|
||||
|
||||
public int[] RowsPercentage { get; set; }
|
||||
|
||||
public int[] ColumnsPercentage { get; set; }
|
||||
|
||||
public int[][] CellChildMap { get; set; }
|
||||
}
|
||||
|
||||
private struct GridLayoutJson
|
||||
{
|
||||
public string Uuid { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public GridLayoutInfo Info { get; set; }
|
||||
}
|
||||
|
||||
// PersistData
|
||||
// Implements the LayoutModel.PersistData abstract method
|
||||
protected override void PersistData()
|
||||
{
|
||||
GridLayoutInfo layoutInfo = new GridLayoutInfo
|
||||
{
|
||||
Rows = Rows,
|
||||
Columns = Columns,
|
||||
RowsPercentage = RowPercents,
|
||||
ColumnsPercentage = ColumnPercents,
|
||||
CellChildMap = new int[Rows][],
|
||||
};
|
||||
for (int row = 0; row < Rows; row++)
|
||||
{
|
||||
layoutInfo.CellChildMap[row] = new int[Columns];
|
||||
for (int col = 0; col < Columns; col++)
|
||||
{
|
||||
layoutInfo.CellChildMap[row][col] = CellChildMap[row, col];
|
||||
}
|
||||
}
|
||||
|
||||
GridLayoutJson jsonObj = new GridLayoutJson
|
||||
{
|
||||
Uuid = "{" + Guid.ToString().ToUpper() + "}",
|
||||
Name = Name,
|
||||
Type = "grid",
|
||||
Info = layoutInfo,
|
||||
};
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
FileStream outputStream = File.Open(Settings.AppliedZoneSetTmpFile, FileMode.Create);
|
||||
using (var writer = new Utf8JsonWriter(outputStream, options: default))
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
|
||||
writer.WriteString("name", Name);
|
||||
|
||||
writer.WriteString("type", "grid");
|
||||
|
||||
writer.WriteStartObject("info");
|
||||
|
||||
writer.WriteNumber("rows", Rows);
|
||||
writer.WriteNumber("columns", Columns);
|
||||
|
||||
writer.WriteStartArray("rows-percentage");
|
||||
for (int row = 0; row < Rows; row++)
|
||||
{
|
||||
writer.WriteNumberValue(RowPercents[row]);
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
|
||||
writer.WriteStartArray("columns-percentage");
|
||||
for (int col = 0; col < Columns; col++)
|
||||
{
|
||||
writer.WriteNumberValue(ColumnPercents[col]);
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
|
||||
writer.WriteStartArray("cell-child-map");
|
||||
for (int row = 0; row < Rows; row++)
|
||||
{
|
||||
writer.WriteStartArray();
|
||||
for (int col = 0; col < Columns; col++)
|
||||
{
|
||||
writer.WriteNumberValue(CellChildMap[row, col]);
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
|
||||
// end info object
|
||||
writer.WriteEndObject();
|
||||
|
||||
// end root object
|
||||
writer.WriteEndObject();
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
outputStream.Close();
|
||||
string jsonString = JsonSerializer.Serialize(jsonObj, options);
|
||||
File.WriteAllText(Settings.AppliedZoneSetTmpFile, jsonString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -135,23 +135,27 @@ namespace FancyZonesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
private struct DeletedCustomZoneSetsWrapper
|
||||
{
|
||||
public List<string> DeletedCustomZoneSets { get; set; }
|
||||
}
|
||||
|
||||
public static void SerializeDeletedCustomZoneSets()
|
||||
{
|
||||
DeletedCustomZoneSetsWrapper deletedLayouts = new DeletedCustomZoneSetsWrapper
|
||||
{
|
||||
DeletedCustomZoneSets = _deletedCustomModels,
|
||||
};
|
||||
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
FileStream outputStream = File.Open(Settings.CustomZoneSetsTmpFile, FileMode.Create);
|
||||
var writer = new Utf8JsonWriter(outputStream, options: default);
|
||||
writer.WriteStartObject();
|
||||
writer.WriteStartArray("deleted-custom-zone-sets");
|
||||
foreach (string zoneSet in _deletedCustomModels)
|
||||
{
|
||||
writer.WriteStringValue(zoneSet);
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
writer.WriteEndObject();
|
||||
writer.Flush();
|
||||
outputStream.Close();
|
||||
string jsonString = JsonSerializer.Serialize(deletedLayouts, options);
|
||||
File.WriteAllText(Settings.CustomZoneSetsTmpFile, jsonString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -258,51 +262,75 @@ namespace FancyZonesEditor.Models
|
||||
Apply();
|
||||
}
|
||||
|
||||
private struct ActiveZoneSetWrapper
|
||||
{
|
||||
public string Uuid { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
}
|
||||
|
||||
private struct AppliedZoneSet
|
||||
{
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
public ActiveZoneSetWrapper ActiveZoneset { get; set; }
|
||||
|
||||
public bool EditorShowSpacing { get; set; }
|
||||
|
||||
public int EditorSpacing { get; set; }
|
||||
|
||||
public int EditorZoneCount { get; set; }
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
ActiveZoneSetWrapper activeZoneSet = new ActiveZoneSetWrapper
|
||||
{
|
||||
Uuid = "{" + Guid.ToString().ToUpper() + "}",
|
||||
};
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case LayoutType.Focus:
|
||||
activeZoneSet.Type = "focus";
|
||||
break;
|
||||
case LayoutType.Rows:
|
||||
activeZoneSet.Type = "rows";
|
||||
break;
|
||||
case LayoutType.Columns:
|
||||
activeZoneSet.Type = "columns";
|
||||
break;
|
||||
case LayoutType.Grid:
|
||||
activeZoneSet.Type = "grid";
|
||||
break;
|
||||
case LayoutType.PriorityGrid:
|
||||
activeZoneSet.Type = "priority-grid";
|
||||
break;
|
||||
case LayoutType.Custom:
|
||||
activeZoneSet.Type = "custom";
|
||||
break;
|
||||
}
|
||||
|
||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
||||
|
||||
AppliedZoneSet zoneSet = new AppliedZoneSet
|
||||
{
|
||||
DeviceId = Settings.UniqueKey,
|
||||
ActiveZoneset = activeZoneSet,
|
||||
EditorShowSpacing = settings.ShowSpacing,
|
||||
EditorSpacing = settings.Spacing,
|
||||
EditorZoneCount = settings.ZoneCount,
|
||||
};
|
||||
|
||||
JsonSerializerOptions options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
FileStream outputStream = File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Create);
|
||||
var writer = new Utf8JsonWriter(outputStream, options: default);
|
||||
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("device-id", Settings.UniqueKey);
|
||||
|
||||
writer.WriteStartObject("active-zoneset");
|
||||
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
|
||||
switch (Type)
|
||||
{
|
||||
case LayoutType.Focus:
|
||||
writer.WriteString("type", "focus");
|
||||
break;
|
||||
case LayoutType.Rows:
|
||||
writer.WriteString("type", "rows");
|
||||
break;
|
||||
case LayoutType.Columns:
|
||||
writer.WriteString("type", "columns");
|
||||
break;
|
||||
case LayoutType.Grid:
|
||||
writer.WriteString("type", "grid");
|
||||
break;
|
||||
case LayoutType.PriorityGrid:
|
||||
writer.WriteString("type", "priority-grid");
|
||||
break;
|
||||
case LayoutType.Custom:
|
||||
writer.WriteString("type", "custom");
|
||||
break;
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
|
||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
||||
|
||||
writer.WriteBoolean("editor-show-spacing", settings.ShowSpacing);
|
||||
writer.WriteNumber("editor-spacing", settings.Spacing);
|
||||
writer.WriteNumber("editor-zone-count", settings.ZoneCount);
|
||||
|
||||
writer.WriteEndObject();
|
||||
writer.Flush();
|
||||
outputStream.Close();
|
||||
string jsonString = JsonSerializer.Serialize(zoneSet, options);
|
||||
File.WriteAllText(Settings.ActiveZoneSetTmpFile, jsonString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -329,15 +329,15 @@ namespace FancyZonesEditor
|
||||
_gridModel.ColumnPercents[col] = ((_multiplier * (col + 1)) / cols) - ((_multiplier * col) / cols);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (int col = cols - 1; col >= 0; col--)
|
||||
int index = ZoneCount - 1;
|
||||
for (int row = rows - 1; row >= 0; row--)
|
||||
{
|
||||
for (int row = rows - 1; row >= 0; row--)
|
||||
for (int col = cols - 1; col >= 0; col--)
|
||||
{
|
||||
_gridModel.CellChildMap[row, col] = index++;
|
||||
if (index == ZoneCount)
|
||||
_gridModel.CellChildMap[row, col] = index--;
|
||||
if (index < 0)
|
||||
{
|
||||
index--;
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -407,7 +407,8 @@ namespace FancyZonesEditor
|
||||
}
|
||||
|
||||
inputStream.Close();
|
||||
} catch (Exception ex)
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LayoutModel.ShowExceptionMessageBox("Error parsing device info data", ex);
|
||||
}
|
||||
|
@ -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.Linq;
|
||||
|
||||
namespace FancyZonesEditor.Utils
|
||||
{
|
||||
public static class StringUtils
|
||||
{
|
||||
public static string UpperCamelCaseToDashCase(this string str)
|
||||
{
|
||||
// If it's single letter variable, leave it as it is
|
||||
if (str.Length == 1)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "-" + x.ToString() : x.ToString())).ToLower();
|
||||
}
|
||||
}
|
||||
}
|
@ -12,9 +12,13 @@
|
||||
|
||||
#include <functional>
|
||||
#include <common/common.h>
|
||||
#include <lib\util.h>
|
||||
#include <common/window_helpers.h>
|
||||
#include <common/notifications.h>
|
||||
#include <lib/util.h>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <common/notifications/fancyzones_notifications.h>
|
||||
|
||||
enum class DisplayChangeType
|
||||
{
|
||||
WorkArea,
|
||||
@ -24,6 +28,8 @@ enum class DisplayChangeType
|
||||
Initialization
|
||||
};
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
@ -83,10 +89,32 @@ public:
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowsOnActiveZoneSetChange() noexcept;
|
||||
IFACEMETHODIMP_(COLORREF)
|
||||
GetZoneColor() noexcept
|
||||
{
|
||||
// Skip the leading # and convert to long
|
||||
const auto color = m_settings->GetSettings()->zoneColor;
|
||||
const auto tmp = std::stol(color.substr(1), nullptr, 16);
|
||||
const auto nR = (tmp & 0xFF0000) >> 16;
|
||||
const auto nG = (tmp & 0xFF00) >> 8;
|
||||
const auto nB = (tmp & 0xFF);
|
||||
return RGB(nR, nG, nB);
|
||||
}
|
||||
IFACEMETHODIMP_(COLORREF)
|
||||
GetZoneBorderColor() noexcept
|
||||
{
|
||||
// Skip the leading # and convert to long
|
||||
const auto color = m_settings->GetSettings()->zoneBorderColor;
|
||||
const auto tmp = std::stol(color.substr(1), nullptr, 16);
|
||||
const auto nR = (tmp & 0xFF0000) >> 16;
|
||||
const auto nG = (tmp & 0xFF00) >> 8;
|
||||
const auto nB = (tmp & 0xFF);
|
||||
return RGB(nR, nG, nB);
|
||||
}
|
||||
IFACEMETHODIMP_(COLORREF)
|
||||
GetZoneHighlightColor() noexcept
|
||||
{
|
||||
// Skip the leading # and convert to long
|
||||
const auto color = m_settings->GetSettings().zoneHightlightColor;
|
||||
const auto color = m_settings->GetSettings()->zoneHightlightColor;
|
||||
const auto tmp = std::stol(color.substr(1), nullptr, 16);
|
||||
const auto nR = (tmp & 0xFF0000) >> 16;
|
||||
const auto nG = (tmp & 0xFF00) >> 8;
|
||||
@ -108,7 +136,13 @@ public:
|
||||
IFACEMETHODIMP_(int)
|
||||
GetZoneHighlightOpacity() noexcept
|
||||
{
|
||||
return m_settings->GetSettings().zoneHighlightOpacity;
|
||||
return m_settings->GetSettings()->zoneHighlightOpacity;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(bool)
|
||||
isMakeDraggedWindowTransparentActive() noexcept
|
||||
{
|
||||
return m_settings->GetSettings()->makeDraggedWindowTransparent;
|
||||
}
|
||||
|
||||
LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
|
||||
@ -146,9 +180,10 @@ private:
|
||||
};
|
||||
|
||||
bool IsInterestingWindow(HWND window) noexcept;
|
||||
bool IsCursorTypeIndicatingSizeEvent();
|
||||
void UpdateZoneWindows() noexcept;
|
||||
void MoveWindowsOnDisplayChange() noexcept;
|
||||
void UpdateDragState(require_write_lock) noexcept;
|
||||
void UpdateDragState(HWND window, require_write_lock) noexcept;
|
||||
void CycleActiveZoneSet(DWORD vkCode) noexcept;
|
||||
bool OnSnapHotkey(DWORD vkCode) noexcept;
|
||||
void MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock) noexcept;
|
||||
@ -162,6 +197,10 @@ private:
|
||||
|
||||
void OnEditorExitEvent() noexcept;
|
||||
|
||||
std::vector<std::pair<HMONITOR, RECT>> GetRawMonitorData() noexcept;
|
||||
std::vector<HMONITOR> GetMonitorsSorted() noexcept;
|
||||
bool MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle);
|
||||
|
||||
const HINSTANCE m_hinstance{};
|
||||
|
||||
HKEY m_virtualDesktopsRegKey{ nullptr };
|
||||
@ -217,7 +256,7 @@ FancyZones::Run() noexcept
|
||||
if (!m_window)
|
||||
return;
|
||||
|
||||
RegisterHotKey(m_window, 1, m_settings->GetSettings().editorHotkey.get_modifiers(), m_settings->GetSettings().editorHotkey.get_code());
|
||||
RegisterHotKey(m_window, 1, m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code());
|
||||
|
||||
VirtualDesktopInitialize();
|
||||
|
||||
@ -309,7 +348,7 @@ FancyZones::VirtualDesktopInitialize() noexcept
|
||||
IFACEMETHODIMP_(void)
|
||||
FancyZones::WindowCreated(HWND window) noexcept
|
||||
{
|
||||
if (m_settings->GetSettings().appLastZone_moveWindows && IsInterestingWindow(window))
|
||||
if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window))
|
||||
{
|
||||
for (const auto& [monitor, zoneWindow] : m_zoneWindowMap)
|
||||
{
|
||||
@ -345,17 +384,18 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
|
||||
bool const ctrl = GetAsyncKeyState(VK_CONTROL) & 0x8000;
|
||||
if (ctrl)
|
||||
{
|
||||
if ((info->vkCode >= '0') && (info->vkCode <= '9'))
|
||||
{
|
||||
// Win+Ctrl+Number will cycle through ZoneSets
|
||||
Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/);
|
||||
CycleActiveZoneSet(info->vkCode);
|
||||
return true;
|
||||
}
|
||||
// Temporarily disable Win+Ctrl+Number functionality
|
||||
//if ((info->vkCode >= '0') && (info->vkCode <= '9'))
|
||||
//{
|
||||
// // Win+Ctrl+Number will cycle through ZoneSets
|
||||
// Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/);
|
||||
// CycleActiveZoneSet(info->vkCode);
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
else if ((info->vkCode == VK_RIGHT) || (info->vkCode == VK_LEFT))
|
||||
{
|
||||
if (m_settings->GetSettings().overrideSnapHotkeys)
|
||||
if (m_settings->GetSettings()->overrideSnapHotkeys)
|
||||
{
|
||||
// Win+Left, Win+Right will cycle through Zones in the active ZoneSet
|
||||
Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/);
|
||||
@ -363,13 +403,14 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_inMoveSize && (info->vkCode >= '0') && (info->vkCode <= '9'))
|
||||
{
|
||||
// This allows you to cycle through ZoneSets while dragging a window
|
||||
Trace::FancyZones::OnKeyDown(info->vkCode, win, false /*control*/, true /*inMoveSize*/);
|
||||
CycleActiveZoneSet(info->vkCode);
|
||||
return false;
|
||||
}
|
||||
// Temporarily disable Win+Ctrl+Number functionality
|
||||
//else if (m_inMoveSize && (info->vkCode >= '0') && (info->vkCode <= '9'))
|
||||
//{
|
||||
// // This allows you to cycle through ZoneSets while dragging a window
|
||||
// Trace::FancyZones::OnKeyDown(info->vkCode, win, false /*control*/, true /*inMoveSize*/);
|
||||
// CycleActiveZoneSet(info->vkCode);
|
||||
// return false;
|
||||
//}
|
||||
if (m_dragEnabled && shift)
|
||||
{
|
||||
return true;
|
||||
@ -397,10 +438,7 @@ void FancyZones::ToggleEditor() noexcept
|
||||
HMONITOR monitor{};
|
||||
HWND foregroundWindow{};
|
||||
|
||||
UINT dpi_x = DPIAware::DEFAULT_DPI;
|
||||
UINT dpi_y = DPIAware::DEFAULT_DPI;
|
||||
|
||||
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings().use_cursorpos_editor_startupscreen;
|
||||
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen;
|
||||
POINT currentCursorPos{};
|
||||
if (use_cursorpos_editor_startupscreen)
|
||||
{
|
||||
@ -433,24 +471,14 @@ void FancyZones::ToggleEditor() noexcept
|
||||
} })
|
||||
.wait();
|
||||
|
||||
if (use_cursorpos_editor_startupscreen)
|
||||
{
|
||||
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
|
||||
}
|
||||
|
||||
auto zoneWindow = iter->second;
|
||||
|
||||
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||
fancyZonesData.CustomZoneSetsToJsonFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
||||
|
||||
const auto taskbar_x_offset = MulDiv(mi.rcWork.left - mi.rcMonitor.left, DPIAware::DEFAULT_DPI, dpi_x);
|
||||
const auto taskbar_y_offset = MulDiv(mi.rcWork.top - mi.rcMonitor.top, DPIAware::DEFAULT_DPI, dpi_y);
|
||||
|
||||
// Do not scale window params by the dpi, that will be done in the editor - see LayoutModel.Apply
|
||||
const auto taskbar_x_offset = mi.rcWork.left - mi.rcMonitor.left;
|
||||
const auto taskbar_y_offset = mi.rcWork.top - mi.rcMonitor.top;
|
||||
const auto x = mi.rcMonitor.left + taskbar_x_offset;
|
||||
const auto y = mi.rcMonitor.top + taskbar_y_offset;
|
||||
const auto width = mi.rcWork.right - mi.rcWork.left;
|
||||
@ -474,9 +502,9 @@ void FancyZones::ToggleEditor() noexcept
|
||||
/*1*/ std::to_wstring(reinterpret_cast<UINT_PTR>(monitor)) + L" " +
|
||||
/*2*/ editorLocation + L" " +
|
||||
/*3*/ zoneWindow->WorkAreaKey() + L" " +
|
||||
/*4*/ ZoneWindowUtils::GetActiveZoneSetTmpPath() + L" " +
|
||||
/*5*/ ZoneWindowUtils::GetAppliedZoneSetTmpPath() + L" " +
|
||||
/*6*/ ZoneWindowUtils::GetCustomZoneSetsTmpPath();
|
||||
/*4*/ L"\"" + ZoneWindowUtils::GetActiveZoneSetTmpPath() + L"\" " +
|
||||
/*5*/ L"\"" + ZoneWindowUtils::GetAppliedZoneSetTmpPath() + L"\" " +
|
||||
/*6*/ L"\"" + ZoneWindowUtils::GetCustomZoneSetsTmpPath() + L"\"";
|
||||
|
||||
SHELLEXECUTEINFO sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
||||
@ -484,6 +512,7 @@ void FancyZones::ToggleEditor() noexcept
|
||||
sei.lpParameters = params.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
ShellExecuteEx(&sei);
|
||||
Trace::FancyZones::EditorLaunched(1);
|
||||
|
||||
// Launch the editor on a background thread
|
||||
// Wait for the editor's process to exit
|
||||
@ -515,14 +544,14 @@ void FancyZones::SettingsChanged() noexcept
|
||||
std::shared_lock readLock(m_lock);
|
||||
// Update the hotkey
|
||||
UnregisterHotKey(m_window, 1);
|
||||
RegisterHotKey(m_window, 1, m_settings->GetSettings().editorHotkey.get_modifiers(), m_settings->GetSettings().editorHotkey.get_code());
|
||||
RegisterHotKey(m_window, 1, m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code());
|
||||
}
|
||||
|
||||
// IZoneWindowHost
|
||||
IFACEMETHODIMP_(void)
|
||||
FancyZones::MoveWindowsOnActiveZoneSetChange() noexcept
|
||||
{
|
||||
if (m_settings->GetSettings().zoneSetChange_moveWindows)
|
||||
if (m_settings->GetSettings()->zoneSetChange_moveWindows)
|
||||
{
|
||||
MoveWindowsOnDisplayChange();
|
||||
}
|
||||
@ -616,21 +645,21 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
|
||||
|
||||
if ((changeType == DisplayChangeType::WorkArea) || (changeType == DisplayChangeType::DisplayChange))
|
||||
{
|
||||
if (m_settings->GetSettings().displayChange_moveWindows)
|
||||
if (m_settings->GetSettings()->displayChange_moveWindows)
|
||||
{
|
||||
MoveWindowsOnDisplayChange();
|
||||
}
|
||||
}
|
||||
else if (changeType == DisplayChangeType::VirtualDesktop)
|
||||
{
|
||||
if (m_settings->GetSettings().virtualDesktopChange_moveWindows)
|
||||
if (m_settings->GetSettings()->virtualDesktopChange_moveWindows)
|
||||
{
|
||||
MoveWindowsOnDisplayChange();
|
||||
}
|
||||
}
|
||||
else if (changeType == DisplayChangeType::Editor)
|
||||
{
|
||||
if (m_settings->GetSettings().zoneSetChange_moveWindows)
|
||||
if (m_settings->GetSettings()->zoneSetChange_moveWindows)
|
||||
{
|
||||
MoveWindowsOnDisplayChange();
|
||||
}
|
||||
@ -647,7 +676,9 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
|
||||
JSONHelpers::FancyZonesDataInstance().SetActiveDeviceId(uniqueId);
|
||||
|
||||
const bool newWorkArea = IsNewWorkArea(m_currentVirtualDesktopId, monitor);
|
||||
const bool flash = m_settings->GetSettings().zoneSetChange_flashZones && newWorkArea;
|
||||
// "Turning FLASHING_ZONE option off"
|
||||
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones && newWorkArea;
|
||||
const bool flash = false;
|
||||
|
||||
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash);
|
||||
if (zoneWindow)
|
||||
@ -706,17 +737,42 @@ bool FancyZones::IsInterestingWindow(HWND window) noexcept
|
||||
CharUpperBuffW(filtered.process_path.data(), (DWORD)filtered.process_path.length());
|
||||
if (m_settings)
|
||||
{
|
||||
for (const auto& excluded : m_settings->GetSettings().excludedAppsArray)
|
||||
const auto& excludedAppsArray = m_settings->GetSettings()->excludedAppsArray;
|
||||
if (find_app_name_in_path(filtered.process_path, excludedAppsArray))
|
||||
{
|
||||
if (filtered.process_path.find(excluded) != std::wstring::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FancyZones::IsCursorTypeIndicatingSizeEvent()
|
||||
{
|
||||
CURSORINFO cursorInfo = { 0 };
|
||||
cursorInfo.cbSize = sizeof(cursorInfo);
|
||||
|
||||
if (::GetCursorInfo(&cursorInfo))
|
||||
{
|
||||
if (::LoadCursor(NULL, IDC_SIZENS) == cursorInfo.hCursor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (::LoadCursor(NULL, IDC_SIZEWE) == cursorInfo.hCursor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (::LoadCursor(NULL, IDC_SIZENESW) == cursorInfo.hCursor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (::LoadCursor(NULL, IDC_SIZENWSE) == cursorInfo.hCursor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FancyZones::UpdateZoneWindows() noexcept
|
||||
{
|
||||
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
|
||||
@ -774,7 +830,7 @@ void FancyZones::MoveWindowsOnDisplayChange() noexcept
|
||||
EnumWindows(callback, reinterpret_cast<LPARAM>(this));
|
||||
}
|
||||
|
||||
void FancyZones::UpdateDragState(require_write_lock) noexcept
|
||||
void FancyZones::UpdateDragState(HWND window, require_write_lock) noexcept
|
||||
{
|
||||
const bool shift = GetAsyncKeyState(VK_SHIFT) & 0x8000;
|
||||
const bool mouseL = GetAsyncKeyState(VK_LBUTTON) & 0x8000;
|
||||
@ -795,7 +851,7 @@ void FancyZones::UpdateDragState(require_write_lock) noexcept
|
||||
mouse |= mouseR;
|
||||
}
|
||||
|
||||
if (m_settings->GetSettings().shiftDrag)
|
||||
if (m_settings->GetSettings()->shiftDrag)
|
||||
{
|
||||
m_dragEnabled = (shift | mouse);
|
||||
}
|
||||
@ -803,6 +859,23 @@ void FancyZones::UpdateDragState(require_write_lock) noexcept
|
||||
{
|
||||
m_dragEnabled = !(shift | mouse);
|
||||
}
|
||||
|
||||
const bool windowElevated = IsProcessOfWindowElevated(window);
|
||||
static const bool meElevated = is_process_elevated();
|
||||
static bool warning_shown = false;
|
||||
if (windowElevated && !meElevated)
|
||||
{
|
||||
m_dragEnabled = false;
|
||||
if (!warning_shown && !is_cant_drag_elevated_warning_disabled())
|
||||
{
|
||||
std::vector<notifications::action_t> actions = {
|
||||
notifications::link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_LEARN_MORE), L"https://aka.ms/powertoysDetectedElevatedHelp" },
|
||||
notifications::link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN), L"powertoys://cant_drag_elevated_disable/" }
|
||||
};
|
||||
notifications::show_toast_with_activations(GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED), {}, std::move(actions));
|
||||
warning_shown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
|
||||
@ -830,17 +903,43 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
auto window = GetForegroundWindow();
|
||||
if (IsInterestingWindow(window))
|
||||
{
|
||||
const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||
if (monitor)
|
||||
const HMONITOR current = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
|
||||
if (current)
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
std::vector<HMONITOR> monitorInfo = GetMonitorsSorted();
|
||||
if (monitorInfo.size() > 1)
|
||||
{
|
||||
const auto& zoneWindowPtr = iter->second;
|
||||
zoneWindowPtr->MoveWindowIntoZoneByDirection(window, vkCode);
|
||||
return true;
|
||||
// Multi monitor environment.
|
||||
auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current);
|
||||
do
|
||||
{
|
||||
if (MoveWindowIntoZoneByDirection(*currMonitorInfo, window, vkCode, false /* cycle through zones */))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
|
||||
if (vkCode == VK_RIGHT)
|
||||
{
|
||||
currMonitorInfo = std::next(currMonitorInfo);
|
||||
if (currMonitorInfo == std::end(monitorInfo))
|
||||
{
|
||||
currMonitorInfo = std::begin(monitorInfo);
|
||||
}
|
||||
}
|
||||
else if (vkCode == VK_LEFT)
|
||||
{
|
||||
if (currMonitorInfo == std::begin(monitorInfo))
|
||||
{
|
||||
currMonitorInfo = std::end(monitorInfo);
|
||||
}
|
||||
currMonitorInfo = std::prev(currMonitorInfo);
|
||||
}
|
||||
} while (*currMonitorInfo != current);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single monitor environment.
|
||||
return MoveWindowIntoZoneByDirection(current, window, vkCode, true /* cycle through zones */);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -849,23 +948,10 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
|
||||
void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept
|
||||
{
|
||||
// Only enter move/size if the cursor is inside the window rect by a certain padding.
|
||||
// This prevents resize from triggering zones.
|
||||
RECT windowRect{};
|
||||
::GetWindowRect(window, &windowRect);
|
||||
|
||||
const auto padding_x = 8;
|
||||
const auto padding_y = 6;
|
||||
windowRect.top += padding_y;
|
||||
windowRect.left += padding_x;
|
||||
windowRect.right -= padding_x;
|
||||
windowRect.bottom -= padding_y;
|
||||
|
||||
if (PtInRect(&windowRect, ptScreen) == FALSE)
|
||||
if (IsCursorTypeIndicatingSizeEvent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_inMoveSize = true;
|
||||
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
@ -877,17 +963,37 @@ void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT cons
|
||||
m_windowMoveSize = window;
|
||||
|
||||
// This updates m_dragEnabled depending on if the shift key is being held down.
|
||||
UpdateDragState(writeLock);
|
||||
UpdateDragState(window, writeLock);
|
||||
|
||||
if (m_dragEnabled)
|
||||
{
|
||||
m_zoneWindowMoveSize = iter->second;
|
||||
m_zoneWindowMoveSize->MoveSizeEnter(window, m_dragEnabled);
|
||||
if (m_settings->GetSettings()->showZonesOnAllMonitors)
|
||||
{
|
||||
for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap)
|
||||
{
|
||||
// Skip calling ShowZoneWindow for iter->second (m_zoneWindowMoveSize) since it
|
||||
// was already called in MoveSizeEnter
|
||||
const bool moveSizeEnterCalled = zoneWindow == m_zoneWindowMoveSize;
|
||||
if (zoneWindow && !moveSizeEnterCalled)
|
||||
{
|
||||
zoneWindow->ShowZoneWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_zoneWindowMoveSize)
|
||||
{
|
||||
m_zoneWindowMoveSize->MoveSizeCancel();
|
||||
m_zoneWindowMoveSize->RestoreOrginalTransparency();
|
||||
m_zoneWindowMoveSize = nullptr;
|
||||
for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap)
|
||||
{
|
||||
if (zoneWindow)
|
||||
{
|
||||
zoneWindow->HideZoneWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -924,6 +1030,15 @@ void FancyZones::MoveSizeEndInternal(HWND window, POINT const& ptScreen, require
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also, hide all windows (regardless of settings)
|
||||
for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap)
|
||||
{
|
||||
if (zoneWindow)
|
||||
{
|
||||
zoneWindow->HideZoneWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZones::MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept
|
||||
@ -931,16 +1046,24 @@ void FancyZones::MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen,
|
||||
if (m_inMoveSize)
|
||||
{
|
||||
// This updates m_dragEnabled depending on if the shift key is being held down.
|
||||
UpdateDragState(writeLock);
|
||||
UpdateDragState(m_windowMoveSize, writeLock);
|
||||
|
||||
if (m_zoneWindowMoveSize)
|
||||
{
|
||||
// Update the ZoneWindow already handling move/size
|
||||
if (!m_dragEnabled)
|
||||
{
|
||||
// Drag got disabled, tell it to cancel and clear out m_zoneWindowMoveSize
|
||||
auto zoneWindow = std::move(m_zoneWindowMoveSize);
|
||||
zoneWindow->MoveSizeCancel();
|
||||
// Drag got disabled, tell it to cancel and hide all windows
|
||||
m_zoneWindowMoveSize = nullptr;
|
||||
|
||||
for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap)
|
||||
{
|
||||
if (zoneWindow)
|
||||
{
|
||||
zoneWindow->RestoreOrginalTransparency();
|
||||
zoneWindow->HideZoneWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -950,12 +1073,20 @@ void FancyZones::MoveSizeUpdateInternal(HMONITOR monitor, POINT const& ptScreen,
|
||||
if (iter->second != m_zoneWindowMoveSize)
|
||||
{
|
||||
// The drag has moved to a different monitor.
|
||||
auto const isDragEnabled = m_zoneWindowMoveSize->IsDragEnabled();
|
||||
m_zoneWindowMoveSize->MoveSizeCancel();
|
||||
m_zoneWindowMoveSize->RestoreOrginalTransparency();
|
||||
|
||||
if (!m_settings->GetSettings()->showZonesOnAllMonitors)
|
||||
{
|
||||
m_zoneWindowMoveSize->HideZoneWindow();
|
||||
}
|
||||
m_zoneWindowMoveSize = iter->second;
|
||||
m_zoneWindowMoveSize->MoveSizeEnter(m_windowMoveSize, isDragEnabled);
|
||||
m_zoneWindowMoveSize->MoveSizeEnter(m_windowMoveSize, m_zoneWindowMoveSize->IsDragEnabled());
|
||||
}
|
||||
|
||||
for (auto [keyMonitor, zoneWindow] : m_zoneWindowMap)
|
||||
{
|
||||
zoneWindow->MoveSizeUpdate(ptScreen, m_dragEnabled);
|
||||
}
|
||||
m_zoneWindowMoveSize->MoveSizeUpdate(ptScreen, m_dragEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1075,6 +1206,46 @@ void FancyZones::OnEditorExitEvent() noexcept
|
||||
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
|
||||
}
|
||||
|
||||
std::vector<HMONITOR> FancyZones::GetMonitorsSorted() noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
|
||||
auto monitorInfo = GetRawMonitorData();
|
||||
OrderMonitors(monitorInfo);
|
||||
std::vector<HMONITOR> output;
|
||||
std::transform(std::begin(monitorInfo), std::end(monitorInfo), std::back_inserter(output), [](const auto& info) { return info.first; });
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<std::pair<HMONITOR, RECT>> FancyZones::GetRawMonitorData() noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo;
|
||||
for (const auto& [monitor, window] : m_zoneWindowMap)
|
||||
{
|
||||
if (window->ActiveZoneSet() != nullptr)
|
||||
{
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
GetMonitorInfo(monitor, &mi);
|
||||
monitorInfo.push_back({ monitor, mi.rcMonitor });
|
||||
}
|
||||
}
|
||||
return monitorInfo;
|
||||
}
|
||||
|
||||
bool FancyZones::MoveWindowIntoZoneByDirection(HMONITOR monitor, HWND window, DWORD vkCode, bool cycle)
|
||||
{
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != std::end(m_zoneWindowMap))
|
||||
{
|
||||
const auto& zoneWindowPtr = iter->second;
|
||||
return zoneWindowPtr->MoveWindowIntoZoneByDirection(window, vkCode, cycle);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept
|
||||
{
|
||||
if (!settings)
|
||||
@ -1083,4 +1254,4 @@ winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com
|
||||
}
|
||||
|
||||
return winrt::make_self<FancyZones>(hinstance, settings);
|
||||
}
|
||||
}
|
||||
|
@ -1,106 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
interface IZoneWindow;
|
||||
interface IFancyZonesSettings;
|
||||
interface IZoneSet;
|
||||
|
||||
interface __declspec(uuid("{50D3F0F5-736E-4186-BDF4-3D6BEE150C3A}")) IFancyZones : public IUnknown
|
||||
{
|
||||
/**
|
||||
* Start and initialize FancyZones.
|
||||
*/
|
||||
IFACEMETHOD_(void, Run)() = 0;
|
||||
/**
|
||||
* Stop FancyZones and do the clean up.
|
||||
*/
|
||||
IFACEMETHOD_(void, Destroy)() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Core FancyZones functionality.
|
||||
*/
|
||||
interface __declspec(uuid("{2CB37E8F-87E6-4AEC-B4B2-E0FDC873343F}")) IFancyZonesCallback : public IUnknown
|
||||
{
|
||||
/**
|
||||
* @returns Boolean indicating whether a move/size operation is currently active.
|
||||
*/
|
||||
IFACEMETHOD_(bool, InMoveSize)() = 0;
|
||||
/**
|
||||
* A window is being moved or resized. Track down window position and give zone layout
|
||||
* hints if dragging functionality is enabled.
|
||||
*
|
||||
* @param window Handle of window being moved or resized.
|
||||
* @param monitor Handle of monitor on which windows is moving / resizing.
|
||||
* @param ptScreen Cursor coordinates.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveSizeStart)(HWND window, HMONITOR monitor, POINT const& ptScreen) = 0;
|
||||
/**
|
||||
* A window has changed location, shape, or size. Track down window position and give zone layout
|
||||
* hints if dragging functionality is enabled.
|
||||
*
|
||||
* @param monitor Handle of monitor on which windows is moving / resizing.
|
||||
* @param ptScreen Cursor coordinates.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveSizeUpdate)(HMONITOR monitor, POINT const& ptScreen) = 0;
|
||||
/**
|
||||
* The movement or resizing of a window has finished. Assign window to the zone if it
|
||||
* is dropped within zone borders.
|
||||
*
|
||||
* @param window Handle of window being moved or resized.
|
||||
* @param ptScreen Cursor coordinates where window is droped.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveSizeEnd)(HWND window, POINT const& ptScreen) = 0;
|
||||
/**
|
||||
* Inform FancyZones that user has switched between virtual desktops.
|
||||
*/
|
||||
IFACEMETHOD_(void, VirtualDesktopChanged)() = 0;
|
||||
/**
|
||||
* Inform FancyZones that new window is created. FancyZones will try to assign it to the
|
||||
* zone insde active zone layout (if information about last zone, in which window was located
|
||||
* before being closed, is available).
|
||||
*
|
||||
* @param window Handle of newly created window.
|
||||
*/
|
||||
IFACEMETHOD_(void, WindowCreated)(HWND window) = 0;
|
||||
/**
|
||||
* Process keyboard event.
|
||||
*
|
||||
* @param info Information about low level keyboard event.
|
||||
* @returns Boolean indicating if this event should be passed on further to other applications
|
||||
* in event chain, or should it be suppressed.
|
||||
*/
|
||||
IFACEMETHOD_(bool, OnKeyDown)(PKBDLLHOOKSTRUCT info) = 0;
|
||||
/**
|
||||
* Toggle FancyZones editor application.
|
||||
*/
|
||||
IFACEMETHOD_(void, ToggleEditor)() = 0;
|
||||
/**
|
||||
* Callback triggered when user changes FancyZones settings.
|
||||
*/
|
||||
IFACEMETHOD_(void, SettingsChanged)() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper functions used by each ZoneWindow (representing work area).
|
||||
*/
|
||||
interface __declspec(uuid("{5C8D99D6-34B2-4F4A-A8E5-7483F6869775}")) IZoneWindowHost : public IUnknown
|
||||
{
|
||||
/**
|
||||
* Assign window to appropriate zone inside new zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowsOnActiveZoneSetChange)() = 0;
|
||||
/**
|
||||
* @returns Color used to highlight zone while giving zone layout hints.
|
||||
*/
|
||||
IFACEMETHOD_(COLORREF, GetZoneHighlightColor)() = 0;
|
||||
/**
|
||||
* @returns ZoneWindow (representing work area) currently being processed.
|
||||
*/
|
||||
IFACEMETHOD_(IZoneWindow*, GetParentZoneWindow) (HMONITOR monitor) = 0;
|
||||
/**
|
||||
* @returns Integer in range [0, 100] indicating opacity of highlited zone (while giving zone layout hints).
|
||||
*/
|
||||
IFACEMETHOD_(int, GetZoneHighlightOpacity)() = 0;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept;
|
||||
#pragma once
|
||||
|
||||
interface IZoneWindow;
|
||||
interface IFancyZonesSettings;
|
||||
interface IZoneSet;
|
||||
|
||||
interface __declspec(uuid("{50D3F0F5-736E-4186-BDF4-3D6BEE150C3A}")) IFancyZones : public IUnknown
|
||||
{
|
||||
/**
|
||||
* Start and initialize FancyZones.
|
||||
*/
|
||||
IFACEMETHOD_(void, Run)() = 0;
|
||||
/**
|
||||
* Stop FancyZones and do the clean up.
|
||||
*/
|
||||
IFACEMETHOD_(void, Destroy)() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Core FancyZones functionality.
|
||||
*/
|
||||
interface __declspec(uuid("{2CB37E8F-87E6-4AEC-B4B2-E0FDC873343F}")) IFancyZonesCallback : public IUnknown
|
||||
{
|
||||
/**
|
||||
* @returns Boolean indicating whether a move/size operation is currently active.
|
||||
*/
|
||||
IFACEMETHOD_(bool, InMoveSize)() = 0;
|
||||
/**
|
||||
* A window is being moved or resized. Track down window position and give zone layout
|
||||
* hints if dragging functionality is enabled.
|
||||
*
|
||||
* @param window Handle of window being moved or resized.
|
||||
* @param monitor Handle of monitor on which windows is moving / resizing.
|
||||
* @param ptScreen Cursor coordinates.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveSizeStart)(HWND window, HMONITOR monitor, POINT const& ptScreen) = 0;
|
||||
/**
|
||||
* A window has changed location, shape, or size. Track down window position and give zone layout
|
||||
* hints if dragging functionality is enabled.
|
||||
*
|
||||
* @param monitor Handle of monitor on which windows is moving / resizing.
|
||||
* @param ptScreen Cursor coordinates.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveSizeUpdate)(HMONITOR monitor, POINT const& ptScreen) = 0;
|
||||
/**
|
||||
* The movement or resizing of a window has finished. Assign window to the zone if it
|
||||
* is dropped within zone borders.
|
||||
*
|
||||
* @param window Handle of window being moved or resized.
|
||||
* @param ptScreen Cursor coordinates where window is droped.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveSizeEnd)(HWND window, POINT const& ptScreen) = 0;
|
||||
/**
|
||||
* Inform FancyZones that user has switched between virtual desktops.
|
||||
*/
|
||||
IFACEMETHOD_(void, VirtualDesktopChanged)() = 0;
|
||||
/**
|
||||
* Inform FancyZones that new window is created. FancyZones will try to assign it to the
|
||||
* zone insde active zone layout (if information about last zone, in which window was located
|
||||
* before being closed, is available).
|
||||
*
|
||||
* @param window Handle of newly created window.
|
||||
*/
|
||||
IFACEMETHOD_(void, WindowCreated)(HWND window) = 0;
|
||||
/**
|
||||
* Process keyboard event.
|
||||
*
|
||||
* @param info Information about low level keyboard event.
|
||||
* @returns Boolean indicating if this event should be passed on further to other applications
|
||||
* in event chain, or should it be suppressed.
|
||||
*/
|
||||
IFACEMETHOD_(bool, OnKeyDown)(PKBDLLHOOKSTRUCT info) = 0;
|
||||
/**
|
||||
* Toggle FancyZones editor application.
|
||||
*/
|
||||
IFACEMETHOD_(void, ToggleEditor)() = 0;
|
||||
/**
|
||||
* Callback triggered when user changes FancyZones settings.
|
||||
*/
|
||||
IFACEMETHOD_(void, SettingsChanged)() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper functions used by each ZoneWindow (representing work area).
|
||||
*/
|
||||
interface __declspec(uuid("{5C8D99D6-34B2-4F4A-A8E5-7483F6869775}")) IZoneWindowHost : public IUnknown
|
||||
{
|
||||
/**
|
||||
* Assign window to appropriate zone inside new zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowsOnActiveZoneSetChange)() = 0;
|
||||
/**
|
||||
* @returns Basic zone color.
|
||||
*/
|
||||
IFACEMETHOD_(COLORREF, GetZoneColor)() = 0;
|
||||
/**
|
||||
* @returns Zone border color.
|
||||
*/
|
||||
IFACEMETHOD_(COLORREF, GetZoneBorderColor)() = 0;
|
||||
/**
|
||||
* @returns Color used to highlight zone while giving zone layout hints.
|
||||
*/
|
||||
IFACEMETHOD_(COLORREF, GetZoneHighlightColor)() = 0;
|
||||
/**
|
||||
* @returns ZoneWindow (representing work area) currently being processed.
|
||||
*/
|
||||
IFACEMETHOD_(IZoneWindow*, GetParentZoneWindow) (HMONITOR monitor) = 0;
|
||||
/**
|
||||
* @returns Integer in range [0, 100] indicating opacity of highlited zone (while giving zone layout hints).
|
||||
*/
|
||||
IFACEMETHOD_(int, GetZoneHighlightOpacity)() = 0;
|
||||
/**
|
||||
* @returns Bool indicating if dragged window should be transparrent
|
||||
*/
|
||||
IFACEMETHOD_(bool, isMakeDraggedWindowTransparentActive) () = 0;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance, const winrt::com_ptr<IFancyZonesSettings>& settings) noexcept;
|
||||
|
@ -46,9 +46,13 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -46,6 +47,84 @@ namespace
|
||||
|
||||
namespace JSONHelpers
|
||||
{
|
||||
bool isValidGuid(const std::wstring& str)
|
||||
{
|
||||
GUID id;
|
||||
return SUCCEEDED_LOG(CLSIDFromString(str.c_str(), &id));
|
||||
}
|
||||
|
||||
bool isValidDeviceId(const std::wstring& str)
|
||||
{
|
||||
std::wstring monitorName;
|
||||
std::wstring temp;
|
||||
std::vector<std::wstring> parts;
|
||||
std::wstringstream wss(str);
|
||||
|
||||
/*
|
||||
Important fix for device info that contains a '_' in the name:
|
||||
1. first search for '#'
|
||||
2. Then split the remaining string by '_'
|
||||
*/
|
||||
|
||||
// Step 1: parse the name until the #, then to the '_'
|
||||
if (str.find(L'#') != std::string::npos)
|
||||
{
|
||||
std::getline(wss, temp, L'#');
|
||||
|
||||
monitorName = temp;
|
||||
|
||||
if (!std::getline(wss, temp, L'_'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
monitorName += L"#" + temp;
|
||||
parts.push_back(monitorName);
|
||||
}
|
||||
|
||||
// Step 2: parse the rest of the id
|
||||
while (std::getline(wss, temp, L'_'))
|
||||
{
|
||||
parts.push_back(temp);
|
||||
}
|
||||
|
||||
if (parts.size() != 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
|
||||
1. monitor id [string]
|
||||
2. width of device [int]
|
||||
3. height of device [int]
|
||||
4. virtual desktop id (GUID) [string]
|
||||
*/
|
||||
try
|
||||
{
|
||||
//check if resolution contain only digits
|
||||
for (const auto& c : parts[1])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
for (const auto& c : parts[2])
|
||||
{
|
||||
std::stoi(std::wstring(&c));
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isValidGuid(parts[3]) || parts[0].empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
json::JsonArray NumVecToJsonArray(const std::vector<int>& vec)
|
||||
{
|
||||
json::JsonArray arr;
|
||||
@ -525,9 +604,6 @@ namespace JSONHelpers
|
||||
|
||||
if (!std::filesystem::exists(jsonFilePath))
|
||||
{
|
||||
TmpMigrateAppliedZoneSetsFromRegistry();
|
||||
|
||||
// Custom zone sets have to be migrated after applied zone sets!
|
||||
MigrateCustomZoneSetsFromRegistry();
|
||||
|
||||
SaveFancyZonesData();
|
||||
@ -560,56 +636,6 @@ namespace JSONHelpers
|
||||
json::to_file(jsonFilePath, root);
|
||||
}
|
||||
|
||||
void FancyZonesData::TmpMigrateAppliedZoneSetsFromRegistry()
|
||||
{
|
||||
std::wregex ex(L"^[0-9]{3,4}_[0-9]{3,4}$");
|
||||
|
||||
std::scoped_lock lock{ dataLock };
|
||||
wchar_t key[256];
|
||||
StringCchPrintf(key, ARRAYSIZE(key), L"%s", RegistryHelpers::REG_SETTINGS);
|
||||
HKEY hkey;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
|
||||
{
|
||||
wchar_t resolutionKey[256]{};
|
||||
DWORD resolutionKeyLength = ARRAYSIZE(resolutionKey);
|
||||
DWORD i = 0;
|
||||
while (RegEnumKeyW(hkey, i++, resolutionKey, resolutionKeyLength) == ERROR_SUCCESS)
|
||||
{
|
||||
std::wstring resolution{ resolutionKey };
|
||||
wchar_t appliedZoneSetskey[256];
|
||||
StringCchPrintf(appliedZoneSetskey, ARRAYSIZE(appliedZoneSetskey), L"%s\\%s", RegistryHelpers::REG_SETTINGS, resolutionKey);
|
||||
HKEY appliedZoneSetsHkey;
|
||||
if (std::regex_match(resolution, ex) && RegOpenKeyExW(HKEY_CURRENT_USER, appliedZoneSetskey, 0, KEY_ALL_ACCESS, &appliedZoneSetsHkey) == ERROR_SUCCESS)
|
||||
{
|
||||
ZoneSetPersistedDataOLD data;
|
||||
DWORD dataSize = sizeof(data);
|
||||
wchar_t value[256]{};
|
||||
DWORD valueLength = ARRAYSIZE(value);
|
||||
DWORD i = 0;
|
||||
|
||||
while (RegEnumValueW(appliedZoneSetsHkey, i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
|
||||
{
|
||||
ZoneSetData appliedZoneSetData;
|
||||
appliedZoneSetData.type = TypeFromLayoutId(data.LayoutId);
|
||||
if (appliedZoneSetData.type != ZoneSetLayoutType::Custom)
|
||||
{
|
||||
appliedZoneSetData.uuid = std::wstring{ value };
|
||||
}
|
||||
else
|
||||
{
|
||||
// uuid is changed later to actual uuid when migrating custom zone sets
|
||||
appliedZoneSetData.uuid = std::to_wstring(data.LayoutId);
|
||||
}
|
||||
appliedZoneSetsMap[value] = appliedZoneSetData;
|
||||
dataSize = sizeof(data);
|
||||
valueLength = ARRAYSIZE(value);
|
||||
}
|
||||
}
|
||||
resolutionKeyLength = ARRAYSIZE(resolutionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZonesData::MigrateCustomZoneSetsFromRegistry()
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
@ -630,29 +656,19 @@ namespace JSONHelpers
|
||||
zoneSetData.type = static_cast<CustomLayoutType>(data[2]);
|
||||
// int version = data[0] * 256 + data[1]; - Not used anymore
|
||||
|
||||
std::wstring uuid = std::to_wstring(data[3] * 256 + data[4]);
|
||||
auto it = std::find_if(appliedZoneSetsMap.begin(), appliedZoneSetsMap.end(), [&uuid](std::pair<std::wstring, ZoneSetData> zoneSetMap) {
|
||||
return zoneSetMap.second.uuid.compare(uuid) == 0;
|
||||
});
|
||||
GUID guid;
|
||||
auto result = CoCreateGuid(&guid);
|
||||
if (result != S_OK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
wil::unique_cotaskmem_string guidString;
|
||||
if (!SUCCEEDED_LOG(StringFromCLSID(guid, &guidString)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it != appliedZoneSetsMap.end())
|
||||
{
|
||||
it->second.uuid = uuid = it->first;
|
||||
}
|
||||
else
|
||||
{
|
||||
GUID guid;
|
||||
auto result = CoCreateGuid(&guid);
|
||||
if (result != S_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
wil::unique_cotaskmem_string guidString;
|
||||
if (SUCCEEDED_LOG(StringFromCLSID(guid, &guidString)))
|
||||
{
|
||||
it->second.uuid = uuid = guidString.get();
|
||||
}
|
||||
}
|
||||
std::wstring uuid = guidString.get();
|
||||
|
||||
switch (zoneSetData.type)
|
||||
{
|
||||
@ -660,14 +676,14 @@ namespace JSONHelpers
|
||||
int j = 5;
|
||||
GridLayoutInfo zoneSetInfo(GridLayoutInfo::Minimal{ .rows = data[j++], .columns = data[j++] });
|
||||
|
||||
for (int row = 0; row < zoneSetInfo.rows(); row++)
|
||||
for (int row = 0; row < zoneSetInfo.rows(); row++, j+=2)
|
||||
{
|
||||
zoneSetInfo.rowsPercents()[row] = data[j++] * 256 + data[j++];
|
||||
zoneSetInfo.rowsPercents()[row] = data[j] * 256 + data[j+1];
|
||||
}
|
||||
|
||||
for (int col = 0; col < zoneSetInfo.columns(); col++)
|
||||
for (int col = 0; col < zoneSetInfo.columns(); col++, j+=2)
|
||||
{
|
||||
zoneSetInfo.columnsPercents()[col] = data[j++] * 256 + data[j++];
|
||||
zoneSetInfo.columnsPercents()[col] = data[j] * 256 + data[j+1];
|
||||
}
|
||||
|
||||
for (int row = 0; row < zoneSetInfo.rows(); row++)
|
||||
@ -733,10 +749,14 @@ namespace JSONHelpers
|
||||
try
|
||||
{
|
||||
ZoneSetData zoneSetData;
|
||||
|
||||
zoneSetData.uuid = zoneSet.GetNamedString(L"uuid");
|
||||
zoneSetData.type = TypeFromString(std::wstring{ zoneSet.GetNamedString(L"type") });
|
||||
|
||||
if (!isValidGuid(zoneSetData.uuid))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return zoneSetData;
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
@ -768,6 +788,11 @@ namespace JSONHelpers
|
||||
result.data.deviceId = zoneSet.GetNamedString(L"device-id");
|
||||
result.data.zoneSetUuid = zoneSet.GetNamedString(L"zoneset-uuid");
|
||||
|
||||
if (!isValidGuid(result.data.zoneSetUuid) || !isValidDeviceId(result.data.deviceId))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
@ -796,6 +821,10 @@ namespace JSONHelpers
|
||||
DeviceInfoJSON result;
|
||||
|
||||
result.deviceId = device.GetNamedString(L"device-id");
|
||||
if (!isValidDeviceId(result.deviceId))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (auto zoneSet = ZoneSetData::FromJson(device.GetNamedObject(L"active-zoneset")); zoneSet.has_value())
|
||||
{
|
||||
@ -988,6 +1017,11 @@ namespace JSONHelpers
|
||||
CustomZoneSetJSON result;
|
||||
|
||||
result.uuid = customZoneSet.GetNamedString(L"uuid");
|
||||
if (!isValidGuid(result.uuid))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
result.data.name = customZoneSet.GetNamedString(L"name");
|
||||
|
||||
json::JsonObject infoJson = customZoneSet.GetNamedObject(L"info");
|
||||
|
@ -16,6 +16,11 @@ namespace JSONHelpers
|
||||
{
|
||||
constexpr int MAX_ZONE_COUNT = 50;
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
bool isValidGuid(const std::wstring& str);
|
||||
bool isValidDeviceId(const std::wstring& str);
|
||||
#endif
|
||||
|
||||
enum class ZoneSetLayoutType : int
|
||||
{
|
||||
Blank = -1,
|
||||
@ -202,7 +207,6 @@ namespace JSONHelpers
|
||||
#if defined(UNIT_TESTS)
|
||||
inline void clear_data()
|
||||
{
|
||||
appliedZoneSetsMap.clear();
|
||||
appZoneHistoryMap.clear();
|
||||
deviceInfoMap.clear();
|
||||
customZoneSetsMap.clear();
|
||||
@ -249,10 +253,8 @@ namespace JSONHelpers
|
||||
void SaveFancyZonesData() const;
|
||||
|
||||
private:
|
||||
void TmpMigrateAppliedZoneSetsFromRegistry();
|
||||
void MigrateCustomZoneSetsFromRegistry();
|
||||
|
||||
std::unordered_map<std::wstring, ZoneSetData> appliedZoneSetsMap{};
|
||||
std::unordered_map<std::wstring, AppZoneHistoryData> appZoneHistoryMap{};
|
||||
std::unordered_map<std::wstring, DeviceInfoData> deviceInfoMap{};
|
||||
std::unordered_map<std::wstring, CustomZoneSetData> customZoneSetsMap{};
|
||||
|
@ -19,7 +19,7 @@ public:
|
||||
IFACEMETHODIMP_(bool) GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_sizeg) noexcept;
|
||||
IFACEMETHODIMP_(void) SetConfig(PCWSTR config) noexcept;
|
||||
IFACEMETHODIMP_(void) CallCustomAction(PCWSTR action) noexcept;
|
||||
IFACEMETHODIMP_(Settings) GetSettings() noexcept { return m_settings; }
|
||||
IFACEMETHODIMP_(const Settings*) GetSettings() const noexcept { return &m_settings; }
|
||||
|
||||
private:
|
||||
void LoadSettings(PCWSTR config, bool fromFile) noexcept;
|
||||
@ -36,17 +36,22 @@ private:
|
||||
PCWSTR name;
|
||||
bool* value;
|
||||
int resourceId;
|
||||
} m_configBools[8] = {
|
||||
} m_configBools[9 /* 10 */] = { // "Turning FLASHING_ZONE option off"
|
||||
{ L"fancyzones_shiftDrag", &m_settings.shiftDrag, IDS_SETTING_DESCRIPTION_SHIFTDRAG },
|
||||
{ L"fancyzones_overrideSnapHotkeys", &m_settings.overrideSnapHotkeys, IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS },
|
||||
{ L"fancyzones_zoneSetChange_flashZones", &m_settings.zoneSetChange_flashZones, IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES },
|
||||
// "Turning FLASHING_ZONE option off"
|
||||
//{ L"fancyzones_zoneSetChange_flashZones", &m_settings.zoneSetChange_flashZones, IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES },
|
||||
{ L"fancyzones_displayChange_moveWindows", &m_settings.displayChange_moveWindows, IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS },
|
||||
{ L"fancyzones_zoneSetChange_moveWindows", &m_settings.zoneSetChange_moveWindows, IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS },
|
||||
{ L"fancyzones_virtualDesktopChange_moveWindows", &m_settings.virtualDesktopChange_moveWindows, IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS },
|
||||
{ L"fancyzones_appLastZone_moveWindows", &m_settings.appLastZone_moveWindows, IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS },
|
||||
{ L"use_cursorpos_editor_startupscreen", &m_settings.use_cursorpos_editor_startupscreen, IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN },
|
||||
{ L"fancyzones_show_on_all_monitors", &m_settings.showZonesOnAllMonitors, IDS_SETTING_DESCRIPTION_SHOW_FANCY_ZONES_ON_ALL_MONITORS},
|
||||
{ L"fancyzones_makeDraggedWindowTransparent", &m_settings.makeDraggedWindowTransparent, IDS_SETTING_DESCRIPTION_MAKE_DRAGGED_WINDOW_TRANSPARENT},
|
||||
};
|
||||
|
||||
const std::wstring m_zoneColorName = L"fancyzones_zoneColor";
|
||||
const std::wstring m_zoneBorderColorName = L"fancyzones_zoneBorderColor";
|
||||
const std::wstring m_zoneHiglightName = L"fancyzones_zoneHighlightColor";
|
||||
const std::wstring m_editorHotkeyName = L"fancyzones_editor_hotkey";
|
||||
const std::wstring m_excludedAppsName = L"fancyzones_excluded_apps";
|
||||
@ -78,8 +83,12 @@ IFACEMETHODIMP_(bool) FancyZonesSettings::GetConfig(_Out_ PWSTR buffer, _Out_ in
|
||||
settings.add_bool_toogle(setting.name, setting.resourceId, *setting.value);
|
||||
}
|
||||
|
||||
settings.add_int_spinner(m_zoneHighlightOpacity, IDS_SETTINGS_HIGHLIGHT_OPACITY, m_settings.zoneHighlightOpacity, 0, 100, 1);
|
||||
settings.add_color_picker(m_zoneHiglightName, IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, m_settings.zoneHightlightColor);
|
||||
settings.add_color_picker(m_zoneColorName, IDS_SETTING_DESCRIPTION_ZONECOLOR, m_settings.zoneColor);
|
||||
settings.add_color_picker(m_zoneBorderColorName, IDS_SETTING_DESCRIPTION_ZONE_BORDER_COLOR, m_settings.zoneBorderColor);
|
||||
|
||||
settings.add_int_spinner(m_zoneHighlightOpacity, IDS_SETTINGS_HIGHLIGHT_OPACITY, m_settings.zoneHighlightOpacity, 0, 100, 1);
|
||||
|
||||
settings.add_multiline_string(m_excludedAppsName, IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, m_settings.excludedApps);
|
||||
|
||||
return settings.serialize_to_buffer(buffer, buffer_size);
|
||||
@ -124,6 +133,16 @@ void FancyZonesSettings::LoadSettings(PCWSTR config, bool fromFile) noexcept try
|
||||
}
|
||||
}
|
||||
|
||||
if (auto val = values.get_string_value(m_zoneColorName))
|
||||
{
|
||||
m_settings.zoneColor = std::move(*val);
|
||||
}
|
||||
|
||||
if (auto val = values.get_string_value(m_zoneBorderColorName))
|
||||
{
|
||||
m_settings.zoneBorderColor = std::move(*val);
|
||||
}
|
||||
|
||||
if (auto val = values.get_string_value(m_zoneHiglightName))
|
||||
{
|
||||
m_settings.zoneHightlightColor = std::move(*val);
|
||||
@ -173,6 +192,8 @@ void FancyZonesSettings::SaveSettings() noexcept try
|
||||
values.add_property(setting.name, *setting.value);
|
||||
}
|
||||
|
||||
values.add_property(m_zoneColorName, m_settings.zoneColor);
|
||||
values.add_property(m_zoneBorderColorName, m_settings.zoneBorderColor);
|
||||
values.add_property(m_zoneHiglightName, m_settings.zoneHightlightColor);
|
||||
values.add_property(m_zoneHighlightOpacity, m_settings.zoneHighlightOpacity);
|
||||
values.add_property(m_editorHotkeyName, m_settings.editorHotkey.get_json());
|
||||
|
@ -14,8 +14,12 @@ struct Settings
|
||||
bool overrideSnapHotkeys = false;
|
||||
bool appLastZone_moveWindows = false;
|
||||
bool use_cursorpos_editor_startupscreen = true;
|
||||
std::wstring zoneHightlightColor = L"#0078D7";
|
||||
int zoneHighlightOpacity = 90;
|
||||
bool showZonesOnAllMonitors = false;
|
||||
bool makeDraggedWindowTransparent = true;
|
||||
std::wstring zoneColor = L"#F5FCFF";
|
||||
std::wstring zoneBorderColor = L"#FFFFFF";
|
||||
std::wstring zoneHightlightColor = L"#008CFF";
|
||||
int zoneHighlightOpacity = 50;
|
||||
PowerToysSettings::HotkeyObject editorHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, VK_OEM_3);
|
||||
std::wstring excludedApps = L"";
|
||||
std::vector<std::wstring> excludedAppsArray;
|
||||
@ -28,7 +32,7 @@ interface __declspec(uuid("{BA4E77C4-6F44-4C5D-93D3-CBDE880495C2}")) IFancyZones
|
||||
IFACEMETHOD_(bool, GetConfig)(_Out_ PWSTR buffer, _Out_ int *buffer_size) = 0;
|
||||
IFACEMETHOD_(void, SetConfig)(PCWSTR serializedPowerToysSettingsJson) = 0;
|
||||
IFACEMETHOD_(void, CallCustomAction)(PCWSTR action) = 0;
|
||||
IFACEMETHOD_(Settings, GetSettings)() = 0;
|
||||
IFACEMETHOD_(const Settings*, GetSettings)() const = 0;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IFancyZonesSettings> MakeFancyZonesSettings(HINSTANCE hinstance, PCWSTR config) noexcept;
|
@ -66,9 +66,9 @@ void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Take care of 1px border
|
||||
RECT zoneRect = m_zoneRect;
|
||||
RECT newWindowRect = m_zoneRect;
|
||||
|
||||
RECT windowRect{};
|
||||
::GetWindowRect(window, &windowRect);
|
||||
@ -82,38 +82,54 @@ void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
|
||||
const auto left_margin = frameRect.left - windowRect.left;
|
||||
const auto right_margin = frameRect.right - windowRect.right;
|
||||
const auto bottom_margin = frameRect.bottom - windowRect.bottom;
|
||||
zoneRect.left -= left_margin;
|
||||
zoneRect.right -= right_margin;
|
||||
zoneRect.bottom -= bottom_margin;
|
||||
newWindowRect.left -= left_margin;
|
||||
newWindowRect.right -= right_margin;
|
||||
newWindowRect.bottom -= bottom_margin;
|
||||
}
|
||||
|
||||
// Map to screen coords
|
||||
MapWindowRect(zoneWindow, nullptr, &zoneRect);
|
||||
MapWindowRect(zoneWindow, nullptr, &newWindowRect);
|
||||
|
||||
MONITORINFO mi{sizeof(mi)};
|
||||
MONITORINFO mi{ sizeof(mi) };
|
||||
if (GetMonitorInfoW(MonitorFromWindow(zoneWindow, MONITOR_DEFAULTTONEAREST), &mi))
|
||||
{
|
||||
const auto taskbar_left_size = std::abs(mi.rcMonitor.left - mi.rcWork.left);
|
||||
const auto taskbar_top_size = std::abs(mi.rcMonitor.top - mi.rcWork.top);
|
||||
OffsetRect(&zoneRect, -taskbar_left_size, -taskbar_top_size);
|
||||
OffsetRect(&newWindowRect, -taskbar_left_size, -taskbar_top_size);
|
||||
if (accountForUnawareness)
|
||||
{
|
||||
zoneRect.left = max(mi.rcMonitor.left, zoneRect.left);
|
||||
zoneRect.right = min(mi.rcMonitor.right - taskbar_left_size, zoneRect.right);
|
||||
zoneRect.top = max(mi.rcMonitor.top, zoneRect.top);
|
||||
zoneRect.bottom = min(mi.rcMonitor.bottom - taskbar_top_size, zoneRect.bottom);
|
||||
newWindowRect.left = max(mi.rcMonitor.left, newWindowRect.left);
|
||||
newWindowRect.right = min(mi.rcMonitor.right - taskbar_left_size, newWindowRect.right);
|
||||
newWindowRect.top = max(mi.rcMonitor.top, newWindowRect.top);
|
||||
newWindowRect.bottom = min(mi.rcMonitor.bottom - taskbar_top_size, newWindowRect.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
if ((::GetWindowLong(window, GWL_STYLE) & WS_SIZEBOX) == 0)
|
||||
{
|
||||
newWindowRect.right = newWindowRect.left + (windowRect.right - windowRect.left);
|
||||
newWindowRect.bottom = newWindowRect.top + (windowRect.bottom - windowRect.top);
|
||||
}
|
||||
|
||||
WINDOWPLACEMENT placement{};
|
||||
::GetWindowPlacement(window, &placement);
|
||||
placement.rcNormalPosition = zoneRect;
|
||||
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
|
||||
|
||||
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
|
||||
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
::GetWindowPlacement(window, &placement);
|
||||
}
|
||||
|
||||
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
|
||||
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
|
||||
{
|
||||
placement.showCmd = SW_RESTORE | SW_SHOWNA;
|
||||
}
|
||||
|
||||
placement.rcNormalPosition = newWindowRect;
|
||||
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
|
||||
|
||||
::SetWindowPlacement(window, &placement);
|
||||
// Do it again, allowing Windows to resize the window and set correct scaling
|
||||
// This fixes Issue #365
|
||||
|
@ -130,8 +130,8 @@ public:
|
||||
GetZones() noexcept { return m_zones; }
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByPoint(HWND window, HWND zoneWindow, POINT ptClient) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
@ -226,7 +226,7 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
|
||||
|
||||
if (index >= int(m_zones.size()))
|
||||
{
|
||||
index = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
while (auto zoneDrop = ZoneFromWindow(window))
|
||||
@ -240,12 +240,12 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND windowZone, DWORD vkCode) noexcept
|
||||
IFACEMETHODIMP_(bool)
|
||||
ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND windowZone, DWORD vkCode, bool cycle) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
winrt::com_ptr<IZone> oldZone = nullptr;
|
||||
@ -262,6 +262,11 @@ ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND windowZone, DWORD vkCod
|
||||
{
|
||||
if (iter == m_zones.begin())
|
||||
{
|
||||
if (!cycle)
|
||||
{
|
||||
oldZone->RemoveWindowFromZone(window, false);
|
||||
return false;
|
||||
}
|
||||
iter = m_zones.end();
|
||||
}
|
||||
iter--;
|
||||
@ -271,6 +276,11 @@ ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND windowZone, DWORD vkCod
|
||||
iter++;
|
||||
if (iter == m_zones.end())
|
||||
{
|
||||
if (!cycle)
|
||||
{
|
||||
oldZone->RemoveWindowFromZone(window, false);
|
||||
return false;
|
||||
}
|
||||
iter = m_zones.begin();
|
||||
}
|
||||
}
|
||||
@ -283,7 +293,9 @@ ZoneSet::MoveWindowIntoZoneByDirection(HWND window, HWND windowZone, DWORD vkCod
|
||||
oldZone->RemoveWindowFromZone(window, false);
|
||||
}
|
||||
newZone->AddWindowToZone(window, windowZone, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
@ -560,7 +572,7 @@ bool ZoneSet::CalculateGridZones(Rect workArea, JSONHelpers::GridLayoutInfo grid
|
||||
|
||||
// Note: The expressions below are carefully written to
|
||||
// make the sum of all zones' sizes exactly total{Width|Height}
|
||||
long long totalPercents = 0;
|
||||
int totalPercents = 0;
|
||||
for (int row = 0; row < gridLayoutInfo.rows(); row++)
|
||||
{
|
||||
rowInfo[row].Start = totalPercents * totalHeight / C_MULTIPLIER + (row + 1) * spacing;
|
||||
|
@ -57,8 +57,12 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
|
||||
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param vkCode Pressed arrow key.
|
||||
* @param cycle Whether we should move window to the first zone if we reached last zone in layout.
|
||||
*
|
||||
* @returns Boolean which is always true if cycle argument is set, otherwise indicating if there is more
|
||||
* zones left in the zone layout in which window can move.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(HWND window, HWND zoneWindow, DWORD vkCode) = 0;
|
||||
IFACEMETHOD_(bool, MoveWindowIntoZoneByDirection)(HWND window, HWND zoneWindow, DWORD vkCode, bool cycle) = 0;
|
||||
/**
|
||||
* Assign window to the zone based on cursor coordinates.
|
||||
*
|
||||
@ -75,7 +79,8 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
|
||||
* @param monitorInfo Information about monitor on which zone layout is applied.
|
||||
* @param zoneCount Number of zones inside zone layout.
|
||||
* @param spacing Spacing between zones in pixels.
|
||||
* @returns Boolean if calculation was successful.
|
||||
*
|
||||
* @returns Boolean indicating if calculation was successful.
|
||||
*/
|
||||
IFACEMETHOD_(bool, CalculateZones)(MONITORINFO monitorInfo, int zoneCount, int spacing) = 0;
|
||||
};
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <ShellScalingApi.h>
|
||||
#include <mutex>
|
||||
|
||||
#include <gdiplus.h>
|
||||
|
||||
namespace ZoneWindowUtils
|
||||
{
|
||||
const std::wstring& GetActiveZoneSetTmpPath()
|
||||
@ -92,146 +94,70 @@ namespace ZoneWindowDrawUtils
|
||||
int thickness{};
|
||||
};
|
||||
|
||||
bool IsOccluded(const std::vector<winrt::com_ptr<IZone>>& zones, POINT pt, size_t index) noexcept
|
||||
{
|
||||
size_t i = 1;
|
||||
|
||||
for (auto iter = zones.begin(); iter != zones.end(); iter++)
|
||||
{
|
||||
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
|
||||
{
|
||||
if (i < index)
|
||||
{
|
||||
if (PtInRect(&zone->GetZoneRect(), pt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DrawBackdrop(wil::unique_hdc& hdc, RECT const& clientRect) noexcept
|
||||
{
|
||||
FillRectARGB(hdc, &clientRect, 0, RGB(0, 0, 0), false);
|
||||
}
|
||||
|
||||
void DrawIndex(wil::unique_hdc& hdc, POINT offset, size_t index, int padding, int size, bool flipX, bool flipY, COLORREF colorFill)
|
||||
void DrawIndex(wil::unique_hdc& hdc, Rect rect, size_t index)
|
||||
{
|
||||
RECT rect = { offset.x, offset.y, offset.x + size, offset.y + size };
|
||||
for (int y = 0; y < 3; y++)
|
||||
{
|
||||
for (int x = 0; x < 3; x++)
|
||||
{
|
||||
RECT useRect = rect;
|
||||
if (flipX)
|
||||
{
|
||||
if (x == 0)
|
||||
useRect.left += (size + padding + size + padding);
|
||||
else if (x == 2)
|
||||
useRect.left -= (size + padding + size + padding);
|
||||
useRect.right = useRect.left + size;
|
||||
}
|
||||
Gdiplus::Graphics g(hdc.get());
|
||||
|
||||
if (flipY)
|
||||
{
|
||||
if (y == 0)
|
||||
useRect.top += (size + padding + size + padding);
|
||||
else if (y == 2)
|
||||
useRect.top -= (size + padding + size + padding);
|
||||
useRect.bottom = useRect.top + size;
|
||||
}
|
||||
Gdiplus::FontFamily fontFamily(L"Segoe ui");
|
||||
Gdiplus::Font font(&fontFamily, 80, Gdiplus::FontStyleRegular, Gdiplus::UnitPixel);
|
||||
Gdiplus::SolidBrush solidBrush(Gdiplus::Color(255, 0, 0, 0));
|
||||
|
||||
FillRectARGB(hdc, &useRect, 200, RGB(50, 50, 50), true);
|
||||
std::wstring text = std::to_wstring(index);
|
||||
|
||||
RECT inside = useRect;
|
||||
InflateRect(&inside, -2, -2);
|
||||
g.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias);
|
||||
Gdiplus::StringFormat stringFormat = new Gdiplus::StringFormat();
|
||||
stringFormat.SetAlignment(Gdiplus::StringAlignmentCenter);
|
||||
stringFormat.SetLineAlignment(Gdiplus::StringAlignmentCenter);
|
||||
|
||||
FillRectARGB(hdc, &inside, 100, colorFill, true);
|
||||
Gdiplus::RectF gdiRect(static_cast<Gdiplus::REAL>(rect.left()),
|
||||
static_cast<Gdiplus::REAL>(rect.top()),
|
||||
static_cast<Gdiplus::REAL>(rect.width()),
|
||||
static_cast<Gdiplus::REAL>(rect.height()));
|
||||
|
||||
rect.left += (size + padding);
|
||||
rect.right = rect.left + size;
|
||||
|
||||
if (--index == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
rect.left = offset.x;
|
||||
rect.right = rect.left + size;
|
||||
rect.top += (size + padding);
|
||||
rect.bottom = rect.top + size;
|
||||
}
|
||||
g.DrawString(text.c_str(), -1, &font, gdiRect, &stringFormat, &solidBrush);
|
||||
}
|
||||
|
||||
void DrawZone(wil::unique_hdc& hdc, ColorSetting const& colorSetting, winrt::com_ptr<IZone> zone, const std::vector<winrt::com_ptr<IZone>>& zones, bool flashMode) noexcept
|
||||
{
|
||||
RECT zoneRect = zone->GetZoneRect();
|
||||
if (colorSetting.borderAlpha > 0)
|
||||
{
|
||||
FillRectARGB(hdc, &zoneRect, colorSetting.borderAlpha, colorSetting.border, false);
|
||||
InflateRect(&zoneRect, colorSetting.thickness, colorSetting.thickness);
|
||||
}
|
||||
FillRectARGB(hdc, &zoneRect, colorSetting.fillAlpha, colorSetting.fill, false);
|
||||
|
||||
if (flashMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
COLORREF const colorFill = RGB(255, 255, 255);
|
||||
Gdiplus::Graphics g(hdc.get());
|
||||
Gdiplus::Color fillColor(colorSetting.fillAlpha, GetRValue(colorSetting.fill), GetGValue(colorSetting.fill), GetBValue(colorSetting.fill));
|
||||
Gdiplus::Color borderColor(colorSetting.borderAlpha, GetRValue(colorSetting.border), GetGValue(colorSetting.border), GetBValue(colorSetting.border));
|
||||
|
||||
size_t const index = zone->Id();
|
||||
int const padding = 5;
|
||||
int const size = 10;
|
||||
POINT offset = { zoneRect.left + padding, zoneRect.top + padding };
|
||||
if (!IsOccluded(zones, offset, index))
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, false, false, colorFill); // top left
|
||||
return;
|
||||
}
|
||||
Gdiplus::Rect rectangle(zoneRect.left, zoneRect.top, zoneRect.right - zoneRect.left - 1, zoneRect.bottom - zoneRect.top - 1);
|
||||
|
||||
offset.x = zoneRect.right - ((padding + size) * 3);
|
||||
if (!IsOccluded(zones, offset, index))
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, true, false, colorFill); // top right
|
||||
return;
|
||||
}
|
||||
Gdiplus::Pen pen(borderColor, static_cast<Gdiplus::REAL>(colorSetting.thickness));
|
||||
g.FillRectangle(new Gdiplus::SolidBrush(fillColor), rectangle);
|
||||
g.DrawRectangle(&pen, rectangle);
|
||||
|
||||
offset.y = zoneRect.bottom - ((padding + size) * 3);
|
||||
if (!IsOccluded(zones, offset, index))
|
||||
if (!flashMode)
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, true, true, colorFill); // bottom right
|
||||
return;
|
||||
DrawIndex(hdc, zoneRect, zone->Id());
|
||||
}
|
||||
|
||||
offset.x = zoneRect.left + padding;
|
||||
DrawIndex(hdc, offset, index, padding, size, false, true, colorFill); // bottom left
|
||||
}
|
||||
|
||||
void DrawActiveZoneSet(wil::unique_hdc& hdc, COLORREF highlightColor, int highlightOpacity, const std::vector<winrt::com_ptr<IZone>>& zones, const winrt::com_ptr<IZone>& highlightZone, bool flashMode, bool drawHints) noexcept
|
||||
void DrawActiveZoneSet(wil::unique_hdc& hdc,
|
||||
COLORREF zoneColor,
|
||||
COLORREF zoneBorderColor,
|
||||
COLORREF highlightColor,
|
||||
int zoneOpacity,
|
||||
const std::vector<winrt::com_ptr<IZone>>& zones,
|
||||
const winrt::com_ptr<IZone>& highlightZone,
|
||||
bool flashMode,
|
||||
bool drawHints) noexcept
|
||||
{
|
||||
static constexpr std::array<COLORREF, 9> colors{
|
||||
RGB(75, 75, 85),
|
||||
RGB(150, 150, 160),
|
||||
RGB(100, 100, 110),
|
||||
RGB(125, 125, 135),
|
||||
RGB(225, 225, 235),
|
||||
RGB(25, 25, 35),
|
||||
RGB(200, 200, 210),
|
||||
RGB(50, 50, 60),
|
||||
RGB(175, 175, 185),
|
||||
};
|
||||
// { fillAlpha, fill, borderAlpha, border, thickness }
|
||||
ColorSetting const colorHints{ OpacitySettingToAlpha(zoneOpacity), RGB(81, 92, 107), 255, RGB(104, 118, 138), -2 };
|
||||
ColorSetting colorViewer{ OpacitySettingToAlpha(zoneOpacity), 0, 255, RGB(40, 50, 60), -2 };
|
||||
ColorSetting colorHighlight{ OpacitySettingToAlpha(zoneOpacity), 0, 255, 0, -2 };
|
||||
ColorSetting const colorFlash{ OpacitySettingToAlpha(zoneOpacity), RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
|
||||
|
||||
// ColorSetting { fillAlpha, fill, borderAlpha, border, thickness }
|
||||
ColorSetting const colorHints{ 225, RGB(81, 92, 107), 255, RGB(104, 118, 138), -2 };
|
||||
ColorSetting colorViewer{ OpacitySettingToAlpha(highlightOpacity), 0, 255, RGB(40, 50, 60), -2 };
|
||||
ColorSetting colorHighlight{ OpacitySettingToAlpha(highlightOpacity), 0, 255, 0, -2 };
|
||||
ColorSetting const colorFlash{ 200, RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
|
||||
|
||||
const size_t maxColorIndex = min(size(zones) - 1, size(colors) - 1);
|
||||
size_t colorIndex = maxColorIndex;
|
||||
for (auto iter = zones.begin(); iter != zones.end(); iter++)
|
||||
{
|
||||
winrt::com_ptr<IZone> zone = iter->try_as<IZone>();
|
||||
@ -251,20 +177,17 @@ namespace ZoneWindowDrawUtils
|
||||
DrawZone(hdc, colorHints, zone, zones, flashMode);
|
||||
}
|
||||
{
|
||||
colorViewer.fill = colors[colorIndex];
|
||||
colorViewer.fill = zoneColor;
|
||||
colorViewer.border = zoneBorderColor;
|
||||
DrawZone(hdc, colorViewer, zone, zones, flashMode);
|
||||
}
|
||||
}
|
||||
colorIndex = colorIndex != 0 ? colorIndex - 1 : maxColorIndex;
|
||||
}
|
||||
|
||||
if (highlightZone)
|
||||
{
|
||||
colorHighlight.fill = highlightColor;
|
||||
colorHighlight.border = RGB(
|
||||
max(0, GetRValue(colorHighlight.fill) - 25),
|
||||
max(0, GetGValue(colorHighlight.fill) - 25),
|
||||
max(0, GetBValue(colorHighlight.fill) - 25));
|
||||
colorHighlight.border = zoneBorderColor;
|
||||
DrawZone(hdc, colorHighlight, highlightZone, zones, flashMode);
|
||||
}
|
||||
}
|
||||
@ -274,18 +197,21 @@ struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
|
||||
{
|
||||
public:
|
||||
ZoneWindow(HINSTANCE hinstance);
|
||||
~ZoneWindow();
|
||||
|
||||
bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones);
|
||||
|
||||
IFACEMETHODIMP MoveSizeEnter(HWND window, bool dragEnabled) noexcept;
|
||||
IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled) noexcept;
|
||||
IFACEMETHODIMP MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
|
||||
IFACEMETHODIMP MoveSizeCancel() noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
RestoreOrginalTransparency() noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
IsDragEnabled() noexcept { return m_dragEnabled; }
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndex(HWND window, int index) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
CycleActiveZoneSet(DWORD vkCode) noexcept;
|
||||
IFACEMETHODIMP_(std::wstring)
|
||||
@ -296,13 +222,15 @@ public:
|
||||
SaveWindowProcessToZoneIndex(HWND window) noexcept;
|
||||
IFACEMETHODIMP_(IZoneSet*)
|
||||
ActiveZoneSet() noexcept { return m_activeZoneSet.get(); }
|
||||
IFACEMETHODIMP_(void)
|
||||
ShowZoneWindow() noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
HideZoneWindow() noexcept;
|
||||
|
||||
protected:
|
||||
static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||
|
||||
private:
|
||||
void ShowZoneWindow() noexcept;
|
||||
void HideZoneWindow() noexcept;
|
||||
void LoadSettings() noexcept;
|
||||
void InitializeZoneSets(MONITORINFO const& mi) noexcept;
|
||||
void CalculateZoneSet() noexcept;
|
||||
@ -330,6 +258,14 @@ private:
|
||||
size_t m_keyCycle{};
|
||||
static const UINT m_showAnimationDuration = 200; // ms
|
||||
static const UINT m_flashDuration = 700; // ms
|
||||
|
||||
HWND draggedWindow = nullptr;
|
||||
long draggedWindowExstyle = 0;
|
||||
COLORREF draggedWindowCrKey = RGB(0, 0, 0);
|
||||
DWORD draggedWindowDwFlags = 0;
|
||||
BYTE draggedWindowInitialAlpha = 0;
|
||||
|
||||
ULONG_PTR gdiplusToken;
|
||||
};
|
||||
|
||||
ZoneWindow::ZoneWindow(HINSTANCE hinstance)
|
||||
@ -341,6 +277,16 @@ ZoneWindow::ZoneWindow(HINSTANCE hinstance)
|
||||
wcex.lpszClassName = L"SuperFancyZones_ZoneWindow";
|
||||
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
RegisterClassExW(&wcex);
|
||||
|
||||
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
|
||||
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
|
||||
}
|
||||
|
||||
ZoneWindow::~ZoneWindow()
|
||||
{
|
||||
RestoreOrginalTransparency();
|
||||
|
||||
Gdiplus::GdiplusShutdown(gdiplusToken);
|
||||
}
|
||||
|
||||
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones)
|
||||
@ -398,6 +344,22 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnter(HWND window, bool dragEnabled) noexcept
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (m_host->isMakeDraggedWindowTransparentActive())
|
||||
{
|
||||
RestoreOrginalTransparency();
|
||||
|
||||
draggedWindowExstyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||
|
||||
draggedWindow = window;
|
||||
SetWindowLong(window,
|
||||
GWL_EXSTYLE,
|
||||
draggedWindowExstyle | WS_EX_LAYERED);
|
||||
|
||||
GetLayeredWindowAttributes(window, &draggedWindowCrKey, &draggedWindowInitialAlpha, &draggedWindowDwFlags);
|
||||
|
||||
SetLayeredWindowAttributes(window, 0, (255 * 50) / 100, LWA_ALPHA);
|
||||
}
|
||||
|
||||
m_dragEnabled = dragEnabled;
|
||||
m_windowMoveSize = window;
|
||||
m_drawHints = true;
|
||||
@ -435,6 +397,8 @@ IFACEMETHODIMP ZoneWindow::MoveSizeUpdate(POINT const& ptScreen, bool dragEnable
|
||||
|
||||
IFACEMETHODIMP ZoneWindow::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
|
||||
{
|
||||
RestoreOrginalTransparency();
|
||||
|
||||
if (m_windowMoveSize != window)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
@ -455,10 +419,15 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnd(HWND window, POINT const& ptScreen) noexc
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP ZoneWindow::MoveSizeCancel() noexcept
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneWindow::RestoreOrginalTransparency() noexcept
|
||||
{
|
||||
HideZoneWindow();
|
||||
return S_OK;
|
||||
if (m_host->isMakeDraggedWindowTransparentActive() && draggedWindow != nullptr)
|
||||
{
|
||||
SetLayeredWindowAttributes(draggedWindow, draggedWindowCrKey, draggedWindowInitialAlpha, draggedWindowDwFlags);
|
||||
SetWindowLong(draggedWindow, GWL_EXSTYLE, draggedWindowExstyle);
|
||||
draggedWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
@ -470,14 +439,18 @@ ZoneWindow::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneWindow::MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode) noexcept
|
||||
IFACEMETHODIMP_(bool)
|
||||
ZoneWindow::MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode, bool cycle) noexcept
|
||||
{
|
||||
if (m_activeZoneSet)
|
||||
{
|
||||
m_activeZoneSet->MoveWindowIntoZoneByDirection(window, m_window.get(), vkCode);
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
if (m_activeZoneSet->MoveWindowIntoZoneByDirection(window, m_window.get(), vkCode, cycle))
|
||||
{
|
||||
SaveWindowProcessToZoneIndex(window);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
@ -514,8 +487,8 @@ ZoneWindow::SaveWindowProcessToZoneIndex(HWND window) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region private
|
||||
void ZoneWindow::ShowZoneWindow() noexcept
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneWindow::ShowZoneWindow() noexcept
|
||||
{
|
||||
if (m_window)
|
||||
{
|
||||
@ -536,7 +509,8 @@ void ZoneWindow::ShowZoneWindow() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneWindow::HideZoneWindow() noexcept
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneWindow::HideZoneWindow() noexcept
|
||||
{
|
||||
if (m_window)
|
||||
{
|
||||
@ -548,6 +522,8 @@ void ZoneWindow::HideZoneWindow() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region private
|
||||
|
||||
void ZoneWindow::LoadSettings() noexcept
|
||||
{
|
||||
JSONHelpers::FancyZonesDataInstance().AddDevice(m_uniqueId);
|
||||
@ -630,7 +606,8 @@ LRESULT ZoneWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_NCDESTROY: {
|
||||
case WM_NCDESTROY:
|
||||
{
|
||||
::DefWindowProc(m_window.get(), message, wparam, lparam);
|
||||
SetWindowLongPtr(m_window.get(), GWLP_USERDATA, 0);
|
||||
}
|
||||
@ -640,7 +617,8 @@ LRESULT ZoneWindow::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept
|
||||
return 1;
|
||||
|
||||
case WM_PRINTCLIENT:
|
||||
case WM_PAINT: {
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
wil::unique_hdc hdc{ reinterpret_cast<HDC>(wparam) };
|
||||
if (!hdc)
|
||||
@ -677,9 +655,18 @@ void ZoneWindow::OnPaint(wil::unique_hdc& hdc) noexcept
|
||||
if (bufferedPaint)
|
||||
{
|
||||
ZoneWindowDrawUtils::DrawBackdrop(hdcMem, clientRect);
|
||||
|
||||
if (m_activeZoneSet && m_host)
|
||||
{
|
||||
ZoneWindowDrawUtils::DrawActiveZoneSet(hdcMem, m_host->GetZoneHighlightColor(), m_host->GetZoneHighlightOpacity(), m_activeZoneSet->GetZones(), m_highlightZone, m_flashMode, m_drawHints);
|
||||
ZoneWindowDrawUtils::DrawActiveZoneSet(hdcMem,
|
||||
m_host->GetZoneColor(),
|
||||
m_host->GetZoneBorderColor(),
|
||||
m_host->GetZoneHighlightColor(),
|
||||
m_host->GetZoneHighlightOpacity(),
|
||||
m_activeZoneSet->GetZones(),
|
||||
m_highlightZone,
|
||||
m_flashMode,
|
||||
m_drawHints);
|
||||
}
|
||||
|
||||
EndBufferedPaint(bufferedPaint, TRUE);
|
||||
@ -757,6 +744,12 @@ void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::Inp
|
||||
|
||||
void ZoneWindow::FlashZones() noexcept
|
||||
{
|
||||
// "Turning FLASHING_ZONE option off"
|
||||
if (true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_flashMode = true;
|
||||
|
||||
ShowWindow(m_window.get(), SW_SHOWNA);
|
||||
|
@ -41,10 +41,6 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
|
||||
* @param ptScreen Cursor coordinates where window is droped.
|
||||
*/
|
||||
IFACEMETHOD(MoveSizeEnd)(HWND window, POINT const& ptScreen) = 0;
|
||||
/**
|
||||
* Abort tracking down of window position and giving zone layout hints (if dragging functionality is enabled).
|
||||
*/
|
||||
IFACEMETHOD(MoveSizeCancel)() = 0;
|
||||
/**
|
||||
* @returns Boolean indicating is giving hints about active zone layout enabled. Hints are
|
||||
* given while dragging window while holding SHIFT key.
|
||||
@ -62,14 +58,22 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param vkCode Pressed arrow key.
|
||||
* @param cycle Whether we should move window to the first zone if we reached last zone in layout.
|
||||
*
|
||||
* @returns Boolean which is always true if cycle argument is set, otherwise indicating if there is more
|
||||
* zones left in the zone layout in which window can move.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByDirection)(HWND window, DWORD vkCode) = 0;
|
||||
IFACEMETHOD_(bool, MoveWindowIntoZoneByDirection)(HWND window, DWORD vkCode, bool cycle) = 0;
|
||||
/**
|
||||
* Cycle through active zone layouts (giving hints about each layout).
|
||||
*
|
||||
* @param vkCode Pressed key representing layout index.
|
||||
*/
|
||||
IFACEMETHOD_(void, CycleActiveZoneSet)(DWORD vkCode) = 0;
|
||||
/**
|
||||
* Restore orginal transaprency of dragged window.
|
||||
*/
|
||||
IFACEMETHOD_(void, RestoreOrginalTransparency) () = 0;
|
||||
/**
|
||||
* Save information about zone in which window was assigned, when closing the window.
|
||||
* Used once we open same window again to assign it to its previous zone.
|
||||
@ -89,6 +93,8 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
|
||||
* @returns Active zone layout for this work area.
|
||||
*/
|
||||
IFACEMETHOD_(IZoneSet*, ActiveZoneSet)() = 0;
|
||||
IFACEMETHOD_(void, ShowZoneWindow)() = 0;
|
||||
IFACEMETHOD_(void, HideZoneWindow)() = 0;
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor,
|
||||
|
@ -1,59 +1,66 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
⼀⼀ഀ<EFBFBD>
|
||||
⌀椀渀挀氀甀搀攀 ∀爀攀猀漀甀爀挀攀⸀栀∀ഀ<EFBFBD>
|
||||
⌀椀渀挀氀甀搀攀 ∀⸀⸀⼀⸀⸀⼀⸀⸀⼀挀漀洀洀漀渀⼀瘀攀爀猀椀漀渀⸀栀∀ഀ<EFBFBD>
|
||||
ഀ<EFBFBD>
|
||||
匀吀刀䤀一䜀吀䄀䈀䰀䔀ഀ<EFBFBD>
|
||||
䈀䔀䜀䤀一ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一 ∀䌀爀攀愀琀攀 眀椀渀搀漀眀 氀愀礀漀甀琀猀 琀漀 栀攀氀瀀 洀愀欀攀 洀甀氀琀椀ⴀ琀愀猀欀椀渀最 攀愀猀礀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开匀䠀䤀䘀吀䐀刀䄀䜀 ∀伀渀㨀 䠀漀氀搀 匀栀椀昀琀 欀攀礀 漀爀 愀渀礀 渀漀渀ⴀ瀀爀椀洀愀爀礀 洀漀甀猀攀 戀甀琀琀漀渀 琀漀 攀渀愀戀氀攀 稀漀渀攀猀 眀栀椀氀攀 搀爀愀最最椀渀最∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开伀嘀䔀刀刀䤀䐀䔀开匀一䄀倀开䠀伀吀䬀䔀夀匀 ∀伀瘀攀爀爀椀搀攀 圀椀渀搀漀眀猀 匀渀愀瀀 栀漀琀欀攀礀猀 ⠀眀椀渀⬀愀爀爀漀眀⤀ 琀漀 洀漀瘀攀 眀椀渀搀漀眀猀 戀攀琀眀攀攀渀 稀漀渀攀猀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开䐀䤀匀倀䰀䄀夀䌀䠀䄀一䜀䔀开䴀伀嘀䔀圀䤀一䐀伀圀匀 ∀䬀攀攀瀀 眀椀渀搀漀眀猀 椀渀 琀栀攀椀爀 稀漀渀攀猀 眀栀攀渀 琀栀攀 猀挀爀攀攀渀 爀攀猀漀氀甀琀椀漀渀 挀栀愀渀最攀猀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开娀伀一䔀匀䔀吀䌀䠀䄀一䜀䔀开䴀伀嘀䔀圀䤀一䐀伀圀匀 ∀䐀甀爀椀渀最 稀漀渀攀 氀愀礀漀甀琀 挀栀愀渀最攀猀Ⰰ 眀椀渀搀漀眀猀 愀猀猀椀最渀攀搀 琀漀 愀 稀漀渀攀 眀椀氀氀 洀愀琀挀栀 渀攀眀 猀椀稀攀⼀瀀漀猀椀琀椀漀渀猀∀ ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开嘀䤀刀吀唀䄀䰀䐀䔀匀䬀吀伀倀䌀䠀䄀一䜀䔀开䴀伀嘀䔀圀䤀一䐀伀圀匀 ∀䬀攀攀瀀 眀椀渀搀漀眀猀 瀀椀渀渀攀搀 琀漀 洀甀氀琀椀瀀氀攀 搀攀猀欀琀漀瀀猀 椀渀 琀栀攀 猀愀洀攀 稀漀渀攀 眀栀攀渀 琀栀攀 愀挀琀椀瘀攀 搀攀猀欀琀漀瀀 挀栀愀渀最攀猀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开娀伀一䔀匀䔀吀䌀䠀䄀一䜀䔀开䘀䰀䄀匀䠀娀伀一䔀匀 ∀䘀氀愀猀栀 稀漀渀攀猀 眀栀攀渀 琀栀攀 愀挀琀椀瘀攀 䘀愀渀挀礀娀漀渀攀猀 氀愀礀漀甀琀 挀栀愀渀最攀猀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开娀伀一䔀䠀䤀䜀䠀䰀䤀䜀䠀吀䌀伀䰀伀刀 ∀娀漀渀攀 䠀椀最栀氀椀最栀琀 䌀漀氀漀爀 ⠀䐀攀昀愀甀氀琀 ⌀ 㜀㠀䐀㜀⤀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开唀匀䔀开䌀唀刀匀伀刀倀伀匀开䔀䐀䤀吀伀刀开匀吀䄀刀吀唀倀匀䌀刀䔀䔀一 ∀䘀漀氀氀漀眀 洀漀甀猀攀 挀甀爀猀漀爀 椀渀猀琀攀愀搀 漀昀 昀漀挀甀猀 眀栀攀渀 氀愀甀渀挀栀椀渀最 攀搀椀琀漀爀 椀渀 愀 洀甀氀琀椀 猀挀爀攀攀渀 攀渀瘀椀爀漀渀洀攀渀琀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䐀䔀匀䌀刀䤀倀吀䤀伀一开䄀倀倀䰀䄀匀吀娀伀一䔀开䴀伀嘀䔀圀䤀一䐀伀圀匀 ∀䴀漀瘀攀 渀攀眀氀礀 挀爀攀愀琀攀搀 眀椀渀搀漀眀猀 琀漀 琀栀攀椀爀 氀愀猀琀 欀渀漀眀渀 稀漀渀攀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䰀䄀唀一䌀䠀开䔀䐀䤀吀伀刀开䰀䄀䈀䔀䰀 ∀娀漀渀攀 挀漀渀昀椀最甀爀愀琀椀漀渀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䰀䄀唀一䌀䠀开䔀䐀䤀吀伀刀开䈀唀吀吀伀一 ∀䔀搀椀琀 稀漀渀攀猀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䰀䄀唀一䌀䠀开䔀䐀䤀吀伀刀开䐀䔀匀䌀刀䤀倀吀䤀伀一 ∀吀漀 氀愀甀渀挀栀 琀栀攀 稀漀渀攀 攀搀椀琀漀爀Ⰰ 猀攀氀攀挀琀 琀栀攀 䔀搀椀琀 稀漀渀攀猀 戀甀琀琀漀渀 戀攀氀漀眀 漀爀 瀀爀攀猀猀 琀栀攀 稀漀渀攀 攀搀椀琀漀爀 栀漀琀欀攀礀 愀渀礀琀椀洀攀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䰀䄀唀一䌀䠀开䔀䐀䤀吀伀刀开䠀伀吀䬀䔀夀开䰀䄀䈀䔀䰀 ∀䌀漀渀昀椀最甀爀攀 琀栀攀 稀漀渀攀 攀搀椀琀漀爀 栀漀琀欀攀礀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀开䔀堀䌀䰀䌀唀䐀䔀䐀开䄀倀倀匀开䐀䔀匀䌀刀䤀倀吀䤀伀一 ∀吀漀 攀砀挀氀甀搀攀 愀渀 愀瀀瀀氀椀挀愀琀椀漀渀 昀爀漀洀 猀渀愀瀀瀀椀渀最 琀漀 稀漀渀攀猀 愀搀搀 椀琀猀 渀愀洀攀 栀攀爀攀 ⠀漀渀攀 瀀攀爀 氀椀渀攀⤀⸀ 䔀砀挀氀甀搀攀搀 愀瀀瀀猀 眀椀氀氀 爀攀愀挀琀 琀漀 琀栀攀 圀椀渀搀漀眀猀 匀渀愀瀀 爀攀最愀爀搀氀攀猀猀 漀昀 愀氀氀 漀琀栀攀爀 猀攀琀琀椀渀最猀⸀∀ഀ<EFBFBD>
|
||||
䤀䐀匀开匀䔀吀吀䤀一䜀匀开䠀䤀䜀䠀䰀䤀䜀䠀吀开伀倀䄀䌀䤀吀夀 ∀娀漀渀攀 伀瀀愀挀椀琀礀∀ ഀ<EFBFBD>
|
||||
䤀䐀匀开䘀䄀一䌀夀娀伀一䔀匀 䰀∀䘀愀渀挀礀娀漀渀攀猀∀ ഀ<EFBFBD>
|
||||
䔀一䐀ഀ<EFBFBD>
|
||||
ഀ<EFBFBD>
|
||||
嘀䔀刀匀䤀伀一䤀一䘀伀ഀ<EFBFBD>
|
||||
䘀䤀䰀䔀嘀䔀刀匀䤀伀一 䘀䤀䰀䔀开嘀䔀刀匀䤀伀一ഀ<EFBFBD>
|
||||
倀刀伀䐀唀䌀吀嘀䔀刀匀䤀伀一 倀刀伀䐀唀䌀吀开嘀䔀刀匀䤀伀一ഀ<EFBFBD>
|
||||
䘀䤀䰀䔀䘀䰀䄀䜀匀䴀䄀匀䬀 砀㌀昀䰀ഀ<EFBFBD>
|
||||
⌀椀昀搀攀昀 开䐀䔀䈀唀䜀ഀ<EFBFBD>
|
||||
䘀䤀䰀䔀䘀䰀䄀䜀匀 砀䰀ഀ<EFBFBD>
|
||||
⌀攀氀猀攀ഀ<EFBFBD>
|
||||
䘀䤀䰀䔀䘀䰀䄀䜀匀 砀 䰀ഀ<EFBFBD>
|
||||
⌀攀渀搀椀昀ഀ<EFBFBD>
|
||||
䘀䤀䰀䔀伀匀 砀㐀 㐀䰀ഀ<EFBFBD>
|
||||
䘀䤀䰀䔀吀夀倀䔀 砀㈀䰀ഀ<EFBFBD>
|
||||
䘀䤀䰀䔀匀唀䈀吀夀倀䔀 砀 䰀ഀ<EFBFBD>
|
||||
䈀䔀䜀䤀一ഀ<EFBFBD>
|
||||
䈀䰀伀䌀䬀 ∀匀琀爀椀渀最䘀椀氀攀䤀渀昀漀∀ഀ<EFBFBD>
|
||||
䈀䔀䜀䤀一ഀ<EFBFBD>
|
||||
䈀䰀伀䌀䬀 ∀ 㐀 㤀 㐀戀 ∀ഀ<EFBFBD>
|
||||
䈀䔀䜀䤀一ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀䌀漀洀瀀愀渀礀一愀洀攀∀Ⰰ 䌀伀䴀倀䄀一夀开一䄀䴀䔀ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀䘀椀氀攀䐀攀猀挀爀椀瀀琀椀漀渀∀Ⰰ ∀䘀愀渀挀礀娀漀渀攀猀 倀漀眀攀爀吀漀礀 䴀漀搀甀氀攀∀ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀䘀椀氀攀嘀攀爀猀椀漀渀∀Ⰰ 䘀䤀䰀䔀开嘀䔀刀匀䤀伀一开匀吀刀䤀一䜀ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀䤀渀琀攀爀渀愀氀一愀洀攀∀Ⰰ ∀䘀愀渀挀礀娀漀渀攀猀 倀漀眀攀爀吀漀礀∀ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀䰀攀最愀氀䌀漀瀀礀爀椀最栀琀∀Ⰰ 䌀伀倀夀刀䤀䜀䠀吀开一伀吀䔀ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀伀爀椀最椀渀愀氀䘀椀氀攀渀愀洀攀∀Ⰰ ∀昀愀渀挀礀稀漀渀攀猀开瀀漀眀攀爀琀漀礀⸀搀氀氀∀ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀倀爀漀搀甀挀琀一愀洀攀∀Ⰰ ∀䘀愀渀挀礀娀漀渀攀猀 倀漀眀攀爀吀漀礀∀ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀倀爀漀搀甀挀琀嘀攀爀猀椀漀渀∀Ⰰ 倀刀伀䐀唀䌀吀开嘀䔀刀匀䤀伀一开匀吀刀䤀一䜀ഀ<EFBFBD>
|
||||
䔀一䐀ഀ<EFBFBD>
|
||||
䔀一䐀ഀ<EFBFBD>
|
||||
䈀䰀伀䌀䬀 ∀嘀愀爀䘀椀氀攀䤀渀昀漀∀ഀ<EFBFBD>
|
||||
䈀䔀䜀䤀一ഀ<EFBFBD>
|
||||
嘀䄀䰀唀䔀 ∀吀爀愀渀猀氀愀琀椀漀渀∀Ⰰ 砀㐀 㤀Ⰰ ㈀ ഀ<EFBFBD>
|
||||
䔀一䐀ഀ<EFBFBD>
|
||||
䔀一䐀ഀ<EFBFBD>
|
||||
<EFBFBD>
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
#include "../../../common/version.h"
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_SETTING_DESCRIPTION "Create window layouts to help make multi-tasking easy"
|
||||
IDS_SETTING_DESCRIPTION_SHIFTDRAG "On: Hold Shift key or any non-primary mouse button to enable zones while dragging"
|
||||
IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS "Override Windows Snap hotkeys (win+arrow) to move windows between zones"
|
||||
IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS "Keep windows in their zones when the screen resolution changes"
|
||||
IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS "During zone layout changes, windows assigned to a zone will match new size/positions"
|
||||
IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS "Keep windows pinned to multiple desktops in the same zone when the active desktop changes"
|
||||
IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES "Flash zones when the active FancyZones layout changes"
|
||||
IDS_SETTING_DESCRIPTION_SHOW_FANCY_ZONES_ON_ALL_MONITORS "Show zones on all monitors while dragging a window"
|
||||
IDS_SETTING_DESCRIPTION_MAKE_DRAGGED_WINDOW_TRANSPARENT "Make dragged window transparent"
|
||||
IDS_SETTING_DESCRIPTION_ZONECOLOR "Zone inactive color (Default #F5FCFF)"
|
||||
IDS_SETTING_DESCRIPTION_ZONE_BORDER_COLOR "Zone border color (Default #FFFFFF)"
|
||||
IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR "Zone highlight color (Default #008CFF)"
|
||||
IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN "Follow mouse cursor instead of focus when launching editor in a multi screen environment"
|
||||
IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS "Move newly created windows to their last known zone"
|
||||
IDS_SETTING_LAUNCH_EDITOR_LABEL "Zone configuration"
|
||||
IDS_SETTING_LAUNCH_EDITOR_BUTTON "Edit zones"
|
||||
IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION "To launch the zone editor, select the Edit zones button below or press the zone editor hotkey anytime"
|
||||
IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL "Configure the zone editor hotkey"
|
||||
IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION "To exclude an application from snapping to zones add its name here (one per line). Excluded apps will react to the Windows Snap regardless of all other settings."
|
||||
IDS_SETTINGS_HIGHLIGHT_OPACITY "Zone opacity (%)"
|
||||
IDS_FANCYZONES L"FancyZones"
|
||||
IDS_CANT_DRAG_ELEVATED L"We've detected an application running with administrator privileges. This blocks some functionality in PowerToys. Visit our wiki page to learn more."
|
||||
IDS_CANT_DRAG_ELEVATED_LEARN_MORE L"Learn more"
|
||||
IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN L"Don't show again"
|
||||
|
||||
END
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", "FancyZones PowerToy Module"
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", "FancyZones PowerToy"
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", "fancyzones_powertoy.dll"
|
||||
VALUE "ProductName", "FancyZones PowerToy"
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
@ -1,17 +1,24 @@
|
||||
#define IDS_SETTING_DESCRIPTION_SHIFTDRAG 101
|
||||
#define IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS 102
|
||||
#define IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS 103
|
||||
#define IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS 104
|
||||
#define IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES 105
|
||||
#define IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS 106
|
||||
#define IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR 107
|
||||
#define IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS 108
|
||||
#define IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN 109
|
||||
#define IDS_SETTING_DESCRIPTION 110
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_LABEL 111
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_BUTTON 112
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION 113
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL 114
|
||||
#define IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION 115
|
||||
#define IDS_SETTINGS_HIGHLIGHT_OPACITY 116
|
||||
#define IDS_FANCYZONES 117
|
||||
#define IDS_SETTING_DESCRIPTION_SHIFTDRAG 101
|
||||
#define IDS_SETTING_DESCRIPTION_OVERRIDE_SNAP_HOTKEYS 102
|
||||
#define IDS_SETTING_DESCRIPTION_DISPLAYCHANGE_MOVEWINDOWS 103
|
||||
#define IDS_SETTING_DESCRIPTION_ZONESETCHANGE_MOVEWINDOWS 104
|
||||
#define IDS_SETTING_DESCRIPTION_ZONESETCHANGE_FLASHZONES 105
|
||||
#define IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS 106
|
||||
#define IDS_SETTING_DESCRIPTION_SHOW_FANCY_ZONES_ON_ALL_MONITORS 107
|
||||
#define IDS_SETTING_DESCRIPTION_MAKE_DRAGGED_WINDOW_TRANSPARENT 108
|
||||
#define IDS_SETTING_DESCRIPTION_ZONECOLOR 109
|
||||
#define IDS_SETTING_DESCRIPTION_ZONE_BORDER_COLOR 110
|
||||
#define IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR 111
|
||||
#define IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS 112
|
||||
#define IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN 113
|
||||
#define IDS_SETTING_DESCRIPTION 114
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_LABEL 115
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_BUTTON 116
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_DESCRIPTION 117
|
||||
#define IDS_SETTING_LAUNCH_EDITOR_HOTKEY_LABEL 118
|
||||
#define IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION 119
|
||||
#define IDS_SETTINGS_HIGHLIGHT_OPACITY 120
|
||||
#define IDS_FANCYZONES 121
|
||||
#define IDS_CANT_DRAG_ELEVATED 122
|
||||
#define IDS_CANT_DRAG_ELEVATED_LEARN_MORE 123
|
||||
#define IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN 124
|
||||
|
@ -147,6 +147,16 @@ void Trace::FancyZones::DataChanged() noexcept
|
||||
TraceLoggingWideString(activeZoneSetInfo.c_str(), "ActiveZoneSetsList"));
|
||||
}
|
||||
|
||||
void Trace::FancyZones::EditorLaunched(int value) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"FancyZones_EditorLaunch",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingInt32(value, "Value"));
|
||||
}
|
||||
|
||||
void Trace::SettingsChanged(const Settings& settings) noexcept
|
||||
{
|
||||
const auto& editorHotkey = settings.editorHotkey;
|
||||
@ -170,6 +180,10 @@ void Trace::SettingsChanged(const Settings& settings) noexcept
|
||||
TraceLoggingBoolean(settings.overrideSnapHotkeys, "OverrideSnapHotKeys"),
|
||||
TraceLoggingBoolean(settings.appLastZone_moveWindows, "MoveWindowsToLastZoneOnAppOpening"),
|
||||
TraceLoggingBoolean(settings.use_cursorpos_editor_startupscreen, "UseCursorPosOnEditorStartup"),
|
||||
TraceLoggingBoolean(settings.showZonesOnAllMonitors, "ShowZonesOnAllMonitors"),
|
||||
TraceLoggingBoolean(settings.makeDraggedWindowTransparent, "MakeDraggedWindowTransparent"),
|
||||
TraceLoggingWideString(settings.zoneColor.c_str(), "ZoneColor"),
|
||||
TraceLoggingWideString(settings.zoneBorderColor.c_str(), "ZoneBorderColor"),
|
||||
TraceLoggingWideString(settings.zoneHightlightColor.c_str(), "ZoneHighlightColor"),
|
||||
TraceLoggingInt32(settings.zoneHighlightOpacity, "ZoneHighlightOpacity"),
|
||||
TraceLoggingWideString(hotkeyStr.c_str(), "Hotkey"),
|
||||
|
@ -15,6 +15,7 @@ public:
|
||||
static void EnableFancyZones(bool enabled) noexcept;
|
||||
static void OnKeyDown(DWORD vkCode, bool win, bool control, bool inMoveSize) noexcept;
|
||||
static void DataChanged() noexcept;
|
||||
static void EditorLaunched(int value) noexcept;
|
||||
};
|
||||
|
||||
static void SettingsChanged(const Settings& settings) noexcept;
|
||||
|
@ -25,3 +25,86 @@ UINT GetDpiForMonitor(HMONITOR monitor) noexcept
|
||||
|
||||
return (dpi == 0) ? DPIAware::DEFAULT_DPI : dpi;
|
||||
}
|
||||
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||
{
|
||||
const size_t nMonitors = monitorInfo.size();
|
||||
// blocking[i][j] - whether monitor i blocks monitor j in the ordering, i.e. monitor i should go before monitor j
|
||||
std::vector<std::vector<bool>> blocking(nMonitors, std::vector<bool>(nMonitors, false));
|
||||
|
||||
// blockingCount[j] - the number of monitors which block monitor j
|
||||
std::vector<size_t> blockingCount(nMonitors, 0);
|
||||
|
||||
for (size_t i = 0; i < nMonitors; i++)
|
||||
{
|
||||
RECT rectI = monitorInfo[i].second;
|
||||
for (size_t j = 0; j < nMonitors; j++)
|
||||
{
|
||||
RECT rectJ = monitorInfo[j].second;
|
||||
blocking[i][j] = rectI.top < rectJ.bottom && rectI.left < rectJ.right && i != j;
|
||||
if (blocking[i][j])
|
||||
{
|
||||
blockingCount[j]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// used[i] - whether the sorting algorithm has used monitor i so far
|
||||
std::vector<bool> used(nMonitors, false);
|
||||
|
||||
// the sorted sequence of monitors
|
||||
std::vector<std::pair<HMONITOR, RECT>> sortedMonitorInfo;
|
||||
|
||||
for (size_t iteration = 0; iteration < nMonitors; iteration++)
|
||||
{
|
||||
// Indices of candidates to become the next monitor in the sequence
|
||||
std::vector<size_t> candidates;
|
||||
|
||||
// First, find indices of all unblocked monitors
|
||||
for (size_t i = 0; i < nMonitors; i++)
|
||||
{
|
||||
if (blockingCount[i] == 0 && !used[i])
|
||||
{
|
||||
candidates.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// In the unlikely event that there are no unblocked monitors, declare all unused monitors as candidates.
|
||||
if (candidates.empty())
|
||||
{
|
||||
for (size_t i = 0; i < nMonitors; i++)
|
||||
{
|
||||
if (!used[i])
|
||||
{
|
||||
candidates.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the lexicographically smallest monitor as the next one
|
||||
size_t smallest = candidates[0];
|
||||
for (size_t j = 1; j < candidates.size(); j++)
|
||||
{
|
||||
size_t current = candidates[j];
|
||||
|
||||
// Compare (top, left) lexicographically
|
||||
if (std::tie(monitorInfo[current].second.top, monitorInfo[current].second.left)
|
||||
< std::tie(monitorInfo[smallest].second.top, monitorInfo[smallest].second.left))
|
||||
{
|
||||
smallest = current;
|
||||
}
|
||||
}
|
||||
|
||||
used[smallest] = true;
|
||||
sortedMonitorInfo.push_back(monitorInfo[smallest]);
|
||||
for (size_t i = 0; i < nMonitors; i++)
|
||||
{
|
||||
if (blocking[smallest][i])
|
||||
{
|
||||
blockingCount[i]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
monitorInfo = std::move(sortedMonitorInfo);
|
||||
}
|
||||
|
@ -1,14 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "gdiplus.h"
|
||||
|
||||
struct Rect
|
||||
{
|
||||
Rect() {}
|
||||
|
||||
Rect(RECT rect) : m_rect(rect)
|
||||
Rect(RECT rect) :
|
||||
m_rect(rect)
|
||||
{
|
||||
}
|
||||
|
||||
Rect(RECT rect, UINT dpi) : m_rect(rect)
|
||||
Rect(RECT rect, UINT dpi) :
|
||||
m_rect(rect)
|
||||
{
|
||||
m_rect.right = m_rect.left + MulDiv(m_rect.right - m_rect.left, dpi, 96);
|
||||
m_rect.bottom = m_rect.top + MulDiv(m_rect.bottom - m_rect.top, dpi, 96);
|
||||
@ -38,7 +42,7 @@ inline void MakeWindowTransparent(HWND window)
|
||||
}
|
||||
}
|
||||
|
||||
inline void InitRGB(_Out_ RGBQUAD *quad, BYTE alpha, COLORREF color)
|
||||
inline void InitRGB(_Out_ RGBQUAD* quad, BYTE alpha, COLORREF color)
|
||||
{
|
||||
ZeroMemory(quad, sizeof(*quad));
|
||||
quad->rgbReserved = alpha;
|
||||
@ -47,7 +51,7 @@ inline void InitRGB(_Out_ RGBQUAD *quad, BYTE alpha, COLORREF color)
|
||||
quad->rgbBlue = GetBValue(color) * alpha / 255;
|
||||
}
|
||||
|
||||
inline void FillRectARGB(wil::unique_hdc& hdc, RECT const *prcFill, BYTE alpha, COLORREF color, bool blendAlpha)
|
||||
inline void FillRectARGB(wil::unique_hdc& hdc, RECT const* prcFill, BYTE alpha, COLORREF color, bool blendAlpha)
|
||||
{
|
||||
BITMAPINFO bi;
|
||||
ZeroMemory(&bi, sizeof(bi));
|
||||
@ -60,63 +64,30 @@ inline void FillRectARGB(wil::unique_hdc& hdc, RECT const *prcFill, BYTE alpha,
|
||||
|
||||
RECT fillRect;
|
||||
CopyRect(&fillRect, prcFill);
|
||||
if ((alpha == 255) || !blendAlpha)
|
||||
{
|
||||
// Opaque or the caller does not want to blend the alpha
|
||||
RGBQUAD bitmapBits;
|
||||
InitRGB(&bitmapBits, alpha, color);
|
||||
StretchDIBits(
|
||||
hdc.get(),
|
||||
fillRect.left,
|
||||
fillRect.top,
|
||||
fillRect.right - fillRect.left,
|
||||
fillRect.bottom - fillRect.top,
|
||||
0, 0, 1, 1, &bitmapBits, &bi, DIB_RGB_COLORS, SRCCOPY);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wil::unique_hdc hdcSrc{ CreateCompatibleDC(hdc.get()) })
|
||||
{
|
||||
void* pBitmapBits;
|
||||
if (wil::unique_hbitmap bitmapSource{ CreateDIBSection(hdcSrc.get(), &bi, DIB_RGB_COLORS, &pBitmapBits, nullptr, 0) })
|
||||
{
|
||||
InitRGB(reinterpret_cast<RGBQUAD *>(pBitmapBits), alpha, color);
|
||||
|
||||
wil::unique_select_object bitmapOld{ SelectObject(hdcSrc.get(), bitmapSource.get()) };
|
||||
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
|
||||
GdiAlphaBlend(
|
||||
hdc.get(),
|
||||
fillRect.left,
|
||||
fillRect.top,
|
||||
fillRect.right - fillRect.left,
|
||||
fillRect.bottom - fillRect.top,
|
||||
hdcSrc.get(), 0, 0, 1, 1, bf);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void FrameRectARGB(wil::unique_hdc& hdc, const RECT &rc, BYTE bAlpha, COLORREF clr, int thickness)
|
||||
{
|
||||
RECT sides[] = {
|
||||
{ rc.left, rc.top, (rc.left + thickness), rc.bottom },
|
||||
{ (rc.right - thickness), rc.top, rc.right, rc.bottom },
|
||||
{ (rc.left + thickness), rc.top, (rc.right - thickness), (rc.top + thickness) },
|
||||
{ (rc.left + thickness), (rc.bottom - thickness), (rc.right - thickness), rc.bottom }
|
||||
};
|
||||
|
||||
for (UINT i = 0; i < ARRAYSIZE(sides); i++)
|
||||
{
|
||||
FillRectARGB(hdc, &(sides[i]), bAlpha, clr, false);
|
||||
}
|
||||
RGBQUAD bitmapBits;
|
||||
InitRGB(&bitmapBits, alpha, color);
|
||||
StretchDIBits(
|
||||
hdc.get(),
|
||||
fillRect.left,
|
||||
fillRect.top,
|
||||
fillRect.right - fillRect.left,
|
||||
fillRect.bottom - fillRect.top,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
&bitmapBits,
|
||||
&bi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY);
|
||||
}
|
||||
|
||||
inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size)
|
||||
{
|
||||
// We're interested in the unique part between the first and last #'s
|
||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||
const std::wstring defaultDeviceId = L"FallbackDevice";
|
||||
if (!deviceId)
|
||||
{
|
||||
@ -140,10 +111,10 @@ inline void ParseDeviceId(PCWSTR deviceId, PWSTR parsedId, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned char OpacitySettingToAlpha(int opacity)
|
||||
inline BYTE OpacitySettingToAlpha(int opacity)
|
||||
{
|
||||
// convert percentage to a 0-255 alpha value
|
||||
return static_cast<unsigned char>(opacity * 2.55);
|
||||
return static_cast<BYTE>(opacity * 2.55);
|
||||
}
|
||||
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
||||
|
@ -12,123 +12,43 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace FancyZonesUnitTests
|
||||
{
|
||||
TEST_CLASS(FancyZonesUnitTests)
|
||||
TEST_CLASS (FancyZonesUnitTests)
|
||||
{
|
||||
HINSTANCE m_hInst;
|
||||
winrt::com_ptr<IFancyZonesSettings> m_settings;
|
||||
|
||||
TEST_METHOD_INITIALIZE(Init)
|
||||
{
|
||||
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||
m_settings = MakeFancyZonesSettings(m_hInst, L"FancyZonesUnitTests");
|
||||
Assert::IsTrue(m_settings != nullptr);
|
||||
}
|
||||
|
||||
TEST_METHOD(Create)
|
||||
{
|
||||
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||
Assert::IsNotNull(actual.get());
|
||||
}
|
||||
TEST_METHOD(CreateWithEmptyHinstance)
|
||||
{
|
||||
auto actual = MakeFancyZones({}, m_settings);
|
||||
Assert::IsNotNull(actual.get());
|
||||
}
|
||||
|
||||
TEST_METHOD(CreateWithNullHinstance)
|
||||
{
|
||||
auto actual = MakeFancyZones(nullptr, m_settings);
|
||||
Assert::IsNotNull(actual.get());
|
||||
}
|
||||
|
||||
TEST_METHOD(CreateWithNullSettings)
|
||||
{
|
||||
auto actual = MakeFancyZones(m_hInst, nullptr);
|
||||
Assert::IsNull(actual.get());
|
||||
}
|
||||
|
||||
TEST_METHOD(Run)
|
||||
{
|
||||
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::atomic<int> counter = 0;
|
||||
const int expectedCount = 10;
|
||||
|
||||
auto runFunc = [&]() {
|
||||
actual->Run();
|
||||
counter++;
|
||||
};
|
||||
|
||||
for (int i = 0; i < expectedCount; i++)
|
||||
{
|
||||
threads.push_back(std::thread(runFunc));
|
||||
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||
m_settings = MakeFancyZonesSettings(m_hInst, L"FancyZonesUnitTests");
|
||||
Assert::IsTrue(m_settings != nullptr);
|
||||
}
|
||||
|
||||
for (auto& thread : threads)
|
||||
TEST_METHOD (Create)
|
||||
{
|
||||
thread.join();
|
||||
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||
Assert::IsNotNull(actual.get());
|
||||
}
|
||||
TEST_METHOD (CreateWithEmptyHinstance)
|
||||
{
|
||||
auto actual = MakeFancyZones({}, m_settings);
|
||||
Assert::IsNotNull(actual.get());
|
||||
}
|
||||
|
||||
Assert::AreEqual(expectedCount, counter.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(Destroy)
|
||||
{
|
||||
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::atomic<int> counter = 0;
|
||||
const int expectedCount = 10;
|
||||
|
||||
auto destroyFunc = [&]() {
|
||||
actual->Destroy();
|
||||
counter++;
|
||||
};
|
||||
|
||||
for (int i = 0; i < expectedCount; i++)
|
||||
TEST_METHOD (CreateWithNullHinstance)
|
||||
{
|
||||
threads.push_back(std::thread(destroyFunc));
|
||||
auto actual = MakeFancyZones(nullptr, m_settings);
|
||||
Assert::IsNotNull(actual.get());
|
||||
}
|
||||
|
||||
for (auto& thread : threads)
|
||||
TEST_METHOD (CreateWithNullSettings)
|
||||
{
|
||||
thread.join();
|
||||
auto actual = MakeFancyZones(m_hInst, nullptr);
|
||||
Assert::IsNull(actual.get());
|
||||
}
|
||||
|
||||
Assert::AreEqual(expectedCount, counter.load());
|
||||
}
|
||||
|
||||
TEST_METHOD(RunDestroy)
|
||||
{
|
||||
auto actual = MakeFancyZones(m_hInst, m_settings);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::atomic<int> counter = 0;
|
||||
const int expectedCount = 20;
|
||||
|
||||
auto func = [&]() {
|
||||
auto idHash = std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
bool run = (idHash % 2 == 0);
|
||||
run ? actual->Run() : actual->Destroy();
|
||||
counter++;
|
||||
};
|
||||
|
||||
for (int i = 0; i < expectedCount; i++)
|
||||
{
|
||||
threads.push_back(std::thread(func));
|
||||
}
|
||||
|
||||
for (auto& thread : threads)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
|
||||
Assert::AreEqual(expectedCount, counter.load());
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(FancyZonesIZoneWindowHostUnitTests)
|
||||
TEST_CLASS (FancyZonesIZoneWindowHostUnitTests)
|
||||
{
|
||||
HINSTANCE m_hInst{};
|
||||
std::wstring m_settingsLocation = L"FancyZonesUnitTests";
|
||||
@ -148,7 +68,11 @@ namespace FancyZonesUnitTests
|
||||
ptSettings.add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, settings.virtualDesktopChange_moveWindows);
|
||||
ptSettings.add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows);
|
||||
ptSettings.add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen);
|
||||
ptSettings.add_bool_toogle(L"fancyzones_show_on_all_monitors", IDS_SETTING_DESCRIPTION_SHOW_FANCY_ZONES_ON_ALL_MONITORS, settings.showZonesOnAllMonitors);
|
||||
ptSettings.add_bool_toogle(L"fancyzones_makeDraggedWindowTransparent", IDS_SETTING_DESCRIPTION_MAKE_DRAGGED_WINDOW_TRANSPARENT, settings.makeDraggedWindowTransparent);
|
||||
ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1);
|
||||
ptSettings.add_color_picker(L"fancyzones_zoneColor", IDS_SETTING_DESCRIPTION_ZONECOLOR, settings.zoneColor);
|
||||
ptSettings.add_color_picker(L"fancyzones_zoneBorderColor", IDS_SETTING_DESCRIPTION_ZONE_BORDER_COLOR, settings.zoneBorderColor);
|
||||
ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHightlightColor);
|
||||
ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, settings.excludedApps);
|
||||
|
||||
@ -156,92 +80,185 @@ namespace FancyZonesUnitTests
|
||||
}
|
||||
|
||||
TEST_METHOD_INITIALIZE(Init)
|
||||
{
|
||||
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||
m_settings = MakeFancyZonesSettings(m_hInst, m_settingsLocation.c_str());
|
||||
Assert::IsTrue(m_settings != nullptr);
|
||||
{
|
||||
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||
m_settings = MakeFancyZonesSettings(m_hInst, m_settingsLocation.c_str());
|
||||
Assert::IsTrue(m_settings != nullptr);
|
||||
|
||||
auto fancyZones = MakeFancyZones(m_hInst, m_settings);
|
||||
Assert::IsTrue(fancyZones != nullptr);
|
||||
auto fancyZones = MakeFancyZones(m_hInst, m_settings);
|
||||
Assert::IsTrue(fancyZones != nullptr);
|
||||
|
||||
m_zoneWindowHost = fancyZones.as<IZoneWindowHost>();
|
||||
Assert::IsTrue(m_zoneWindowHost != nullptr);
|
||||
}
|
||||
m_zoneWindowHost = fancyZones.as<IZoneWindowHost>();
|
||||
Assert::IsTrue(m_zoneWindowHost != nullptr);
|
||||
}
|
||||
|
||||
TEST_METHOD_CLEANUP(Cleanup)
|
||||
{
|
||||
auto settingsFolder = PTSettingsHelper::get_module_save_folder_location(m_settingsLocation);
|
||||
const auto settingsFile = settingsFolder + L"\\settings.json";
|
||||
std::filesystem::remove(settingsFile);
|
||||
std::filesystem::remove(settingsFolder);
|
||||
}
|
||||
TEST_METHOD_CLEANUP(Cleanup)
|
||||
{
|
||||
auto settingsFolder = PTSettingsHelper::get_module_save_folder_location(m_settingsLocation);
|
||||
const auto settingsFile = settingsFolder + L"\\settings.json";
|
||||
std::filesystem::remove(settingsFile);
|
||||
std::filesystem::remove(settingsFolder);
|
||||
}
|
||||
|
||||
TEST_METHOD(GetZoneHighlightColor)
|
||||
{
|
||||
const auto expected = RGB(171, 175, 238);
|
||||
const Settings settings{
|
||||
.shiftDrag = true,
|
||||
.displayChange_moveWindows = true,
|
||||
.virtualDesktopChange_moveWindows = true,
|
||||
.zoneSetChange_flashZones = false,
|
||||
.zoneSetChange_moveWindows = true,
|
||||
.overrideSnapHotkeys = false,
|
||||
.appLastZone_moveWindows = true,
|
||||
.use_cursorpos_editor_startupscreen = true,
|
||||
.zoneHightlightColor = L"#abafee",
|
||||
.zoneHighlightOpacity = 45,
|
||||
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||
.excludedApps = L"app\r\napp2",
|
||||
.excludedAppsArray = { L"APP", L"APP2" },
|
||||
};
|
||||
TEST_METHOD (GetZoneColor)
|
||||
{
|
||||
const auto expected = RGB(171, 175, 238);
|
||||
const Settings settings{
|
||||
.shiftDrag = true,
|
||||
.displayChange_moveWindows = true,
|
||||
.virtualDesktopChange_moveWindows = true,
|
||||
.zoneSetChange_flashZones = false,
|
||||
.zoneSetChange_moveWindows = true,
|
||||
.overrideSnapHotkeys = false,
|
||||
.appLastZone_moveWindows = true,
|
||||
.use_cursorpos_editor_startupscreen = true,
|
||||
.zoneColor = L"#abafee",
|
||||
.zoneBorderColor = L"FAFAFA",
|
||||
.zoneHightlightColor = L"#FAFAFA",
|
||||
.zoneHighlightOpacity = 45,
|
||||
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||
.excludedApps = L"app\r\napp2",
|
||||
.excludedAppsArray = { L"APP", L"APP2" },
|
||||
};
|
||||
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
|
||||
const auto actual = m_zoneWindowHost->GetZoneHighlightColor();
|
||||
Assert::AreEqual(expected, actual);
|
||||
}
|
||||
const auto actual = m_zoneWindowHost->GetZoneColor();
|
||||
Assert::AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(GetZoneHighlightOpacity)
|
||||
{
|
||||
const auto expected = 88;
|
||||
const Settings settings{
|
||||
.shiftDrag = true,
|
||||
.displayChange_moveWindows = true,
|
||||
.virtualDesktopChange_moveWindows = true,
|
||||
.zoneSetChange_flashZones = false,
|
||||
.zoneSetChange_moveWindows = true,
|
||||
.overrideSnapHotkeys = false,
|
||||
.appLastZone_moveWindows = true,
|
||||
.use_cursorpos_editor_startupscreen = true,
|
||||
.zoneHightlightColor = L"#abafee",
|
||||
.zoneHighlightOpacity = expected,
|
||||
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||
.excludedApps = L"app\r\napp2",
|
||||
.excludedAppsArray = { L"APP", L"APP2" },
|
||||
};
|
||||
TEST_METHOD (GetZoneBorderColor)
|
||||
{
|
||||
const auto expected = RGB(171, 175, 238);
|
||||
const Settings settings{
|
||||
.shiftDrag = true,
|
||||
.displayChange_moveWindows = true,
|
||||
.virtualDesktopChange_moveWindows = true,
|
||||
.zoneSetChange_flashZones = false,
|
||||
.zoneSetChange_moveWindows = true,
|
||||
.overrideSnapHotkeys = false,
|
||||
.appLastZone_moveWindows = true,
|
||||
.use_cursorpos_editor_startupscreen = true,
|
||||
.zoneColor = L"#FAFAFA",
|
||||
.zoneBorderColor = L"#abafee",
|
||||
.zoneHightlightColor = L"#FAFAFA",
|
||||
.zoneHighlightOpacity = 45,
|
||||
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||
.excludedApps = L"app\r\napp2",
|
||||
.excludedAppsArray = { L"APP", L"APP2" },
|
||||
};
|
||||
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
|
||||
const auto actual = m_zoneWindowHost->GetZoneHighlightOpacity();
|
||||
Assert::AreEqual(expected, actual);
|
||||
}
|
||||
const auto actual = m_zoneWindowHost->GetZoneBorderColor();
|
||||
Assert::AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(GetCurrentMonitorZoneSetEmpty)
|
||||
{
|
||||
const auto* actual = m_zoneWindowHost->GetParentZoneWindow(Mocks::Monitor());
|
||||
Assert::IsNull(actual);
|
||||
}
|
||||
TEST_METHOD (GetZoneHighlightColor)
|
||||
{
|
||||
const auto expected = RGB(171, 175, 238);
|
||||
const Settings settings{
|
||||
.shiftDrag = true,
|
||||
.displayChange_moveWindows = true,
|
||||
.virtualDesktopChange_moveWindows = true,
|
||||
.zoneSetChange_flashZones = false,
|
||||
.zoneSetChange_moveWindows = true,
|
||||
.overrideSnapHotkeys = false,
|
||||
.appLastZone_moveWindows = true,
|
||||
.use_cursorpos_editor_startupscreen = true,
|
||||
.showZonesOnAllMonitors = false,
|
||||
.makeDraggedWindowTransparent = true,
|
||||
.zoneColor = L"#FAFAFA",
|
||||
.zoneBorderColor = L"FAFAFA",
|
||||
.zoneHightlightColor = L"#abafee",
|
||||
.zoneHighlightOpacity = 45,
|
||||
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||
.excludedApps = L"app\r\napp2",
|
||||
.excludedAppsArray = { L"APP", L"APP2" },
|
||||
};
|
||||
|
||||
TEST_METHOD(GetCurrentMonitorZoneSetNullMonitor)
|
||||
{
|
||||
const auto* actual = m_zoneWindowHost->GetParentZoneWindow(nullptr);
|
||||
Assert::IsNull(actual);
|
||||
}
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
|
||||
const auto actual = m_zoneWindowHost->GetZoneHighlightColor();
|
||||
Assert::AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD (GetZoneHighlightOpacity)
|
||||
{
|
||||
const auto expected = 88;
|
||||
const Settings settings{
|
||||
.shiftDrag = true,
|
||||
.displayChange_moveWindows = true,
|
||||
.virtualDesktopChange_moveWindows = true,
|
||||
.zoneSetChange_flashZones = false,
|
||||
.zoneSetChange_moveWindows = true,
|
||||
.overrideSnapHotkeys = false,
|
||||
.appLastZone_moveWindows = true,
|
||||
.use_cursorpos_editor_startupscreen = true,
|
||||
.showZonesOnAllMonitors = false,
|
||||
.makeDraggedWindowTransparent = true,
|
||||
.zoneColor = L"#FAFAFA",
|
||||
.zoneBorderColor = L"FAFAFA",
|
||||
.zoneHightlightColor = L"#abafee",
|
||||
.zoneHighlightOpacity = expected,
|
||||
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||
.excludedApps = L"app\r\napp2",
|
||||
.excludedAppsArray = { L"APP", L"APP2" },
|
||||
};
|
||||
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
|
||||
const auto actual = m_zoneWindowHost->GetZoneHighlightOpacity();
|
||||
Assert::AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD (IsMakeDraggenWindowTransparentActive)
|
||||
{
|
||||
const auto expected = true;
|
||||
const Settings settings{
|
||||
.shiftDrag = true,
|
||||
.displayChange_moveWindows = true,
|
||||
.virtualDesktopChange_moveWindows = true,
|
||||
.zoneSetChange_flashZones = false,
|
||||
.zoneSetChange_moveWindows = true,
|
||||
.overrideSnapHotkeys = false,
|
||||
.appLastZone_moveWindows = true,
|
||||
.use_cursorpos_editor_startupscreen = true,
|
||||
.showZonesOnAllMonitors = false,
|
||||
.makeDraggedWindowTransparent = true,
|
||||
.zoneColor = L"#FAFAFA",
|
||||
.zoneBorderColor = L"FAFAFA",
|
||||
.zoneHightlightColor = L"#abafee",
|
||||
.zoneHighlightOpacity = expected,
|
||||
.editorHotkey = PowerToysSettings::HotkeyObject::from_settings(false, false, false, false, VK_OEM_3),
|
||||
.excludedApps = L"app\r\napp2",
|
||||
.excludedAppsArray = { L"APP", L"APP2" },
|
||||
};
|
||||
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
|
||||
Assert::AreEqual(expected, m_zoneWindowHost->isMakeDraggedWindowTransparentActive());
|
||||
}
|
||||
|
||||
TEST_METHOD (GetCurrentMonitorZoneSetEmpty)
|
||||
{
|
||||
const auto* actual = m_zoneWindowHost->GetParentZoneWindow(Mocks::Monitor());
|
||||
Assert::IsNull(actual);
|
||||
}
|
||||
|
||||
TEST_METHOD (GetCurrentMonitorZoneSetNullMonitor)
|
||||
{
|
||||
const auto* actual = m_zoneWindowHost->GetParentZoneWindow(nullptr);
|
||||
Assert::IsNull(actual);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CLASS(FancyZonesIFancyZonesCallbackUnitTests)
|
||||
TEST_CLASS (FancyZonesIFancyZonesCallbackUnitTests)
|
||||
{
|
||||
HINSTANCE m_hInst{};
|
||||
std::wstring m_settingsLocation = L"FancyZonesUnitTests";
|
||||
@ -263,7 +280,11 @@ namespace FancyZonesUnitTests
|
||||
ptSettings.add_bool_toogle(L"fancyzones_virtualDesktopChange_moveWindows", IDS_SETTING_DESCRIPTION_VIRTUALDESKTOPCHANGE_MOVEWINDOWS, settings.virtualDesktopChange_moveWindows);
|
||||
ptSettings.add_bool_toogle(L"fancyzones_appLastZone_moveWindows", IDS_SETTING_DESCRIPTION_APPLASTZONE_MOVEWINDOWS, settings.appLastZone_moveWindows);
|
||||
ptSettings.add_bool_toogle(L"use_cursorpos_editor_startupscreen", IDS_SETTING_DESCRIPTION_USE_CURSORPOS_EDITOR_STARTUPSCREEN, settings.use_cursorpos_editor_startupscreen);
|
||||
ptSettings.add_bool_toogle(L"fancyzones_show_on_all_monitors", IDS_SETTING_DESCRIPTION_SHOW_FANCY_ZONES_ON_ALL_MONITORS, settings.showZonesOnAllMonitors);
|
||||
ptSettings.add_bool_toogle(L"fancyzones_makeDraggedWindowTransparent", IDS_SETTING_DESCRIPTION_MAKE_DRAGGED_WINDOW_TRANSPARENT, settings.makeDraggedWindowTransparent);
|
||||
ptSettings.add_int_spinner(L"fancyzones_highlight_opacity", IDS_SETTINGS_HIGHLIGHT_OPACITY, settings.zoneHighlightOpacity, 0, 100, 1);
|
||||
ptSettings.add_color_picker(L"fancyzones_zoneColor", IDS_SETTING_DESCRIPTION_ZONECOLOR, settings.zoneColor);
|
||||
ptSettings.add_color_picker(L"fancyzones_zoneBorderColor", IDS_SETTING_DESCRIPTION_ZONE_BORDER_COLOR, settings.zoneBorderColor);
|
||||
ptSettings.add_color_picker(L"fancyzones_zoneHighlightColor", IDS_SETTING_DESCRIPTION_ZONEHIGHLIGHTCOLOR, settings.zoneHightlightColor);
|
||||
ptSettings.add_multiline_string(L"fancyzones_excluded_apps", IDS_SETTING_EXCLCUDED_APPS_DESCRIPTION, settings.excludedApps);
|
||||
|
||||
@ -283,157 +304,158 @@ namespace FancyZonesUnitTests
|
||||
}
|
||||
|
||||
TEST_METHOD_INITIALIZE(Init)
|
||||
{
|
||||
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||
m_settings = MakeFancyZonesSettings(m_hInst, m_settingsLocation.c_str());
|
||||
Assert::IsTrue(m_settings != nullptr);
|
||||
|
||||
auto fancyZones = MakeFancyZones(m_hInst, m_settings);
|
||||
Assert::IsTrue(fancyZones != nullptr);
|
||||
|
||||
m_fzCallback = fancyZones.as<IFancyZonesCallback>();
|
||||
Assert::IsTrue(m_fzCallback != nullptr);
|
||||
|
||||
m_fancyZonesData.clear_data();
|
||||
}
|
||||
|
||||
TEST_METHOD_CLEANUP(Cleanup)
|
||||
{
|
||||
sendKeyboardInput(VK_SHIFT, true);
|
||||
sendKeyboardInput(VK_LWIN, true);
|
||||
sendKeyboardInput(VK_CONTROL, true);
|
||||
|
||||
auto settingsFolder = PTSettingsHelper::get_module_save_folder_location(m_settingsLocation);
|
||||
const auto settingsFile = settingsFolder + L"\\settings.json";
|
||||
std::filesystem::remove(settingsFile);
|
||||
std::filesystem::remove(settingsFolder);
|
||||
}
|
||||
|
||||
TEST_METHOD(OnKeyDownNothingPressed)
|
||||
{
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||
m_settings = MakeFancyZonesSettings(m_hInst, m_settingsLocation.c_str());
|
||||
Assert::IsTrue(m_settings != nullptr);
|
||||
|
||||
auto fancyZones = MakeFancyZones(m_hInst, m_settings);
|
||||
Assert::IsTrue(fancyZones != nullptr);
|
||||
|
||||
m_fzCallback = fancyZones.as<IFancyZonesCallback>();
|
||||
Assert::IsTrue(m_fzCallback != nullptr);
|
||||
|
||||
m_fancyZonesData.clear_data();
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
TEST_METHOD_CLEANUP(Cleanup)
|
||||
{
|
||||
sendKeyboardInput(VK_SHIFT, true);
|
||||
sendKeyboardInput(VK_LWIN, true);
|
||||
sendKeyboardInput(VK_CONTROL, true);
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
auto settingsFolder = PTSettingsHelper::get_module_save_folder_location(m_settingsLocation);
|
||||
const auto settingsFile = settingsFolder + L"\\settings.json";
|
||||
std::filesystem::remove(settingsFile);
|
||||
std::filesystem::remove(settingsFolder);
|
||||
}
|
||||
|
||||
TEST_METHOD(OnKeyDownShiftPressed)
|
||||
{
|
||||
sendKeyboardInput(VK_SHIFT);
|
||||
TEST_METHOD (OnKeyDownNothingPressed)
|
||||
{
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
TEST_METHOD (OnKeyDownShiftPressed)
|
||||
{
|
||||
sendKeyboardInput(VK_SHIFT);
|
||||
|
||||
TEST_METHOD(OnKeyDownWinPressed)
|
||||
{
|
||||
sendKeyboardInput(VK_LWIN);
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
TEST_METHOD (OnKeyDownWinPressed)
|
||||
{
|
||||
sendKeyboardInput(VK_LWIN);
|
||||
|
||||
TEST_METHOD(OnKeyDownWinShiftPressed)
|
||||
{
|
||||
sendKeyboardInput(VK_LWIN);
|
||||
sendKeyboardInput(VK_SHIFT);
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
TEST_METHOD (OnKeyDownWinShiftPressed)
|
||||
{
|
||||
sendKeyboardInput(VK_LWIN);
|
||||
sendKeyboardInput(VK_SHIFT);
|
||||
|
||||
TEST_METHOD(OnKeyDownWinCtrlPressed)
|
||||
{
|
||||
sendKeyboardInput(VK_LWIN);
|
||||
sendKeyboardInput(VK_CONTROL);
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
const Settings settings{
|
||||
.overrideSnapHotkeys = false,
|
||||
};
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
/*
|
||||
TEST_METHOD (OnKeyDownWinCtrlPressed)
|
||||
{
|
||||
sendKeyboardInput(VK_LWIN);
|
||||
sendKeyboardInput(VK_CONTROL);
|
||||
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsTrue(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
const Settings settings{
|
||||
.overrideSnapHotkeys = false,
|
||||
};
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
auto config = serializedPowerToySettings(settings);
|
||||
m_settings->SetConfig(config.c_str());
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
for (DWORD code = '0'; code <= '9'; code++)
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = code;
|
||||
Assert::IsTrue(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_LEFT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
|
||||
{
|
||||
tagKBDLLHOOKSTRUCT input{};
|
||||
input.vkCode = VK_RIGHT;
|
||||
Assert::IsFalse(m_fzCallback->OnKeyDown(&input));
|
||||
}
|
||||
}
|
||||
*/
|
||||
};
|
||||
}
|
@ -49,9 +49,13 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
|
@ -1,32 +1,235 @@
|
||||
#include "pch.h"
|
||||
#include "Util.h"
|
||||
#include "lib\util.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace FancyZonesUnitTests
|
||||
{
|
||||
TEST_CLASS(UtilUnitTests){
|
||||
public:
|
||||
TEST_METHOD(TestParseDeviceId){
|
||||
// We're interested in the unique part between the first and last #'s
|
||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||
PCWSTR input = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
|
||||
wchar_t output[256]{};
|
||||
ParseDeviceId(input, output, ARRAYSIZE(output));
|
||||
Assert::AreEqual(0, wcscmp(output, L"DELA026#5&10a58c63&0&UID16777488"));
|
||||
void TestMonitorSetPermutations(const std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||
{
|
||||
auto monitorInfoPermutation = monitorInfo;
|
||||
|
||||
do {
|
||||
auto monitorInfoCopy = monitorInfoPermutation;
|
||||
OrderMonitors(monitorInfoCopy);
|
||||
CustomAssert::AreEqual(monitorInfo, monitorInfoCopy);
|
||||
} while (std::next_permutation(monitorInfoPermutation.begin(), monitorInfoPermutation.end(), [](auto x, auto y) { return x.first < y.first; }));
|
||||
}
|
||||
|
||||
void TestMonitorSetPermutationsOffsets(const std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||
{
|
||||
for (int offsetX = -3000; offsetX <= 3000; offsetX += 1000)
|
||||
{
|
||||
for (int offsetY = -3000; offsetY <= 3000; offsetY += 1000)
|
||||
{
|
||||
auto monitorInfoCopy = monitorInfo;
|
||||
for (auto& [monitor, rect] : monitorInfoCopy)
|
||||
{
|
||||
rect.left += offsetX;
|
||||
rect.right += offsetX;
|
||||
rect.top += offsetY;
|
||||
rect.bottom += offsetY;
|
||||
}
|
||||
TestMonitorSetPermutations(monitorInfoCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CLASS(UtilUnitTests)
|
||||
{
|
||||
TEST_METHOD(TestParseDeviceId)
|
||||
{
|
||||
// We're interested in the unique part between the first and last #'s
|
||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||
PCWSTR input = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
|
||||
wchar_t output[256]{};
|
||||
ParseDeviceId(input, output, ARRAYSIZE(output));
|
||||
Assert::AreEqual(0, wcscmp(output, L"DELA026#5&10a58c63&0&UID16777488"));
|
||||
}
|
||||
|
||||
TEST_METHOD(TestParseInvalidDeviceId)
|
||||
{
|
||||
// We're interested in the unique part between the first and last #'s
|
||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||
PCWSTR input = L"AnInvalidDeviceId";
|
||||
wchar_t output[256]{};
|
||||
ParseDeviceId(input, output, ARRAYSIZE(output));
|
||||
Assert::AreEqual(0, wcscmp(output, L"FallbackDevice"));
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering01)
|
||||
{
|
||||
// Three horizontally arranged monitors, bottom aligned, with increasing sizes
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 200, .right = 1600, .bottom = 1100} },
|
||||
{Mocks::Monitor(), RECT{.left = 1600, .top = 100, .right = 3300, .bottom = 1100} },
|
||||
{Mocks::Monitor(), RECT{.left = 3300, .top = 0, .right = 5100, .bottom = 1100} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering02)
|
||||
{
|
||||
// Three horizontally arranged monitors, bottom aligned, with equal sizes
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 0, .right = 1600, .bottom = 900} },
|
||||
{Mocks::Monitor(), RECT{.left = 1600, .top = 0, .right = 3200, .bottom = 900} },
|
||||
{Mocks::Monitor(), RECT{.left = 3200, .top = 0, .right = 4800, .bottom = 900} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering03)
|
||||
{
|
||||
// Three horizontally arranged monitors, bottom aligned, with decreasing sizes
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 0, .right = 1800, .bottom = 1100} },
|
||||
{Mocks::Monitor(), RECT{.left = 1800, .top = 100, .right = 3500, .bottom = 1100} },
|
||||
{Mocks::Monitor(), RECT{.left = 3500, .top = 200, .right = 5100, .bottom = 1100} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering04)
|
||||
{
|
||||
// Three horizontally arranged monitors, top aligned, with increasing sizes
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 0, .right = 1600, .bottom = 900} },
|
||||
{Mocks::Monitor(), RECT{.left = 1600, .top = 0, .right = 3300, .bottom = 1000} },
|
||||
{Mocks::Monitor(), RECT{.left = 3300, .top = 0, .right = 5100, .bottom = 1100} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering05)
|
||||
{
|
||||
// Three horizontally arranged monitors, top aligned, with equal sizes
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 0, .right = 1600, .bottom = 900} },
|
||||
{Mocks::Monitor(), RECT{.left = 1600, .top = 0, .right = 3200, .bottom = 900} },
|
||||
{Mocks::Monitor(), RECT{.left = 3200, .top = 0, .right = 4800, .bottom = 900} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering06)
|
||||
{
|
||||
// Three horizontally arranged monitors, top aligned, with decreasing sizes
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 0, .right = 1800, .bottom = 1100} },
|
||||
{Mocks::Monitor(), RECT{.left = 1800, .top = 0, .right = 3500, .bottom = 1000} },
|
||||
{Mocks::Monitor(), RECT{.left = 3500, .top = 0, .right = 5100, .bottom = 900} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering07)
|
||||
{
|
||||
// Three vertically arranged monitors, center aligned, with equal sizes, except the middle monitor is a bit wider
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 100, .top = 0, .right = 1700, .bottom = 900} },
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 900, .right = 1800, .bottom = 1800} },
|
||||
{Mocks::Monitor(), RECT{.left = 100, .top = 1800, .right = 1700, .bottom = 2700} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering08)
|
||||
{
|
||||
// ------------------
|
||||
// | || || |
|
||||
// | || || |
|
||||
// ------------------
|
||||
// | || |
|
||||
// | || |
|
||||
// ------------------
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 0, .right = 600, .bottom = 400} },
|
||||
{Mocks::Monitor(), RECT{.left = 600, .top = 0, .right = 1200, .bottom = 400} },
|
||||
{Mocks::Monitor(), RECT{.left = 1200, .top = 0, .right = 1800, .bottom = 400} },
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 400, .right = 900, .bottom = 800} },
|
||||
{Mocks::Monitor(), RECT{.left = 900, .top = 400, .right = 1800, .bottom = 800} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering09)
|
||||
{
|
||||
// Regular 3x3 grid
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 0, .right = 400, .bottom = 300} },
|
||||
{Mocks::Monitor(), RECT{.left = 400, .top = 0, .right = 800, .bottom = 300} },
|
||||
{Mocks::Monitor(), RECT{.left = 800, .top = 0, .right = 1200, .bottom = 300} },
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 300, .right = 400, .bottom = 600} },
|
||||
{Mocks::Monitor(), RECT{.left = 400, .top = 300, .right = 800, .bottom = 600} },
|
||||
{Mocks::Monitor(), RECT{.left = 800, .top = 300, .right = 1200, .bottom = 600} },
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 600, .right = 400, .bottom = 900} },
|
||||
{Mocks::Monitor(), RECT{.left = 400, .top = 600, .right = 800, .bottom = 900} },
|
||||
{Mocks::Monitor(), RECT{.left = 800, .top = 600, .right = 1200, .bottom = 900} },
|
||||
};
|
||||
|
||||
// Reduce running time by testing only rotations
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
auto monitorInfoCopy = monitorInfo;
|
||||
std::rotate(monitorInfoCopy.begin(), monitorInfoCopy.begin() + i, monitorInfoCopy.end());
|
||||
OrderMonitors(monitorInfoCopy);
|
||||
CustomAssert::AreEqual(monitorInfo, monitorInfoCopy);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering10)
|
||||
{
|
||||
// ------------------
|
||||
// | || |
|
||||
// | || |
|
||||
// ------------------
|
||||
// | || || |
|
||||
// | || || |
|
||||
// ------------------
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 0, .right = 900, .bottom = 400} },
|
||||
{Mocks::Monitor(), RECT{.left = 900, .top = 0, .right = 1800, .bottom = 400} },
|
||||
{Mocks::Monitor(), RECT{.left = 0, .top = 400, .right = 600, .bottom = 800} },
|
||||
{Mocks::Monitor(), RECT{.left = 600, .top = 400, .right = 1200, .bottom = 800} },
|
||||
{Mocks::Monitor(), RECT{.left = 1200, .top = 400, .right = 1800, .bottom = 800} },
|
||||
};
|
||||
|
||||
TestMonitorSetPermutationsOffsets(monitorInfo);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMonitorOrdering11)
|
||||
{
|
||||
// Random values, some monitors overlap, don't check order, just ensure it doesn't crash and it's the same every time
|
||||
std::vector<std::pair<HMONITOR, RECT>> monitorInfo = {
|
||||
{Mocks::Monitor(), RECT{.left = 410, .top = 630, .right = 988, .bottom = 631} },
|
||||
{Mocks::Monitor(), RECT{.left = 302, .top = 189, .right = 550, .bottom = 714} },
|
||||
{Mocks::Monitor(), RECT{.left = 158, .top = 115, .right = 657, .bottom = 499} },
|
||||
{Mocks::Monitor(), RECT{.left = 341, .top = 340, .right = 723, .bottom = 655} },
|
||||
{Mocks::Monitor(), RECT{.left = 433, .top = 393, .right = 846, .bottom = 544} },
|
||||
};
|
||||
|
||||
auto monitorInfoPermutation = monitorInfo;
|
||||
auto firstTime = monitorInfo;
|
||||
OrderMonitors(firstTime);
|
||||
|
||||
do {
|
||||
auto monitorInfoCopy = monitorInfoPermutation;
|
||||
OrderMonitors(monitorInfoCopy);
|
||||
CustomAssert::AreEqual(firstTime, monitorInfoCopy);
|
||||
} while (next_permutation(monitorInfoPermutation.begin(), monitorInfoPermutation.end(), [](auto x, auto y) { return x.first < y.first; }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST_METHOD(TestParseInvalidDeviceId)
|
||||
{
|
||||
// We're interested in the unique part between the first and last #'s
|
||||
// Example input: \\?\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
|
||||
// Example output: DELA026#5&10a58c63&0&UID16777488
|
||||
PCWSTR input = L"AnInvalidDeviceId";
|
||||
wchar_t output[256]{};
|
||||
ParseDeviceId(input, output, ARRAYSIZE(output));
|
||||
Assert::AreEqual(0, wcscmp(output, L"FallbackDevice"));
|
||||
}
|
||||
}
|
||||
;
|
||||
}
|
||||
|
@ -146,4 +146,28 @@ namespace Mocks
|
||||
m_conditionVar.notify_one();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring Helpers::GuidToString(const GUID& guid)
|
||||
{
|
||||
OLECHAR* guidString;
|
||||
if (StringFromCLSID(guid, &guidString) == S_OK)
|
||||
{
|
||||
std::wstring guidStr{ guidString };
|
||||
CoTaskMemFree(guidString);
|
||||
return guidStr;
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
std::wstring Helpers::CreateGuidString()
|
||||
{
|
||||
GUID guid;
|
||||
if (CoCreateGuid(&guid) == S_OK)
|
||||
{
|
||||
return GuidToString(guid);
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
@ -19,6 +19,15 @@ namespace CustomAssert
|
||||
{
|
||||
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(t1 == t2);
|
||||
}
|
||||
|
||||
static void AreEqual(const std::vector<std::pair<HMONITOR, RECT>>& a1, const std::vector<std::pair<HMONITOR, RECT>>& a2)
|
||||
{
|
||||
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(a1.size() == a2.size());
|
||||
for (size_t i = 0; i < a1.size(); i++)
|
||||
{
|
||||
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(a1[i].first == a2[i].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Mocks
|
||||
@ -43,3 +52,9 @@ namespace Mocks
|
||||
|
||||
HWND WindowCreate(HINSTANCE hInst);
|
||||
}
|
||||
|
||||
namespace Helpers
|
||||
{
|
||||
std::wstring GuidToString(const GUID& guid);
|
||||
std::wstring CreateGuidString();
|
||||
}
|