diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index bc5ba04289da..c5a58b670d3b 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -1145,6 +1145,7 @@ OUTOFCONTEXT
Outptr
outputtype
outsettings
+outsourced
OVERLAPPEDWINDOW
Oversampling
OVERWRITEPROMPT
@@ -1280,6 +1281,7 @@ projectname
PROPERTYKEY
Propset
PROPVARIANT
+Prt
PRTL
prvpane
psapi
@@ -1918,6 +1920,7 @@ WNDCLASSW
WNDPROC
wnode
wom
+workerw
WORKSPACESEDITOR
WORKSPACESLAUNCHER
WORKSPACESSNAPSHOTTOOL
diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json
index 6f8f817492cf..74b5cea14565 100644
--- a/.pipelines/ESRPSigning_core.json
+++ b/.pipelines/ESRPSigning_core.json
@@ -216,8 +216,12 @@
"WinUI3Apps\\PowerToys.RegistryPreview.dll",
"WinUI3Apps\\PowerToys.RegistryPreview.exe",
- "PowerToys.ShortcutGuide.exe",
- "PowerToys.ShortcutGuideModuleInterface.dll",
+ "WinUI3Apps\\PowerToys.ShortcutGuide.exe",
+ "WinUI3Apps\\PowerToys.ShortcutGuide.dll",
+ "WinUI3Apps\\PowerToys.ShortcutGuideModuleInterface.dll",
+ "WinUI3Apps\\PowerToys.ShortcutGuide.IndexYmlGenerator.dll",
+ "WinUI3Apps\\PowerToys.ShortcutGuide.IndexYmlGenerator.exe",
+ "WinUI3Apps\\ShortcutGuide.CPPProject.dll",
"PowerToys.ZoomIt.exe",
"PowerToys.ZoomItModuleInterface.dll",
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 71bbda504296..61cb71ec8e3e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -107,6 +107,7 @@
+
diff --git a/NOTICE.md b/NOTICE.md
index d75fe9952271..626db2b11b6e 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -1581,3 +1581,4 @@ SOFTWARE.
- WinUIEx 2.2.0
- WPF-UI 3.0.5
- WyHash 1.0.5
+- YamlDotNet 16.3.0
diff --git a/PowerToys.sln b/PowerToys.sln
index 00986aae2904..507566045697 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -39,14 +39,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fancyzones", "fancyzones",
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FancyZonesLib", "src\modules\fancyzones\FancyZonesLib\FancyZonesLib.vcxproj", "{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FancyZones.UnitTests", "src\modules\fancyzones\FancyZonesTests\UnitTests\UnitTests.vcxproj", "{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "src\modules\fancyzones\FancyZonesTests\UnitTests\UnitTests.vcxproj", "{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}"
ProjectSection(ProjectDependencies) = postProject
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{1AFB6476-670D-4E80-A464-657E01DFF482}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common.Lib.UnitTests", "src\common\UnitTests-CommonLib\UnitTests-CommonLib.vcxproj", "{1A066C63-64B3-45F8-92FE-664E1CCE8077}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests-CommonLib", "src\common\UnitTests-CommonLib\UnitTests-CommonLib.vcxproj", "{1A066C63-64B3-45F8-92FE-664E1CCE8077}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FancyZonesEditor", "src\modules\fancyzones\editor\FancyZonesEditor\FancyZonesEditor.csproj", "{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}"
EndProject
@@ -61,7 +61,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameLib", "src\modul
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameTest", "src\modules\powerrename\testapp\PowerRenameTest.vcxproj", "{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.UnitTests", "src\modules\powerrename\unittests\PowerRenameLibUnitTests.vcxproj", "{2151F984-E006-4A9F-92EF-C6DDE3DC8413}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameLibUnitTests", "src\modules\powerrename\unittests\PowerRenameLibUnitTests.vcxproj", "{2151F984-E006-4A9F-92EF-C6DDE3DC8413}"
ProjectSection(ProjectDependencies) = postProject
{51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2}
{B25AC7A5-FB9F-4789-B392-D5C85E948670} = {B25AC7A5-FB9F-4789-B392-D5C85E948670}
@@ -79,12 +79,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageResizerExt", "src\modu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageResizer.UnitTests", "src\modules\imageresizer\tests\ImageResizer.UnitTests.csproj", "{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToys.ActionRunner", "src\ActionRunner\ActionRunner.vcxproj", "{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ActionRunner", "src\ActionRunner\ActionRunner.vcxproj", "{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}"
ProjectSection(ProjectDependencies) = postProject
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
EndProjectSection
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ApplicationUpdate", "src\common\updating\updating.vcxproj", "{17DA04DF-E393-4397-9CF0-84DABE11032E}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "updating", "src\common\updating\updating.vcxproj", "{17DA04DF-E393-4397-9CF0-84DABE11032E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "keyboardmanager", "keyboardmanager", "{38BDB927-829B-4C65-9CD9-93FB05D66D65}"
EndProject
@@ -213,7 +213,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Settings.UI.UnitTests", "sr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest\Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj", "{632BBE62-5421-49EA-835A-7FFA4F499BD6}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spdlog", "src\logging\logging.vcxproj", "{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "logging", "src\logging\logging.vcxproj", "{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.System", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.System\Microsoft.PowerToys.Run.Plugin.System.csproj", "{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B}"
EndProject
@@ -232,7 +232,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common.Interop.UnitTests",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "notifications", "notifications", "{D92131D6-7610-4D60-A7DB-1C169783F83B}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Notifications", "src\common\notifications\notifications.vcxproj", "{1D5BE09D-78C0-4FD7-AF00-AE7C1AF7C525}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notifications", "src\common\notifications\notifications.vcxproj", "{1D5BE09D-78C0-4FD7-AF00-AE7C1AF7C525}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BackgroundActivatorDLL", "src\common\notifications\BackgroundActivatorDLL\BackgroundActivatorDLL.vcxproj", "{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}"
ProjectSection(ProjectDependencies) = postProject
@@ -241,7 +241,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BackgroundActivatorDLL", "s
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BackgroundActivator", "src\common\notifications\BackgroundActivator\BackgroundActivator.vcxproj", "{0B593A6C-4143-4337-860E-DB5710FB87DB}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Version", "src\common\version\version.vcxproj", "{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "version", "src\common\version\version.vcxproj", "{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "interop", "interop", "{5A7818A8-109C-4E1C-850D-1A654E234B0E}"
EndProject
@@ -310,13 +310,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngine", "sr
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngineLibrary", "src\modules\keyboardmanager\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj", "{E496B7FC-1E99-4BAB-849B-0E8367040B02}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManager.Engine.UnitTests", "src\modules\keyboardmanager\KeyboardManagerEngineTest\KeyboardManagerEngineTest.vcxproj", "{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEngineTest", "src\modules\keyboardmanager\KeyboardManagerEngineTest\KeyboardManagerEngineTest.vcxproj", "{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditor", "src\modules\keyboardmanager\KeyboardManagerEditor\KeyboardManagerEditor.vcxproj", "{8DF78B53-200E-451F-9328-01EB907193AE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditorLibrary", "src\modules\keyboardmanager\KeyboardManagerEditorLibrary\KeyboardManagerEditorLibrary.vcxproj", "{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManager.Editor.UnitTests", "src\modules\keyboardmanager\KeyboardManagerEditorTest\KeyboardManagerEditorTest.vcxproj", "{62173D9A-6724-4C00-A1C8-FB646480A9EC}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyboardManagerEditorTest", "src\modules\keyboardmanager\KeyboardManagerEditorTest\KeyboardManagerEditorTest.vcxproj", "{62173D9A-6724-4C00-A1C8-FB646480A9EC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "awake", "awake", "{127F38E0-40AA-4594-B955-5616BF206882}"
EndProject
@@ -328,12 +328,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.UnitConverter.UnitTest", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.UnitConverter.UnitTest\Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj", "{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shortcutguide", "shortcutguide", "{106CBECA-0701-4FC3-838C-9DF816A19AE2}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ShortcutGuide", "ShortcutGuide", "{106CBECA-0701-4FC3-838C-9DF816A19AE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuideModuleInterface", "src\modules\ShortcutGuide\ShortcutGuideModuleInterface\ShortcutGuideModuleInterface.vcxproj", "{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuide", "src\modules\ShortcutGuide\ShortcutGuide\ShortcutGuide.vcxproj", "{2EDB3EB4-FA92-4BFF-B2D8-566584837231}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FancyZonesModuleInterface", "src\modules\fancyzones\FancyZonesModuleInterface\FancyZonesModuleInterface.vcxproj", "{48804216-2A0E-4168-A6D8-9CD068D14227}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FancyZones", "src\modules\fancyzones\FancyZones\FancyZones.vcxproj", "{FF1D7936-842A-4BBB-8BEA-E9FE796DE700}"
@@ -445,7 +443,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithExt", "src\mod
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileLocksmithUI", "src\modules\FileLocksmith\FileLocksmithUI\FileLocksmithUI.csproj", "{E69B044A-2F8A-45AA-AD0B-256C59421807}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToys.FileLocksmithLib.Interop", "src\modules\FileLocksmith\FileLocksmithLibInterop\FileLocksmithLibInterop.vcxproj", "{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithLibInterop", "src\modules\FileLocksmith\FileLocksmithLibInterop\FileLocksmithLibInterop.vcxproj", "{C604B37E-9D0E-4484-8778-E8B31B0E1B3A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPOWrapper", "src\common\GPOWrapper\GPOWrapper.vcxproj", "{E599C30B-9DC8-4E5A-BF27-93D4CCEDE788}"
EndProject
@@ -453,7 +451,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GPOWrapperProjection", "src
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Peek", "Peek", "{17B4FA70-001E-4D33-BBBB-0D142DBC2E20}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Peek", "src\modules\peek\peek\peek.vcxproj", "{A1425B53-3D61-4679-8623-E64A0D3D0A48}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "peek", "src\modules\peek\peek\peek.vcxproj", "{A1425B53-3D61-4679-8623-E64A0D3D0A48}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peek.UI", "src\modules\peek\Peek.UI\Peek.UI.csproj", "{9D7A6DE0-7D27-424D-ABAE-41B2161F9A03}"
EndProject
@@ -568,7 +566,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings.DSC.Sche
{020A7474-3601-4160-A159-D7B70B77B15F} = {020A7474-3601-4160-A159-D7B70B77B15F}
EndProjectSection
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension", "src\modules\NewPlus\NewShellExtensionContextMenu\NewShellExtensionContextMenu.vcxproj", "{8ACB33D9-C95B-47D4-8363-9731EE0930A0}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewShellExtensionContextMenu", "src\modules\NewPlus\NewShellExtensionContextMenu\NewShellExtensionContextMenu.vcxproj", "{8ACB33D9-C95B-47D4-8363-9731EE0930A0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "New+", "New+", "{CA716AE6-FE5C-40AC-BB8F-2C87912687AC}"
EndProject
@@ -595,7 +593,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WindowProperties", "WindowP
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLib", "src\modules\Workspaces\WorkspacesLib\WorkspacesLib.vcxproj", "{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Workspaces.Lib.UnitTests", "src\modules\Workspaces\WorkspacesLib.UnitTests\WorkspacesLibUnitTests.vcxproj", "{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLibUnitTests", "src\modules\Workspaces\WorkspacesLib.UnitTests\WorkspacesLibUnitTests.vcxproj", "{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkspacesLauncherUI", "src\modules\Workspaces\WorkspacesLauncherUI\WorkspacesLauncherUI.csproj", "{9C53CC25-0623-4569-95BC-B05410675EE3}"
EndProject
@@ -697,7 +695,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "src\modules\cmdpal\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj", "{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzTests", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BgcodePreviewHandler", "src\modules\previewpane\BgcodePreviewHandler\BgcodePreviewHandler.csproj", "{9E0CBC06-F29A-4810-B93C-97D53863B95E}"
EndProject
@@ -786,8 +784,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.TimeDa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WindowWalker.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.WindowWalker.UnitTests\Microsoft.CmdPal.Ext.WindowWalker.UnitTests.csproj", "{E816D7B0-4688-4ECB-97CC-3D8E798F3829}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuide.CPPProject", "src\modules\ShortcutGuide\ShortcutGuide.CPPProject\ShortcutGuide.CPPProject.vcxproj", "{C992FD2C-83B8-4941-9FC1-09730068D8EC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShortcutGuide.IndexYmlGenerator", "src\modules\ShortcutGuide\ShortcutGuide.IndexYmlGenerator\ShortcutGuide.IndexYmlGenerator.csproj", "{30F57201-9B54-5253-8033-8A28ECD3F1CE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShortcutGuide.Ui", "src\modules\ShortcutGuide\ShortcutGuide.Ui\ShortcutGuide.Ui.csproj", "{D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.UnitTestBase", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.UnitTestsBase\Microsoft.CmdPal.Ext.UnitTestBase.csproj", "{00D8659C-2068-40B6-8B86-759CD6284BBB}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuide.CPPProject", "src\modules\ShortcutGuide\ShortcutGuide.CPPProject\ShortcutGuide.CPPProject.vcxproj", "{C992FD2C-83B8-4941-9FC1-09730068D8EC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShortcutGuide.IndexYmlGenerator", "src\modules\ShortcutGuide\ShortcutGuide.IndexYmlGenerator\ShortcutGuide.IndexYmlGenerator.csproj", "{30F57201-9B54-5253-8033-8A28ECD3F1CE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShortcutGuide.Ui", "src\modules\ShortcutGuide\ShortcutGuide.Ui\ShortcutGuide.Ui.csproj", "{D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -1428,14 +1438,6 @@ Global
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Release|ARM64.Build.0 = Release|ARM64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Release|x64.ActiveCfg = Release|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Release|x64.Build.0 = Release|x64
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|ARM64.ActiveCfg = Debug|ARM64
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|ARM64.Build.0 = Debug|ARM64
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|x64.ActiveCfg = Debug|x64
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|x64.Build.0 = Debug|x64
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|ARM64.ActiveCfg = Release|ARM64
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|ARM64.Build.0 = Release|ARM64
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|x64.ActiveCfg = Release|x64
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|x64.Build.0 = Release|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|ARM64.ActiveCfg = Debug|ARM64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|ARM64.Build.0 = Debug|ARM64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x64.ActiveCfg = Debug|x64
@@ -2842,6 +2844,30 @@ Global
{E816D7B0-4688-4ECB-97CC-3D8E798F3829}.Release|ARM64.Build.0 = Release|ARM64
{E816D7B0-4688-4ECB-97CC-3D8E798F3829}.Release|x64.ActiveCfg = Release|x64
{E816D7B0-4688-4ECB-97CC-3D8E798F3829}.Release|x64.Build.0 = Release|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|ARM64.Build.0 = Debug|ARM64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|x64.ActiveCfg = Debug|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|x64.Build.0 = Debug|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|ARM64.Build.0 = Release|ARM64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|x64.ActiveCfg = Release|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|x64.Build.0 = Release|x64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Debug|ARM64.Build.0 = Debug|ARM64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Debug|x64.ActiveCfg = Debug|x64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Debug|x64.Build.0 = Debug|x64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Release|ARM64.ActiveCfg = Release|ARM64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Release|ARM64.Build.0 = Release|ARM64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Release|x64.ActiveCfg = Release|x64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Release|x64.Build.0 = Release|x64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Debug|ARM64.Build.0 = Debug|ARM64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Debug|x64.ActiveCfg = Debug|x64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Debug|x64.Build.0 = Debug|x64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Release|ARM64.ActiveCfg = Release|ARM64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Release|ARM64.Build.0 = Release|ARM64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Release|x64.ActiveCfg = Release|x64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Release|x64.Build.0 = Release|x64
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Debug|ARM64.ActiveCfg = Debug|ARM64
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Debug|ARM64.Build.0 = Debug|ARM64
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Debug|x64.ActiveCfg = Debug|x64
@@ -2850,6 +2876,30 @@ Global
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|ARM64.Build.0 = Release|ARM64
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|x64.ActiveCfg = Release|x64
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|x64.Build.0 = Release|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|ARM64.Build.0 = Debug|ARM64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|x64.ActiveCfg = Debug|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|x64.Build.0 = Debug|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|ARM64.Build.0 = Release|ARM64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|x64.ActiveCfg = Release|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|x64.Build.0 = Release|x64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Debug|ARM64.Build.0 = Debug|ARM64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Debug|x64.ActiveCfg = Debug|x64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Debug|x64.Build.0 = Debug|x64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Release|ARM64.ActiveCfg = Release|ARM64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Release|ARM64.Build.0 = Release|ARM64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Release|x64.ActiveCfg = Release|x64
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE}.Release|x64.Build.0 = Release|x64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Debug|ARM64.Build.0 = Debug|ARM64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Debug|x64.ActiveCfg = Debug|x64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Debug|x64.Build.0 = Debug|x64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Release|ARM64.ActiveCfg = Release|ARM64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Release|ARM64.Build.0 = Release|ARM64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Release|x64.ActiveCfg = Release|x64
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2947,7 +2997,6 @@ Global
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1} = {B9617A31-0F0A-4397-851D-BF2FBEE32D7F}
{106CBECA-0701-4FC3-838C-9DF816A19AE2} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
- {2EDB3EB4-FA92-4BFF-B2D8-566584837231} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
{48804216-2A0E-4168-A6D8-9CD068D14227} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{5043CECE-E6A7-4867-9CBE-02D27D83747A} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
@@ -3160,9 +3209,15 @@ Global
{E816D7AE-4688-4ECB-97CC-3D8E798F3827} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7AF-4688-4ECB-97CC-3D8E798F3828} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7B0-4688-4ECB-97CC-3D8E798F3829} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
{00D8659C-2068-40B6-8B86-759CD6284BBB} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
+ {30F57201-9B54-5253-8033-8A28ECD3F1CE} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
+ {D5BD72DD-B461-FDD4-FD7D-AF0B620AE75D} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
EndGlobalSection
-EndGlobal
+EndGlobal
\ No newline at end of file
diff --git a/deps/cziplib b/deps/cziplib
index 81314fff0a88..7a5741426136 160000
--- a/deps/cziplib
+++ b/deps/cziplib
@@ -1 +1 @@
-Subproject commit 81314fff0a882b72a9ad321e7a3311660125b56e
+Subproject commit 7a57414261361ca991ff8053881343eb6bb6f205
diff --git a/doc/devdocs/images/shortcutguide/diagram.png b/doc/devdocs/images/shortcutguide/diagram.png
deleted file mode 100644
index 12d7256828a8..000000000000
Binary files a/doc/devdocs/images/shortcutguide/diagram.png and /dev/null differ
diff --git a/doc/devdocs/modules/shortcut_guide.md b/doc/devdocs/modules/shortcut_guide.md
index c8cefbc3f3a6..64bc483b0998 100644
--- a/doc/devdocs/modules/shortcut_guide.md
+++ b/doc/devdocs/modules/shortcut_guide.md
@@ -9,12 +9,14 @@
[Pull Requests](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+is%3Aopen+label%3A%22Product-Shortcut+Guide%22+)
## Overview
-Shortcut Guide is a PowerToy that displays an overlay of available keyboard shortcuts when the Windows key is pressed and held. It provides a visual reference for Windows key combinations, helping users discover and utilize built-in Windows shortcuts.
+Shortcut Guide is a PowerToy that displays an overlay of available keyboard shortcuts when a user-set keyboard shortcut is pressed. It helps users discover and remember keyboard shortcuts for Windows and apps.
+
+> [!NOTE]
+> The spec for the manifest files is in development and will be linked here once available.
## Usage
-- Press and hold the Windows key to display the overlay of available shortcuts
-- Press the hotkey again to dismiss the overlay
-- The overlay displays Windows shortcuts with their corresponding actions
+- Press the user-defined hotkey to display the overlay
+- Press the hotkey again or press ESC to dismiss the overlay
## Build and Debug Instructions
@@ -25,67 +27,83 @@ Shortcut Guide is a PowerToy that displays an overlay of available keyboard shor
4. The executable is named PowerToys.ShortcutGuide.exe
### Debug
-1. Right-click the ShortcutGuide project and select 'Set as Startup Project'
+1. Right-click the ShortcutGuide.Ui project and select 'Set as Startup Project'
2. Right-click the project again and select 'Debug'
-## Code Structure
+> [!NOTE]
+> When run in debug mode, the window behaves differently than in release mode. It will not automatically close when loosing focus, it will be displayed on top of all other windows, and it is not hidden from the taskbar.
+
+## Project Structure
+
+The Shortcut Guide module consists of the following 4 projects:
+
+### [`ShortcutGuide.Ui`](/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
+
+This is the main UI project for the Shortcut Guide module. Upon startup it does the following tasks:
+
+1. Copies the built-in manifest files to the users manifest directory (overwriting existing files).
+2. Generate the `index.yml` manifest file.
+3. Populate the PowerToys shortcut manifest with the user-defined shortcuts.
+4. Starts the UI.
+
+### [`ShortcutGuide.CPPProject`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj)
+
+This project exports certain functions to be used by the Shortcut Guide module, that were not able to be implemented in C#.
+
+#### [`excluded_app.cpp`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.cpp)
+
+This file contains one function with the following signature:
-
+```cpp
+__declspec(dllexport) bool IsCurrentWindowExcludedFromShortcutGuide()
+```
-### Core Files
+This function checks if the current window is excluded from the Shortcut Guide overlay. It returns `true` if the current window is excluded otherwise it returns `false`.
-#### [`dllmain.cpp`](/src/modules/shortcut_guide/dllmain.cpp)
-Contains DLL boilerplate code. Implements the PowertoyModuleIface, including enable/disable functionality and GPO policy handling. Captures hotkey events and starts the PowerToys.ShortcutGuide.exe process to display the shortcut guide window.
+#### [`tasklist_positions.cpp`](/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.cpp)
-#### [`shortcut_guide.cpp`](/src/modules/shortcut_guide/shortcut_guide.cpp)
-Contains the module interface code. It initializes the settings values and the keyboard event listener. Defines the OverlayWindow class, which manages the overall logic and event handling for the PowerToys Shortcut Guide.
+This file contains helper functions to retrieve the positions of the taskbar buttons. It exports the following function:
-#### [`overlay_window.cpp`](/src/modules/shortcut_guide/overlay_window.cpp)
-Contains the code for loading the SVGs, creating and rendering of the overlay window. Manages and displays overlay windows with SVG graphics through two main classes:
-- D2DOverlaySVG: Handles loading, resizing, and manipulation of SVG graphics
-- D2DOverlayWindow: Manages the display and behavior of the overlay window
+```cpp
+__declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size)
+```
-#### [`keyboard_state.cpp`](/src/modules/shortcut_guide/keyboard_state.cpp)
-Contains helper methods for checking the current state of the keyboard.
+This function retrieves the positions of the taskbar buttons for a given monitor. It returns an array of `TasklistButton` structures (max 10), which contain the position and size of each button.
-#### [`target_state.cpp`](/src/modules/shortcut_guide/target_state.cpp)
-State machine that handles the keyboard events. It's responsible for deciding when to show the overlay, when to suppress the Start menu (if the overlay is displayed long enough), etc. Handles state transitions and synchronization to ensure the overlay is shown or hidden appropriately based on user interactions.
+`monitor` must be the monitor handle of the monitor containing the taskbar instance of which the buttons should be retrieved.
-#### [`trace.cpp`](/src/modules/shortcut_guide/trace.cpp)
-Contains code for telemetry.
+`size` will contain the resulting array size.
-### Supporting Files
+It determines the positions through Windows `FindWindowEx` function.
+For the primary taskbar it searches for:
+* A window called "Shell_TrayWnd"
+* that contains a window called "ReBarWindow32"
+* that contains a window called "MSTaskSwWClass"
+* that contains a window called "MSTaskListWClass"
-#### [`animation.cpp`](/src/modules/shortcut_guide/animation.cpp)
-Handles the timing and interpolation of animations. Calculates the current value of an animation based on elapsed time and a specified easing function.
+For any secondary taskbar it searches for:
+* A window called "Shell_SecondaryTrayWnd"
+* that contains a window called "WorkerW"
+* that contains a window called "MSTaskListWClass"
-#### [`d2d_svg.cpp`](/src/modules/shortcut_guide/d2d_svg.cpp)
-Provides functionality for loading, resizing, recoloring, rendering, and manipulating SVG images using Direct2D.
+It then enumerates all the button elements inside "MSTaskListWClass" while skipping such with a same name (which implies the user does not use combining taskbar buttons)
-#### [`d2d_text.cpp`](/src/modules/shortcut_guide/d2d_text.cpp)
-Handles creation, resizing, alignment, and rendering of text using Direct2D and DirectWrite.
+### [`ShortcutGuide.IndexYmlGenerator`](/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/)
-#### [`d2d_window.cpp`](/src/modules/shortcut_guide/d2d_window.cpp)
-Manages a window using Direct2D and Direct3D for rendering. Handles window creation, resizing, rendering, and destruction.
+This application generates the `index.yml` manifest file.
-#### [`native_event_waiter.cpp`](/src/modules/shortcut_guide/native_event_waiter.cpp)
-Waits for a named event and executes a specified action when the event is triggered. Uses a separate thread to handle event waiting and action execution.
+It is a separate project so that its code can be easier ported to WinGet in the future.
-#### [`tasklist_positions.cpp`](/src/modules/shortcut_guide/tasklist_positions.cpp)
-Handles retrieving and updating the positions and information of taskbar buttons in Windows.
+### [`ShortcutGuideModuleInterface`](/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj)
-#### [`main.cpp`](/src/modules/shortcut_guide/main.cpp)
-The entry point for the PowerToys Shortcut Guide application. Handles initialization, ensures single instance execution, manages parent process termination, creates and displays the overlay window, and runs the main event loop.
+The module interface that handles opening and closing the user interface.
## Features and Limitations
-- The overlay displays Windows shortcuts (Windows key combinations)
-- The module supports localization, but only for the Windows controls on the left side of the overlay
+- Currently the displayed shortcuts (Except the ones from PowerToys) are not localized.
- It's currently rated as a P3 (lower priority) module
## Future Development
-A community-contributed version 2 is in development that will support:
-- Application-specific shortcuts based on the active application
-- Additional shortcuts beyond Windows key combinations
-- PowerToys shortcuts
+- Implementing with WinGet to get new shortcut manifest files
+- Adding localization support for the built-in manifest files
\ No newline at end of file
diff --git a/installer/PowerToysSetup/ShortcutGuide.wxs b/installer/PowerToysSetup/ShortcutGuide.wxs
index 729a80586101..ed03201f5076 100644
--- a/installer/PowerToysSetup/ShortcutGuide.wxs
+++ b/installer/PowerToysSetup/ShortcutGuide.wxs
@@ -4,26 +4,25 @@
-
-
+
+
-
-
-
+
+
-
+
-
+
-
+
-
+
diff --git a/installer/PowerToysSetup/generateAllFileComponents.ps1 b/installer/PowerToysSetup/generateAllFileComponents.ps1
index a8365d8649e8..207fb8bb38c3 100644
--- a/installer/PowerToysSetup/generateAllFileComponents.ps1
+++ b/installer/PowerToysSetup/generateAllFileComponents.ps1
@@ -305,8 +305,8 @@ Generate-FileComponents -fileListName "ValueGeneratorImagesCmpFiles" -wxsFilePat
## Plugins
#ShortcutGuide
-Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideSvgFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ShortcutGuide\"
-Generate-FileComponents -fileListName "ShortcutGuideSvgFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -regroot $registryroot
+Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideAssetsFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ShortcutGuide\"
+Generate-FileComponents -fileListName "ShortcutGuideAssetsFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -regroot $registryroot
#Settings
Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\"
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj
new file mode 100644
index 000000000000..612b81decb24
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj
@@ -0,0 +1,161 @@
+
+
+
+
+ 15.0
+ Win32Proj
+ {2d604c07-51fc-46bb-9eb7-75aecc7f5e81}
+ ShortcutGuide.CPPProject
+ ShortcutGuide.CPPProject
+ 10.0.26100.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+ Spectre
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+ Spectre
+
+
+
+
+
+
+
+
+
+
+
+ 17.0
+ Win32Proj
+ {c992fd2c-83b8-4941-9fc1-09730068d8ec}
+ ShortcutGuideCPPProject
+ 10.0.22621.0
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\..\..\common\utils;..\..\..;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\..\..\common\utils;..\..\..;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+
+
+
+
+ ..\..\..\common\utils;..\..\..;%(AdditionalIncludeDirectories)
+
+
+
+
+ ..\..\..\common\utils;..\..\..;%(AdditionalIncludeDirectories)
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
+
+
+ {6955446d-23f7-4023-9bb3-8657f904af99}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj.filters b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj.filters
new file mode 100644
index 000000000000..76e726ffc627
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj.filters
@@ -0,0 +1,42 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.cpp b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.cpp
new file mode 100644
index 000000000000..0f7fe4a33675
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.cpp
@@ -0,0 +1,39 @@
+#include "pch.h"
+#include "excluded_app.h"
+#include
+
+extern "C"
+{
+ __declspec(dllexport) bool IsCurrentWindowExcludedFromShortcutGuide()
+ {
+ PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(L"Shortcut Guide");
+ auto settingsObject = settings.get_raw_json();
+ std::wstring apps = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"disabled_apps").GetNamedString(L"value").c_str();
+ auto excludedUppercase = apps;
+ CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length()));
+ std::wstring_view view(excludedUppercase);
+ view = left_trim(trim(view));
+
+ while (!view.empty())
+ {
+ auto pos = (std::min)(view.find_first_of(L"\r\n"), view.length());
+ m_excludedApps.emplace_back(view.substr(0, pos));
+ view.remove_prefix(pos);
+ view = left_trim(trim(view));
+ }
+
+ if (m_excludedApps.empty())
+ {
+ return false;
+ }
+
+ if (HWND foregroundApp{ GetForegroundWindow() })
+ {
+ auto processPath = get_process_path(foregroundApp);
+ CharUpperBuffW(processPath.data(), static_cast(processPath.length()));
+
+ return check_excluded_app(foregroundApp, processPath, m_excludedApps);
+ }
+ return false;
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.h b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.h
new file mode 100644
index 000000000000..652449a138a1
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/excluded_app.h
@@ -0,0 +1,7 @@
+#pragma once
+
+extern "C"
+{
+ std::vector m_excludedApps;
+ __declspec(dllexport) bool IsCurrentWindowExcludedFromShortcutGuide();
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/pch.cpp b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/pch.cpp
similarity index 100%
rename from src/modules/ShortcutGuide/ShortcutGuide/pch.cpp
rename to src/modules/ShortcutGuide/ShortcutGuide.CPPProject/pch.cpp
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/pch.h b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/pch.h
similarity index 73%
rename from src/modules/ShortcutGuide/ShortcutGuide/pch.h
rename to src/modules/ShortcutGuide/ShortcutGuide.CPPProject/pch.h
index 9ec2bbcb4184..9e16b802560f 100644
--- a/src/modules/ShortcutGuide/ShortcutGuide/pch.h
+++ b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/pch.h
@@ -1,9 +1,11 @@
-#pragma once
+#pragma once
#define NOMINMAX
+#include
#include
+#include
+#include
#include
#include
-#include
#include
#include
#include
@@ -24,6 +26,7 @@
#include
#include
#include
-#include
#include
-#include
\ No newline at end of file
+#include
+#include
+#include <../SettingsAPI/settings_objects.h>
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.cpp b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.cpp
new file mode 100644
index 000000000000..23cfd37e3b0c
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.cpp
@@ -0,0 +1,185 @@
+#include "pch.h"
+#include "tasklist_positions.h"
+
+// Tried my hardest adapting this to C#, but FindWindowW didn't work properly in C#. ~Noraa Junker
+
+extern "C"
+{
+ HWND GetTaskbarHwndForCursorMonitor(HMONITOR monitor)
+ {
+ POINT pt;
+ if (!GetCursorPos(&pt))
+ return nullptr;
+
+ // Find the primary taskbar
+ HWND primaryTaskbar = FindWindowW(L"Shell_TrayWnd", nullptr);
+ if (primaryTaskbar)
+ {
+ MONITORINFO mi = { sizeof(mi) };
+ if (GetWindowRect(primaryTaskbar, &mi.rcMonitor))
+ {
+ HMONITOR primaryMonitor = MonitorFromRect(&mi.rcMonitor, MONITOR_DEFAULTTONEAREST);
+ if (primaryMonitor == monitor)
+ return primaryTaskbar;
+ }
+ }
+
+ // Find the secondary taskbar(s)
+ HWND secondaryTaskbar = nullptr;
+ while ((secondaryTaskbar = FindWindowExW(nullptr, secondaryTaskbar, L"Shell_SecondaryTrayWnd", nullptr)) != nullptr)
+ {
+ MONITORINFO mi = { sizeof(mi) };
+ RECT rc;
+ if (GetWindowRect(secondaryTaskbar, &rc))
+ {
+ HMONITOR taskbarMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);
+ if (monitor == taskbarMonitor)
+ return secondaryTaskbar;
+ }
+ }
+
+ return nullptr;
+ }
+
+ void update(HMONITOR monitor)
+ {
+ // Get HWND of the tasklist for the monitor under the cursor
+ auto taskbar_hwnd = GetTaskbarHwndForCursorMonitor(monitor);
+ if (!taskbar_hwnd)
+ return;
+
+ wchar_t class_name[64] = {};
+ GetClassNameW(taskbar_hwnd, class_name, 64);
+
+ HWND tasklist_hwnd = nullptr;
+
+ if (wcscmp(class_name, L"Shell_TrayWnd") == 0)
+ {
+ // Primary taskbar structure
+ tasklist_hwnd = FindWindowExW(taskbar_hwnd, 0, L"ReBarWindow32", nullptr);
+ if (!tasklist_hwnd)
+ return;
+ tasklist_hwnd = FindWindowExW(tasklist_hwnd, 0, L"MSTaskSwWClass", nullptr);
+ if (!tasklist_hwnd)
+ return;
+ tasklist_hwnd = FindWindowExW(tasklist_hwnd, 0, L"MSTaskListWClass", nullptr);
+ if (!tasklist_hwnd)
+ return;
+ }
+ else if (wcscmp(class_name, L"Shell_SecondaryTrayWnd") == 0)
+ {
+ // Secondary taskbar structure
+ HWND workerw = FindWindowExW(taskbar_hwnd, 0, L"WorkerW", nullptr);
+ if (!workerw)
+ return;
+ tasklist_hwnd = FindWindowExW(workerw, 0, L"MSTaskListWClass", nullptr);
+ if (!tasklist_hwnd)
+ return;
+ }
+ else
+ {
+ // Unknown taskbar type
+ return;
+ }
+
+ if (!automation)
+ {
+ winrt::check_hresult(CoCreateInstance(CLSID_CUIAutomation,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IUIAutomation,
+ automation.put_void()));
+ winrt::check_hresult(automation->CreateTrueCondition(true_condition.put()));
+ }
+ element = nullptr;
+ winrt::check_hresult(automation->ElementFromHandle(tasklist_hwnd, element.put()));
+ }
+
+ bool update_buttons(std::vector& buttons)
+ {
+ if (!automation || !element)
+ {
+ return false;
+ }
+ winrt::com_ptr elements;
+ if (element->FindAll(TreeScope_Children, true_condition.get(), elements.put()) < 0)
+ return false;
+ if (!elements)
+ return false;
+ int count;
+ if (elements->get_Length(&count) < 0)
+ return false;
+ winrt::com_ptr child;
+ std::vector found_buttons;
+ found_buttons.reserve(count);
+ for (int i = 0; i < count; ++i)
+ {
+ child = nullptr;
+ if (elements->GetElement(i, child.put()) < 0)
+ return false;
+ TasklistButton button = {};
+ if (VARIANT var_rect; child->GetCurrentPropertyValue(UIA_BoundingRectanglePropertyId, &var_rect) >= 0)
+ {
+ if (var_rect.vt == (VT_R8 | VT_ARRAY))
+ {
+ LONG pos;
+ double value;
+ pos = 0;
+ SafeArrayGetElement(var_rect.parray, &pos, &value);
+ button.x = static_cast(value);
+ pos = 1;
+ SafeArrayGetElement(var_rect.parray, &pos, &value);
+ button.y = static_cast(value);
+ pos = 2;
+ SafeArrayGetElement(var_rect.parray, &pos, &value);
+ button.width = static_cast(value);
+ pos = 3;
+ SafeArrayGetElement(var_rect.parray, &pos, &value);
+ button.height = static_cast(value);
+ }
+ VariantClear(&var_rect);
+ }
+ else
+ {
+ return false;
+ }
+ if (BSTR automation_id; child->get_CurrentAutomationId(&automation_id) >= 0)
+ {
+ wcsncpy_s(button.name, automation_id, _countof(button.name));
+ SysFreeString(automation_id);
+ }
+ found_buttons.push_back(button);
+ }
+ // assign keynums
+ buttons.clear();
+ for (auto& button : found_buttons)
+ {
+ if (buttons.empty())
+ {
+ button.keynum = 1;
+ buttons.push_back(std::move(button));
+ }
+ else
+ {
+ if (button.x < buttons.back().x || button.y < buttons.back().y) // skip 2nd row
+ break;
+ if (wcsncmp(button.name, buttons.back().name, _countof(button.name)) == 0)
+ continue; // skip buttons from the same app
+ button.keynum = buttons.back().keynum + 1;
+ buttons.push_back(std::move(button));
+ if (buttons.back().keynum == 10)
+ break; // no more than 10 buttons
+ }
+ }
+ return true;
+ }
+
+ __declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size)
+ {
+ update(monitor);
+ static std::vector buttons;
+ update_buttons(buttons);
+ *size = static_cast(buttons.size());
+ return buttons.data();
+ }
+}
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.h b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.h
new file mode 100644
index 000000000000..0f8c4ca35aa4
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.CPPProject/tasklist_positions.h
@@ -0,0 +1,23 @@
+#pragma once
+
+struct TasklistButton
+{
+ wchar_t name[256];
+ int x;
+ int y;
+ int width;
+ int height;
+ int keynum;
+};
+
+extern "C"
+{
+ winrt::com_ptr automation;
+ winrt::com_ptr element;
+ winrt::com_ptr true_condition;
+
+ // Helper to get the taskbar HWND for the monitor under the cursor
+ HWND GetTaskbarHwndForCursorMonitor(HMONITOR monitor);
+ bool update_buttons(std::vector& buttons);
+ __declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size);
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/IndexYmlGenerator.cs b/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/IndexYmlGenerator.cs
new file mode 100644
index 000000000000..1997bc102397
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/IndexYmlGenerator.cs
@@ -0,0 +1,76 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.IO;
+using ShortcutGuide.Helpers;
+using ShortcutGuide.Models;
+using YamlDotNet.Serialization;
+
+// This should all be outsourced to WinGet in the future
+namespace ShortcutGuide.IndexYmlGenerator
+{
+ public class IndexYmlGenerator
+ {
+ public static void Main()
+ {
+ CreateIndexYmlFile();
+ }
+
+ // Todo: Exception handling
+ public static void CreateIndexYmlFile()
+ {
+ string path = ManifestInterpreter.PathOfManifestFiles;
+ if (File.Exists(Path.Combine(path, "index.yml")))
+ {
+ File.Delete(Path.Combine(path, "index.yml"));
+ }
+
+ IndexFile indexFile = new() { };
+ Dictionary<(string WindowFilter, bool BackgroundProcess), List> processes = [];
+
+ foreach (string file in Directory.EnumerateFiles(path, "*.yml"))
+ {
+ string content = File.ReadAllText(file);
+ Deserializer deserializer = new();
+ ShortcutFile shortcutFile = deserializer.Deserialize(content);
+ if (processes.TryGetValue((shortcutFile.WindowFilter, shortcutFile.BackgroundProcess), out List? apps))
+ {
+ if (apps.Contains(shortcutFile.PackageName))
+ {
+ continue;
+ }
+
+ apps.Add(shortcutFile.PackageName);
+ continue;
+ }
+
+ processes[(shortcutFile.WindowFilter, shortcutFile.BackgroundProcess)] = [shortcutFile.PackageName];
+ }
+
+ indexFile.Index = [];
+
+ foreach (var item in processes)
+ {
+ indexFile.Index =
+ [
+ .. indexFile.Index,
+ new IndexFile.IndexItem
+ {
+ WindowFilter = item.Key.WindowFilter,
+ BackgroundProcess = item.Key.BackgroundProcess,
+ Apps = [.. item.Value],
+ },
+ ];
+ }
+
+ // Todo: Take the default shell name from the settings or environment variable, default to "+WindowsNT.Shell"
+ indexFile.DefaultShellName = "+WindowsNT.Shell";
+
+ Serializer serializer = new();
+ string yamlContent = serializer.Serialize(indexFile);
+ File.WriteAllText(Path.Combine(path, "index.yml"), yamlContent);
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/ShortcutGuide.IndexYmlGenerator.csproj b/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/ShortcutGuide.IndexYmlGenerator.csproj
new file mode 100644
index 000000000000..571ca9de0363
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/ShortcutGuide.IndexYmlGenerator.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ WinExe
+ ShortcutGuide.IndexYmlGenerator
+ enable
+ false
+ false
+ ..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps
+ PowerToys.ShortcutGuide.IndexYmlGenerator
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Notepad.en-US.yml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Notepad.en-US.yml
new file mode 100644
index 000000000000..d0d607253c36
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Notepad.en-US.yml
@@ -0,0 +1,247 @@
+PackageName: +WindowsNT.Notepad
+Name: Notepad
+WindowFilter: "Notepad.exe"
+BackgroundProcess: false
+Shortcuts:
+ - SectionName: File
+ Properties:
+ - Name: New tab
+ Recommended: true
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - N
+ - Name: New window
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - N
+ - Name: Open
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - O
+ - Name: Save
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - S
+ - Name: Save As
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - S
+ - Name: Save all
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: true
+ Keys:
+ - S
+ - Name: Print
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - P
+ - Name: Close tab
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - W
+ - Name: Close window
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - W
+ - SectionName: Edit
+ Properties:
+ - Name: Undo
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - Z
+ - Name: Redo
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: true
+ Keys:
+ - Z
+ - Name: Cut
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - X
+ - Name: Copy
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - C
+ - Name: Paste
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - V
+ - Name: Search with Bing
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - E
+ - Name: Find
+ Recommended: true
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - F
+ - Name: Find next
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - F3
+ - Name: Find previous
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: true
+ Alt: false
+ Keys:
+ - F3
+ - Name: Replace
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - H
+ - Name: Go to
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - G
+ - Name: Select all
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - A
+ - Name: Time/Date
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - F5
+ - SectionName: View
+ Properties:
+ - Name: Zoom in
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - Plus
+ - Name: Zoom out
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - Minus
+ - Name: Reset zoom
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - 0
+ - SectionName: Formatting
+ Properties:
+ - Name: Bold
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - B
+ - Name: Italic
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - I
+ - Name: Insert link
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - K
+ - Name: Clear formatting
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - Space
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Shell.en-US.yml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Shell.en-US.yml
new file mode 100644
index 000000000000..e845324580da
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Shell.en-US.yml
@@ -0,0 +1,773 @@
+PackageName: +WindowsNT.Shell
+WindowFilter: "*"
+BackgroundProcess: true
+Shortcuts:
+ - SectionName: Desktop Shortcuts
+ Properties:
+ - Name: Close active window
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - F4
+ - Name: Open shutdown box
+ Description: When no windows are open
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - F4
+ - Name: Cycle through open Windows
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - Esc
+ - Name: Reveal typed password
+ Description: On sign-in screen
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - F8
+ - Name: Go back
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ - Name: Go forward
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ - Name: Move up one screen
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ - Name: Move down one screen
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ - Name: Window context menu
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - Space
+ - Name: Switch between open apps
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - Tab
+ Description: While pressing Tab multiple times
+ - Name: Run command
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ Description: for the underlined letter in the app
+ - Name: View open apps
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: true
+ Keys:
+ - Tab
+ - Name: Change start menu size
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Move cursor
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ Description: To the beginning or end of a word
+ - Name: Switch keyboard layout
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - ""
+ - Name: Select block of text
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open Task Manager
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - Esc
+ - Name: Enable/Disable Chinese IME
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - Space
+ - Name: Open context menu
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: true
+ Alt: false
+ Keys:
+ - F10
+ Description: For the selected item
+ - SectionName: Virtual desktop
+ Properties:
+ - Name: Open task view
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - Tab
+ - Name: Add a virtual desktop
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - D
+ - Name: Close current desktop
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - F4
+ - Name: Switch desktop
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ Recommended: true
+ - SectionName: "Windows key"
+ Properties:
+ - Name: Open start menu
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open Action Center
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "A"
+ - Name: Open Date and Time
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - "D"
+ - Name: Focus on the notification area
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "B"
+ - Name: Open narrator
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "Enter"
+ - Name: Open domain search
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "F"
+ - Name: Open Quick Assist
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "Q"
+ - Name: Wake up device
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - "B"
+ Description: When black or a blank screen.
+ - Name: Change input option
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "Space"
+ Description: To next option
+ - Name: Change input option
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "Space"
+ Description: To previous option
+ - Name: Display/Hide desktop
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "D"
+ - Name: Minimize the active window
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "Down"
+ Recommended: true
+ - Name: Open file Explorer
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "E"
+ - Name: Close Magnifier
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "Esc"
+ - Name: Open Feedback Hub
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "F"
+ Recommended: true
+ - Name: Start IME reconversion
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "/"
+ - Name: Open Game Bar
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "G"
+ - Name: Open voice dictation
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "H"
+ - Name: Minimize or restore all other windows
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open Settings
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "I"
+ - Name: Set focus to a Windows tip
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "J"
+ - Name: Open Cast
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "K"
+ - Name: Lock the device
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "L"
+ - Name: Snap the window
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Minimize all windows
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "M"
+ - Name: Zoom out Magnifier
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "-"
+ - Name: Zoom in Magnifier
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "="
+ - Name: Open notification center
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "N"
+ - Name: Lock the device orientation
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "O"
+ - Name: Open project Settings
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "P"
+ - Name: Open Settings about Page
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open the emoji panel
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "."
+ - Name: Open the emoji panel
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ";"
+ - Name: Capture a screenshot
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ Description: Save to the pictures folder
+ - Name: Open search
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "Q"
+ - Name: Open search
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "S"
+ - Name: Open Run dialog
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "R"
+ - Name: Restore window
+ Description: If a window is snapped or maximized
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: true
+ Alt: false
+ Keys:
+ - ""
+ - Name: Make UWP app full screen
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: true
+ Alt: false
+ Keys:
+ - ""
+ - Name: Move window to monitor
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: true
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open Snipping Tool
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: true
+ Alt: false
+ Keys:
+ - "S"
+ - Name: Stretch window
+ Description: To the top and bottom of the screen
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: true
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open task view
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "Tab"
+ - Name: Open Accessibility Settings
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "U"
+ - Name: Maximize the active window
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open the clipboard history
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "V"
+ - Name: Open widgets
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "W"
+ - Name: Open Quick Link menu
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "X"
+ - Name: Open snap layouts
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "Z"
+ - SectionName: Clipboard
+ Properties:
+ - Name: Copy
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "C"
+ - Name: Cut
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "X"
+ - Name: Paste
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "V"
+ - Name: Paste
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: true
+ Alt: false
+ Keys:
+ - ""
+ Description: Paste as plain text
+ - SectionName: Taskbar Shortcuts
+ Properties:
+ - Name: Open app in Taskbar
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open jump list
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ - Name: Switch to last active window
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Open as administrator
+ Shortcut:
+ - Win: true
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - ""
+ - SectionName: Copilot key
+ Properties:
+ - Name: Open Copilot
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ''
+ Description: When copilot is available
+ - Name: Open Windows search
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ''
+ Description: When copilot is not available
+ - SectionName: Office key
+ Properties:
+ - Name: Open Word
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "W"
+ - Name: Open Excel
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "X"
+ - Name: Open PowerPoint
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "P"
+ - Name: Open Outlook
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "O"
+ - Name: Open Microsoft Teams
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "T"
+ - Name: Open OneNote
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "N"
+ - Name: Open OneDrive
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "D"
+ - Name: Open Yammer
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "Y"
+ - Name: Open LinkedIn
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - "L"
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.WindowsExplorer.en-US.yml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.WindowsExplorer.en-US.yml
new file mode 100644
index 000000000000..ca134ea7926d
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.WindowsExplorer.en-US.yml
@@ -0,0 +1,266 @@
+PackageName: +WindowsNT.WindowsExplorer
+WindowFilter: "explorer.exe"
+Name: File Explorer
+Shortcuts:
+ - SectionName: General
+ Properties:
+ - Name: Open File Explorer
+ Shortcut:
+ - Win: true
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "E"
+ Recommended: true
+ - Name: Select the address bar
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - "D"
+ - Name: Select the address bar
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "L"
+ - Name: Select the address bar
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "F4"
+ - Name: Select the search box
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "E"
+ - Name: Select the search box
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "F3"
+ - Name: Select the search box
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "F"
+ - Name: Refresh the window
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "F5"
+ - Name: Cycle through elements in the active window
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "F6"
+ - Name: Maximize or restore the active window
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "F11"
+ - SectionName: Navigation
+ Properties:
+ - Name: Navigate to the previous folder
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ - Name: Navigate to the previous folder
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Navigate to the next folder
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ - Name: Move up a level in the folder path
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - ""
+ - SectionName: "Window management"
+ Properties:
+ - Name: Open a new window
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "N"
+ - Name: Open a new tab
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "T"
+ Recommended: true
+ - Name: Close the current active tab
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "W"
+ - Name: Move to the next tab
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "Tab"
+ - Name: Move to the previous tab
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - "Tab"
+ - Name: Move to that tab number
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "Number (1-9)"
+ - Name: Show/Hide the preview pane
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - "P"
+ - Name: Show/Hide the details pane
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: true
+ Alt: true
+ Keys:
+ - "P"
+ - Name: Resize all columns to fit text
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "+"
+ - Name: Expand all folders
+ Description: In the navigation pane
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - "E"
+ - SectionName: "File management"
+ Properties:
+ - Name: Display properties for the selected item
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: true
+ Keys:
+ - "Enter"
+ - Name: Delete the selected item
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+ - Name: Delete the selected item
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "D"
+ - Name: Delete the selected item permanently
+ Description: "This removes the item without sending it to the Recycle Bin"
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - "D"
+ - Name: Create a new folder
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: true
+ Alt: false
+ Keys:
+ - "N"
+ Recommended: true
+ - Name: Rename the selected item
+ Shortcut:
+ - Win: false
+ Ctrl: false
+ Shift: false
+ Alt: false
+ Keys:
+ - "F2"
+ - Name: Select multiple items
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - ""
+
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/CopilotKey.png b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/CopilotKey.png
new file mode 100644
index 000000000000..ccb59a6075af
Binary files /dev/null and b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/CopilotKey.png differ
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/HeroImage-dark.png b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/HeroImage-dark.png
new file mode 100644
index 000000000000..aa1a055744b5
Binary files /dev/null and b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/HeroImage-dark.png differ
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/HeroImage.png b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/HeroImage.png
new file mode 100644
index 000000000000..54c028ec5467
Binary files /dev/null and b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/HeroImage.png differ
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/Microsoft.PowerToys.en-US.yml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/Microsoft.PowerToys.en-US.yml
new file mode 100644
index 000000000000..c768b4bb5ebe
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/Microsoft.PowerToys.en-US.yml
@@ -0,0 +1,9 @@
+PackageName: Microsoft.PowerToys
+Name: PowerToys
+BackgroundProcess: True
+WindowFilter: "powertoys.exe"
+Shortcuts:
+ - SectionName: General
+ Properties:
+#
+#
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/OfficeKey.png b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/OfficeKey.png
new file mode 100644
index 000000000000..ab524b1793be
Binary files /dev/null and b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/OfficeKey.png differ
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Shortcut-Guide.ico b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/ShortcutGuide.ico
similarity index 100%
rename from src/modules/ShortcutGuide/ShortcutGuide/Shortcut-Guide.ico
rename to src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/ShortcutGuide.ico
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DisplayHelper.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DisplayHelper.cs
new file mode 100644
index 000000000000..e638ff1c7e2d
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DisplayHelper.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Windows.Foundation;
+using WinUIEx;
+
+namespace ShortcutGuide.Helpers
+{
+ public static class DisplayHelper
+ {
+ ///
+ /// Returns the display work area for the monitor that contains the specified window.
+ ///
+ /// The window handle
+ /// A element containing the display area
+ public static Rect GetWorkAreaForDisplayWithWindow(nint hwnd)
+ {
+ _foundMonitorIndex = -1;
+ _monitorIndex = 0;
+ var monitor = NativeMethods.MonitorFromWindow(hwnd, (int)NativeMethods.MonitorFromWindowDwFlags.MONITOR_DEFAULTTONEAREST);
+ NativeMethods.EnumDisplayMonitors(nint.Zero, nint.Zero, MonitorEnumProc, new NativeMethods.LPARAM(monitor));
+ return MonitorInfo.GetDisplayMonitors()[_foundMonitorIndex].RectWork;
+ }
+
+ ///
+ /// The index of the monitor that contains the specified window. -1 indicates that no monitor was found (yet).
+ ///
+ private static int _foundMonitorIndex = -1;
+
+ ///
+ /// The index of the monitor in the enumeration. This is used to find the correct monitor in the list of monitors.
+ ///
+ private static int _monitorIndex;
+
+ private static bool MonitorEnumProc(nint hMonitor, nint hdcMonitor, ref NativeMethods.RECT lprcMonitor, nint dwData)
+ {
+ nint targetMonitor = dwData;
+
+ if (hMonitor == targetMonitor)
+ {
+ _foundMonitorIndex = _monitorIndex;
+ return false;
+ }
+
+ _monitorIndex++;
+ return true;
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DpiHelper.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DpiHelper.cs
new file mode 100644
index 000000000000..78f5fa8b12b7
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/DpiHelper.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace ShortcutGuide.Helpers
+{
+ // This class is rewritten from C++ to C# from the measure tool project
+ internal static class DpiHelper
+ {
+#pragma warning disable SA1310 // Field names should not contain underscore
+ private const int DEFAULT_DPI = 96;
+ private const int MONITOR_DEFAULTTONEAREST = 2;
+ private const int MDT_EFFECTIVE_DPI = 0;
+#pragma warning restore SA1310 // Field names should not contain underscore
+
+ public static float GetDPIScaleForWindow(int hwnd)
+ {
+ int dpi = DEFAULT_DPI;
+ GetScreenDPIForWindow(hwnd, ref dpi);
+ return (float)dpi / DEFAULT_DPI;
+ }
+
+ private static long GetScreenDPIForWindow(int hwnd, ref int dpi)
+ {
+ var targetMonitor = NativeMethods.MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ return GetScreenDPIForMonitor(targetMonitor.ToInt32(), ref dpi);
+ }
+
+ private static long GetScreenDPIForMonitor(int targetMonitor, ref int dpi)
+ {
+ if (targetMonitor != 0)
+ {
+ int dummy = 0;
+ return NativeMethods.GetDpiForMonitor(targetMonitor, MDT_EFFECTIVE_DPI, ref dpi, ref dummy);
+ }
+ else
+ {
+ dpi = DEFAULT_DPI;
+ return 0x80004005L;
+ }
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ManifestInterpreter.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ManifestInterpreter.cs
new file mode 100644
index 000000000000..e45ccb58d1be
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ManifestInterpreter.cs
@@ -0,0 +1,150 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using ShortcutGuide.Models;
+using YamlDotNet.Serialization;
+
+namespace ShortcutGuide.Helpers
+{
+ ///
+ /// Helps to interpret the manifest files for the Shortcut Guide.
+ ///
+ public class ManifestInterpreter
+ {
+ // Todo: Get language from settings or environment variable, default to "en-US"
+
+ ///
+ /// Gets the language used for the manifest files.
+ ///
+ public static string Language => "en-US";
+
+ ///
+ /// Returns the shortcuts for a specific application.
+ ///
+ ///
+ /// The method should only be called if the application is known to have a shortcuts file.
+ ///
+ /// The manifest id.
+ /// The deserialized shortcuts file.
+ /// The requested file was not found.
+ public static ShortcutFile GetShortcutsOfApplication(string applicationName)
+ {
+ string path = PathOfManifestFiles;
+ IEnumerable files = Directory.EnumerateFiles(path, applicationName + ".*.yml") ??
+ throw new FileNotFoundException($"The file for the application '{applicationName}' was not found in '{path}'.");
+
+ IEnumerable filesEnumerable = files as string[] ?? [.. files];
+ return filesEnumerable.Any(f => f.EndsWith($".{Language}.yml", StringComparison.InvariantCulture))
+ ? YamlToShortcutList(File.ReadAllText(Path.Combine(path, applicationName + $".{Language}.yml")))
+ : filesEnumerable.Any(f => f.EndsWith(".en-US.yml", StringComparison.InvariantCulture))
+ ? YamlToShortcutList(File.ReadAllText(filesEnumerable.First(f => f.EndsWith(".en-US.yml", StringComparison.InvariantCulture))))
+ : throw new FileNotFoundException($"The file for the application '{applicationName}' was not found in '{path}' with the language '{Language}' or 'en-US'.");
+ }
+
+ ///
+ /// Deserializes the content of a YAML file to a .
+ ///
+ /// The content of the YAML file.
+ /// A deserialized object.
+ private static ShortcutFile YamlToShortcutList(string content)
+ {
+ Deserializer deserializer = new();
+ return deserializer.Deserialize(content);
+ }
+
+ ///
+ /// Gets the path to the directory where the manifest files are stored.
+ ///
+ public static string PathOfManifestFiles => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "WinGet", "KeyboardShortcuts");
+
+ ///
+ /// Retrieves the index YAML file that contains the list of all applications and their shortcuts.
+ ///
+ /// A deserialized object.
+ public static IndexFile GetIndexYamlFile()
+ {
+ string path = PathOfManifestFiles;
+ string content = File.ReadAllText(Path.Combine(path, "index.yml"));
+ Deserializer deserializer = new();
+ return deserializer.Deserialize(content);
+ }
+
+ ///
+ /// Retrieves all application IDs that should be displayed, based on the foreground window and background processes.
+ ///
+ /// An array of all application IDs.
+ public static string[] GetAllCurrentApplicationIds()
+ {
+ nint handle = NativeMethods.GetForegroundWindow();
+
+ List applicationIds = [];
+
+ Process[] processes = Process.GetProcesses();
+
+ if (NativeMethods.GetWindowThreadProcessId(handle, out uint processId) > 0)
+ {
+ string? name = Process.GetProcessById((int)processId).MainModule?.ModuleName;
+
+ if (name is not null)
+ {
+ try
+ {
+ foreach (var item in GetIndexYamlFile().Index.First((s) => !s.BackgroundProcess && IsMatch(name, s.WindowFilter)).Apps)
+ {
+ applicationIds.Add(item);
+ }
+ }
+ catch (InvalidOperationException)
+ {
+ }
+ }
+ }
+
+ foreach (var item in GetIndexYamlFile().Index.Where((s) => s.BackgroundProcess))
+ {
+ try
+ {
+ if (processes.Any((p) =>
+ {
+ try
+ {
+ return IsMatch(p.MainModule!.ModuleName, item.WindowFilter);
+ }
+ catch (Win32Exception)
+ {
+ return false;
+ }
+ }))
+ {
+ foreach (var app in item.Apps)
+ {
+ applicationIds.Add(app);
+ }
+ }
+ }
+ catch (InvalidOperationException)
+ {
+ }
+ }
+
+ return [.. applicationIds];
+
+ static bool IsMatch(string input, string filter)
+ {
+ input = input.ToLower(CultureInfo.InvariantCulture);
+ filter = filter.ToLower(CultureInfo.InvariantCulture);
+ string regexPattern = "^" + Regex.Escape(filter).Replace("\\*", ".*") + "$";
+ return Regex.IsMatch(input, regexPattern);
+ }
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/PowerToysShortcutsPopulator.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/PowerToysShortcutsPopulator.cs
new file mode 100644
index 000000000000..b96bcfa8b90b
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/PowerToysShortcutsPopulator.cs
@@ -0,0 +1,174 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using Microsoft.PowerToys.Settings.UI.Library;
+using static ShortcutGuide.Helpers.ResourceLoaderInstance;
+
+namespace ShortcutGuide.Helpers
+{
+ ///
+ /// Populates the PowerToys shortcuts in the manifest files.
+ ///
+ internal sealed partial class PowerToysShortcutsPopulator
+ {
+ ///
+ /// Populates the PowerToys shortcuts in the manifest files.
+ ///
+ public static void Populate()
+ {
+ string path = Path.Combine(ManifestInterpreter.PathOfManifestFiles, $"Microsoft.PowerToys.{ManifestInterpreter.Language}.yml");
+
+ StringBuilder content = new(File.ReadAllText(path));
+
+ const string populateStartString = "# ";
+ const string populateEndString = "# ";
+
+ content = new(PopulateRegex().Replace(content.ToString(), populateStartString + Environment.NewLine));
+
+ ISettingsUtils settingsUtils = new SettingsUtils();
+ EnabledModules enabledModules = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Enabled;
+ if (enabledModules.AdvancedPaste)
+ {
+ AdvancedPasteProperties advancedPasteProperties = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties;
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdvancedPasteUIShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("AdvancedPasteUI_Shortcut/Header")));
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.PasteAsPlainTextShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsPlainText_Shortcut/Header")));
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.PasteAsMarkdownShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsMarkdown_Shortcut/Header")));
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.PasteAsJsonShortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsJson_Shortcut/Header")));
+ if (advancedPasteProperties.AdditionalActions.ImageToText.IsShown)
+ {
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.ImageToText.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("ImageToText/Header")));
+ }
+
+ if (advancedPasteProperties.AdditionalActions.PasteAsFile.IsShown)
+ {
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsTxtFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsTxtFile/Header")));
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsPngFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsPngFile/Header")));
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.PasteAsFile.PasteAsHtmlFile.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("PasteAsHtmlFile/Header")));
+ }
+
+ if (advancedPasteProperties.AdditionalActions.Transcode.IsShown)
+ {
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.Transcode.TranscodeToMp3.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("TranscodeToMp3/Header")));
+ content.Append(HotkeySettingsToYaml(advancedPasteProperties.AdditionalActions.Transcode.TranscodeToMp4.Shortcut, SettingsResourceLoader.GetString("AdvancedPaste/ModuleTitle"), SettingsResourceLoader.GetString("TranscodeToMp4/Header")));
+ }
+ }
+
+ if (enabledModules.AlwaysOnTop)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("AlwaysOnTop/ModuleTitle"), SettingsResourceLoader.GetString("AlwaysOnTop_ShortDescription")));
+ }
+
+ if (enabledModules.ColorPicker)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("ColorPicker/ModuleTitle"), SettingsResourceLoader.GetString("ColorPicker_ShortDescription")));
+ }
+
+ if (enabledModules.CmdPal)
+ {
+ content.Append(HotkeySettingsToYaml(new CmdPalProperties().Hotkey, SettingsResourceLoader.GetString("CmdPal/ModuleTitle")));
+ }
+
+ if (enabledModules.CropAndLock)
+ {
+ CropAndLockProperties cropAndLockProperties = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties;
+ content.Append(HotkeySettingsToYaml(cropAndLockProperties.ThumbnailHotkey, SettingsResourceLoader.GetString("CropAndLock/ModuleTitle"), SettingsResourceLoader.GetString("CropAndLock_Thumbnail")));
+ content.Append(HotkeySettingsToYaml(cropAndLockProperties.ReparentHotkey, SettingsResourceLoader.GetString("CropAndLock/ModuleTitle"), SettingsResourceLoader.GetString("CropAndLock_Reparent")));
+ }
+
+ if (enabledModules.FancyZones)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.FancyzonesEditorHotkey, SettingsResourceLoader.GetString("FancyZones/ModuleTitle"), SettingsResourceLoader.GetString("FancyZones_OpenEditor")));
+ }
+
+ if (enabledModules.MouseHighlighter)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseHighlighter/Header"), SettingsResourceLoader.GetString("MouseHighlighter_ShortDescription")));
+ }
+
+ if (enabledModules.MouseJump)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MouseJump/Header"), SettingsResourceLoader.GetString("MouseJump_ShortDescription")));
+ }
+
+ if (enabledModules.MousePointerCrosshairs)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MouseUtils_MousePointerCrosshairs/Header"), SettingsResourceLoader.GetString("MouseCrosshairs_ShortDescription")));
+ }
+
+ if (enabledModules.Peek)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("Peek/ModuleTitle")));
+ }
+
+ if (enabledModules.PowerLauncher)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.OpenPowerLauncher, SettingsResourceLoader.GetString("PowerLauncher/ModuleTitle")));
+ }
+
+ if (enabledModules.MeasureTool)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("MeasureTool/ModuleTitle"), SettingsResourceLoader.GetString("ScreenRuler_ShortDescription")));
+ }
+
+ if (enabledModules.ShortcutGuide)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.DefaultOpenShortcutGuide, SettingsResourceLoader.GetString("ShortcutGuide/ModuleTitle"), SettingsResourceLoader.GetString("ShortcutGuide_ShortDescription")));
+ }
+
+ if (enabledModules.PowerOcr)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.ActivationShortcut, SettingsResourceLoader.GetString("TextExtractor/ModuleTitle"), SettingsResourceLoader.GetString("PowerOcr_ShortDescription")));
+ }
+
+ if (enabledModules.Workspaces)
+ {
+ content.Append(HotkeySettingsToYaml(SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Properties.Hotkey, SettingsResourceLoader.GetString("Workspaces/ModuleTitle"), SettingsResourceLoader.GetString("Workspaces_ShortDescription")));
+ }
+
+ content.Append(populateEndString);
+
+ File.WriteAllText(path, content.ToString());
+ }
+
+ ///
+ /// Converts the hotkey settings to a YAML format string for the manifest file.
+ ///
+ /// Object containing a hotkey from the settings.
+ /// The name of the PowerToys module.
+ /// Description of the action.
+ /// Yaml code for the manifest file.
+ private static string HotkeySettingsToYaml(HotkeySettings hotkeySettings, string moduleName, string? description = null)
+ {
+ string content = string.Empty;
+ content += " - Name: " + moduleName + Environment.NewLine;
+ content += " Shortcut: " + Environment.NewLine;
+ content += " - Win: " + hotkeySettings.Win.ToString() + Environment.NewLine;
+ content += " Ctrl: " + hotkeySettings.Ctrl.ToString() + Environment.NewLine;
+ content += " Alt: " + hotkeySettings.Alt.ToString() + Environment.NewLine;
+ content += " Shift: " + hotkeySettings.Shift.ToString() + Environment.NewLine;
+ content += " Keys:" + Environment.NewLine;
+ content += " - " + hotkeySettings.Code.ToString(CultureInfo.InvariantCulture) + Environment.NewLine;
+ if (description != null)
+ {
+ content += " Description: " + description + Environment.NewLine;
+ }
+
+ return content;
+ }
+
+ ///
+ private static string HotkeySettingsToYaml(KeyboardKeysProperty hotkeySettings, string moduleName, string? description = null)
+ {
+ return HotkeySettingsToYaml(hotkeySettings.Value, moduleName, description);
+ }
+
+ [GeneratedRegex(@"# [\s\S\n\r]*# ")]
+ private static partial Regex PopulateRegex();
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ResourceLoaderInstance.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ResourceLoaderInstance.cs
new file mode 100644
index 000000000000..f4e18161e9db
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/ResourceLoaderInstance.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Windows.ApplicationModel.Resources;
+
+namespace ShortcutGuide.Helpers
+{
+ internal static class ResourceLoaderInstance
+ {
+ ///
+ /// Gets the resource loader for the Shortcut Guide module.
+ ///
+ internal static ResourceLoader ResourceLoader { get; private set; }
+
+ ///
+ /// Gets the resource loader for the Settings module.
+ ///
+ internal static ResourceLoader SettingsResourceLoader { get; private set; }
+
+ static ResourceLoaderInstance()
+ {
+ ResourceLoader = new ResourceLoader("PowerToys.ShortcutGuide.pri");
+ SettingsResourceLoader = new ResourceLoader("PowerToys.Settings.pri");
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/TasklistPositions.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/TasklistPositions.cs
new file mode 100644
index 000000000000..efbf4448411f
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Helpers/TasklistPositions.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+using TasklistButton = ShortcutGuide.NativeMethods.TasklistButton;
+
+namespace ShortcutGuide.Helpers
+{
+ ///
+ /// Provides methods to retrieve the positions of taskbar buttons on the current monitor.
+ ///
+ internal static class TasklistPositions
+ {
+ ///
+ /// Retrieves the taskbar buttons for the current monitor.
+ ///
+ /// An array of the taskbar buttons.
+ public static TasklistButton[] GetButtons()
+ {
+ var monitor = NativeMethods.MonitorFromWindow(MainWindow.WindowHwnd, 0);
+ nint ptr = NativeMethods.GetTasklistButtons(monitor, out int size);
+ if (ptr == nint.Zero)
+ {
+ return [];
+ }
+
+ if (size <= 0)
+ {
+ return [];
+ }
+
+ TasklistButton[] buttons = new TasklistButton[size];
+ nint currentPtr = ptr;
+ for (int i = 0; i < size; i++)
+ {
+ buttons[i] = Marshal.PtrToStructure(currentPtr);
+ currentPtr += Marshal.SizeOf();
+ }
+
+ return buttons;
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/IndexFile.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/IndexFile.cs
new file mode 100644
index 000000000000..e8c64d813b82
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/IndexFile.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace ShortcutGuide.Models
+{
+ public struct IndexFile
+ {
+ public struct IndexItem
+ {
+ public string WindowFilter { get; set; }
+
+ public bool BackgroundProcess { get; set; }
+
+ public string[] Apps { get; set; }
+ }
+
+ public string DefaultShellName { get; set; }
+
+ public IndexItem[] Index { get; set; }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutCategory.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutCategory.cs
new file mode 100644
index 000000000000..0dcd3d1323af
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutCategory.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace ShortcutGuide.Models
+{
+ public struct ShortcutCategory
+ {
+ public string SectionName { get; set; }
+
+ public ShortcutEntry[] Properties { get; set; }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutEntry.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutEntry.cs
new file mode 100644
index 000000000000..911bb535b2bd
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutEntry.cs
@@ -0,0 +1,378 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.PowerToys.Settings.UI.Library.Utilities;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Markup;
+using Windows.UI.Text;
+using static ShortcutGuide.Models.ShortcutEntry;
+using Orientation = Microsoft.UI.Xaml.Controls.Orientation;
+
+namespace ShortcutGuide.Models
+{
+ public class ShortcutEntry(string name, string? description, bool recommended, ShortcutDescription[] shortcutDescriptions)
+ {
+ public override bool Equals(object? obj)
+ {
+ return obj is ShortcutEntry other && Name == other.Name &&
+ Description == other.Description &&
+ Shortcut.Length == other.Shortcut.Length &&
+ Shortcut.SequenceEqual(other.Shortcut);
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+
+ public static bool operator ==(ShortcutEntry? left, ShortcutEntry? right)
+ {
+ return (left is null && right is null) || (left is not null && right is not null && left.Equals(right));
+ }
+
+ public static bool operator !=(ShortcutEntry? left, ShortcutEntry? right)
+ {
+ return !(left == right);
+ }
+
+ public ShortcutEntry()
+ : this(string.Empty, string.Empty, false, [])
+ {
+ }
+
+ [JsonPropertyName(nameof(Name))]
+ public string Name { get; set; } = name;
+
+ [JsonPropertyName(nameof(Description))]
+ public string? Description { get; set; } = description;
+
+ [JsonPropertyName(nameof(Recommended))]
+ public bool Recommended { get; set; } = recommended;
+
+ [JsonPropertyName(nameof(Shortcut))]
+ public ShortcutDescription[] Shortcut { get; set; } = shortcutDescriptions;
+
+ public class ShortcutDescription(bool ctrl, bool shift, bool alt, bool win, string[] keys)
+ {
+ public ShortcutDescription()
+ : this(false, false, false, false, [])
+ {
+ }
+
+ [JsonPropertyName(nameof(Ctrl))]
+ public bool Ctrl { get; set; } = ctrl;
+
+ [JsonPropertyName(nameof(Shift))]
+ public bool Shift { get; set; } = shift;
+
+ [JsonPropertyName(nameof(Alt))]
+ public bool Alt { get; set; } = alt;
+
+ [JsonPropertyName(nameof(Win))]
+ public bool Win { get; set; } = win;
+
+ [JsonPropertyName(nameof(Keys))]
+ public string[] Keys { get; set; } = keys;
+
+ public override bool Equals(object? obj)
+ {
+ return obj is ShortcutDescription other && Ctrl == other.Ctrl &&
+ Shift == other.Shift &&
+ Alt == other.Alt &&
+ Win == other.Win &&
+ Keys.SequenceEqual(other.Keys);
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+
+ public static bool operator ==(ShortcutDescription? left, ShortcutDescription? right)
+ {
+ return (left is null && right is null) || (left is not null && right is not null && left.Equals(right));
+ }
+
+ public static bool operator !=(ShortcutDescription? left, ShortcutDescription? right)
+ {
+ return !(left == right);
+ }
+ }
+
+ public static implicit operator ShortcutTemplateDataObject(ShortcutEntry shortcut)
+ {
+ List shortcutStackPanels = [];
+
+ for (int i = 0; i < shortcut.Shortcut.Length; i++)
+ {
+ ShortcutDescription shortcutEntry = shortcut.Shortcut[i];
+ StackPanel shortcutStackPanel = new() { Orientation = Orientation.Horizontal };
+ shortcutStackPanels.Add(shortcutStackPanel);
+
+ // If any entry is blank, we skip the whole shortcut
+ if (shortcutEntry is { Ctrl: false, Alt: false, Shift: false, Win: false, Keys.Length: 0 })
+ {
+ return new ShortcutTemplateDataObject(shortcut.Name, shortcut.Description ?? string.Empty, shortcutStackPanel, shortcut);
+ }
+
+ if (shortcut.Shortcut.Length > 1)
+ {
+ TextBlock shortcutIndexTextBlock = new()
+ {
+ Text = $"{i + 1}.",
+ Margin = new Thickness(3),
+ VerticalAlignment = VerticalAlignment.Center,
+ FontWeight = new FontWeight { Weight = 600 },
+ };
+ shortcutStackPanel.Children.Add(shortcutIndexTextBlock);
+ }
+
+ if (shortcutEntry.Win)
+ {
+ PathIcon winIcon = (XamlReader.Load(@"") as PathIcon)!;
+ Viewbox winIconContainer = new()
+ {
+ Child = winIcon,
+ Height = 20,
+ Width = 20,
+ Margin = new Thickness(3, -1, 3, 2),
+ };
+ shortcutStackPanel.Children.Add(winIconContainer);
+ }
+
+ if (shortcutEntry.Ctrl)
+ {
+ AddNewTextToStackPanel("Ctrl");
+ }
+
+ if (shortcutEntry.Alt)
+ {
+ AddNewTextToStackPanel("Alt");
+ }
+
+ if (shortcutEntry.Shift)
+ {
+ AddNewTextToStackPanel("Shift");
+ }
+
+ foreach (string key in shortcutEntry.Keys)
+ {
+ AddKeyToStackPanel(key, shortcutStackPanel);
+ }
+
+ continue;
+
+ void AddNewTextToStackPanel(string text)
+ {
+ shortcutStackPanel.Children.Add(new TextBlock { Text = text, Margin = new Thickness(3), VerticalAlignment = VerticalAlignment.Center });
+ }
+ }
+
+ StackPanel stackPanelToReturn = shortcutStackPanels[0];
+
+ switch (shortcutStackPanels.Count)
+ {
+ case 0:
+ return new ShortcutTemplateDataObject(shortcut.Name, shortcut.Description ?? string.Empty, new StackPanel(), shortcut);
+ case <= 1:
+ return new ShortcutTemplateDataObject(shortcut.Name, shortcut.Description ?? string.Empty, stackPanelToReturn, shortcut);
+ default:
+ stackPanelToReturn = new StackPanel
+ {
+ Orientation = Orientation.Vertical,
+ };
+
+ foreach (StackPanel panel in shortcutStackPanels)
+ {
+ panel.Visibility = Visibility.Collapsed;
+ stackPanelToReturn.Children.Add(panel);
+ }
+
+ shortcutStackPanels[0].Visibility = Visibility.Visible;
+ for (int i = 1; i < shortcutStackPanels.Count; i++)
+ {
+ shortcutStackPanels[i].Visibility = Visibility.Collapsed;
+ }
+
+ AnimateStackPanels([.. shortcutStackPanels]);
+
+ return new ShortcutTemplateDataObject(shortcut.Name, shortcut.Description ?? string.Empty, stackPanelToReturn, shortcut);
+ }
+ }
+
+ ///
+ /// Transforms a key string into a visual representation in the stack panel.
+ ///
+ /// The string representation of the key.
+ /// The StackPanel to add the key to.
+ private static void AddKeyToStackPanel(string key, StackPanel shortcutStackPanel)
+ {
+ Microsoft.UI.Xaml.Media.FontFamily monospaceFont = new("Courier New");
+
+ switch (key)
+ {
+ case "":
+ shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/CopilotKey.png") });
+ break;
+ case "":
+ shortcutStackPanel.Children.Add(new BitmapIcon() { UriSource = new("ms-appx:///Assets/ShortcutGuide/OfficeKey.png"), Height = 20, Width = 20 });
+ break;
+ case "":
+ AddNewTextToStackPanel("←");
+ break;
+ case "":
+ AddNewTextToStackPanel("→");
+ break;
+ case "":
+ AddNewTextToStackPanel("↑");
+ break;
+ case "":
+ AddNewTextToStackPanel("↓");
+ break;
+ case "":
+ AddNewTextToStackPanel("...");
+ break;
+ case "":
+ TextBlock animatedTextBlock = new()
+ {
+ Text = "A",
+ Margin = new Thickness(3, 3, 3, 1),
+ VerticalAlignment = VerticalAlignment.Center,
+
+ // Use monospaced font to ensure the text doesn't move
+ FontFamily = monospaceFont,
+ TextDecorations = TextDecorations.Underline,
+ };
+
+ shortcutStackPanel.Children.Add(animatedTextBlock);
+
+ AnimateTextBlock(animatedTextBlock, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ break;
+ case "":
+ TextBlock arrowTextBlock = new()
+ {
+ Text = "→",
+ Margin = new Thickness(3),
+ FontFamily = monospaceFont,
+ VerticalAlignment = VerticalAlignment.Center,
+ };
+
+ shortcutStackPanel.Children.Add(arrowTextBlock);
+
+ AnimateTextBlock(arrowTextBlock, "→↓←↑", 1000);
+ break;
+ case "":
+ TextBlock arrowLRTextBlock = new()
+ {
+ Text = "→",
+ Margin = new Thickness(3),
+ FontFamily = monospaceFont,
+ VerticalAlignment = VerticalAlignment.Center,
+ };
+ shortcutStackPanel.Children.Add(arrowLRTextBlock);
+ AnimateTextBlock(arrowLRTextBlock, "→←", 1000);
+ break;
+ case "":
+ TextBlock arrowUDTextBlock = new()
+ {
+ Text = "↑",
+ Margin = new Thickness(3),
+ FontFamily = monospaceFont,
+ VerticalAlignment = VerticalAlignment.Center,
+ };
+ shortcutStackPanel.Children.Add(arrowUDTextBlock);
+ AnimateTextBlock(arrowUDTextBlock, "↑↓", 1000);
+ break;
+ case { } name when name.StartsWith('<') && name.EndsWith('>'):
+ AddNewTextToStackPanel(name[1..^1]);
+ break;
+ case { } num when int.TryParse(num, out int parsedNum):
+ if (parsedNum == 0)
+ {
+ break;
+ }
+
+ AddNewTextToStackPanel(Helper.GetKeyName((uint)parsedNum));
+ break;
+ default:
+ AddNewTextToStackPanel(key);
+ break;
+ }
+
+ void AddNewTextToStackPanel(string text)
+ {
+ shortcutStackPanel.Children.Add(new TextBlock { Text = text, Margin = new Thickness(3), VerticalAlignment = VerticalAlignment.Center });
+ }
+ }
+
+ ///
+ /// Animates the text of a TextBlock by cycling through the characters of a given string.
+ ///
+ ///
+ /// This function runs asynchronously and will not block the UI thread. Exceptions that occur during the animation will be caught and ignored to prevent crashes.
+ ///
+ /// The textblock to animate.
+ /// The characters to cycle through.
+ /// The delay to the next animation frame.
+ private static async void AnimateTextBlock(TextBlock animatedTextBlock, string text, int delay = 500)
+ {
+ try
+ {
+ int index = 0;
+ CancellationToken cancellationToken = ShortcutView.AnimationCancellationTokenSource.Token;
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ animatedTextBlock.Text = text[index].ToString();
+ index = (index + 1) % text.Length;
+ await Task.Delay(delay);
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ ///
+ /// Animates the visibility of the stack panels one after another.
+ ///
+ ///
+ /// This function runs asynchronously and will not block the UI thread. Exceptions that occur during the animation will be caught and ignored to prevent crashes.
+ ///
+ /// The panels to animate.
+ /// The delay to the next animation frame.
+ private static async void AnimateStackPanels(StackPanel[] panels, int delay = 2000)
+ {
+ try
+ {
+ int index = 0;
+ CancellationToken cancellationToken = ShortcutView.AnimationCancellationTokenSource.Token;
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ foreach (StackPanel panel in panels)
+ {
+ panel.Visibility = Visibility.Collapsed;
+ }
+
+ panels[index].Visibility = Visibility.Visible;
+ index = (index + 1) % panels.Length;
+ await Task.Delay(delay);
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutFile.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutFile.cs
new file mode 100644
index 000000000000..46c114d6453c
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutFile.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace ShortcutGuide.Models
+{
+ public struct ShortcutFile
+ {
+ public string PackageName { get; set; }
+
+ public ShortcutCategory[] Shortcuts { get; set; }
+
+ public string WindowFilter { get; set; }
+
+ public bool BackgroundProcess { get; set; }
+
+ public string Name { get; set; }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutPageParameters.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutPageParameters.cs
new file mode 100644
index 000000000000..d88c4cda9134
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Models/ShortcutPageParameters.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace ShortcutGuide.Models
+{
+ ///
+ /// Represents the parameters for the shortcut page in the Shortcut Guide module.
+ ///
+ internal struct ShortcutPageParameters
+ {
+ ///
+ /// Gets or sets the content of the search box.
+ ///
+ public static SearchFilterObservable SearchFilter = new();
+
+ ///
+ /// Gets or sets the pinned shortcuts for the Shortcut Guide.
+ ///
+ public static Dictionary> PinnedShortcuts = [];
+
+ ///
+ /// Gets or sets the name of the current page being displayed in the Shortcut Guide.
+ ///
+ public static string CurrentPageName = string.Empty;
+
+ ///
+ /// The height of the frame that displays the shortcuts.
+ ///
+ public static FrameHeightObservable FrameHeight = new();
+
+ internal sealed class SearchFilterObservable
+ {
+ public event EventHandler? FilterChanged;
+
+ public void OnFilterChanged(string filter)
+ {
+ FilterChanged?.Invoke(this, filter);
+ }
+ }
+
+ internal sealed class FrameHeightObservable
+ {
+ public event EventHandler? FrameHeightChanged;
+
+ public void OnFrameHeightChanged(double height)
+ {
+ if (height <= 0)
+ {
+ return;
+ }
+
+ FrameHeightChanged?.Invoke(this, height);
+ }
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/NativeMethods.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/NativeMethods.cs
new file mode 100644
index 000000000000..e1d830dcc9c6
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/NativeMethods.cs
@@ -0,0 +1,127 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using Windows.Graphics;
+
+namespace ShortcutGuide;
+
+internal static partial class NativeMethods
+{
+ internal const int GWL_STYLE = -16;
+ internal const int WS_CAPTION = 0x00C00000;
+
+ [LibraryImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
+
+ [LibraryImport("user32.dll", SetLastError = true)]
+ internal static partial int GetWindowLongW(IntPtr hWnd, int nIndex);
+
+ [LibraryImport("user32.dll")]
+ internal static partial int SetWindowLongW(IntPtr hWnd, int nIndex, int dwNewLong);
+
+ [LibraryImport("user32.dll", StringMarshalling = StringMarshalling.Utf16)]
+ internal static partial IntPtr FindWindowA(in string lpClassName, in string? lpWindowName);
+
+ [LibraryImport("User32.dll")]
+ internal static partial IntPtr MonitorFromWindow(IntPtr hwnd, int dwFlags);
+
+ [LibraryImport("Shcore.dll")]
+ internal static partial long GetDpiForMonitor(IntPtr hmonitor, int dpiType, ref int dpiX, ref int dpiY);
+
+ [LibraryImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool GetCursorPos(out POINT lpPoint);
+
+ [LibraryImport("user32.dll")]
+ internal static partial IntPtr GetForegroundWindow();
+
+ [LibraryImport("user32.dll", SetLastError = true)]
+ internal static partial uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
+
+ [LibraryImport("user32.dll")]
+ internal static partial short GetAsyncKeyState(int vKey);
+
+ [LibraryImport("ShortcutGuide.CPPProject.dll", EntryPoint = "get_buttons")]
+ internal static partial IntPtr GetTasklistButtons(IntPtr monitor, out int size);
+
+ [LibraryImport("ShortcutGuide.CPPProject.dll", EntryPoint = "IsCurrentWindowExcludedFromShortcutGuide")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool IsCurrentWindowExcludedFromShortcutGuide();
+
+ [LibraryImport("User32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);
+
+ internal delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
+
+ internal struct LPARAM(IntPtr value)
+ {
+ internal IntPtr Value = value;
+
+ public static implicit operator IntPtr(LPARAM lParam)
+ {
+ return lParam.Value;
+ }
+
+ public static implicit operator LPARAM(IntPtr value)
+ {
+ return new LPARAM(value);
+ }
+
+ public static implicit operator LPARAM(int value)
+ {
+ return new LPARAM(new IntPtr(value));
+ }
+
+ public static implicit operator int(LPARAM lParam)
+ {
+ return lParam.Value.ToInt32();
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct RECT
+ {
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
+ }
+
+ internal struct POINT
+ {
+ internal int X;
+ internal int Y;
+
+ public static implicit operator PointInt32(POINT point)
+ {
+ return new PointInt32(point.X, point.Y);
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct TasklistButton
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+ public string Name;
+
+ public int X;
+
+ public int Y;
+
+ public int Width;
+
+ public int Height;
+
+ public int Keynum;
+ }
+
+ public enum MonitorFromWindowDwFlags
+ {
+ MONITOR_DEFAULTTONEAREST = 2,
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs
new file mode 100644
index 000000000000..61a61883b9d2
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Program.cs
@@ -0,0 +1,84 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Windows;
+using ManagedCommon;
+using Microsoft.UI.Dispatching;
+using Microsoft.Windows.AppLifecycle;
+using ShortcutGuide.Helpers;
+using Application = Microsoft.UI.Xaml.Application;
+
+namespace ShortcutGuide
+{
+ public sealed class Program
+ {
+ private static readonly string[] InbuiltManifestFiles = [
+ "+WindowsNT.Shell.en-US.yml",
+ "+WindowsNT.WindowsExplorer.en-US.yml",
+ "+WindowsNT.Notepad.en-US.yml",
+ "Microsoft.PowerToys.en-US.yml",
+ ];
+
+ [STAThread]
+ public static void Main()
+ {
+ if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredShortcutGuideEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
+ {
+ Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
+ return;
+ }
+
+ Directory.CreateDirectory(ManifestInterpreter.PathOfManifestFiles);
+
+ if (NativeMethods.IsCurrentWindowExcludedFromShortcutGuide())
+ {
+ return;
+ }
+
+ // Todo: Only copy files after an update.
+ // Todo: Handle error
+ foreach (var file in InbuiltManifestFiles)
+ {
+ File.Copy(Path.GetDirectoryName(Environment.ProcessPath) + "\\Assets\\ShortcutGuide\\" + file, ManifestInterpreter.PathOfManifestFiles + "\\" + file, true);
+ }
+
+ Process indexGeneration = Process.Start(Path.GetDirectoryName(Environment.ProcessPath) + "\\PowerToys.ShortcutGuide.IndexYmlGenerator.exe");
+ indexGeneration.WaitForExit();
+ if (indexGeneration.ExitCode != 0)
+ {
+ Logger.LogError("Index generation failed with exit code: " + indexGeneration.ExitCode);
+ MessageBox.Show($"Shortcut Guide encountered an error while generating the index file. There is likely a corrupt shortcuts file in \"{ManifestInterpreter.PathOfManifestFiles}\". Try deleting this directory.", "Error displaying shortcuts", MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ PowerToysShortcutsPopulator.Populate();
+
+ Logger.InitializeLogger("\\ShortcutGuide\\Logs");
+ WinRT.ComWrappersSupport.InitializeComWrappers();
+
+ var instanceKey = AppInstance.FindOrRegisterForKey("PowerToys_ShortcutGuide_Instance");
+
+ if (instanceKey.IsCurrent)
+ {
+ Application.Start((p) =>
+ {
+ var context = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread());
+ SynchronizationContext.SetSynchronizationContext(context);
+ _ = new App();
+ });
+ }
+ else
+ {
+ Logger.LogWarning("Another instance of ShortcutGuide is running. Exiting ShortcutGuide");
+ }
+
+ // Something prevents the process from exiting, so we need to kill it manually.
+ Process.GetCurrentProcess().Kill();
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
new file mode 100644
index 000000000000..ef7b310e0ee1
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+ WinExe
+ ShortcutGuide
+ app.manifest
+ true
+ true
+ None
+ enable
+ false
+ false
+ true
+ ..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps
+ PowerToys.ShortcutGuide
+ DISABLE_XAML_GENERATED_MAIN,TRACE
+ Assets\ShortcutGuide\ShortcutGuide.ico
+
+ PowerToys.ShortcutGuide.pri
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tlbimp
+ 0
+ 1
+ 944de083-8fb8-45cf-bcb7-c477acb2f897
+ 0
+ false
+ true
+
+
+
+
+
+ PowerToys.GPOWrapper
+ $(OutDir)
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+
+
+
+
+ MSBuild:Compile
+
+
+
+
+ MSBuild:Compile
+
+
+
+
+ true
+
+
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/App.xaml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/App.xaml
new file mode 100644
index 000000000000..7856e3635637
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/App.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/App.xaml.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/App.xaml.cs
new file mode 100644
index 000000000000..923d8da42647
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/App.xaml.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml;
+
+namespace ShortcutGuide
+{
+ public partial class App
+ {
+ public App()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ _window = new MainWindow();
+ _window.Activate();
+ _window.Closed += (_, _) =>
+ {
+ _window = null;
+ Current.Exit();
+ };
+ }
+
+ private Window? _window;
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml
new file mode 100644
index 000000000000..01bbfd2ea7ff
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs
new file mode 100644
index 000000000000..1f0c02791d48
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs
@@ -0,0 +1,253 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Text.Json;
+using Common.UI;
+using ManagedCommon;
+using Microsoft.PowerToys.Settings.UI.Library;
+using Microsoft.UI;
+using Microsoft.UI.Windowing;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Input;
+using ShortcutGuide.Helpers;
+using ShortcutGuide.Models;
+using Windows.Foundation;
+using Windows.Graphics;
+using Windows.System;
+using WinUIEx;
+using static ShortcutGuide.NativeMethods;
+
+namespace ShortcutGuide
+{
+ ///
+ /// An empty window that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class MainWindow
+ {
+ private readonly string[] _currentApplicationIds;
+ private readonly bool _firstRun;
+
+ public static nint WindowHwnd { get; set; }
+
+ private AppWindow _appWindow;
+ private bool _setPosition;
+
+ public MainWindow()
+ {
+ _currentApplicationIds = ManifestInterpreter.GetAllCurrentApplicationIds();
+
+ InitializeComponent();
+
+ Title = ResourceLoaderInstance.ResourceLoader.GetString("Title")!;
+
+ var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
+ WindowHwnd = hwnd;
+ WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
+ _appWindow = AppWindow.GetFromWindowId(windowId);
+#if !DEBUG
+ this.SetIsAlwaysOnTop(true);
+ this.SetIsShownInSwitchers(false);
+#endif
+ this.SetIsResizable(false);
+ this.SetIsMinimizable(false);
+ this.SetIsMaximizable(false);
+ IsTitleBarVisible = false;
+
+ // Remove the caption style from the window style. Windows App SDK 1.6 added it, which made the title bar and borders appear. This code removes it.
+ var windowStyle = GetWindowLongW(hwnd, GWL_STYLE);
+ windowStyle &= ~WS_CAPTION;
+ _ = SetWindowLongW(hwnd, GWL_STYLE, windowStyle);
+
+ Activated += Window_Activated;
+
+ SettingsUtils settingsUtils = new();
+
+ if (settingsUtils.SettingsExists(ShortcutGuideSettings.ModuleName, "Pinned.json"))
+ {
+ string pinnedPath = settingsUtils.GetSettingsFilePath(ShortcutGuideSettings.ModuleName, "Pinned.json");
+ ShortcutPageParameters.PinnedShortcuts = JsonSerializer.Deserialize>>(File.ReadAllText(pinnedPath))!;
+ }
+
+ Content.KeyUp += (_, e) =>
+ {
+ if (e.Key == VirtualKey.Escape)
+ {
+ Close();
+ }
+ };
+
+ ShortcutGuideSettings shortcutGuideSettings = SettingsRepository.GetInstance(settingsUtils).SettingsConfig;
+ ShortcutGuideProperties shortcutGuideProperties = shortcutGuideSettings.Properties;
+
+ switch (shortcutGuideProperties.Theme.Value)
+ {
+ case "dark":
+ ((FrameworkElement)Content).RequestedTheme = ElementTheme.Dark;
+ MainPage.RequestedTheme = ElementTheme.Dark;
+ MainPage.Background = new Microsoft.UI.Xaml.Media.SolidColorBrush(Windows.UI.Color.FromArgb(255, 0, 0, 0));
+ break;
+ case "light":
+ ((FrameworkElement)Content).RequestedTheme = ElementTheme.Light;
+ MainPage.RequestedTheme = ElementTheme.Light;
+ MainPage.Background = new Microsoft.UI.Xaml.Media.SolidColorBrush(Windows.UI.Color.FromArgb(255, 255, 255, 255));
+ break;
+ case "system":
+ // Ignore, as the theme will be set by the system.
+ break;
+ default:
+ Logger.LogError("Invalid theme value in settings: " + shortcutGuideProperties.Theme.Value);
+ break;
+ }
+
+ _firstRun = shortcutGuideProperties.FirstRun.Value;
+ shortcutGuideProperties.FirstRun = new BoolProperty(false);
+#pragma warning disable CA1869 // Cache and reuse 'JsonSerializerOptions' instances
+ settingsUtils.SaveSettings(JsonSerializer.Serialize(shortcutGuideSettings, new JsonSerializerOptions { WriteIndented = true }), "Shortcut Guide");
+#pragma warning restore CA1869 // Cache and reuse 'JsonSerializerOptions' instances
+ }
+
+ private void Window_Activated(object sender, WindowActivatedEventArgs e)
+ {
+ if (e.WindowActivationState == WindowActivationState.Deactivated)
+ {
+#if !DEBUG
+ Close();
+#endif
+ }
+
+ // The code below sets the position of the window to the center of the monitor, but only if it hasn't been set before.
+ if (!_setPosition)
+ {
+ var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
+ WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
+
+ _appWindow = AppWindow.GetFromWindowId(windowId);
+
+ GetCursorPos(out POINT lpPoint);
+ _appWindow.Move(new POINT { Y = lpPoint.Y - ((int)Height / 2), X = lpPoint.X - ((int)Width / 2) });
+
+ float dpiScale = DpiHelper.GetDPIScaleForWindow((int)hwnd);
+
+ Rect monitorRect = DisplayHelper.GetWorkAreaForDisplayWithWindow(hwnd);
+ this.SetWindowSize(monitorRect.Width / dpiScale, monitorRect.Height / dpiScale / 2);
+
+ // Move top of the window to the center of the monitor
+ _appWindow.Move(new PointInt32((int)monitorRect.X, (int)(monitorRect.Y + (int)(monitorRect.Height / 2))));
+ _setPosition = true;
+ AppWindow.Changed += (_, a) =>
+ {
+ if (!a.DidPresenterChange)
+ {
+ return;
+ }
+
+ Rect monitorRect = DisplayHelper.GetWorkAreaForDisplayWithWindow(hwnd);
+ float dpiScale = DpiHelper.GetDPIScaleForWindow((int)hwnd);
+ this.SetWindowSize(monitorRect.Width / dpiScale, monitorRect.Height / dpiScale / 2);
+ _appWindow.Move(new PointInt32((int)monitorRect.X, (int)(monitorRect.Y + (int)(monitorRect.Height / 2))));
+ };
+ }
+
+ // Populate the window selector with the current application IDs if it is empty.
+ if (WindowSelector.Items.Count == 0)
+ {
+ foreach (var item in _currentApplicationIds)
+ {
+ if (item == ManifestInterpreter.GetIndexYamlFile().DefaultShellName)
+ {
+ WindowSelector.Items.Add(new SelectorBarItem { Name = item, Text = "Windows", Icon = new FontIcon() { Glyph = "\xE770" } });
+ }
+ else
+ {
+ try
+ {
+ WindowSelector.Items.Add(new SelectorBarItem { Name = item, Text = ManifestInterpreter.GetShortcutsOfApplication(item).Name, Icon = new FontIcon { Glyph = "\uEB91" } });
+ }
+ catch (IOException)
+ {
+ }
+ }
+ }
+
+ if (_firstRun)
+ {
+ CreateAndOpenWelcomePage();
+ }
+
+ WindowSelector.SelectedItem = WindowSelector.Items[0];
+ }
+ }
+
+ public void WindowSelectionChanged(object sender, SelectorBarSelectionChangedEventArgs e)
+ {
+ string newPageName = ((SelectorBar)sender).SelectedItem.Name;
+
+ if (newPageName == "")
+ {
+ ContentFrame.Navigate(typeof(OOBEView));
+ return;
+ }
+
+ ShortcutPageParameters.CurrentPageName = newPageName;
+
+ ContentFrame.Loaded += (_, _) => ShortcutPageParameters.FrameHeight.OnFrameHeightChanged(ContentFrame.ActualHeight);
+
+ ContentFrame.Navigate(typeof(ShortcutView));
+
+ // I don't know why this has to be called again, but it does.
+ ShortcutPageParameters.FrameHeight.OnFrameHeightChanged(ContentFrame.ActualHeight);
+ }
+
+ public void CloseButton_Clicked(object sender, RoutedEventArgs e)
+ {
+ ShortcutView.AnimationCancellationTokenSource.Cancel();
+ Close();
+ }
+
+ private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ ShortcutPageParameters.SearchFilter.OnFilterChanged(SearchBox.Text);
+ }
+
+ private void SearchBox_KeyboardAcceleratorInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
+ {
+ SearchBox.Focus(FocusState.Programmatic);
+ }
+
+ private void SettingsButton_Clicked(object sender, RoutedEventArgs e)
+ {
+ SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.ShortcutGuide, true);
+ }
+
+ private void InformationButton_Click(object sender, RoutedEventArgs e)
+ {
+ InformationTip.IsOpen = !InformationTip.IsOpen;
+ }
+
+ private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
+ {
+ InformationTip.IsOpen = false;
+ if (WindowSelector.Items[0].Name == "")
+ {
+ WindowSelector.SelectedItem = WindowSelector.Items[0];
+ }
+ else
+ {
+ CreateAndOpenWelcomePage();
+ }
+ }
+
+ ///
+ /// Adds the welcome page to the window selector and opens it.
+ ///
+ private void CreateAndOpenWelcomePage()
+ {
+ WindowSelector.Items.Insert(0, new SelectorBarItem { Name = "", Text = ResourceLoaderInstance.ResourceLoader.GetString("Welcome"), Icon = new FontIcon { Glyph = "\uE789" } });
+ WindowSelector.SelectedItem = WindowSelector.Items[0];
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/OOBEView.xaml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/OOBEView.xaml
new file mode 100644
index 000000000000..429cb8fe70ae
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/OOBEView.xaml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/OOBEView.xaml.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/OOBEView.xaml.cs
new file mode 100644
index 000000000000..51fa26216f6b
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/OOBEView.xaml.cs
@@ -0,0 +1,102 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Media.Animation;
+
+namespace ShortcutGuide
+{
+ public sealed partial class OOBEView : Page
+ {
+ public OOBEView()
+ {
+ InitializeComponent();
+
+ /*SizeChanged += (_, _) =>
+ {
+ HeroImageCompositeTransform.TranslateX = ActualWidth - 1350;
+ };*/
+
+ HeroImage.ImageSource = ActualTheme == ElementTheme.Dark
+ ? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(new Uri("ms-appx:///Assets/ShortcutGuide/HeroImage-dark.png"))
+ : new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(new Uri("ms-appx:///Assets/ShortcutGuide/HeroImage.png"));
+
+ ActualThemeChanged += (_, _) =>
+ {
+ HeroImage.ImageSource = ActualTheme == ElementTheme.Dark
+ ? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(new Uri("ms-appx:///Assets/ShortcutGuide/HeroImage-dark.png"))
+ : new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(new Uri("ms-appx:///Assets/ShortcutGuide/HeroImage.png"));
+ };
+
+ Loaded += (_, _) => AnimateStackPanelChildren(MainStackPanel);
+ }
+
+ ///
+ /// Animates the children of a StackPanel by fading them in and translating them from the left.
+ ///
+ /// The StackPanel to animate.
+ private void AnimateStackPanelChildren(StackPanel panel)
+ {
+ Storyboard storyboard = new();
+ double delay = 0.0;
+ Duration duration = new(TimeSpan.FromSeconds(0.3));
+
+ foreach (UIElement child in panel.Children)
+ {
+ if (child is not FrameworkElement childFrameworkElement || string.IsNullOrEmpty(childFrameworkElement.Name))
+ {
+ continue;
+ }
+
+ if (child.RenderTransform is null or not CompositeTransform)
+ {
+ child.RenderTransform = new CompositeTransform { TranslateX = -30 };
+ child.RenderTransformOrigin = new Windows.Foundation.Point(0.5, 0.5);
+ }
+
+ child.Opacity = 0;
+
+ DoubleAnimation opacityAnimation = new()
+ {
+ From = 0,
+ To = 1,
+ Duration = duration,
+ BeginTime = TimeSpan.FromSeconds(delay),
+ };
+ Storyboard.SetTarget(opacityAnimation, childFrameworkElement);
+ Storyboard.SetTargetProperty(opacityAnimation, "Opacity");
+ storyboard.Children.Add(opacityAnimation);
+
+ DoubleAnimation translateAnimation = new()
+ {
+ From = -30,
+ To = 0,
+ Duration = duration,
+ BeginTime = TimeSpan.FromSeconds(delay),
+ };
+ Storyboard.SetTarget(translateAnimation, childFrameworkElement);
+ Storyboard.SetTargetProperty(translateAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)");
+ storyboard.Children.Add(translateAnimation);
+
+ delay += 0.2;
+ }
+
+ DoubleAnimation heroImageTranslateAnimation = new()
+ {
+ From = ActualWidth,
+ To = ActualWidth - 1350,
+ Duration = duration,
+ BeginTime = TimeSpan.FromSeconds(delay),
+ };
+ Storyboard.SetTarget(heroImageTranslateAnimation, HeroImage);
+ Storyboard.SetTargetProperty(heroImageTranslateAnimation, "(ImageBrush.Transform).(CompositeTransform.TranslateX)");
+ storyboard.Children.Add(heroImageTranslateAnimation);
+
+ storyboard.Begin();
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutTemplateDataObject.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutTemplateDataObject.cs
new file mode 100644
index 000000000000..6186b7172769
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutTemplateDataObject.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using ShortcutGuide.Models;
+
+namespace ShortcutGuide
+{
+ public sealed class ShortcutTemplateDataObject
+ {
+ public string Name { get; set; }
+
+ public string Description { get; set; }
+
+ public StackPanel Shortcut { get; set; }
+
+ public ShortcutEntry OriginalShortcutObject { get; set; }
+
+ public Visibility DescriptionVisible { get; set; }
+
+ public ShortcutTemplateDataObject(string name, string description, StackPanel shortcut, ShortcutEntry originalShortcutObject)
+ {
+ Name = name;
+ Description = description;
+ OriginalShortcutObject = originalShortcutObject;
+
+ DescriptionVisible = string.IsNullOrWhiteSpace(description) ? Visibility.Collapsed : Visibility.Visible;
+
+ shortcut.Orientation = Orientation.Horizontal;
+ Shortcut = shortcut;
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml
new file mode 100644
index 000000000000..844dcc146a93
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml.cs
new file mode 100644
index 000000000000..2808d03a4efa
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml.cs
@@ -0,0 +1,383 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text.Json;
+using System.Threading;
+using System.Windows.Controls;
+using Microsoft.PowerToys.Settings.UI.Library;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Media.Animation;
+using ShortcutGuide.Helpers;
+using ShortcutGuide.Models;
+using Windows.Foundation;
+using Grid = Microsoft.UI.Xaml.Controls.Grid;
+
+namespace ShortcutGuide
+{
+ public sealed partial class ShortcutView : INotifyPropertyChanged
+ {
+ private readonly DispatcherTimer _taskbarIconsUpdateTimer = new() { Interval = TimeSpan.FromMilliseconds(500) };
+ private readonly ShortcutFile _shortcutList = ManifestInterpreter.GetShortcutsOfApplication(ShortcutPageParameters.CurrentPageName);
+ private bool _showTaskbarShortcuts;
+ private static CancellationTokenSource _animationCancellationTokenSource = new();
+
+ ///
+ /// Gets or sets a cancellation token source for animations in shortcut view.
+ /// When setting a new token source, the previous one is cancelled to stop ongoing animations.
+ ///
+ public static CancellationTokenSource AnimationCancellationTokenSource
+ {
+ get => _animationCancellationTokenSource;
+ set
+ {
+ _animationCancellationTokenSource?.Cancel();
+ _animationCancellationTokenSource = value;
+ }
+ }
+
+ public ShortcutView()
+ {
+ InitializeComponent();
+ DataContext = this;
+
+ // Stop any ongoing animations by cancelling the previous token source
+ AnimationCancellationTokenSource = new();
+
+ try
+ {
+ PopulateCategorySelector();
+
+ CategorySelector.SelectedItem = CategorySelector.Items[0];
+ CategorySelector.SelectionChanged += CategorySelector_SelectionChanged;
+
+ foreach (var shortcut in _shortcutList.Shortcuts[0].Properties)
+ {
+ ShortcutListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
+ }
+
+ ShortcutPageParameters.FrameHeight.FrameHeightChanged += ContentHeightChanged;
+ ShortcutPageParameters.SearchFilter.FilterChanged += SearchFilter_FilterChanged;
+
+ if (!ShortcutPageParameters.PinnedShortcuts.TryGetValue(ShortcutPageParameters.CurrentPageName, out var _))
+ {
+ ShortcutPageParameters.PinnedShortcuts.Add(ShortcutPageParameters.CurrentPageName, []);
+ }
+
+ if (_showTaskbarShortcuts)
+ {
+ TaskbarIndicators.Visibility = Visibility.Visible;
+ ShortcutsScrollViewer.Margin = new Thickness(0, 0, 0, 20);
+ _taskbarIconsUpdateTimer.Tick += UpdateTaskbarIndicators;
+ _taskbarIconsUpdateTimer.Start();
+ }
+
+ OpenOverview();
+ }
+ catch (Exception)
+ {
+ OverviewStackPanel.Visibility = Visibility.Collapsed;
+ ErrorMessage.Visibility = Visibility.Visible;
+ ErrorMessage.Text = ResourceLoaderInstance.ResourceLoader.GetString("ErrorInAppParsing");
+ }
+
+ Unloaded += (_, _) =>
+ {
+ _taskbarIconsUpdateTimer.Tick -= UpdateTaskbarIndicators;
+ _taskbarIconsUpdateTimer.Stop();
+ };
+ }
+
+ ///
+ /// Populates the selector and sets .
+ ///
+ private void PopulateCategorySelector()
+ {
+ int i = -1;
+ CategorySelector.Items.Add(new SelectorBarItem()
+ {
+ Text = ResourceLoaderInstance.ResourceLoader.GetString("Overview"),
+ Name = i.ToString(CultureInfo.InvariantCulture),
+ });
+
+ i++;
+
+ foreach (var category in _shortcutList.Shortcuts)
+ {
+ switch (category.SectionName)
+ {
+ case { } name when name.StartsWith("", StringComparison.Ordinal):
+ _showTaskbarShortcuts = true;
+ break;
+ case { } name when name.StartsWith('<') && name.EndsWith('>'):
+ break;
+ default:
+ CategorySelector.Items.Add(new SelectorBarItem() { Text = category.SectionName, Name = i.ToString(CultureInfo.InvariantCulture) });
+ break;
+ }
+
+ i++;
+ }
+ }
+
+ ///
+ /// Updates the taskbar indicators.
+ ///
+ private void UpdateTaskbarIndicators(object? sender, object? e)
+ {
+ NativeMethods.TasklistButton[] buttons = TasklistPositions.GetButtons();
+
+ for (int i = 0; i < TaskbarIndicators.Children.Count; i++)
+ {
+ if (i < buttons.Length)
+ {
+ TaskbarIndicators.Children[i].Visibility = Visibility.Visible;
+ Rect workArea = DisplayHelper.GetWorkAreaForDisplayWithWindow(MainWindow.WindowHwnd);
+ DoubleAnimation animation = new()
+ {
+ To = (buttons[i].X - workArea.Left) / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32()),
+ Duration = TimeSpan.FromMilliseconds(500),
+ };
+
+ // Create the storyboard
+ Storyboard storyboard = new();
+ storyboard.Children.Add(animation);
+
+ // Set the target and property
+ Storyboard.SetTarget(animation, TaskbarIndicators.Children[i]);
+ Storyboard.SetTargetProperty(animation, "(Canvas.Left)");
+
+ // Start the animation
+ storyboard.Begin();
+
+ ((TaskbarIndicator)TaskbarIndicators.Children[i]).Width = buttons[i].Width / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
+ ((TaskbarIndicator)TaskbarIndicators.Children[i]).Height = buttons[i].Height / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
+
+ continue;
+ }
+
+ TaskbarIndicators.Children[i].Visibility = Visibility.Collapsed;
+ }
+ }
+
+ private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ private double _contentHeight;
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ public double ContentHeight
+ {
+ get => _contentHeight - CategorySelector.ActualHeight;
+ set
+ {
+ _contentHeight = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public void ContentHeightChanged(object? sender, double e)
+ {
+ ContentHeight = e;
+ }
+
+ private void OpenOverview()
+ {
+ RecommendedListElement.Items.Clear();
+ PinnedListElement.Items.Clear();
+ OverviewStackPanel.Visibility = Visibility.Visible;
+ RecommendedListElement.Visibility = Visibility.Visible;
+ PinnedListElement.Visibility = Visibility.Visible;
+ RecommendedListTitle.Visibility = Visibility.Visible;
+ PinnedListTitle.Visibility = Visibility.Visible;
+ ShortcutListElement.Visibility = Visibility.Collapsed;
+
+ foreach (var shortcut in _shortcutList.Shortcuts.SelectMany(list => list.Properties.Where(s => s.Recommended)))
+ {
+ RecommendedListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
+ }
+
+ if (RecommendedListElement.Items.Count == 0)
+ {
+ RecommendedListTitle.Visibility = Visibility.Collapsed;
+ RecommendedListElement.Visibility = Visibility.Collapsed;
+ }
+
+ foreach (var shortcut in ShortcutPageParameters.PinnedShortcuts[ShortcutPageParameters.CurrentPageName])
+ {
+ PinnedListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
+ }
+
+ if (PinnedListElement.Items.Count == 0)
+ {
+ PinnedListTitle.Visibility = Visibility.Collapsed;
+ PinnedListElement.Visibility = Visibility.Collapsed;
+ }
+
+ if (RecommendedListElement.Items.Count == 0 && PinnedListElement.Items.Count == 0)
+ {
+ OverviewStackPanel.Visibility = Visibility.Collapsed;
+ ErrorMessage.Visibility = Visibility.Visible;
+ ErrorMessage.Text = ResourceLoaderInstance.ResourceLoader.GetString("NoShortcutsInOverview");
+ }
+
+ if (_showTaskbarShortcuts)
+ {
+ TaskbarIndicators.Visibility = Visibility.Visible;
+ ShortcutsScrollViewer.Margin = new Thickness(0, 0, 0, 20);
+ TaskbarLaunchShortcutsListElement.Visibility = Visibility.Visible;
+ TaskbarLaunchShortcutsListElement.Items.Clear();
+ TaskbarLaunchShortcutsTitle.Visibility = Visibility.Visible;
+ foreach (var item in _shortcutList.Shortcuts.First(x => x.SectionName.StartsWith("", StringComparison.InvariantCulture)).Properties)
+ {
+ TaskbarLaunchShortcutsListElement.Items.Add((ShortcutTemplateDataObject)item);
+ }
+
+ return;
+ }
+
+ TaskbarLaunchShortcutsListElement.Visibility = Visibility.Collapsed;
+ TaskbarLaunchShortcutsTitle.Visibility = Visibility.Collapsed;
+ TaskbarLaunchShortcutsDescription.Visibility = Visibility.Collapsed;
+ }
+
+ private string _searchFilter = string.Empty;
+
+ private void SearchFilter_FilterChanged(object? sender, string e)
+ {
+ FilterBy(e);
+ _searchFilter = e;
+ }
+
+ public void FilterBy(string filter)
+ {
+ TaskbarIndicators.Visibility = Visibility.Collapsed;
+ ShortcutsScrollViewer.Margin = new Thickness(0);
+ ShortcutListElement.Items.Clear();
+ ShortcutListElement.Visibility = Visibility.Visible;
+ ErrorMessage.Visibility = Visibility.Collapsed;
+
+ if (int.Parse(CategorySelector.SelectedItem.Name, CultureInfo.InvariantCulture) == -1)
+ {
+ if (string.IsNullOrWhiteSpace(filter))
+ {
+ OpenOverview();
+ return;
+ }
+
+ OverviewStackPanel.Visibility = Visibility.Collapsed;
+ foreach (var shortcut in _shortcutList.Shortcuts.SelectMany(list => list.Properties.Where(s => s.Name.Contains(filter, StringComparison.InvariantCultureIgnoreCase))))
+ {
+ ShortcutListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
+ }
+ }
+ else
+ {
+ foreach (var shortcut in _shortcutList.Shortcuts[int.Parse(CategorySelector.SelectedItem.Name, CultureInfo.InvariantCulture)].Properties.Where(s => s.Name.Contains(filter, StringComparison.InvariantCultureIgnoreCase)))
+ {
+ ShortcutListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
+ }
+ }
+
+ if (ShortcutListElement.Items.Count != 0)
+ {
+ return;
+ }
+
+ ShortcutListElement.Visibility = Visibility.Collapsed;
+ ErrorMessage.Visibility = Visibility.Visible;
+ ErrorMessage.Text = ResourceLoaderInstance.ResourceLoader.GetString("SearchBlank");
+ }
+
+ public void CategorySelector_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs e)
+ {
+ ShortcutListElement.Items.Clear();
+ RecommendedListElement.Items.Clear();
+ PinnedListElement.Items.Clear();
+ TaskbarLaunchShortcutsListElement.Items.Clear();
+ ErrorMessage.Visibility = Visibility.Collapsed;
+ RecommendedListElement.Visibility = Visibility.Collapsed;
+ PinnedListElement.Visibility = Visibility.Collapsed;
+ OverviewStackPanel.Visibility = Visibility.Collapsed;
+ TaskbarLaunchShortcutsListElement.Visibility = Visibility.Collapsed;
+ TaskbarLaunchShortcutsTitle.Visibility = Visibility.Collapsed;
+ TaskbarLaunchShortcutsDescription.Visibility = Visibility.Collapsed;
+ ShortcutListElement.Visibility = Visibility.Visible;
+ TaskbarIndicators.Visibility = Visibility.Collapsed;
+ ShortcutsScrollViewer.Margin = new Thickness(0);
+
+ try
+ {
+ if (int.Parse(sender.SelectedItem.Name, CultureInfo.InvariantCulture) == -1)
+ {
+ OpenOverview();
+ FilterBy(_searchFilter);
+ return;
+ }
+
+ foreach (var shortcut in _shortcutList.Shortcuts[int.Parse(sender.SelectedItem.Name, CultureInfo.InvariantCulture)].Properties)
+ {
+ ShortcutListElement.Items.Add((ShortcutTemplateDataObject)shortcut);
+ }
+ }
+ catch (NullReferenceException)
+ {
+ ErrorMessage.Visibility = Visibility.Visible;
+ ErrorMessage.Text = ResourceLoaderInstance.ResourceLoader.GetString("ErrorInCategoryParsing");
+ }
+
+ FilterBy(_searchFilter);
+ }
+
+ private void PinShortcut(object sender, RoutedEventArgs e)
+ {
+ if (ShortcutPageParameters.PinnedShortcuts[ShortcutPageParameters.CurrentPageName].Contains(((ShortcutTemplateDataObject)((MenuFlyoutItem)sender).DataContext).OriginalShortcutObject))
+ {
+ ShortcutPageParameters.PinnedShortcuts[ShortcutPageParameters.CurrentPageName].Remove(((ShortcutTemplateDataObject)((MenuFlyoutItem)sender).DataContext).OriginalShortcutObject);
+ }
+ else
+ {
+ ShortcutPageParameters.PinnedShortcuts[ShortcutPageParameters.CurrentPageName].Add(((ShortcutTemplateDataObject)((MenuFlyoutItem)sender).DataContext).OriginalShortcutObject);
+ }
+
+ if (int.Parse(CategorySelector.SelectedItem.Name, CultureInfo.InvariantCulture) == -1)
+ {
+ OpenOverview();
+ }
+
+ string serialized = JsonSerializer.Serialize(ShortcutPageParameters.PinnedShortcuts);
+
+ SettingsUtils settingsUtils = new();
+ string pinnedPath = settingsUtils.GetSettingsFilePath(ShortcutGuideSettings.ModuleName, "Pinned.json");
+ File.WriteAllText(pinnedPath, serialized);
+ }
+
+ private void MenuFlyout_Opening(object sender, object e)
+ {
+ if (sender is not MenuFlyout menu ||
+ menu.Target is not Grid parentGrid ||
+ parentGrid.DataContext is not ShortcutTemplateDataObject dataObject ||
+ menu.Items[0] is not MenuFlyoutItem pinItem)
+ {
+ return;
+ }
+
+ ShortcutEntry originalObject = dataObject.OriginalShortcutObject;
+
+ bool isItemPinned = ShortcutPageParameters.PinnedShortcuts[ShortcutPageParameters.CurrentPageName].Any(x => x.Equals(originalObject));
+
+ pinItem.Text = isItemPinned ? ResourceLoaderInstance.ResourceLoader.GetString("UnpinShortcut") : ResourceLoaderInstance.ResourceLoader.GetString("PinShortcut");
+ pinItem.Icon = new SymbolIcon(isItemPinned ? Symbol.UnPin : Symbol.Pin);
+ }
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/TaskbarIndicator.xaml b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/TaskbarIndicator.xaml
new file mode 100644
index 000000000000..f7e8d56bb7a8
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/TaskbarIndicator.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/TaskbarIndicator.xaml.cs b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/TaskbarIndicator.xaml.cs
new file mode 100644
index 000000000000..0945c848fcaa
--- /dev/null
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/TaskbarIndicator.xaml.cs
@@ -0,0 +1,52 @@
+// 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.Globalization;
+using Microsoft.UI.Xaml.Controls;
+
+namespace ShortcutGuide;
+
+public sealed partial class TaskbarIndicator : UserControl
+{
+ private int _indicatorNumber;
+
+ public int IndicatorNumber
+ {
+ get => _indicatorNumber;
+ set
+ {
+ _indicatorNumber = value;
+ IndicatorText.Text = value > 9 ? "0" : value.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+
+ public new double Width
+ {
+ get => (double)GetValue(WidthProperty);
+ set
+ {
+ SetValue(WidthProperty, value);
+ IndicatorText.Width = Width;
+ IndicatorCanvas.Width = Width;
+ IndicatorRectangle.Width = Width;
+ }
+ }
+
+ public new double Height
+ {
+ get => (double)GetValue(HeightProperty);
+ set
+ {
+ SetValue(HeightProperty, value);
+ IndicatorText.Height = Height;
+ IndicatorCanvas.Height = Height;
+ IndicatorRectangle.Height = Height;
+ }
+ }
+
+ public TaskbarIndicator()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Resources.resx b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Strings/en-us/Resources.resw
similarity index 56%
rename from src/modules/ShortcutGuide/ShortcutGuide/Resources.resx
rename to src/modules/ShortcutGuide/ShortcutGuide.Ui/Strings/en-us/Resources.resw
index 34942e7a5295..6514f775bef3 100644
--- a/src/modules/ShortcutGuide/ShortcutGuide/Resources.resx
+++ b/src/modules/ShortcutGuide/ShortcutGuide.Ui/Strings/en-us/Resources.resw
@@ -1,4 +1,4 @@
-
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/0.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/0.svg
deleted file mode 100644
index b797f8b7ef73..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/0.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/1.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/1.svg
deleted file mode 100644
index 6e1e3d28f146..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/1.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/2.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/2.svg
deleted file mode 100644
index 5183242c1b2e..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/2.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/3.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/3.svg
deleted file mode 100644
index 63ad68f061df..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/3.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/4.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/4.svg
deleted file mode 100644
index 4f7e36f8bd7b..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/4.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/5.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/5.svg
deleted file mode 100644
index 0fd52f658589..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/5.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/6.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/6.svg
deleted file mode 100644
index 00303cdb3208..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/6.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/7.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/7.svg
deleted file mode 100644
index cce8aedbaa61..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/7.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/8.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/8.svg
deleted file mode 100644
index 57f4856b6b90..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/8.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/9.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/9.svg
deleted file mode 100644
index 2e0f33c493ff..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/9.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/no_active_window.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/no_active_window.svg
deleted file mode 100644
index 69d390bd081b..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/no_active_window.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/overlay.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/overlay.svg
deleted file mode 100644
index fff83ab395ee..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/overlay.svg
+++ /dev/null
@@ -1,205 +0,0 @@
-
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/overlay_portrait.svg b/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/overlay_portrait.svg
deleted file mode 100644
index 82c03149733b..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/Assets/ShortcutGuide/overlay_portrait.svg
+++ /dev/null
@@ -1,208 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/PropertySheet.props b/src/modules/ShortcutGuide/ShortcutGuide/PropertySheet.props
deleted file mode 100644
index b0c622690fed..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/PropertySheet.props
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.base.rc b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.base.rc
deleted file mode 100644
index 1f88309dd2dc..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.base.rc
+++ /dev/null
@@ -1,54 +0,0 @@
-#include
-#include "Generated Files/resource.h"
-#include "../../../../common/version/version.h"
-
-#define APSTUDIO_READONLY_SYMBOLS
-#include "winres.h"
-#undef APSTUDIO_READONLY_SYMBOLS
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Icon
-//
-
-// Icon with lowest ID value placed first to ensure application icon
-// remains consistent on all systems.
-IDI_ICON1 ICON "Shortcut-Guide.ico"
-
- /////////////////////////////////////////////////////////////////////////////
-//
-// Version
-//
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION FILE_VERSION
- PRODUCTVERSION PRODUCT_VERSION
- FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
-#ifdef _DEBUG
- FILEFLAGS VS_FF_DEBUG
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS VOS_NT_WINDOWS32
- FILETYPE VFT_DLL
- FILESUBTYPE VFT2_UNKNOWN
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904b0"
- BEGIN
- VALUE "CompanyName", COMPANY_NAME
- VALUE "FileDescription", FILE_DESCRIPTION
- VALUE "FileVersion", FILE_VERSION_STRING
- VALUE "InternalName", INTERNAL_NAME
- VALUE "LegalCopyright", COPYRIGHT_NOTE
- VALUE "OriginalFilename", ORIGINAL_FILENAME
- VALUE "ProductName", PRODUCT_NAME
- VALUE "ProductVersion", PRODUCT_VERSION_STRING
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x409, 1200
- END
-END
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.exe.manifest b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.exe.manifest
deleted file mode 100644
index 4747d3bd23dd..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.exe.manifest
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
- true/PM
- PerMonitorV2
-
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj
deleted file mode 100644
index 045be94f2bda..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj
+++ /dev/null
@@ -1,193 +0,0 @@
-
-
-
-
-
-
-
- true
- true
- true
- true
- 15.0
- {2edb3eb4-fa92-4bff-b2d8-566584837231}
- Win32Proj
- ShortcutGuide
-
-
-
- Application
- v143
- v141
- v142
- Unicode
-
-
- true
- true
-
-
- false
- true
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PowerToys.$(MSBuildProjectName)
-
-
- ..\..\..\..\$(Platform)\$(Configuration)\
-
-
-
- ;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\..\;..\;%(AdditionalIncludeDirectories)
-
-
- ole32.lib;Shell32.lib;OleAut32.lib;Dbghelp.lib;Dwmapi.lib;Dcomp.lib;Shlwapi.lib;%(AdditionalDependencies)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Create
-
-
-
-
-
-
-
-
-
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
- Document
- $(OutDir)\Assets\ShortcutGuide
-
-
-
-
- {caba8dfb-823b-4bf2-93ac-3f31984150d9}
-
-
- {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
-
-
- {6955446d-23f7-4023-9bb3-8657f904af99}
-
-
- {8f021b46-362b-485c-bfba-ccf83e820cbd}
-
-
- {98537082-0fdb-40de-abd8-0dc5a4269bab}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj.filters b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj.filters
deleted file mode 100644
index 006a6196ef35..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj.filters
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
-
-
- {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
- cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
-
-
- {93995380-89BD-4b04-88EB-625FBE52EBFB}
- h;hh;hpp;hxx;hm;inl;inc;xsd
-
-
- {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
- rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;svg;tiff;tif;png;wav;mfcribbon-ms
-
-
- {cb917ac7-30da-494b-81f1-cbe4415e91f4}
-
-
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Resource Files
-
-
- Generated Files
-
-
- Header Files
-
-
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
-
-
-
- Resource Files
-
-
-
-
-
- Resource Files
-
-
-
-
- Resource Files
-
-
-
-
- Generated Files
-
-
-
-
- Resource Files
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuideConstants.h b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuideConstants.h
deleted file mode 100644
index 3d0e434abfb2..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuideConstants.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-#include
-
-namespace ShortcutGuideConstants
-{
- // Name of the powertoy module.
- inline const std::wstring ModuleKey = L"Shortcut Guide";
-}
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuideSettings.h b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuideSettings.h
deleted file mode 100644
index a39f2f25114e..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuideSettings.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-#include
-
-struct ShortcutGuideSettings
-{
- std::wstring hotkey = L"shift+win+/";
- int overlayOpacity = 90;
- std::wstring theme = L"system";
- std::wstring disabledApps = L"";
- bool shouldReactToPressedWinKey = false;
- int windowsKeyPressTimeForGlobalWindowsShortcuts = 900;
- int windowsKeyPressTimeForTaskbarIconShortcuts = 900;
-};
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/animation.cpp b/src/modules/ShortcutGuide/ShortcutGuide/animation.cpp
deleted file mode 100644
index 4bc039a3e279..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/animation.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "pch.h"
-#include "animation.h"
-
-Animation::Animation(double duration, double start, double stop) :
- duration(duration), start_value(start), end_value(stop), start(std::chrono::high_resolution_clock::now()) {}
-
-void Animation::reset()
-{
- start = std::chrono::high_resolution_clock::now();
-}
-void Animation::reset(double animation_duration)
-{
- duration = animation_duration;
- reset();
-}
-void Animation::reset(double animation_duration, double animation_start, double animation_stop)
-{
- start_value = animation_start;
- end_value = animation_stop;
- reset(animation_duration);
-}
-
-static double ease_out_expo(double t)
-{
- return 1 - pow(2, -8 * t);
-}
-
-double Animation::apply_animation_function(double t, AnimFunctions apply_function)
-{
- switch (apply_function)
- {
- case EASE_OUT_EXPO:
- return ease_out_expo(t);
- case LINEAR:
- default:
- return t;
- }
-}
-
-double Animation::value(AnimFunctions apply_function) const
-{
- auto anim_duration = std::chrono::high_resolution_clock::now() - start;
- double t = std::chrono::duration(anim_duration).count() / duration;
- if (t >= 1)
- return end_value;
- return start_value + (end_value - start_value) * apply_animation_function(t, apply_function);
-}
-bool Animation::done() const
-{
- return std::chrono::high_resolution_clock::now() - start >= std::chrono::duration(duration);
-}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/animation.h b/src/modules/ShortcutGuide/ShortcutGuide/animation.h
deleted file mode 100644
index 61ae59b661ad..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/animation.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-#include
-
-/*
- Usage:
- When creating animation constructor takes one parameter - how long
- should the animation take in seconds.
-
- Call reset() when starting animation.
-
- When rendering, call value() to get value from 0 to 1 - depending on animation
- progress.
-*/
-class Animation
-{
-public:
- enum AnimFunctions
- {
- LINEAR = 0,
- EASE_OUT_EXPO
- };
-
- Animation(double duration = 1, double start = 0, double stop = 1);
- void reset();
- void reset(double animation_duration);
- void reset(double animation_duration, double animation_start, double animation_stop);
- double value(AnimFunctions apply_function) const;
- bool done() const;
-
-private:
- static double apply_animation_function(double t, AnimFunctions apply_function);
- std::chrono::high_resolution_clock::time_point start;
- double start_value, end_value, duration;
-};
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp
deleted file mode 100644
index 0f01aaf403d3..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-#include "pch.h"
-#include "d2d_svg.h"
-
-D2DSVG& D2DSVG::load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc)
-{
- svg = nullptr;
- winrt::com_ptr svg_stream;
- auto h = SHCreateStreamOnFileEx(filename.c_str(),
- STGM_READ,
- FILE_ATTRIBUTE_NORMAL,
- FALSE,
- nullptr,
- svg_stream.put());
- winrt::check_hresult(h);
-
- auto h1 = d2d_dc->CreateSvgDocument(
- svg_stream.get(),
- D2D1::SizeF(1, 1),
- svg.put());
-
- winrt::check_hresult(h1);
-
- winrt::com_ptr root;
- svg->GetRoot(root.put());
- float tmp;
- winrt::check_hresult(root->GetAttributeValue(L"width", &tmp));
- svg_width = static_cast(tmp);
- winrt::check_hresult(root->GetAttributeValue(L"height", &tmp));
- svg_height = static_cast(tmp);
- return *this;
-}
-
-D2DSVG& D2DSVG::resize(int x, int y, int width, int height, float fill, float max_scale)
-{
- // Center
- transform = D2D1::Matrix3x2F::Identity();
- transform = transform * D2D1::Matrix3x2F::Translation((width - svg_width) / 2.0f, (height - svg_height) / 2.0f);
- float h_scale = fill * height / svg_height;
- float v_scale = fill * width / svg_width;
- used_scale = std::min(h_scale, v_scale);
- if (max_scale > 0)
- {
- used_scale = std::min(used_scale, max_scale);
- }
- transform = transform * D2D1::Matrix3x2F::Scale(used_scale, used_scale, D2D1::Point2F(width / 2.0f, height / 2.0f));
- transform = transform * D2D1::Matrix3x2F::Translation(static_cast(x), static_cast(y));
- return *this;
-}
-
-D2DSVG& D2DSVG::recolor(uint32_t oldcolor, uint32_t newcolor)
-{
- auto new_color = D2D1::ColorF(newcolor & 0xFFFFFF, 1);
- auto old_color = D2D1::ColorF(oldcolor & 0xFFFFFF, 1);
- std::function recurse = [&](ID2D1SvgElement* element) {
- if (!element)
- return;
- if (element->IsAttributeSpecified(L"fill"))
- {
- D2D1_COLOR_F elem_fill;
- winrt::com_ptr paint;
- element->GetAttributeValue(L"fill", paint.put());
- paint->GetColor(&elem_fill);
- if (elem_fill.r == old_color.r && elem_fill.g == old_color.g && elem_fill.b == old_color.b)
- {
- winrt::check_hresult(element->SetAttributeValue(L"fill", new_color));
- }
- }
- winrt::com_ptr sub;
- element->GetFirstChild(sub.put());
- while (sub)
- {
- recurse(sub.get());
- winrt::com_ptr next;
- element->GetNextChild(sub.get(), next.put());
- sub = next;
- }
- };
- winrt::com_ptr root;
- svg->GetRoot(root.put());
- recurse(root.get());
- return *this;
-}
-
-D2DSVG& D2DSVG::render(ID2D1DeviceContext5* d2d_dc)
-{
- D2D1_MATRIX_3X2_F current;
- d2d_dc->GetTransform(¤t);
- d2d_dc->SetTransform(transform * current);
- d2d_dc->DrawSvgDocument(svg.get());
- d2d_dc->SetTransform(current);
- return *this;
-}
-
-D2DSVG& D2DSVG::toggle_element(const wchar_t* id, bool visible)
-{
- winrt::com_ptr element;
- if (svg->FindElementById(id, element.put()) != S_OK)
- return *this;
- if (!element)
- return *this;
- element->SetAttributeValue(L"display", visible ? D2D1_SVG_DISPLAY::D2D1_SVG_DISPLAY_INLINE : D2D1_SVG_DISPLAY::D2D1_SVG_DISPLAY_NONE);
- return *this;
-}
-
-winrt::com_ptr D2DSVG::find_element(const std::wstring& id)
-{
- winrt::com_ptr element;
- winrt::check_hresult(svg->FindElementById(id.c_str(), element.put()));
- return element;
-}
-
-D2D1_RECT_F D2DSVG::rescale(D2D1_RECT_F rect)
-{
- D2D1_RECT_F result;
- auto src = reinterpret_cast(&rect);
- auto dst = reinterpret_cast(&result);
- dst[0] = src[0] * transform;
- dst[1] = src[1] * transform;
- return result;
-}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.h b/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.h
deleted file mode 100644
index 08869b2de193..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-#include
-#include
-#include
-#include
-
-class D2DSVG
-{
-public:
- D2DSVG& load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc);
- D2DSVG& resize(int x, int y, int width, int height, float fill, float max_scale = -1.0f);
- D2DSVG& render(ID2D1DeviceContext5* d2d_dc);
- D2DSVG& recolor(uint32_t oldcolor, uint32_t newcolor);
- float get_scale() const { return used_scale; }
- int width() const { return svg_width; }
- int height() const { return svg_height; }
- D2DSVG& toggle_element(const wchar_t* id, bool visible);
- winrt::com_ptr find_element(const std::wstring& id);
- D2D1_RECT_F rescale(D2D1_RECT_F rect);
-
-protected:
- float used_scale = 1.0f;
- winrt::com_ptr svg;
- int svg_width = -1, svg_height = -1;
- D2D1::Matrix3x2F transform;
-};
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp
deleted file mode 100644
index 7f25c4e32c13..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "pch.h"
-#include "d2d_text.h"
-
-D2DText::D2DText(float text_size, float scale)
-{
- winrt::check_hresult(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast(factory.put_void())));
- resize(text_size, scale);
- winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
- winrt::check_hresult(format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
-}
-
-D2DText& D2DText::resize(float text_size, float scale)
-{
- format = nullptr;
- winrt::check_hresult(factory->CreateTextFormat(L"Segoe UI",
- nullptr,
- DWRITE_FONT_WEIGHT_NORMAL,
- DWRITE_FONT_STYLE_NORMAL,
- DWRITE_FONT_STRETCH_NORMAL,
- text_size * scale,
- L"en-us",
- format.put()));
- winrt::check_hresult(format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
- return *this;
-}
-
-D2DText& D2DText::set_alignment_left()
-{
- winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING));
- return *this;
-}
-
-D2DText& D2DText::set_alignment_center()
-{
- winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
- return *this;
-}
-
-D2DText& D2DText::set_alignment_right()
-{
- winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING));
- return *this;
-}
-
-void D2DText::write(ID2D1DeviceContext5* d2d_dc, D2D1_COLOR_F color, D2D1_RECT_F rect, std::wstring text)
-{
- winrt::com_ptr brush;
- d2d_dc->CreateSolidColorBrush(color, brush.put());
- d2d_dc->DrawText(text.c_str(),
- static_cast(text.length()),
- format.get(),
- rect,
- brush.get());
-}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.h b/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.h
deleted file mode 100644
index 513dc120e58b..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-#include
-#include
-
-class D2DText
-{
-public:
- D2DText(float text_size = 15.0f, float scale = 1.0f);
- D2DText& resize(float text_size, float scale);
- D2DText& set_alignment_left();
- D2DText& set_alignment_center();
- D2DText& set_alignment_right();
- void write(ID2D1DeviceContext5* d2d_dc, D2D1_COLOR_F color, D2D1_RECT_F rect, std::wstring text);
-
-private:
- winrt::com_ptr factory;
- winrt::com_ptr format;
-};
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp
deleted file mode 100644
index f4bf7e9cf607..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#include "pch.h"
-#include "d2d_window.h"
-
-#include
-
-D2DWindow::D2DWindow()
-{
- static const WCHAR* class_name = L"PToyD2DPopup";
- WNDCLASS wc = {};
- wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wc.hInstance = reinterpret_cast(&__ImageBase);
- wc.lpszClassName = class_name;
- wc.style = CS_HREDRAW | CS_VREDRAW;
- wc.lpfnWndProc = d2d_window_proc;
- RegisterClass(&wc);
- hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOREDIRECTIONBITMAP | WS_EX_LAYERED,
- wc.lpszClassName,
- L"PToyD2DPopup",
- WS_POPUP | WS_VISIBLE,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- nullptr,
- nullptr,
- wc.hInstance,
- this);
- WINRT_VERIFY(hwnd);
-}
-
-void D2DWindow::show(UINT x, UINT y, UINT width, UINT height)
-{
- if (!initialized)
- {
- base_init();
- }
- base_resize(width, height);
- render_empty();
- hidden = false;
- on_show();
- SetWindowPos(hwnd, HWND_TOPMOST, x, y, width, height, 0);
- ShowWindow(hwnd, SW_SHOWNORMAL);
- SetForegroundWindow(hwnd);
- UpdateWindow(hwnd);
-}
-
-void D2DWindow::hide()
-{
- hidden = true;
- ShowWindow(hwnd, SW_HIDE);
- on_hide();
-}
-
-void D2DWindow::initialize()
-{
- base_init();
-}
-
-void D2DWindow::base_init()
-{
- std::unique_lock lock(mutex);
- // D2D1Factory is independent from the device, no need to recreate it if we need to recreate the device.
- if (!d2d_factory)
- {
-#ifdef _DEBUG
- D2D1_FACTORY_OPTIONS options = { D2D1_DEBUG_LEVEL_INFORMATION };
-#else
- D2D1_FACTORY_OPTIONS options = {};
-#endif
- winrt::check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
- __uuidof(d2d_factory),
- &options,
- d2d_factory.put_void()));
- }
- // For all other stuff - assign nullptr first to release the object, to reset the com_ptr.
- d2d_dc = nullptr;
- d2d_device = nullptr;
- dxgi_factory = nullptr;
- dxgi_device = nullptr;
- d3d_device = nullptr;
- winrt::check_hresult(D3D11CreateDevice(nullptr,
- D3D_DRIVER_TYPE_HARDWARE,
- nullptr,
- D3D11_CREATE_DEVICE_BGRA_SUPPORT,
- nullptr,
- 0,
- D3D11_SDK_VERSION,
- d3d_device.put(),
- nullptr,
- nullptr));
- winrt::check_hresult(d3d_device->QueryInterface(__uuidof(dxgi_device), dxgi_device.put_void()));
- winrt::check_hresult(CreateDXGIFactory2(0, __uuidof(dxgi_factory), dxgi_factory.put_void()));
- winrt::check_hresult(d2d_factory->CreateDevice(dxgi_device.get(), d2d_device.put()));
- winrt::check_hresult(d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2d_dc.put()));
- init();
- initialized = true;
-}
-
-void D2DWindow::base_resize(UINT width, UINT height)
-{
- std::unique_lock lock(mutex);
- if (!initialized)
- {
- return;
- }
- window_width = width;
- window_height = height;
- if (window_width == 0 || window_height == 0)
- {
- return;
- }
- DXGI_SWAP_CHAIN_DESC1 sc_description = {};
- sc_description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
- sc_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
- sc_description.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
- sc_description.BufferCount = 2;
- sc_description.SampleDesc.Count = 1;
- sc_description.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
- sc_description.Width = window_width;
- sc_description.Height = window_height;
- dxgi_swap_chain = nullptr;
- winrt::check_hresult(dxgi_factory->CreateSwapChainForComposition(dxgi_device.get(),
- &sc_description,
- nullptr,
- dxgi_swap_chain.put()));
- composition_device = nullptr;
- winrt::check_hresult(DCompositionCreateDevice(dxgi_device.get(),
- __uuidof(composition_device),
- composition_device.put_void()));
-
- composition_target = nullptr;
- winrt::check_hresult(composition_device->CreateTargetForHwnd(hwnd, true, composition_target.put()));
-
- composition_visual = nullptr;
- winrt::check_hresult(composition_device->CreateVisual(composition_visual.put()));
- winrt::check_hresult(composition_visual->SetContent(dxgi_swap_chain.get()));
- winrt::check_hresult(composition_target->SetRoot(composition_visual.get()));
-
- dxgi_surface = nullptr;
- winrt::check_hresult(dxgi_swap_chain->GetBuffer(0, __uuidof(dxgi_surface), dxgi_surface.put_void()));
- D2D1_BITMAP_PROPERTIES1 properties = {};
- properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
- properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
- properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
-
- d2d_bitmap = nullptr;
- winrt::check_hresult(d2d_dc->CreateBitmapFromDxgiSurface(dxgi_surface.get(),
- properties,
- d2d_bitmap.put()));
- d2d_dc->SetTarget(d2d_bitmap.get());
- resize();
-}
-
-void D2DWindow::base_render()
-{
- std::unique_lock lock(mutex);
- if (!initialized || !d2d_dc || !d2d_bitmap)
- return;
- d2d_dc->BeginDraw();
- render(d2d_dc.get());
- winrt::check_hresult(d2d_dc->EndDraw());
- winrt::check_hresult(dxgi_swap_chain->Present(1, 0));
- winrt::check_hresult(composition_device->Commit());
-}
-
-void D2DWindow::render_empty()
-{
- std::unique_lock lock(mutex);
- if (!initialized || !d2d_dc || !d2d_bitmap)
- return;
- d2d_dc->BeginDraw();
- d2d_dc->Clear();
- winrt::check_hresult(d2d_dc->EndDraw());
- winrt::check_hresult(dxgi_swap_chain->Present(1, 0));
- winrt::check_hresult(composition_device->Commit());
-}
-
-D2DWindow::~D2DWindow()
-{
- ShowWindow(hwnd, SW_HIDE);
- DestroyWindow(hwnd);
-}
-
-D2DWindow* D2DWindow::this_from_hwnd(HWND window)
-{
- return reinterpret_cast(GetWindowLongPtr(window, GWLP_USERDATA));
-}
-
-LRESULT __stdcall D2DWindow::d2d_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
-{
- auto self = this_from_hwnd(window);
- switch (message)
- {
- case WM_NCCREATE:
- {
- auto create_struct = reinterpret_cast(lparam);
- SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast(create_struct->lpCreateParams));
- return TRUE;
- }
- case WM_MOVE:
- case WM_SIZE:
- self->base_resize(static_cast(lparam) & 0xFFFF, static_cast(lparam) >> 16);
- [[fallthrough]];
- case WM_PAINT:
- self->base_render();
- return 0;
-
- default:
- return DefWindowProc(window, message, wparam, lparam);
- }
-}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.h b/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.h
deleted file mode 100644
index b062962d281f..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "d2d_svg.h"
-
-#include
-#include
-
-class D2DWindow
-{
-public:
- D2DWindow();
- void show(UINT x, UINT y, UINT width, UINT height);
- void hide();
- void initialize();
- virtual ~D2DWindow();
-
-protected:
- // Implement this:
-
- // Initialization - called when D2D device needs to be created.
- // When called all D2DWindow members will be initialized, including d2d_dc
- virtual void init() = 0;
- // resize - when called, window_width and window_height will have current window size
- virtual void resize() = 0;
- // render - called on WM_PAINT, BeginPaint/EndPaint is handled by D2DWindow
- virtual void render(ID2D1DeviceContext5* d2d_dc) = 0;
- // on_show, on_hide - called when the window is about to be shown or about to be hidden
- virtual void on_show() = 0;
- virtual void on_hide() = 0;
-
- static LRESULT __stdcall d2d_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam);
- static D2DWindow* this_from_hwnd(HWND window);
-
- void base_init();
- void base_resize(UINT width, UINT height);
- void base_render();
- void render_empty();
-
- std::recursive_mutex mutex;
- bool hidden = true;
- bool initialized = false;
- HWND hwnd;
- UINT window_width{};
- UINT window_height{};
- winrt::com_ptr d3d_device;
- winrt::com_ptr dxgi_device;
- winrt::com_ptr dxgi_factory;
- winrt::com_ptr dxgi_swap_chain;
- winrt::com_ptr composition_device;
- winrt::com_ptr composition_target;
- winrt::com_ptr composition_visual;
- winrt::com_ptr dxgi_surface;
- winrt::com_ptr d2d_bitmap;
- winrt::com_ptr d2d_factory;
- winrt::com_ptr d2d_device;
- winrt::com_ptr d2d_dc;
-};
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/main.cpp b/src/modules/ShortcutGuide/ShortcutGuide/main.cpp
deleted file mode 100644
index 713446403bd3..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/main.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-#include "pch.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#include "shortcut_guide.h"
-#include "target_state.h"
-#include "ShortcutGuideConstants.h"
-#include "trace.h"
-
-const std::wstring instanceMutexName = L"Local\\PowerToys_ShortcutGuide_InstanceMutex";
-
-// set current path to the executable path
-bool SetCurrentPath()
-{
- TCHAR buffer[MAX_PATH] = { 0 };
- if (!GetModuleFileName(NULL, buffer, MAX_PATH))
- {
- Logger::error(L"Failed to get module path. {}", get_last_error_or_default(GetLastError()));
- return false;
- }
-
- if (!PathRemoveFileSpec(buffer))
- {
- Logger::error(L"Failed to remove file from module path. {}", get_last_error_or_default(GetLastError()));
- return false;
- }
-
- std::error_code err;
- std::filesystem::current_path(buffer, err);
- if (err.value())
- {
- Logger::error("Failed to set current path. {}", err.message());
- return false;
- }
-
- return true;
-}
-
-int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR lpCmdLine, _In_ int /*nCmdShow*/)
-{
- winrt::init_apartment();
- LoggerHelpers::init_logger(ShortcutGuideConstants::ModuleKey, L"ShortcutGuide", LogSettings::shortcutGuideLoggerName);
-
- Shared::Trace::ETWTrace trace;
- trace.UpdateState(true);
-
- if (powertoys_gpo::getConfiguredShortcutGuideEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)
- {
- Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
- return 0;
- }
-
- InitUnhandledExceptionHandler();
- Logger::trace("Starting Shortcut Guide");
-
- if (!SetCurrentPath())
- {
- return false;
- }
-
- Trace::RegisterProvider();
- if (std::wstring(lpCmdLine).find(L' ') != std::wstring::npos)
- {
- Logger::trace("Sending settings telemetry");
- auto settings = OverlayWindow::GetSettings();
- Trace::SendSettings(settings);
- Trace::UnregisterProvider();
- return 0;
- }
-
- auto mutex = CreateMutex(nullptr, true, instanceMutexName.c_str());
- if (mutex == nullptr)
- {
- Logger::error(L"Failed to create mutex. {}", get_last_error_or_default(GetLastError()));
- }
-
- if (GetLastError() == ERROR_ALREADY_EXISTS)
- {
- Logger::warn(L"Shortcut Guide instance is already running");
- Trace::UnregisterProvider();
- return 0;
- }
-
- std::wstring pid = std::wstring(lpCmdLine);
- if (!pid.empty())
- {
- auto mainThreadId = GetCurrentThreadId();
- ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) {
- if (err != ERROR_SUCCESS)
- {
- Logger::error(L"Failed to wait for parent process exit. {}", get_last_error_or_default(err));
- }
- else
- {
- Logger::trace(L"PowerToys runner exited.");
- }
-
- Logger::trace(L"Exiting Shortcut Guide");
- PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
- });
- }
-
- auto hwnd = GetForegroundWindow();
- auto window = OverlayWindow(hwnd);
- EventWaiter exitEventWaiter;
- if (window.IsDisabled())
- {
- Logger::trace("SG is disabled for the current foreground app. Exiting SG");
- Trace::UnregisterProvider();
- return 0;
- }
- else
- {
- auto mainThreadId = GetCurrentThreadId();
- exitEventWaiter = EventWaiter(CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, [mainThreadId, &window](int err) {
- if (err != ERROR_SUCCESS)
- {
- Logger::error(L"Failed to wait for {} event. {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, get_last_error_or_default(err));
- }
- else
- {
- Logger::trace(L"{} event was signaled", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT);
- }
-
- window.CloseWindow(HideWindowType::THE_SHORTCUT_PRESSED, mainThreadId);
- });
- }
-
- window.ShowWindow();
- run_message_loop();
-
- trace.Flush();
- Trace::UnregisterProvider();
- return 0;
-}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/native_event_waiter.cpp b/src/modules/ShortcutGuide/ShortcutGuide/native_event_waiter.cpp
deleted file mode 100644
index b038ea4ffda8..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/native_event_waiter.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "pch.h"
-#include "native_event_waiter.h"
-
-void NativeEventWaiter::run()
-{
- while (!aborting)
- {
- auto result = WaitForSingleObject(event_handle, timeout);
- if (!aborting && result == WAIT_OBJECT_0)
- {
- action();
- }
- }
-}
-
-NativeEventWaiter::NativeEventWaiter(const std::wstring& event_name, std::function action)
-{
- event_handle = CreateEventW(NULL, FALSE, FALSE, event_name.c_str());
- this->action = action;
- running_thread = std::thread([&]() { run(); });
-}
-
-NativeEventWaiter::~NativeEventWaiter()
-{
- aborting = true;
- SetEvent(event_handle);
- running_thread.join();
- CloseHandle(event_handle);
-}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/native_event_waiter.h b/src/modules/ShortcutGuide/ShortcutGuide/native_event_waiter.h
deleted file mode 100644
index d7dcaa603eca..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/native_event_waiter.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#include "pch.h"
-#include "common/interop/shared_constants.h"
-
-class NativeEventWaiter
-{
- static const int timeout = 1000;
-
- HANDLE event_handle = nullptr;
- std::function action = nullptr;
- std::atomic aborting = false;
-
- void run();
- std::thread running_thread;
-
-public:
- NativeEventWaiter(const std::wstring& event_name, std::function action);
- ~NativeEventWaiter();
-};
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp b/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp
deleted file mode 100644
index 825bd10a1f7a..000000000000
--- a/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp
+++ /dev/null
@@ -1,947 +0,0 @@
-#include "pch.h"
-#include "overlay_window.h"
-#include
-#include "tasklist_positions.h"
-#include "start_visible.h"
-#include
-#include
-#include
-
-#include "shortcut_guide.h"
-#include "trace.h"
-#include "Generated Files/resource.h"
-
-namespace
-{
- // Gets position of given window.
- std::optional get_window_pos(HWND hwnd)
- {
- RECT window;
- if (DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &window, sizeof(window)) == S_OK)
- {
- return window;
- }
- else
- {
- return {};
- }
- }
-
- enum WindowState
- {
- UNKNOWN,
- MINIMIZED,
- MAXIMIZED,
- SNAPPED_TOP_LEFT,
- SNAPPED_LEFT,
- SNAPPED_BOTTOM_LEFT,
- SNAPPED_TOP_RIGHT,
- SNAPPED_RIGHT,
- SNAPPED_BOTTOM_RIGHT,
- RESTORED
- };
-
- inline WindowState get_window_state(HWND hwnd)
- {
- WINDOWPLACEMENT placement;
- placement.length = sizeof(WINDOWPLACEMENT);
-
- if (GetWindowPlacement(hwnd, &placement) == 0)
- {
- return UNKNOWN;
- }
-
- if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_SHOWMINIMIZED || IsIconic(hwnd))
- {
- return MINIMIZED;
- }
-
- if (placement.showCmd == SW_MAXIMIZE || placement.showCmd == SW_SHOWMAXIMIZED)
- {
- return MAXIMIZED;
- }
-
- auto rectp = get_window_pos(hwnd);
- if (!rectp)
- {
- return UNKNOWN;
- }
-
- auto rect = *rectp;
- MONITORINFO monitor;
- monitor.cbSize = sizeof(MONITORINFO);
- auto h_monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
- GetMonitorInfo(h_monitor, &monitor);
- bool top_left = monitor.rcWork.top == rect.top && monitor.rcWork.left == rect.left;
- bool bottom_left = monitor.rcWork.bottom == rect.bottom && monitor.rcWork.left == rect.left;
- bool top_right = monitor.rcWork.top == rect.top && monitor.rcWork.right == rect.right;
- bool bottom_right = monitor.rcWork.bottom == rect.bottom && monitor.rcWork.right == rect.right;
-
- if (top_left && bottom_left)
- return SNAPPED_LEFT;
- if (top_left)
- return SNAPPED_TOP_LEFT;
- if (bottom_left)
- return SNAPPED_BOTTOM_LEFT;
- if (top_right && bottom_right)
- return SNAPPED_RIGHT;
- if (top_right)
- return SNAPPED_TOP_RIGHT;
- if (bottom_right)
- return SNAPPED_BOTTOM_RIGHT;
-
- return RESTORED;
- }
-
-}
-
-D2DOverlaySVG& D2DOverlaySVG::load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc)
-{
- D2DSVG::load(filename, d2d_dc);
- window_group = nullptr;
- thumbnail_top_left = {};
- thumbnail_bottom_right = {};
- thumbnail_scaled_rect = {};
- return *this;
-}
-
-D2DOverlaySVG& D2DOverlaySVG::resize(int x, int y, int width, int height, float fill, float max_scale)
-{
- D2DSVG::resize(x, y, width, height, fill, max_scale);
- if (thumbnail_bottom_right.x != 0 && thumbnail_bottom_right.y != 0)
- {
- auto scaled_top_left = transform.TransformPoint(thumbnail_top_left);
- auto scanled_bottom_right = transform.TransformPoint(thumbnail_bottom_right);
- thumbnail_scaled_rect.left = static_cast(scaled_top_left.x);
- thumbnail_scaled_rect.top = static_cast(scaled_top_left.y);
- thumbnail_scaled_rect.right = static_cast(scanled_bottom_right.x);
- thumbnail_scaled_rect.bottom = static_cast(scanled_bottom_right.y);
- }
- return *this;
-}
-
-D2DOverlaySVG& D2DOverlaySVG::find_thumbnail(const std::wstring& id)
-{
- winrt::com_ptr thumbnail_box;
- winrt::check_hresult(svg->FindElementById(id.c_str(), thumbnail_box.put()));
- winrt::check_hresult(thumbnail_box->GetAttributeValue(L"x", &thumbnail_top_left.x));
- winrt::check_hresult(thumbnail_box->GetAttributeValue(L"y", &thumbnail_top_left.y));
- winrt::check_hresult(thumbnail_box->GetAttributeValue(L"width", &thumbnail_bottom_right.x));
- thumbnail_bottom_right.x += thumbnail_top_left.x;
- winrt::check_hresult(thumbnail_box->GetAttributeValue(L"height", &thumbnail_bottom_right.y));
- thumbnail_bottom_right.y += thumbnail_top_left.y;
- return *this;
-}
-
-D2DOverlaySVG& D2DOverlaySVG::find_window_group(const std::wstring& id)
-{
- window_group = nullptr;
- winrt::check_hresult(svg->FindElementById(id.c_str(), window_group.put()));
- return *this;
-}
-
-ScaleResult D2DOverlaySVG::get_thumbnail_rect_and_scale(int x_offset, int y_offset, int window_cx, int window_cy, float fill)
-{
- if (thumbnail_bottom_right.x == 0 && thumbnail_bottom_right.y == 0)
- {
- return {};
- }
- int thumbnail_scaled_rect_width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left;
- int thumbnail_scaled_rect_heigh = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top;
- if (thumbnail_scaled_rect_heigh == 0 || thumbnail_scaled_rect_width == 0 ||
- window_cx == 0 || window_cy == 0)
- {
- return {};
- }
- float scale_h = fill * thumbnail_scaled_rect_width / window_cx;
- float scale_v = fill * thumbnail_scaled_rect_heigh / window_cy;
- float use_scale = std::min(scale_h, scale_v);
- RECT thumb_rect;
- thumb_rect.left = thumbnail_scaled_rect.left + static_cast(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset;
- thumb_rect.right = thumbnail_scaled_rect.right - static_cast(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset;
- thumb_rect.top = thumbnail_scaled_rect.top + static_cast(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset;
- thumb_rect.bottom = thumbnail_scaled_rect.bottom - static_cast(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset;
- ScaleResult result;
- result.scale = use_scale;
- result.rect = thumb_rect;
- return result;
-}
-
-winrt::com_ptr D2DOverlaySVG::find_element(const std::wstring& id)
-{
- winrt::com_ptr element;
- winrt::check_hresult(svg->FindElementById(id.c_str(), element.put()));
- return element;
-}
-
-D2DOverlaySVG& D2DOverlaySVG::toggle_window_group(bool active)
-{
- if (window_group)
- {
- window_group->SetAttributeValue(L"fill-opacity", active ? 1.0f : 0.3f);
- }
- return *this;
-}
-
-D2D1_RECT_F D2DOverlaySVG::get_maximize_label() const
-{
- D2D1_RECT_F result;
- auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top;
- auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left;
- if (width >= height)
- {
- result.top = thumbnail_scaled_rect.bottom + height * 0.210f;
- result.bottom = thumbnail_scaled_rect.bottom + height * 0.310f;
- result.left = thumbnail_scaled_rect.left + width * 0.009f;
- result.right = thumbnail_scaled_rect.right + width * 0.009f;
- }
- else
- {
- result.top = thumbnail_scaled_rect.top + height * 0.323f;
- result.bottom = thumbnail_scaled_rect.top + height * 0.398f;
- result.left = static_cast(thumbnail_scaled_rect.right);
- result.right = thumbnail_scaled_rect.right + width * 1.45f;
- }
- return result;
-}
-D2D1_RECT_F D2DOverlaySVG::get_minimize_label() const
-{
- D2D1_RECT_F result;
- auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top;
- auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left;
- if (width >= height)
- {
- result.top = thumbnail_scaled_rect.bottom + height * 0.8f;
- result.bottom = thumbnail_scaled_rect.bottom + height * 0.9f;
- result.left = thumbnail_scaled_rect.left + width * 0.009f;
- result.right = thumbnail_scaled_rect.right + width * 0.009f;
- }
- else
- {
- result.top = thumbnail_scaled_rect.top + height * 0.725f;
- result.bottom = thumbnail_scaled_rect.top + height * 0.800f;
- result.left =static_cast(thumbnail_scaled_rect.right);
- result.right = thumbnail_scaled_rect.right + width * 1.45f;
- }
- return result;
-}
-D2D1_RECT_F D2DOverlaySVG::get_snap_left() const
-{
- D2D1_RECT_F result;
- auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top;
- auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left;
- if (width >= height)
- {
- result.top = thumbnail_scaled_rect.bottom + height * 0.5f;
- result.bottom = thumbnail_scaled_rect.bottom + height * 0.6f;
- result.left = thumbnail_scaled_rect.left + width * 0.009f;
- result.right = thumbnail_scaled_rect.left + width * 0.339f;
- }
- else
- {
- result.top = thumbnail_scaled_rect.top + height * 0.523f;
- result.bottom = thumbnail_scaled_rect.top + height * 0.598f;
- result.left = static_cast(thumbnail_scaled_rect.right);
- result.right = thumbnail_scaled_rect.right + width * 0.450f;
- }
- return result;
-}
-D2D1_RECT_F D2DOverlaySVG::get_snap_right() const
-{
- D2D1_RECT_F result;
- auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top;
- auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left;
- if (width >= height)
- {
- result.top = thumbnail_scaled_rect.bottom + height * 0.5f;
- result.bottom = thumbnail_scaled_rect.bottom + height * 0.6f;
- result.left = thumbnail_scaled_rect.left + width * 0.679f;
- result.right = thumbnail_scaled_rect.right + width * 1.009f;
- }
- else
- {
- result.top = thumbnail_scaled_rect.top + height * 0.523f;
- result.bottom = thumbnail_scaled_rect.top + height * 0.598f;
- result.left = static_cast(thumbnail_scaled_rect.right + width);
- result.right = thumbnail_scaled_rect.right + width * 1.45f;
- }
- return result;
-}
-
-D2DOverlayWindow::D2DOverlayWindow() :
- total_screen({}),
- D2DWindow()
-{
- BOOL isEnabledAnimations = GetAnimationsEnabled();
- background_animation = isEnabledAnimations? 0.3f : 0.f;
- global_windows_shortcuts_animation = isEnabledAnimations ? 0.3f : 0.f;
- taskbar_icon_shortcuts_animation = isEnabledAnimations ? 0.3f : 0.f;
- tasklist_thread = std::thread([&] {
- while (running)
- {
- // Removing causes C3538 on std::unique_lock lock(mutex); in show(..)
- std::unique_lock task_list_lock(tasklist_cv_mutex);
- tasklist_cv.wait(task_list_lock, [&] { return !running || tasklist_update; });
- if (!running)
- return;
- task_list_lock.unlock();
- while (running && tasklist_update)
- {
- std::vector buttons;
- if (tasklist.update_buttons(buttons))
- {
- std::unique_lock lock(mutex);
- tasklist_buttons.swap(buttons);
- }
- std::this_thread::sleep_for(std::chrono::milliseconds(500));
- }
- }
- });
-}
-
-void D2DOverlayWindow::show(HWND window, bool snappable)
-{
- std::unique_lock lock(mutex);
- hidden = false;
- tasklist_buttons.clear();
- active_window = window;
- active_window_snappable = snappable;
- auto old_bck = colors.start_color_menu;
- auto colors_updated = colors.update();
- auto new_light_mode = (theme_setting == Light) || (theme_setting == System && colors.light_mode);
- if (initialized && (colors_updated || light_mode != new_light_mode))
- {
- // update background colors
- landscape.recolor(old_bck, colors.start_color_menu);
- portrait.recolor(old_bck, colors.start_color_menu);
- for (auto& arrow : arrows)
- {
- arrow.recolor(old_bck, colors.start_color_menu);
- }
- light_mode = new_light_mode;
- if (light_mode)
- {
- landscape.recolor(0xDDDDDD, 0x222222);
- portrait.recolor(0xDDDDDD, 0x222222);
- for (auto& arrow : arrows)
- {
- arrow.recolor(0xDDDDDD, 0x222222);
- }
- }
- else
- {
- landscape.recolor(0x222222, 0xDDDDDD);
- portrait.recolor(0x222222, 0xDDDDDD);
- for (auto& arrow : arrows)
- {
- arrow.recolor(0x222222, 0xDDDDDD);
- }
- }
- }
- monitors = MonitorInfo::GetMonitors(true);
- // calculate the rect covering all the screens
- total_screen = monitors[0].GetScreenSize(true);
- for (auto& monitor : monitors)
- {
- const auto monitorSize = monitor.GetScreenSize(true);
- total_screen.rect.left = std::min(total_screen.left(), monitorSize.left());
- total_screen.rect.top = std::min(total_screen.top(), monitorSize.top());
- total_screen.rect.right = std::max(total_screen.right(), monitorSize.right());
- total_screen.rect.bottom = std::max(total_screen.bottom(), monitorSize.bottom());
- }
- // make sure top-right corner of all the monitor rects is (0,0)
- monitor_dx = -total_screen.left();
- monitor_dy = -total_screen.top();
- total_screen.rect.left += monitor_dx;
- total_screen.rect.right += monitor_dx;
- total_screen.rect.top += monitor_dy;
- total_screen.rect.bottom += monitor_dy;
- tasklist.update();
- if (window)
- {
- // Ignore errors, if this fails we will just not show the thumbnail
- DwmRegisterThumbnail(hwnd, window, &thumbnail);
- }
-
- background_animation.reset();
-
- if (milliseconds_press_time_for_global_windows_shortcuts < milliseconds_press_time_for_taskbar_icon_shortcuts)
- {
- global_windows_shortcuts_shown = true;
- taskbar_icon_shortcuts_shown = false;
- global_windows_shortcuts_animation.reset();
- }
- else if (milliseconds_press_time_for_global_windows_shortcuts > milliseconds_press_time_for_taskbar_icon_shortcuts)
- {
- global_windows_shortcuts_shown = false;
- taskbar_icon_shortcuts_shown = true;
- taskbar_icon_shortcuts_animation.reset();
- }
- else
- {
- global_windows_shortcuts_shown = true;
- taskbar_icon_shortcuts_shown = true;
- global_windows_shortcuts_animation.reset();
- taskbar_icon_shortcuts_animation.reset();
- }
-
- auto primary_size = MonitorInfo::GetPrimaryMonitor().GetScreenSize(false);
- shown_start_time = std::chrono::steady_clock::now();
- lock.unlock();
- D2DWindow::show(primary_size.left(), primary_size.top(), primary_size.width(), primary_size.height());
- // Check if taskbar is auto-hidden. If so, don't display the number arrows
- APPBARDATA param = {};
- param.cbSize = sizeof(APPBARDATA);
- if (static_cast(SHAppBarMessage(ABM_GETSTATE, ¶m)) != ABS_AUTOHIDE)
- {
- tasklist_cv_mutex.lock();
- tasklist_update = true;
- tasklist_cv_mutex.unlock();
- tasklist_cv.notify_one();
- }
-}
-
-void D2DOverlayWindow::on_show()
-{
- // show override does everything
-}
-
-void D2DOverlayWindow::on_hide()
-{
- Logger::trace("D2DOverlayWindow::on_hide()");
- tasklist_cv_mutex.lock();
- tasklist_update = false;
- tasklist_cv_mutex.unlock();
- tasklist_cv.notify_one();
- if (thumbnail)
- {
- DwmUnregisterThumbnail(thumbnail);
- }
- std::chrono::steady_clock::time_point shown_end_time = std::chrono::steady_clock::now();
- // Trace the event only if the overlay window was visible.
- if (shown_start_time.time_since_epoch().count() > 0)
- {
- auto duration = std::chrono::duration_cast(shown_end_time - shown_start_time).count();
- Logger::trace(L"Duration: {}. Close Type: {}", duration, windowCloseType);
- Trace::SendGuideSession(duration, windowCloseType.c_str());
- shown_start_time = {};
- }
-}
-
-D2DOverlayWindow::~D2DOverlayWindow()
-{
- tasklist_cv_mutex.lock();
- running = false;
- tasklist_cv_mutex.unlock();
- tasklist_cv.notify_one();
- tasklist_thread.join();
-}
-
-void D2DOverlayWindow::apply_overlay_opacity(float opacity)
-{
- if (opacity <= 0.0f)
- {
- opacity = 0.0f;
- }
- if (opacity >= 1.0f)
- {
- opacity = 1.0f;
- }
- overlay_opacity = opacity;
-}
-
-void D2DOverlayWindow::apply_press_time_for_global_windows_shortcuts(int press_time)
-{
- milliseconds_press_time_for_global_windows_shortcuts = std::max(press_time, 0);
-}
-
-void D2DOverlayWindow::apply_press_time_for_taskbar_icon_shortcuts(int press_time)
-{
- milliseconds_press_time_for_taskbar_icon_shortcuts = std::max(press_time, 0);
-}
-
-void D2DOverlayWindow::set_theme(const std::wstring& theme)
-{
- if (theme == L"light")
- {
- theme_setting = Light;
- }
- else if (theme == L"dark")
- {
- theme_setting = Dark;
- }
- else
- {
- theme_setting = System;
- }
-}
-
-/* Hide the window but do not call on_hide(). Use this to quickly hide the window when needed.
- Note, that a proper hide should be made after this before showing the window again.
-*/
-void D2DOverlayWindow::quick_hide()
-{
- ShowWindow(hwnd, SW_HIDE);
- if (thumbnail)
- {
- DwmUnregisterThumbnail(thumbnail);
- }
-}
-
-HWND D2DOverlayWindow::get_window_handle()
-{
- return hwnd;
-}
-
-float D2DOverlayWindow::get_overlay_opacity()
-{
- return overlay_opacity;
-}
-
-void D2DOverlayWindow::init()
-{
- colors.update();
- landscape.load(L"Assets\\ShortcutGuide\\overlay.svg", d2d_dc.get())
- .find_thumbnail(L"monitorRect")
- .find_window_group(L"WindowControlsGroup")
- .recolor(0x2582FB, colors.start_color_menu);
- portrait.load(L"Assets\\ShortcutGuide\\overlay_portrait.svg", d2d_dc.get())
- .find_thumbnail(L"monitorRect")
- .find_window_group(L"WindowControlsGroup")
- .recolor(0x2582FB, colors.start_color_menu);
- no_active.load(L"Assets\\ShortcutGuide\\no_active_window.svg", d2d_dc.get());
- arrows.resize(10);
- for (unsigned i = 0; i < arrows.size(); ++i)
- {
- arrows[i].load(L"Assets\\ShortcutGuide\\" + std::to_wstring((i + 1) % 10) + L".svg", d2d_dc.get()).recolor(0x2582FB, colors.start_color_menu);
- }
- light_mode = (theme_setting == Light) || (theme_setting == System && colors.light_mode);
- if (light_mode)
- {
- landscape.recolor(0x2E17FC, 0x000000);
- portrait.recolor(0x2E17FC, 0x000000);
- for (auto& arrow : arrows)
- {
- arrow.recolor(0x222222, 0x000000);
- }
- }
- else
- {
- landscape.recolor(0x2E17FC, 0xFFFFFF);
- portrait.recolor(0x2E17FC, 0xFFFFFF);
- for (auto& arrow : arrows)
- {
- arrow.recolor(0x222222, 0xFFFFFF);
- }
- }
-}
-
-void D2DOverlayWindow::resize()
-{
- window_rect = *get_window_pos(hwnd);
- float no_active_scale, font;
- if (window_width >= window_height)
- { // portrait is broke right now
- use_overlay = &landscape;
- no_active_scale = 0.3f;
- font = 12.0f;
- }
- else
- {
- use_overlay = &portrait;
- no_active_scale = 0.5f;
- font = 13.0f;
- }
- use_overlay->resize(0, 0, window_width, window_height, 0.8f);
- auto thumb_no_active_rect = use_overlay->get_thumbnail_rect_and_scale(0, 0, no_active.width(), no_active.height(), no_active_scale).rect;
- no_active.resize(thumb_no_active_rect.left,
- thumb_no_active_rect.top,
- thumb_no_active_rect.right - thumb_no_active_rect.left,
- thumb_no_active_rect.bottom - thumb_no_active_rect.top,
- 1.0f);
- text.resize(font, use_overlay->get_scale());
-}
-
-void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_scale, ID2D1DeviceContext5* d2d_dc, int x_offset, int y_offset)
-{
- int dx = 0, dy = 0;
- // Calculate taskbar orientation
- arrow.toggle_element(L"left", false);
- arrow.toggle_element(L"right", false);
- arrow.toggle_element(L"top", false);
- arrow.toggle_element(L"bottom", false);
- if (button.x <= window.left)
- { // taskbar on left
- dx = 1;
- arrow.toggle_element(L"left", true);
- }
- if (button.x >= window.right)
- { // taskbar on right
- dx = -1;
- arrow.toggle_element(L"right", true);
- }
- if (button.y <= window.top)
- { // taskbar on top
- dy = 1;
- arrow.toggle_element(L"top", true);
- }
- if (button.y >= window.bottom)
- { // taskbar on bottom
- dy = -1;
- arrow.toggle_element(L"bottom", true);
- }
- double arrow_ratio = static_cast(arrow.height()) / arrow.width();
- if (dy != 0)
- {
- // assume button is 25% wider than taller, +10% to make room for each of the arrows that are hidden
- auto render_arrow_width = static_cast(button.height * 1.25f * 1.2f);
- auto render_arrow_height = static_cast(render_arrow_width * arrow_ratio);
- arrow.resize((button.x + (button.width - render_arrow_width) / 2) + x_offset,
- (dy == -1 ? button.y - render_arrow_height : 0) + y_offset,
- render_arrow_width,
- render_arrow_height,
- 0.95f,
- max_scale)
- .render(d2d_dc);
- }
- else
- {
- // same as above - make room for the hidden arrow
- auto render_arrow_height = static_cast(button.height * 1.2f);
- auto render_arrow_width = static_cast(render_arrow_height / arrow_ratio);
- arrow.resize((dx == -1 ? button.x - render_arrow_width : 0) + x_offset,
- (button.y + (button.height - render_arrow_height) / 2) + y_offset,
- render_arrow_width,
- render_arrow_height,
- 0.95f,
- max_scale)
- .render(d2d_dc);
- }
-}
-
-bool D2DOverlayWindow::show_thumbnail(const RECT& rect, double alpha)
-{
- if (!thumbnail)
- {
- return false;
- }
- DWM_THUMBNAIL_PROPERTIES thumb_properties;
- thumb_properties.dwFlags = DWM_TNP_SOURCECLIENTAREAONLY | DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY;
- thumb_properties.fSourceClientAreaOnly = FALSE;
- thumb_properties.fVisible = TRUE;
- thumb_properties.opacity = static_cast(255 * alpha);
- thumb_properties.rcDestination = rect;
- if (DwmUpdateThumbnailProperties(thumbnail, &thumb_properties) != S_OK)
- {
- return false;
- }
- return true;
-}
-
-void D2DOverlayWindow::hide_thumbnail()
-{
- DWM_THUMBNAIL_PROPERTIES thumb_properties;
- thumb_properties.dwFlags = DWM_TNP_VISIBLE;
- thumb_properties.fVisible = FALSE;
- DwmUpdateThumbnailProperties(thumbnail, &thumb_properties);
-}
-
-void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context)
-{
- if (!hidden && !overlay_window_instance->overlay_visible())
- {
- hide();
- return;
- }
-
- d2d_device_context->Clear();
- int taskbar_icon_shortcuts_x_offset = 0, taskbar_icon_shortcuts_y_offset = 0;
-
- double current_background_anim_value = background_animation.value(Animation::AnimFunctions::LINEAR);
- double current_global_windows_shortcuts_anim_value = global_windows_shortcuts_animation.value(Animation::AnimFunctions::LINEAR);
- double pos_global_windows_shortcuts_anim_value = 1 - global_windows_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
- double pos_taskbar_icon_shortcuts_anim_value = 1 - taskbar_icon_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO);
-
- // Draw background
- SetLayeredWindowAttributes(hwnd, 0, static_cast(255 * current_background_anim_value), LWA_ALPHA);
- winrt::com_ptr brush;
- float brush_opacity = get_overlay_opacity();
- D2D1_COLOR_F brushColor = light_mode ? D2D1::ColorF(1.0f, 1.0f, 1.0f, brush_opacity) : D2D1::ColorF(0, 0, 0, brush_opacity);
- winrt::check_hresult(d2d_device_context->CreateSolidColorBrush(brushColor, brush.put()));
- D2D1_RECT_F background_rect = {};
- background_rect.bottom = static_cast(window_height);
- background_rect.right = static_cast(window_width);
- d2d_device_context->SetTransform(D2D1::Matrix3x2F::Identity());
- d2d_device_context->FillRectangle(background_rect, brush.get());
-
- // Draw the taskbar shortcuts (the arrows with numbers)
- if (taskbar_icon_shortcuts_shown)
- {
- if (!tasklist_buttons.empty())
- {
- if (tasklist_buttons[0].x <= window_rect.left)
- {
- // taskbar on left
- taskbar_icon_shortcuts_x_offset = static_cast