diff --git a/.gitignore b/.gitignore index e77200d68f..6946e9478d 100644 --- a/.gitignore +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/.pipelines/ci/templates/build-powertoys-steps.yml b/.pipelines/ci/templates/build-powertoys-steps.yml index bfff3c7782..a06cb6514a 100644 --- a/.pipelines/ci/templates/build-powertoys-steps.yml +++ b/.pipelines/ci/templates/build-powertoys-steps.yml @@ -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\** diff --git a/.pipelines/pipeline.user.windows.yml b/.pipelines/pipeline.user.windows.yml index f149be5786..804cd9a133 100644 --- a/.pipelines/pipeline.user.windows.yml +++ b/.pipelines/pipeline.user.windows.yml @@ -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 diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000000..02bba477c4 --- /dev/null +++ b/NOTICE.md @@ -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. diff --git a/PowerToys.sln b/PowerToys.sln index a19ac80cb4..3e563f1f47 100644 --- a/PowerToys.sln +++ b/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} diff --git a/README.md b/README.md index 675d874b91..34dcedbe4c 100644 --- a/README.md +++ b/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 diff --git a/doc/devdocs/guidance.md b/doc/devdocs/guidance.md new file mode 100644 index 0000000000..329297cbc5 --- /dev/null +++ b/doc/devdocs/guidance.md @@ -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 + +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 + + Description to be displayed on UI. + This text is displayed when XYZ button clicked. + +``` + +- 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 \ No newline at end of file diff --git a/doc/devdocs/readme.md b/doc/devdocs/readme.md index dde9861f90..934700b398 100644 --- a/doc/devdocs/readme.md +++ b/doc/devdocs/readme.md @@ -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 diff --git a/doc/devdocs/run-as-admin-detection.md b/doc/devdocs/run-as-admin-detection.md new file mode 100644 index 0000000000..9a8f2f66db --- /dev/null +++ b/doc/devdocs/run-as-admin-detection.md @@ -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" diff --git a/doc/images/imageresizer/resizeDragAndDrop.gif b/doc/images/imageresizer/resizeDragAndDrop.gif new file mode 100644 index 0000000000..49e688602b Binary files /dev/null and b/doc/images/imageresizer/resizeDragAndDrop.gif differ diff --git a/doc/images/imageresizer/resizeNormal.gif b/doc/images/imageresizer/resizeNormal.gif new file mode 100644 index 0000000000..f293fa5753 Binary files /dev/null and b/doc/images/imageresizer/resizeNormal.gif differ diff --git a/doc/images/imageresizer/resizeSettings.gif b/doc/images/imageresizer/resizeSettings.gif new file mode 100644 index 0000000000..622351187d Binary files /dev/null and b/doc/images/imageresizer/resizeSettings.gif differ diff --git a/doc/images/preview_pane/demo.gif b/doc/images/preview_pane/demo.gif new file mode 100644 index 0000000000..42c9ac8c41 Binary files /dev/null and b/doc/images/preview_pane/demo.gif differ diff --git a/doc/images/preview_pane/general-settings.png b/doc/images/preview_pane/general-settings.png new file mode 100644 index 0000000000..2700015fd5 Binary files /dev/null and b/doc/images/preview_pane/general-settings.png differ diff --git a/doc/images/preview_pane/settings-ui.png b/doc/images/preview_pane/settings-ui.png new file mode 100644 index 0000000000..2da9ad0bf8 Binary files /dev/null and b/doc/images/preview_pane/settings-ui.png differ diff --git a/doc/images/runAsAdmin/elevatedWindows.png b/doc/images/runAsAdmin/elevatedWindows.png new file mode 100644 index 0000000000..b296d2536e Binary files /dev/null and b/doc/images/runAsAdmin/elevatedWindows.png differ diff --git a/doc/images/runAsAdmin/uac.png b/doc/images/runAsAdmin/uac.png new file mode 100644 index 0000000000..d26c3f04e8 Binary files /dev/null and b/doc/images/runAsAdmin/uac.png differ diff --git a/License.rtf b/installer/License.rtf similarity index 100% rename from License.rtf rename to installer/License.rtf diff --git a/installer/MSIX/PackagingLayout.xml b/installer/MSIX/PackagingLayout.xml index 9bd2e40b61..6202cbcd0a 100644 --- a/installer/MSIX/PackagingLayout.xml +++ b/installer/MSIX/PackagingLayout.xml @@ -3,7 +3,7 @@ - + @@ -16,7 +16,14 @@ - + + + + + + + + @@ -26,6 +33,13 @@ + + + + + + + @@ -34,6 +48,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/MSIX/appxmanifest.xml b/installer/MSIX/appxmanifest.xml index eed2b6e7f4..a57983a327 100644 --- a/installer/MSIX/appxmanifest.xml +++ b/installer/MSIX/appxmanifest.xml @@ -2,9 +2,12 @@ @@ -28,6 +31,12 @@ + + + images\logo.png + Powertoys custom protocol + + @@ -36,6 +45,9 @@ + + + @@ -46,8 +58,35 @@ + + + + + + + .md + + + + + + + + .svg + + + + + + + + + + + + diff --git a/installer/MSIX/build_msix.ps1 b/installer/MSIX/build_msix.ps1 index 6762d0286a..f494242400 100644 --- a/installer/MSIX/build_msix.ps1 +++ b/installer/MSIX/build_msix.ps1 @@ -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\ diff --git a/installer/MSIX/registry.dat b/installer/MSIX/registry.dat new file mode 100644 index 0000000000..a843231211 Binary files /dev/null and b/installer/MSIX/registry.dat differ diff --git a/installer/MSIX/registry.reg b/installer/MSIX/registry.reg new file mode 100644 index 0000000000..b8cc53e4e5 Binary files /dev/null and b/installer/MSIX/registry.reg differ diff --git a/installer/PowerToysSetup/PowerToysSetup.wixproj b/installer/PowerToysSetup/PowerToysSetup.wixproj index 857b47f270..d1a2eeb484 100644 --- a/installer/PowerToysSetup/PowerToysSetup.wixproj +++ b/installer/PowerToysSetup/PowerToysSetup.wixproj @@ -15,7 +15,6 @@ - Debug $(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index a4601221f8..cfd75beaff 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -22,7 +22,7 @@ Property="PREVIOUSVERSIONSINSTALLED" IncludeMinimum="yes" IncludeMaximum="no" /> - + @@ -32,11 +32,12 @@ = 17134)]]> - - + + + @@ -55,7 +56,7 @@ - + @@ -66,6 +67,9 @@ + + + @@ -144,7 +148,7 @@ BinaryKey="PTCustomActions" DllEntry="TelemetryLogUninstallFailCA" /> - + - + - EXISTINGPOWERRENAMEEXTPATH + EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH @@ -177,7 +181,25 @@ - + + + + + + + + + + + + + + + + + + + @@ -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"> @@ -214,8 +236,20 @@ + + + + + + + + + + + + @@ -223,7 +257,7 @@ - + @@ -274,7 +308,167 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -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"/> @@ -323,10 +517,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp index f72115d9b9..cf932f901b 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp @@ -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."); diff --git a/installer/README.md b/installer/README.md index cd319ed1e9..2ff0e7f074 100644 --- a/installer/README.md +++ b/installer/README.md @@ -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 diff --git a/installer/Version.props b/installer/Version.props index a73cd02a53..0aaba86cbd 100644 --- a/installer/Version.props +++ b/installer/Version.props @@ -1,7 +1,7 @@ - 0.15.3 + 0.16.2 Version=$(Version); \ No newline at end of file diff --git a/src/action_runner/action_runner.vcxproj b/src/action_runner/action_runner.vcxproj index d14b5f2e34..5e14d3ada3 100644 --- a/src/action_runner/action_runner.vcxproj +++ b/src/action_runner/action_runner.vcxproj @@ -73,6 +73,7 @@ false ../;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ true @@ -80,6 +81,7 @@ true ../;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ false diff --git a/src/common/UnitTests-CommonLib/Settings.Tests.cpp b/src/common/UnitTests-CommonLib/Settings.Tests.cpp index 853b330303..1d0ef8f36d 100644 --- a/src/common/UnitTests-CommonLib/Settings.Tests.cpp +++ b/src/common/UnitTests-CommonLib/Settings.Tests.cpp @@ -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."; diff --git a/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj b/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj index 11dfe947e2..9667cf1f52 100644 --- a/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj +++ b/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj @@ -48,9 +48,11 @@ false + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ true + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ @@ -91,10 +93,11 @@ Windows $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - RuntimeObject.lib;%(AdditionalDependencies) + RuntimeObject.lib;shlwapi.lib;%(AdditionalDependencies) + Create diff --git a/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj.filters b/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj.filters index fa2d64c46a..07ce02df23 100644 --- a/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj.filters +++ b/src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj.filters @@ -24,6 +24,9 @@ Source Files + + Source Files + diff --git a/src/common/UnitTests-CommonLib/UnitTestsCommon.cpp b/src/common/UnitTests-CommonLib/UnitTestsCommon.cpp new file mode 100644 index 0000000000..e495863d10 --- /dev/null +++ b/src/common/UnitTests-CommonLib/UnitTestsCommon.cpp @@ -0,0 +1,57 @@ +#include "pch.h" +#include "common.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace UnitTestsCommon +{ + TEST_CLASS (CommonUtils) + { + std::vector 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 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 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 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); + } + }; +} diff --git a/src/common/UnitTests-CommonLib/UnitTestsVersionHelper.cpp b/src/common/UnitTests-CommonLib/UnitTestsVersionHelper.cpp index cbd8cbd720..5c41af0098 100644 --- a/src/common/UnitTests-CommonLib/UnitTestsVersionHelper.cpp +++ b/src/common/UnitTests-CommonLib/UnitTestsVersionHelper.cpp @@ -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); } diff --git a/src/common/VersionHelper.cpp b/src/common/VersionHelper.cpp index 0682707105..92f014d65c 100644 --- a/src/common/VersionHelper.cpp +++ b/src/common/VersionHelper.cpp @@ -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; - } - } - } -} diff --git a/src/common/VersionHelper.h b/src/common/VersionHelper.h index 19a08b545b..1f575dd507 100644 --- a/src/common/VersionHelper.h +++ b/src/common/VersionHelper.h @@ -1,13 +1,14 @@ #pragma once #include +#include 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; diff --git a/src/common/common.cpp b/src/common/common.cpp index a30fd90e16..7e379b2976 100644 --- a/src/common/common.cpp +++ b/src/common/common.cpp @@ -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& 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; +} diff --git a/src/common/common.h b/src/common/common.h index ac5d65df87..e99327a021 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -4,6 +4,7 @@ #include #include #include +#include // 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& 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 diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 206522946b..eecc209872 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -8,12 +8,7 @@ - + @@ -34,6 +29,9 @@ common + + + StaticLibrary true @@ -61,9 +59,11 @@ true + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ false + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ @@ -167,7 +167,16 @@ + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/src/common/json.cpp b/src/common/json.cpp index b8c06d73aa..0c8533a46e 100644 --- a/src/common/json.cpp +++ b/src/common/json.cpp @@ -9,9 +9,14 @@ namespace json { try { - std::wifstream file(file_name.data(), std::ios::binary); - using isbi = std::istreambuf_iterator; - 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; + 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); } } diff --git a/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.cpp b/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.cpp index f7bc51f723..a333db4fd4 100644 --- a/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.cpp +++ b/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.cpp @@ -14,6 +14,7 @@ #include #include +#include #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> 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; } -} \ No newline at end of file +} + +std::future 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; +} diff --git a/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.h b/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.h index 8c70e53de3..8a54481766 100644 --- a/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.h +++ b/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.h @@ -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 uninstall_previous_msix_version_async(); + struct new_version_download_info { winrt::Windows::Foundation::Uri release_page_uri; diff --git a/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade_lib.vcxproj b/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade_lib.vcxproj index 767a9d12c0..26f9cc00fc 100644 --- a/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade_lib.vcxproj +++ b/src/common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade_lib.vcxproj @@ -73,6 +73,7 @@ false ../../;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ true @@ -80,6 +81,7 @@ true ../../;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ false diff --git a/src/common/msi_to_msix_upgrade_lib/pch.h b/src/common/msi_to_msix_upgrade_lib/pch.h index d3445d47d1..3b153d0513 100644 --- a/src/common/msi_to_msix_upgrade_lib/pch.h +++ b/src/common/msi_to_msix_upgrade_lib/pch.h @@ -3,7 +3,9 @@ #ifndef PCH_H #define PCH_H +#pragma warning (disable: 5205) #include +#pragma warning (default: 5205) #include #include #include diff --git a/src/common/notifications.cpp b/src/common/notifications.cpp index cf500bd7cc..e7051e423c 100644 --- a/src/common/notifications.cpp +++ b/src/common/notifications.cpp @@ -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 buttons) +void notifications::show_toast_with_activations(std::wstring message, std::wstring_view background_handler_id, std::vector 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""; toast_xml += message; toast_xml += L""; + 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(L'0' + i); + if (has_durations) + { + toast_xml += LR"()"; + for (const auto& duration : b.durations) + { + toast_xml += LR"()"; + } + toast_xml += LR"()"; + } + }, + [](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"()"; + toast_xml += LR"(" />)"; }, [&](const background_activated_button& b) { - 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(L'0' + i); + toast_xml += LR"()"; + } }, + actions[i]); } toast_xml += L""; @@ -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); } diff --git a/src/common/notifications.h b/src/common/notifications.h index c4c8e9ebce..65be031ed3 100644 --- a/src/common/notifications.h +++ b/src/common/notifications.h @@ -1,8 +1,10 @@ #pragma once +#include #include #include #include +#include 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 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; + struct toast_params + { + std::optional 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 buttons); + using action_t = std::variant; + + 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 actions, toast_params params = {}); } diff --git a/src/common/notifications/fancyzones_notifications.h b/src/common/notifications/fancyzones_notifications.h new file mode 100644 index 0000000000..0e79e15427 --- /dev/null +++ b/src/common/notifications/fancyzones_notifications.h @@ -0,0 +1,71 @@ +#pragma once + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include + +#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(&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::digits10 + 2, L'\0'); + time_t last_disabled_time{}; + DWORD time_size = static_cast(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; +} \ No newline at end of file diff --git a/src/common/notifications/notifications_dll.vcxproj b/src/common/notifications/notifications_dll.vcxproj index e2c31f1883..dc7b54cc16 100644 --- a/src/common/notifications/notifications_dll.vcxproj +++ b/src/common/notifications/notifications_dll.vcxproj @@ -73,6 +73,7 @@ Notifications false + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ NOTIFICATIONSDLL @@ -81,6 +82,7 @@ Notifications true + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ NOTIFICATIONSDLL diff --git a/src/common/notifications_winrt/BackgroundHandler.idl b/src/common/notifications_winrt/BackgroundHandler.idl index 9a85cb8c0d..528ff5c57f 100644 --- a/src/common/notifications_winrt/BackgroundHandler.idl +++ b/src/common/notifications_winrt/BackgroundHandler.idl @@ -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); } } diff --git a/src/common/notifications_winrt/notifications.vcxproj b/src/common/notifications_winrt/notifications.vcxproj index 627e79f833..0599997ee5 100644 --- a/src/common/notifications_winrt/notifications.vcxproj +++ b/src/common/notifications_winrt/notifications.vcxproj @@ -75,9 +75,11 @@ notifications + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ notifications + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ diff --git a/src/common/packages.config b/src/common/packages.config new file mode 100644 index 0000000000..09cb116327 --- /dev/null +++ b/src/common/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/common/settings_objects.cpp b/src/common/settings_objects.cpp index 450532116b..3bb95c1fcf 100644 --- a/src/common/settings_objects.cpp +++ b/src/common/settings_objects.cpp @@ -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) { diff --git a/src/common/settings_objects.h b/src/common/settings_objects.h index 5001daf928..5bcd26f672 100644 --- a/src/common/settings_objects.h +++ b/src/common/settings_objects.h @@ -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. diff --git a/src/common/window_helpers.cpp b/src/common/window_helpers.cpp index 35b935072d..925e6f48f7 100644 --- a/src/common/window_helpers.cpp +++ b/src/common/window_helpers.cpp @@ -1,5 +1,7 @@ #include "window_helpers.h" #include "pch.h" +#include + 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; -} \ No newline at end of file +} + +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; +} diff --git a/src/common/window_helpers.h b/src/common/window_helpers.h index 67167f116c..2494b8125c 100644 --- a/src/common/window_helpers.h +++ b/src/common/window_helpers.h @@ -1,4 +1,7 @@ #pragma once #include "common.h" -HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p); \ No newline at end of file +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); \ No newline at end of file diff --git a/src/modules/example_powertoy/example_powertoy.vcxproj b/src/modules/example_powertoy/example_powertoy.vcxproj index 14bd686b53..786347819a 100644 --- a/src/modules/example_powertoy/example_powertoy.vcxproj +++ b/src/modules/example_powertoy/example_powertoy.vcxproj @@ -46,10 +46,12 @@ true $(SolutionDir)$(Platform)\$(Configuration)\modules\ + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\modules\ + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ diff --git a/src/modules/fancyzones/dll/FancyZonesModule.vcxproj b/src/modules/fancyzones/dll/FancyZonesModule.vcxproj index ad6aefab2a..0f8bf3e3ac 100644 --- a/src/modules/fancyzones/dll/FancyZonesModule.vcxproj +++ b/src/modules/fancyzones/dll/FancyZonesModule.vcxproj @@ -48,10 +48,12 @@ true $(SolutionDir)$(Platform)\$(Configuration)\modules\ + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ false $(SolutionDir)$(Platform)\$(Configuration)\modules\ + $(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\ diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml index 2d445b51fe..8baea2aed9 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml @@ -12,6 +12,10 @@ + + + + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml.cs index 00ce0a98ca..7ef8fd423d 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditor.xaml.cs @@ -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; } } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs index 26b3f786a8..07c1dfd3de 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs @@ -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; } } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml index fb3fb316cc..ec036867a0 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml @@ -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"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + +