diff --git a/PowerToys.sln b/PowerToys.sln
index 804a9ac60e..f24422ffec 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -718,6 +718,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShortcutGuide.IndexYmlGenerator", "src\modules\ShortcutGuideV2\ShortcutGuide.IndexYmlGenerator\ShortcutGuide.IndexYmlGenerator\ShortcutGuide.IndexYmlGenerator.csproj", "{D640B00C-9149-4C4F-8B7D-E2B8B2590A0B}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuide.CPPProject", "src\modules\ShortcutGuideV2\ShortcutGuide.CPPProject\ShortcutGuide.CPPProject.vcxproj", "{C992FD2C-83B8-4941-9FC1-09730068D8EC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2632,6 +2634,14 @@ Global
{D640B00C-9149-4C4F-8B7D-E2B8B2590A0B}.Release|ARM64.Build.0 = Release|ARM64
{D640B00C-9149-4C4F-8B7D-E2B8B2590A0B}.Release|x64.ActiveCfg = Release|x64
{D640B00C-9149-4C4F-8B7D-E2B8B2590A0B}.Release|x64.Build.0 = Release|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|ARM64.ActiveCfg = Debug|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Debug|ARM64.Build.0 = Debug|x64
+ {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|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|ARM64.Build.0 = Release|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|x64.ActiveCfg = Release|x64
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2908,6 +2918,7 @@ Global
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
{D640B00C-9149-4C4F-8B7D-E2B8B2590A0B} = {21275C57-1C20-4F07-8A56-21BFA8363BDB}
+ {C992FD2C-83B8-4941-9FC1-09730068D8EC} = {21275C57-1C20-4F07-8A56-21BFA8363BDB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj
index 045be94f2b..4bb709f180 100644
--- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj
+++ b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj
@@ -65,14 +65,12 @@
-
-
@@ -83,13 +81,9 @@
-
- Create
-
-
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj.filters b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj.filters
index 006a6196ef..af14da58f3 100644
--- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj.filters
+++ b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj.filters
@@ -18,9 +18,6 @@
-
- Header Files
-
Header Files
@@ -51,9 +48,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -68,9 +62,6 @@
-
- Source Files
-
Source Files
@@ -101,9 +92,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -116,7 +104,79 @@
-
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
Resource Files
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/pch.h b/src/modules/ShortcutGuide/ShortcutGuide/pch.h
index 9ec2bbcb41..9bff3b54c1 100644
--- a/src/modules/ShortcutGuide/ShortcutGuide/pch.h
+++ b/src/modules/ShortcutGuide/ShortcutGuide/pch.h
@@ -25,5 +25,4 @@
#include
#include
#include
-#include
-#include
\ No newline at end of file
+#include
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp b/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp
index 687fc86566..4572f412b3 100644
--- a/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp
+++ b/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp
@@ -1,116 +1,193 @@
#include "pch.h"
#include "tasklist_positions.h"
+#include
+#include
+#include
+#include
-void Tasklist::update()
+extern "C"
{
- // Get HWND of the tasklist
- auto tasklist_hwnd = FindWindowA("Shell_TrayWnd", nullptr);
- if (!tasklist_hwnd)
- return;
- tasklist_hwnd = FindWindowExA(tasklist_hwnd, 0, "ReBarWindow32", nullptr);
- if (!tasklist_hwnd)
- return;
- tasklist_hwnd = FindWindowExA(tasklist_hwnd, 0, "MSTaskSwWClass", nullptr);
- if (!tasklist_hwnd)
- return;
- tasklist_hwnd = FindWindowExA(tasklist_hwnd, 0, "MSTaskListWClass", nullptr);
- if (!tasklist_hwnd)
- 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()));
-}
+ winrt::com_ptr automation;
+ winrt::com_ptr element;
+ winrt::com_ptr true_condition;
-bool Tasklist::update_buttons(std::vector& buttons)
-{
- if (!automation || !element)
+ // Helper to get the taskbar HWND for the monitor under the cursor
+ HWND GetTaskbarHwndForCursorMonitor(HMONITOR monitor)
{
- 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)
+ POINT pt;
+ if (!GetCursorPos(&pt))
+ return nullptr;
+
+ // Find the primary taskbar
+ HWND primaryTaskbar = FindWindowW(L"Shell_TrayWnd", nullptr);
+ if (primaryTaskbar)
{
- if (var_rect.vt == (VT_R8 | VT_ARRAY))
+ MONITORINFO mi = { sizeof(mi) };
+ if (GetWindowRect(primaryTaskbar, &mi.rcMonitor))
{
- 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);
+ HMONITOR primaryMonitor = MonitorFromRect(&mi.rcMonitor, MONITOR_DEFAULTTONEAREST);
+ if (primaryMonitor == monitor)
+ return primaryTaskbar;
}
- VariantClear(&var_rect);
+ }
+
+ // 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 monitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);
+ if (monitor == monitor)
+ return secondaryTaskbar;
+ }
+ }
+
+ return nullptr;
+ }
+
+ __declspec(dllexport) 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()));
+ }
+
+ __declspec(dllexport) bool update_buttons(std::vector& buttons)
+ {
+ if (!automation || !element)
{
return false;
}
- if (BSTR automation_id; child->get_CurrentAutomationId(&automation_id) >= 0)
+ 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)
{
- button.name = automation_id;
- SysFreeString(automation_id);
+ 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);
}
- 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;
}
- // assign keynums
- buttons.clear();
- for (auto& button : found_buttons)
+
+ __declspec(dllexport) TasklistButton* get_buttons(HMONITOR monitor, int* size)
{
- 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 (button.name == buttons.back().name)
- 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
- }
+ update(monitor);
+ static std::vector buttons;
+ update_buttons(buttons);
+ *size = static_cast(buttons.size());
+ return buttons.data();
}
- return true;
}
-std::vector Tasklist::get_buttons()
-{
- std::vector buttons;
- update_buttons(buttons);
- return buttons;
-}
\ No newline at end of file
diff --git a/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.h b/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.h
index 4ac5bed8a9..cf71ee838f 100644
--- a/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.h
+++ b/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.h
@@ -7,23 +7,10 @@
struct TasklistButton
{
- std::wstring name;
- long x{};
- long y{};
- long width{};
- long height{};
- long keynum{};
-};
-
-class Tasklist
-{
-public:
- void update();
- std::vector get_buttons();
- bool update_buttons(std::vector& buttons);
-
-private:
- winrt::com_ptr automation;
- winrt::com_ptr element;
- winrt::com_ptr true_condition;
+ wchar_t name[256];
+ int x;
+ int y;
+ int width;
+ int height;
+ int keynum;
};
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj b/src/modules/ShortcutGuideV2/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj
new file mode 100644
index 0000000000..dcfe871e5b
--- /dev/null
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj
@@ -0,0 +1,149 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 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
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
+
+
+ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj.filters b/src/modules/ShortcutGuideV2/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj.filters
new file mode 100644
index 0000000000..da95b2b982
--- /dev/null
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.CPPProject/ShortcutGuide.CPPProject.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {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
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Shell.en-US.yml b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Shell.en-US.yml
index cf7aa4b240..0e3913b0c5 100644
--- a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Shell.en-US.yml
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/Assets/ShortcutGuide/+WindowsNT.Shell.en-US.yml
@@ -12,6 +12,21 @@ Shortcuts:
Alt: true
Keys:
- F4
+ - Name: Test shortcut
+ Shortcut:
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - K
+ - Win: false
+ Ctrl: true
+ Shift: false
+ Alt: false
+ Keys:
+ - C
+
- Name: Open shutdown box
Description: When no windows are open
Shortcut:
@@ -29,12 +44,6 @@ Shortcuts:
Alt: true
Keys:
- Esc
- - Win: false
- Ctrl: false
- Shift: false
- Alt: true
- Keys:
- - Esc
- Name: Reveal typed password
Description: On sign-in screen
Shortcut:
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/DisplayHelper.cs b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/DisplayHelper.cs
index 5df8283b8f..a41d0a9b1b 100644
--- a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/DisplayHelper.cs
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/DisplayHelper.cs
@@ -19,6 +19,7 @@ namespace ShortcutGuide
public static Rect GetWorkAreaForDisplayWithWindow(IntPtr hwnd)
{
foundMonitorIndex = -1;
+ monitorIndex = 0;
var monitor = MonitorFromWindow(hwnd, (int)MonitorFromWindowDwFlags.MONITOR_DEFAULTTONEAREST);
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumProc, new LPARAM(monitor));
return MonitorInfo.GetDisplayMonitors()[foundMonitorIndex].RectWork;
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/Helpers/TaskListPositions.cs b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/Helpers/TaskListPositions.cs
deleted file mode 100644
index b408d8e3e2..0000000000
--- a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/Helpers/TaskListPositions.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-
-namespace ShortcutGuide.Helpers
-{
- internal sealed class TaskListPositions
- {
- public static List GetTaskbarIconPositions()
- {
- throw new NotImplementedException();
- }
-
- [StructLayout(LayoutKind.Sequential)]
- public struct RECT
- {
- public int Left;
- public int Top;
- public int Right;
- public int Bottom;
- }
- }
-}
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/NativeMethods.cs b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/NativeMethods.cs
index e16b8ffb71..466551d453 100644
--- a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/NativeMethods.cs
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/NativeMethods.cs
@@ -26,10 +26,10 @@ internal static partial class NativeMethods
internal static partial int SetWindowLongW(IntPtr hWnd, int nIndex, int dwNewLong);
[LibraryImport("user32.dll", StringMarshalling = StringMarshalling.Utf16)]
- internal static partial int FindWindowA(in string lpClassName, in string? lpWindowName);
+ internal static partial IntPtr FindWindowA(in string lpClassName, in string? lpWindowName);
[LibraryImport("user32.dll", StringMarshalling = StringMarshalling.Utf16)]
- internal static partial int FindWindowExA(int hwndParent, int hwndChildAfter, in string lpClassName, in string? lpWindowName);
+ public static partial IntPtr FindWindowExA(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[LibraryImport("User32.dll")]
internal static partial IntPtr MonitorFromWindow(int hwnd, int dwFlags);
@@ -47,6 +47,14 @@ internal static partial class NativeMethods
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
+ [DllImport("ole32.dll", SetLastError = true)]
+ public static extern uint CoCreateInstance(
+ in Guid rclsid,
+ IntPtr pUnkOuter,
+ uint dwClsContext,
+ in Guid riid,
+ out IntPtr ppv);
+
public struct POINT
{
public int X;
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
index cba7566b17..911ac0502f 100644
--- a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj
@@ -60,6 +60,17 @@
+
+
+ tlbimp
+ 0
+ 1
+ 944de083-8fb8-45cf-bcb7-c477acb2f897
+ 0
+ false
+ true
+
+
@@ -95,6 +106,7 @@
+
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs
index c0440cb735..a1561262a0 100644
--- a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs
@@ -28,6 +28,8 @@ namespace ShortcutGuide
///
public sealed partial class MainWindow : WindowEx
{
+ public static nint WindowHwnd { get; set; }
+
private AppWindow _appWindow;
private string[] _currentApplicationIds;
@@ -41,6 +43,7 @@ namespace ShortcutGuide
Title = Resource.ResourceManager.GetString("Title", CultureInfo.InvariantCulture)!;
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
+ WindowHwnd = hwnd;
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
_appWindow = AppWindow.GetFromWindowId(windowId);
#if !DEBUG
@@ -75,7 +78,9 @@ namespace ShortcutGuide
{
if (e.WindowActivationState == WindowActivationState.Deactivated)
{
+#if !DEBUG
Environment.Exit(0);
+#endif
}
if (!_setPosition)
@@ -96,6 +101,16 @@ namespace ShortcutGuide
// 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)
+ {
+ 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))));
+ }
+ };
}
if (WindowSelector.Items.Count == 0)
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml
index 803178f318..971809d29b 100644
--- a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml
@@ -38,6 +38,7 @@
+
@@ -78,5 +79,47 @@
+
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml.cs b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml.cs
index ffbd507880..7ad99dcc3f 100644
--- a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml.cs
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/ShortcutGuideXAML/ShortcutView.xaml.cs
@@ -7,13 +7,16 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.Json;
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.Media;
-using ShortcutGuide;
+using Microsoft.UI.Xaml.Media.Animation;
+using Microsoft.UI.Xaml.Shapes;
using ShortcutGuide.Models;
using Windows.Foundation;
@@ -21,6 +24,8 @@ namespace ShortcutGuide
{
public sealed partial class ShortcutView : Page, INotifyPropertyChanged
{
+ private readonly DispatcherTimer _taskbarUpdateTimer = new() { Interval = TimeSpan.FromMilliseconds(500) };
+ private bool _showTaskbarShortcuts = true;
private ShortcutFile shortcutList = ManifestInterpreter.GetShortcutsOfApplication(ShortcutPageParameters.CurrentPageName);
public ShortcutView()
@@ -41,7 +46,7 @@ namespace ShortcutGuide
switch (category.SectionName)
{
case string name when name.StartsWith("", StringComparison.Ordinal):
- // Todo: Implement GetTaskbarIconPositions
+ _showTaskbarShortcuts = true;
break;
case string name when name.StartsWith('<') && name.EndsWith('>'):
break;
@@ -69,6 +74,13 @@ namespace ShortcutGuide
ShortcutPageParameters.PinnedShortcuts.Add(ShortcutPageParameters.CurrentPageName, []);
}
+ if (_showTaskbarShortcuts)
+ {
+ TaskbarIndicators.Visibility = Visibility.Visible;
+ _taskbarUpdateTimer.Tick += UpdateTaskbarIndicators;
+ _taskbarUpdateTimer.Start();
+ }
+
OpenOverview();
}
catch (Exception)
@@ -79,6 +91,59 @@ namespace ShortcutGuide
}
}
+ private void UpdateTaskbarIndicators(object? sender, object? e)
+ {
+ var buttons = TasklistPositions.GetButtons();
+ Canvas[] canvases = [
+ TaskbarIndicator1,
+ TaskbarIndicator2,
+ TaskbarIndicator3,
+ TaskbarIndicator4,
+ TaskbarIndicator5,
+ TaskbarIndicator6,
+ TaskbarIndicator7,
+ TaskbarIndicator8,
+ TaskbarIndicator9,
+ TaskbarIndicator10,
+ ];
+
+ for (int i = 0; i < canvases.Length; i++)
+ {
+ if (i < buttons.Length)
+ {
+ canvases[i].Visibility = Visibility.Visible;
+ Rect workArea = DisplayHelper.GetWorkAreaForDisplayWithWindow(MainWindow.WindowHwnd);
+ DoubleAnimation animation = new DoubleAnimation
+ {
+ To = (buttons[i].X - workArea.Left) / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32()),
+ Duration = TimeSpan.FromMilliseconds(500),
+ };
+
+ // Create the storyboard
+ Storyboard storyboard = new Storyboard();
+ storyboard.Children.Add(animation);
+
+ // Set the target and property
+ Storyboard.SetTarget(animation, canvases[i]);
+ Storyboard.SetTargetProperty(animation, "(Canvas.Left)");
+
+ // Start the animation
+ storyboard.Begin();
+
+ canvases[i].Width = buttons[i].Width / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
+ canvases[i].Height = buttons[i].Height / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
+ ((Rectangle)canvases[i].Children[0]).Width = buttons[i].Width / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
+ ((Rectangle)canvases[i].Children[0]).Height = buttons[i].Height / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
+ ((TextBlock)canvases[i].Children[1]).Width = buttons[i].Width / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
+ ((TextBlock)canvases[i].Children[1]).Height = buttons[i].Height / DpiHelper.GetDPIScaleForWindow(MainWindow.WindowHwnd.ToInt32());
+ }
+ else
+ {
+ canvases[i].Visibility = Visibility.Collapsed;
+ }
+ }
+ }
+
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
diff --git a/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/TasklistPositions.cs b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/TasklistPositions.cs
new file mode 100644
index 0000000000..e924348802
--- /dev/null
+++ b/src/modules/ShortcutGuideV2/ShortcutGuide.Ui/TasklistPositions.cs
@@ -0,0 +1,188 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using UIAutomationClient;
+
+namespace ShortcutGuide
+{
+ internal sealed partial class TasklistPositions
+ {
+ [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;
+ }
+
+ /*
+ private IUIAutomation? _automation;
+ private IUIAutomationElement? _element;
+ private IUIAutomationCondition? _trueCondition;
+ public void Update()
+ {
+
+ // Get HWND of the tasklist
+ var tasklistHwnd = NativeMethods.FindWindowA("Shell_TrayWnd", null);
+ if (tasklistHwnd == IntPtr.Zero)
+ {
+ return;
+ }
+
+ tasklistHwnd = NativeMethods.FindWindowExA(tasklistHwnd, IntPtr.Zero, "ReBarWindow32", string.Empty);
+ if (tasklistHwnd == IntPtr.Zero)
+ {
+ return;
+ }
+
+ tasklistHwnd = NativeMethods.FindWindowExA(tasklistHwnd, IntPtr.Zero, "MSTaskSwWClass", string.Empty);
+ if (tasklistHwnd == IntPtr.Zero)
+ {
+ return;
+ }
+
+ tasklistHwnd = NativeMethods.FindWindowExA(tasklistHwnd, IntPtr.Zero, "MSTaskListWClass", string.Empty);
+ if (tasklistHwnd == IntPtr.Zero)
+ {
+ return;
+ }
+
+ if (_automation == null)
+ {
+ _automation = new CUIAutomation();
+ _trueCondition = _automation.CreateTrueCondition();
+ }
+
+ _element = null;
+ _element = _automation.ElementFromHandle(tasklistHwnd);
+ }
+
+ public bool UpdateButtons(List buttons)
+ {
+ if (_automation == null || _element == null)
+ {
+ return false;
+ }
+
+ IUIAutomationElementArray elements = _element.FindAll(TreeScope.TreeScope_Children, _trueCondition);
+ if (elements == null)
+ {
+ return false;
+ }
+
+ int count = elements.Length;
+ var foundButtons = new List(count);
+
+ for (int i = 0; i < count; ++i)
+ {
+ var child = elements.GetElement(i);
+ var button = default(TasklistButton);
+
+ object rectObj = child.GetCurrentPropertyValue(30001);
+ if (rectObj is double[] arr && arr.Length == 4)
+ {
+ button.X = (long)arr[0];
+ button.Y = (long)arr[1];
+ button.Width = (long)arr[2];
+ button.Height = (long)arr[3];
+ }
+ else if (rectObj is Windows.Foundation.Rect wrect)
+ {
+ button.X = (long)wrect.X;
+ button.Y = (long)wrect.Y;
+ button.Width = (long)wrect.Width;
+ button.Height = (long)wrect.Height;
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine($"rectObj type: {rectObj?.GetType()} value: {rectObj}");
+ continue; // Don't return false, just skip
+ }
+
+ object nameObj = child.GetCurrentPropertyValue(30011);
+ button.Name = nameObj as string ?? string.Empty;
+
+ foundButtons.Add(button);
+ }
+
+ // assign keynums
+ buttons.Clear();
+ foreach (var button in foundButtons)
+ {
+ if (buttons.Count == 0)
+ {
+ var b = button;
+ b.KeyNumber = 1;
+ buttons.Add(b);
+ }
+ else
+ {
+ var last = buttons[^1];
+ if (button.X < last.X || button.Y < last.Y)
+ {
+ break;
+ }
+
+ if (button.Name == last.Name)
+ {
+ continue;
+ }
+
+ var b = button;
+ b.KeyNumber = last.KeyNumber + 1;
+ buttons.Add(b);
+ if (b.KeyNumber == 10)
+ {
+ break;
+ }
+ }
+ }
+
+ return true;
+ }*/
+
+ [DllImport("ShortcutGuide.CPPProject.dll", EntryPoint = "get_buttons")]
+ public static extern IntPtr GetTasklistButtons(nint monitor, out int size);
+
+ [LibraryImport("User32.dll")]
+ private static partial IntPtr MonitorFromWindow(nint hwnd, int dwFlags);
+
+ public static TasklistButton[] GetButtons()
+ {
+ var monitor = MonitorFromWindow(MainWindow.WindowHwnd, 0);
+ IntPtr ptr = GetTasklistButtons(monitor, out int size);
+ if (ptr == IntPtr.Zero)
+ {
+ return [];
+ }
+
+ if (size <= 0)
+ {
+ return [];
+ }
+
+ TasklistButton[] buttons = new TasklistButton[size];
+ IntPtr currentPtr = ptr;
+ for (int i = 0; i < size; i++)
+ {
+ buttons[i] = Marshal.PtrToStructure(currentPtr);
+ currentPtr += Marshal.SizeOf();
+ }
+
+ return buttons;
+ }
+ }
+}