diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index 91df199e2c..94e32fc8ee 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -365,6 +365,7 @@ DEFAULTICON
defaultlib
DEFAULTONLY
DEFAULTTONEAREST
+Defaulttonearest
DEFAULTTONULL
DEFAULTTOPRIMARY
DEFERERASE
@@ -868,6 +869,7 @@ lastcodeanalysissucceeded
LASTEXITCODE
LAYOUTRTL
lbl
+Lbuttondown
LCh
lcid
LCIDTo
@@ -990,6 +992,7 @@ maxversiontested
mber
MBM
MBR
+Mbuttondown
MDICHILD
MDL
mdtext
@@ -1448,6 +1451,7 @@ RAWINPUTHEADER
RAWMODE
RAWPATH
rbhid
+Rbuttondown
rclsid
RCZOOMIT
remotedesktop
@@ -1753,6 +1757,7 @@ svgz
SVSI
SWFO
SWP
+Swp
SWPNOSIZE
SWPNOZORDER
SWRESTORE
@@ -1772,6 +1777,7 @@ syskeydown
SYSKEYUP
SYSLIB
SYSMENU
+Sysmenu
systemai
SYSTEMAPPS
SYSTEMMODAL
@@ -2097,6 +2103,7 @@ Wwanpp
xap
XAxis
XButton
+Xbuttondown
xclip
xcopy
XDeployment
diff --git a/PowerToys.slnx b/PowerToys.slnx
index ac006fdbf4..70786b58fb 100644
--- a/PowerToys.slnx
+++ b/PowerToys.slnx
@@ -1005,6 +1005,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/common/ManagedCommon/ModuleType.cs b/src/common/ManagedCommon/ModuleType.cs
index d7ae386191..8461b4a6d8 100644
--- a/src/common/ManagedCommon/ModuleType.cs
+++ b/src/common/ManagedCommon/ModuleType.cs
@@ -36,5 +36,6 @@ namespace ManagedCommon
PowerOCR,
Workspaces,
ZoomIt,
+ GeneralSettings,
}
}
diff --git a/src/common/SettingsAPI/settings_objects.h b/src/common/SettingsAPI/settings_objects.h
index 84b064d5af..8927ba5657 100644
--- a/src/common/SettingsAPI/settings_objects.h
+++ b/src/common/SettingsAPI/settings_objects.h
@@ -119,6 +119,16 @@ namespace PowerToysSettings
class HotkeyObject
{
public:
+ HotkeyObject() :
+ m_json(json::JsonObject())
+ {
+ m_json.SetNamedValue(L"win", json::value(false));
+ m_json.SetNamedValue(L"ctrl", json::value(false));
+ m_json.SetNamedValue(L"alt", json::value(false));
+ m_json.SetNamedValue(L"shift", json::value(false));
+ m_json.SetNamedValue(L"code", json::value(0));
+ m_json.SetNamedValue(L"key", json::value(L""));
+ }
static HotkeyObject from_json(json::JsonObject json)
{
return HotkeyObject(std::move(json));
diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp
index c6770731a6..2ae775a0fc 100644
--- a/src/runner/general_settings.cpp
+++ b/src/runner/general_settings.cpp
@@ -2,6 +2,7 @@
#include "general_settings.h"
#include "auto_start_helper.h"
#include "tray_icon.h"
+#include "quick_access_host.h"
#include "Generated files/resource.h"
#include "hotkey_conflict_detector.h"
@@ -72,6 +73,8 @@ static bool download_updates_automatically = true;
static bool show_whats_new_after_updates = true;
static bool enable_experimentation = true;
static bool enable_warnings_elevated_apps = true;
+static bool enable_quick_access = true;
+static PowerToysSettings::HotkeyObject quick_access_shortcut;
static DashboardSortOrder dashboard_sort_order = DashboardSortOrder::Alphabetical;
static json::JsonObject ignored_conflict_properties = create_default_ignored_conflict_properties();
@@ -105,6 +108,8 @@ json::JsonObject GeneralSettings::to_json()
result.SetNamedValue(L"dashboard_sort_order", json::value(static_cast(dashboardSortOrder)));
result.SetNamedValue(L"is_admin", json::value(isAdmin));
result.SetNamedValue(L"enable_warnings_elevated_apps", json::value(enableWarningsElevatedApps));
+ result.SetNamedValue(L"enable_quick_access", json::value(enableQuickAccess));
+ result.SetNamedValue(L"quick_access_shortcut", quickAccessShortcut.get_json());
result.SetNamedValue(L"theme", json::value(theme));
result.SetNamedValue(L"system_theme", json::value(systemTheme));
result.SetNamedValue(L"powertoys_version", json::value(powerToysVersion));
@@ -127,6 +132,11 @@ json::JsonObject load_general_settings()
show_whats_new_after_updates = loaded.GetNamedBoolean(L"show_whats_new_after_updates", true);
enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation", true);
enable_warnings_elevated_apps = loaded.GetNamedBoolean(L"enable_warnings_elevated_apps", true);
+ enable_quick_access = loaded.GetNamedBoolean(L"enable_quick_access", true);
+ if (json::has(loaded, L"quick_access_shortcut", json::JsonValueType::Object))
+ {
+ quick_access_shortcut = PowerToysSettings::HotkeyObject::from_json(loaded.GetNamedObject(L"quick_access_shortcut"));
+ }
dashboard_sort_order = parse_dashboard_sort_order(loaded, dashboard_sort_order);
if (json::has(loaded, L"ignored_conflict_properties", json::JsonValueType::Object))
@@ -153,6 +163,8 @@ GeneralSettings get_general_settings()
.isRunElevated = run_as_elevated,
.isAdmin = is_user_admin,
.enableWarningsElevatedApps = enable_warnings_elevated_apps,
+ .enableQuickAccess = enable_quick_access,
+ .quickAccessShortcut = quick_access_shortcut,
.showNewUpdatesToastNotification = show_new_updates_toast_notification,
.downloadUpdatesAutomatically = download_updates_automatically && is_user_admin,
.showWhatsNewAfterUpdates = show_whats_new_after_updates,
@@ -178,11 +190,47 @@ GeneralSettings get_general_settings()
void apply_general_settings(const json::JsonObject& general_configs, bool save)
{
+ std::wstring old_settings_json_string;
+ if (save)
+ {
+ old_settings_json_string = get_general_settings().to_json().Stringify().c_str();
+ }
+
Logger::info(L"apply_general_settings: {}", std::wstring{ general_configs.ToString() });
run_as_elevated = general_configs.GetNamedBoolean(L"run_elevated", false);
enable_warnings_elevated_apps = general_configs.GetNamedBoolean(L"enable_warnings_elevated_apps", true);
+ bool new_enable_quick_access = general_configs.GetNamedBoolean(L"enable_quick_access", true);
+ Logger::info(L"apply_general_settings: enable_quick_access={}, new_enable_quick_access={}", enable_quick_access, new_enable_quick_access);
+
+ PowerToysSettings::HotkeyObject new_quick_access_shortcut;
+ if (json::has(general_configs, L"quick_access_shortcut", json::JsonValueType::Object))
+ {
+ new_quick_access_shortcut = PowerToysSettings::HotkeyObject::from_json(general_configs.GetNamedObject(L"quick_access_shortcut"));
+ }
+
+ auto hotkey_equals = [](const PowerToysSettings::HotkeyObject& a, const PowerToysSettings::HotkeyObject& b) {
+ return a.get_code() == b.get_code() &&
+ a.get_modifiers() == b.get_modifiers();
+ };
+
+ if (enable_quick_access != new_enable_quick_access || !hotkey_equals(quick_access_shortcut, new_quick_access_shortcut))
+ {
+ enable_quick_access = new_enable_quick_access;
+ quick_access_shortcut = new_quick_access_shortcut;
+
+ if (enable_quick_access)
+ {
+ QuickAccessHost::start();
+ }
+ else
+ {
+ QuickAccessHost::stop();
+ }
+ update_quick_access_hotkey(enable_quick_access, quick_access_shortcut);
+ }
+
show_new_updates_toast_notification = general_configs.GetNamedBoolean(L"show_new_updates_toast_notification", true);
download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true);
@@ -321,8 +369,12 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save)
if (save)
{
GeneralSettings save_settings = get_general_settings();
- PTSettingsHelper::save_general_settings(save_settings.to_json());
- Trace::SettingsChanged(save_settings);
+ std::wstring new_settings_json_string = save_settings.to_json().Stringify().c_str();
+ if (old_settings_json_string != new_settings_json_string)
+ {
+ PTSettingsHelper::save_general_settings(save_settings.to_json());
+ Trace::SettingsChanged(save_settings);
+ }
}
}
@@ -412,3 +464,5 @@ void start_enabled_powertoys()
}
}
}
+
+
diff --git a/src/runner/general_settings.h b/src/runner/general_settings.h
index b4f7638846..033f75b087 100644
--- a/src/runner/general_settings.h
+++ b/src/runner/general_settings.h
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
enum class DashboardSortOrder
{
@@ -18,6 +19,8 @@ struct GeneralSettings
bool isRunElevated;
bool isAdmin;
bool enableWarningsElevatedApps;
+ bool enableQuickAccess;
+ PowerToysSettings::HotkeyObject quickAccessShortcut;
bool showNewUpdatesToastNotification;
bool downloadUpdatesAutomatically;
bool showWhatsNewAfterUpdates;
diff --git a/src/runner/main.cpp b/src/runner/main.cpp
index 2ceba89161..d9da20d93c 100644
--- a/src/runner/main.cpp
+++ b/src/runner/main.cpp
@@ -37,6 +37,7 @@
#include
#include "centralized_kb_hook.h"
#include "centralized_hotkeys.h"
+#include "quick_access_host.h"
#include "ai_detection.h"
#include
@@ -189,6 +190,11 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
#endif
Trace::RegisterProvider();
start_tray_icon(isProcessElevated);
+ if (get_general_settings().enableQuickAccess)
+ {
+ QuickAccessHost::start();
+ }
+ update_quick_access_hotkey(get_general_settings().enableQuickAccess, get_general_settings().quickAccessShortcut);
set_tray_icon_visible(get_general_settings().showSystemTrayIcon);
CentralizedKeyboardHook::Start();
@@ -316,7 +322,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
{
window = winrt::to_hstring(settingsWindow);
}
- open_settings_window(window, false);
+ open_settings_window(window);
}
if (openOobe)
@@ -339,6 +345,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
result = -1;
}
Trace::UnregisterProvider();
+ QuickAccessHost::stop();
return result;
}
diff --git a/src/runner/quick_access_host.cpp b/src/runner/quick_access_host.cpp
new file mode 100644
index 0000000000..609b8f2f36
--- /dev/null
+++ b/src/runner/quick_access_host.cpp
@@ -0,0 +1,269 @@
+#include "pch.h"
+#include "quick_access_host.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+extern void receive_json_send_to_main_thread(const std::wstring& msg);
+
+namespace
+{
+ wil::unique_handle quick_access_process;
+ wil::unique_handle show_event;
+ wil::unique_handle exit_event;
+ std::wstring show_event_name;
+ std::wstring exit_event_name;
+ std::wstring runner_pipe_name;
+ std::wstring app_pipe_name;
+ std::unique_ptr quick_access_ipc;
+ std::mutex quick_access_mutex;
+
+ bool is_process_active_locked()
+ {
+ if (!quick_access_process)
+ {
+ return false;
+ }
+
+ DWORD exit_code = 0;
+ if (!GetExitCodeProcess(quick_access_process.get(), &exit_code))
+ {
+ Logger::warn(L"QuickAccessHost: failed to read Quick Access process exit code. error={}.", GetLastError());
+ return false;
+ }
+
+ return exit_code == STILL_ACTIVE;
+ }
+
+ void reset_state_locked()
+ {
+ if (quick_access_ipc)
+ {
+ quick_access_ipc->end();
+ quick_access_ipc.reset();
+ }
+
+ quick_access_process.reset();
+ show_event.reset();
+ exit_event.reset();
+ show_event_name.clear();
+ exit_event_name.clear();
+ runner_pipe_name.clear();
+ app_pipe_name.clear();
+ }
+
+ std::wstring build_event_name(const wchar_t* suffix)
+ {
+ std::wstring name = L"Local\\PowerToysQuickAccess_";
+ name += std::to_wstring(GetCurrentProcessId());
+ if (suffix)
+ {
+ name += suffix;
+ }
+ return name;
+ }
+
+ std::wstring build_command_line(const std::wstring& exe_path)
+ {
+ std::wstring command_line = L"\"";
+ command_line += exe_path;
+ command_line += L"\" --show-event=\"";
+ command_line += show_event_name;
+ command_line += L"\" --exit-event=\"";
+ command_line += exit_event_name;
+ command_line += L"\"";
+ if (!runner_pipe_name.empty())
+ {
+ command_line.append(L" --runner-pipe=\"");
+ command_line += runner_pipe_name;
+ command_line += L"\"";
+ }
+ if (!app_pipe_name.empty())
+ {
+ command_line.append(L" --app-pipe=\"");
+ command_line += app_pipe_name;
+ command_line += L"\"";
+ }
+ return command_line;
+ }
+}
+
+namespace QuickAccessHost
+{
+ bool is_running()
+ {
+ std::scoped_lock lock(quick_access_mutex);
+ return is_process_active_locked();
+ }
+
+ void start()
+ {
+ Logger::info(L"QuickAccessHost::start() called");
+ std::scoped_lock lock(quick_access_mutex);
+ if (is_process_active_locked())
+ {
+ Logger::info(L"QuickAccessHost::start: process already active");
+ return;
+ }
+
+ reset_state_locked();
+
+ show_event_name = build_event_name(L"_Show");
+ exit_event_name = build_event_name(L"_Exit");
+
+ show_event.reset(CreateEventW(nullptr, FALSE, FALSE, show_event_name.c_str()));
+ if (!show_event)
+ {
+ Logger::error(L"QuickAccessHost: failed to create show event. error={}.", GetLastError());
+ reset_state_locked();
+ return;
+ }
+
+ exit_event.reset(CreateEventW(nullptr, FALSE, FALSE, exit_event_name.c_str()));
+ if (!exit_event)
+ {
+ Logger::error(L"QuickAccessHost: failed to create exit event. error={}.", GetLastError());
+ reset_state_locked();
+ return;
+ }
+
+ runner_pipe_name = L"\\\\.\\pipe\\powertoys_quick_access_runner_";
+ app_pipe_name = L"\\\\.\\pipe\\powertoys_quick_access_ui_";
+ UUID temp_uuid;
+ wchar_t* uuid_chars = nullptr;
+ if (UuidCreate(&temp_uuid) == RPC_S_UUID_NO_ADDRESS)
+ {
+ Logger::warn(L"QuickAccessHost: failed to create UUID for pipe names. error={}.", GetLastError());
+ }
+ else if (UuidToString(&temp_uuid, reinterpret_cast(&uuid_chars)) != RPC_S_OK)
+ {
+ Logger::warn(L"QuickAccessHost: failed to convert UUID to string. error={}.", GetLastError());
+ }
+
+ if (uuid_chars != nullptr)
+ {
+ runner_pipe_name += std::wstring(uuid_chars);
+ app_pipe_name += std::wstring(uuid_chars);
+ RpcStringFree(reinterpret_cast(&uuid_chars));
+ uuid_chars = nullptr;
+ }
+ else
+ {
+ const std::wstring fallback_suffix = std::to_wstring(GetTickCount64());
+ runner_pipe_name += fallback_suffix;
+ app_pipe_name += fallback_suffix;
+ }
+
+ HANDLE token_handle = nullptr;
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle))
+ {
+ Logger::error(L"QuickAccessHost: failed to open process token. error={}.", GetLastError());
+ reset_state_locked();
+ return;
+ }
+
+ wil::unique_handle token(token_handle);
+ quick_access_ipc.reset(new (std::nothrow) TwoWayPipeMessageIPC(runner_pipe_name, app_pipe_name, receive_json_send_to_main_thread));
+ if (!quick_access_ipc)
+ {
+ Logger::error(L"QuickAccessHost: failed to allocate IPC instance.");
+ reset_state_locked();
+ return;
+ }
+
+ try
+ {
+ quick_access_ipc->start(token.get());
+ }
+ catch (...)
+ {
+ Logger::error(L"QuickAccessHost: failed to start IPC server for Quick Access.");
+ reset_state_locked();
+ return;
+ }
+
+ const std::wstring exe_path = get_module_folderpath() + L"\\WinUI3Apps\\PowerToys.QuickAccess.exe";
+ if (GetFileAttributesW(exe_path.c_str()) == INVALID_FILE_ATTRIBUTES)
+ {
+ Logger::warn(L"QuickAccessHost: missing Quick Access executable at {}", exe_path);
+ reset_state_locked();
+ return;
+ }
+
+ const std::wstring command_line = build_command_line(exe_path);
+ std::vector command_line_buffer(command_line.begin(), command_line.end());
+ command_line_buffer.push_back(L'\0');
+ STARTUPINFOW startup_info{};
+ startup_info.cb = sizeof(startup_info);
+ PROCESS_INFORMATION process_info{};
+
+ BOOL created = CreateProcessW(exe_path.c_str(), command_line_buffer.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup_info, &process_info);
+ if (!created)
+ {
+ Logger::error(L"QuickAccessHost: failed to launch Quick Access host. error={}.", GetLastError());
+ reset_state_locked();
+ return;
+ }
+
+ quick_access_process.reset(process_info.hProcess);
+ CloseHandle(process_info.hThread);
+ }
+
+ void show()
+ {
+ start();
+ std::scoped_lock lock(quick_access_mutex);
+
+ if (show_event)
+ {
+ if (!SetEvent(show_event.get()))
+ {
+ Logger::warn(L"QuickAccessHost: failed to signal show event. error={}.", GetLastError());
+ }
+ }
+ }
+
+ void stop()
+ {
+ Logger::info(L"QuickAccessHost::stop() called");
+ std::unique_lock lock(quick_access_mutex);
+ if (exit_event)
+ {
+ SetEvent(exit_event.get());
+ }
+
+ if (quick_access_process)
+ {
+ const DWORD wait_result = WaitForSingleObject(quick_access_process.get(), 2000);
+ Logger::info(L"QuickAccessHost::stop: WaitForSingleObject result={}", wait_result);
+ if (wait_result == WAIT_TIMEOUT)
+ {
+ Logger::warn(L"QuickAccessHost: Quick Access process did not exit in time, terminating.");
+ if (!TerminateProcess(quick_access_process.get(), 0))
+ {
+ Logger::error(L"QuickAccessHost: failed to terminate Quick Access process. error={}.", GetLastError());
+ }
+ else
+ {
+ Logger::info(L"QuickAccessHost: TerminateProcess succeeded.");
+ WaitForSingleObject(quick_access_process.get(), 5000);
+ }
+ }
+ else if (wait_result == WAIT_FAILED)
+ {
+ Logger::error(L"QuickAccessHost: failed while waiting for Quick Access process. error={}.", GetLastError());
+ }
+ }
+
+ reset_state_locked();
+ }
+}
diff --git a/src/runner/quick_access_host.h b/src/runner/quick_access_host.h
new file mode 100644
index 0000000000..22a65a9c26
--- /dev/null
+++ b/src/runner/quick_access_host.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include
+#include
+
+namespace QuickAccessHost
+{
+ void start();
+ void show();
+ void stop();
+ bool is_running();
+}
diff --git a/src/runner/runner.vcxproj b/src/runner/runner.vcxproj
index 57cb55b6bd..1bfd036290 100644
--- a/src/runner/runner.vcxproj
+++ b/src/runner/runner.vcxproj
@@ -70,6 +70,7 @@
+
@@ -85,6 +86,7 @@
+
diff --git a/src/runner/runner.vcxproj.filters b/src/runner/runner.vcxproj.filters
index 904e213405..ac5fe3ec36 100644
--- a/src/runner/runner.vcxproj.filters
+++ b/src/runner/runner.vcxproj.filters
@@ -48,6 +48,9 @@
Utils
+
+ Utils
+
@@ -102,6 +105,9 @@
Utils
+
+ Utils
+
diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp
index e31b934d40..a1ec3f81b9 100644
--- a/src/runner/settings_window.cpp
+++ b/src/runner/settings_window.cpp
@@ -198,6 +198,8 @@ void dispatch_received_json(const std::wstring& json_to_parse)
return;
}
+ Logger::info(L"dispatch_received_json: {}", json_to_parse);
+
for (const auto& base_element : j)
{
const auto name = base_element.Key();
@@ -206,12 +208,12 @@ void dispatch_received_json(const std::wstring& json_to_parse)
if (name == L"general")
{
apply_general_settings(value.GetObjectW());
- const std::wstring settings_string{ get_all_settings().Stringify().c_str() };
- {
- std::unique_lock lock{ ipc_mutex };
- if (current_settings_ipc)
- current_settings_ipc->send(settings_string);
- }
+ // const std::wstring settings_string{ get_all_settings().Stringify().c_str() };
+ // {
+ // std::unique_lock lock{ ipc_mutex };
+ // if (current_settings_ipc)
+ // current_settings_ipc->send(settings_string);
+ // }
}
else if (name == L"powertoys")
{
@@ -421,7 +423,7 @@ BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args,
DWORD g_settings_process_id = 0;
-void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional settings_window, bool show_flyout = false, const std::optional& flyout_position = std::nullopt)
+void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional settings_window)
{
g_isLaunchInProgress = true;
@@ -491,22 +493,16 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op
// Arg 9: should scoobe window be shown
std::wstring settings_showScoobe = show_scoobe_window ? L"true" : L"false";
- // Arg 10: should flyout be shown
- std::wstring settings_showFlyout = show_flyout ? L"true" : L"false";
-
- // Arg 11: contains if there's a settings window argument. If true, will add one extra argument with the value to the call.
+ // Arg 10: contains if there's a settings window argument. If true, will add one extra argument with the value to the call.
std::wstring settings_containsSettingsWindow = settings_window.has_value() ? L"true" : L"false";
- // Arg 12: contains if there's flyout coordinates. If true, will add two extra arguments to the call containing the x and y coordinates.
- std::wstring settings_containsFlyoutPosition = flyout_position.has_value() ? L"true" : L"false";
-
- // Args 13, .... : Optional arguments depending on the options presented before. All by the same value.
+ // Args 11, .... : Optional arguments depending on the options presented before. All by the same value.
// create general settings file to initialize the settings file with installation configurations like :
// 1. Run on start up.
PTSettingsHelper::save_general_settings(save_settings.to_json());
- std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {} {} {}",
+ std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {}",
executable_path,
powertoys_pipe_name,
settings_pipe_name,
@@ -516,9 +512,7 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op
settings_isUserAnAdmin,
settings_showOobe,
settings_showScoobe,
- settings_showFlyout,
- settings_containsSettingsWindow,
- settings_containsFlyoutPosition);
+ settings_containsSettingsWindow);
if (settings_window.has_value())
{
@@ -526,14 +520,6 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op
executable_args.append(settings_window.value());
}
- if (flyout_position)
- {
- executable_args.append(L" ");
- executable_args.append(std::to_wstring(flyout_position.value().x));
- executable_args.append(L" ");
- executable_args.append(std::to_wstring(flyout_position.value().y));
- }
-
BOOL process_created = false;
// Commented out to fix #22659
@@ -684,39 +670,22 @@ void bring_settings_to_front()
EnumWindows(callback, 0);
}
-void open_settings_window(std::optional settings_window, bool show_flyout = false, const std::optional& flyout_position)
+void open_settings_window(std::optional settings_window)
{
if (g_settings_process_id != 0)
{
- if (show_flyout)
+ // nl instead of showing the window, send message to it (flyout might need to be hidden, main setting window activated)
+ // bring_settings_to_front();
+ if (current_settings_ipc)
{
- if (current_settings_ipc)
+ if (settings_window.has_value())
{
- if (!flyout_position.has_value())
- {
- current_settings_ipc->send(L"{\"ShowYourself\":\"flyout\"}");
- }
- else
- {
- current_settings_ipc->send(fmt::format(L"{{\"ShowYourself\":\"flyout\", \"x_position\":{}, \"y_position\":{} }}", std::to_wstring(flyout_position.value().x), std::to_wstring(flyout_position.value().y)));
- }
+ std::wstring msg = L"{\"ShowYourself\":\"" + settings_window.value() + L"\"}";
+ current_settings_ipc->send(msg);
}
- }
- else
- {
- // nl instead of showing the window, send message to it (flyout might need to be hidden, main setting window activated)
- // bring_settings_to_front();
- if (current_settings_ipc)
+ else
{
- if (settings_window.has_value())
- {
- std::wstring msg = L"{\"ShowYourself\":\"" + settings_window.value() + L"\"}";
- current_settings_ipc->send(msg);
- }
- else
- {
- current_settings_ipc->send(L"{\"ShowYourself\":\"Dashboard\"}");
- }
+ current_settings_ipc->send(L"{\"ShowYourself\":\"Dashboard\"}");
}
}
}
@@ -724,8 +693,8 @@ void open_settings_window(std::optional settings_window, bool show
{
if (!g_isLaunchInProgress)
{
- std::thread([settings_window, show_flyout, flyout_position]() {
- run_settings_window(false, false, settings_window, show_flyout, flyout_position);
+ std::thread([settings_window]() {
+ run_settings_window(false, false, settings_window);
}).detach();
}
}
diff --git a/src/runner/settings_window.h b/src/runner/settings_window.h
index e15108059f..507d1c65b4 100644
--- a/src/runner/settings_window.h
+++ b/src/runner/settings_window.h
@@ -41,9 +41,8 @@ enum class ESettingsWindowNames
std::string ESettingsWindowNames_to_string(ESettingsWindowNames value);
ESettingsWindowNames ESettingsWindowNames_from_string(std::string value);
-void open_settings_window(std::optional settings_window, bool show_flyout, const std::optional& flyout_position);
+void open_settings_window(std::optional settings_window);
void close_settings_window();
void open_oobe_window();
void open_scoobe_window();
-void open_flyout();
diff --git a/src/runner/tray_icon.cpp b/src/runner/tray_icon.cpp
index 749c921659..92b723c9cb 100644
--- a/src/runner/tray_icon.cpp
+++ b/src/runner/tray_icon.cpp
@@ -5,6 +5,8 @@
#include "general_settings.h"
#include "centralized_hotkeys.h"
#include "centralized_kb_hook.h"
+#include "quick_access_host.h"
+#include "hotkey_conflict_detector.h"
#include
#include
@@ -69,9 +71,9 @@ void change_menu_item_text(const UINT item_id, wchar_t* new_text)
SetMenuItemInfoW(h_menu, item_id, false, &menuitem);
}
-void open_quick_access_flyout_window(const POINT flyout_position)
+void open_quick_access_flyout_window()
{
- open_settings_window(std::nullopt, true, flyout_position);
+ QuickAccessHost::show();
}
void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam)
@@ -81,7 +83,7 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam)
case ID_SETTINGS_MENU_COMMAND:
{
std::wstring settings_window{ winrt::to_hstring(ESettingsWindowNames_to_string(static_cast(lparam))) };
- open_settings_window(settings_window, false);
+ open_settings_window(settings_window);
}
break;
case ID_CLOSE_MENU_COMMAND:
@@ -113,9 +115,7 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam)
}
case ID_QUICK_ACCESS_MENU_COMMAND:
{
- POINT mouse_pointer;
- GetCursorPos(&mouse_pointer);
- open_quick_access_flyout_window(mouse_pointer);
+ open_quick_access_flyout_window();
break;
}
}
@@ -126,7 +126,14 @@ void click_timer_elapsed()
double_click_timer_running = false;
if (!double_clicked)
{
- open_quick_access_flyout_window(tray_icon_click_point);
+ if (get_general_settings().enableQuickAccess)
+ {
+ open_quick_access_flyout_window();
+ }
+ else
+ {
+ open_settings_window(std::nullopt);
+ }
}
}
@@ -218,9 +225,6 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam
// ignore event if this is the second click of a double click
if (!double_click_timer_running)
{
- // save the cursor position for sending where to show the popup.
- GetCursorPos(&tray_icon_click_point);
-
// start timer for detecting single or double click
double_click_timer_running = true;
double_clicked = false;
@@ -236,7 +240,7 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam
case WM_LBUTTONDBLCLK:
{
double_clicked = true;
- open_settings_window(std::nullopt, false);
+ open_settings_window(std::nullopt);
break;
}
break;
@@ -349,4 +353,37 @@ void stop_tray_icon()
BugReportManager::instance().clear_callbacks();
SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0);
}
-}
\ No newline at end of file
+}
+void update_quick_access_hotkey(bool enabled, PowerToysSettings::HotkeyObject hotkey)
+{
+ static PowerToysSettings::HotkeyObject current_hotkey;
+ static bool is_registered = false;
+ auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
+
+ if (is_registered)
+ {
+ CentralizedKeyboardHook::ClearModuleHotkeys(L"QuickAccess");
+ hkmng.RemoveHotkeyByModule(L"GeneralSettings");
+ is_registered = false;
+ }
+
+ if (enabled && hotkey.get_code() != 0)
+ {
+ HotkeyConflictDetector::Hotkey hk = {
+ hotkey.win_pressed(),
+ hotkey.ctrl_pressed(),
+ hotkey.shift_pressed(),
+ hotkey.alt_pressed(),
+ static_cast(hotkey.get_code())
+ };
+
+ hkmng.AddHotkey(hk, L"GeneralSettings", 0, true);
+ CentralizedKeyboardHook::SetHotkeyAction(L"QuickAccess", hk, []() {
+ open_quick_access_flyout_window();
+ return true;
+ });
+
+ current_hotkey = hotkey;
+ is_registered = true;
+ }
+}
diff --git a/src/runner/tray_icon.h b/src/runner/tray_icon.h
index 4fa7ebfe5a..e94b7630f4 100644
--- a/src/runner/tray_icon.h
+++ b/src/runner/tray_icon.h
@@ -1,6 +1,7 @@
#pragma once
#include
#include
+#include
// Start the Tray Icon
void start_tray_icon(bool isProcessElevated);
@@ -9,7 +10,9 @@ void set_tray_icon_visible(bool shouldIconBeVisible);
// Stop the Tray Icon
void stop_tray_icon();
// Open the Settings Window
-void open_settings_window(std::optional settings_window, bool show_flyout, const std::optional& flyout_position = std::nullopt);
+void open_settings_window(std::optional settings_window);
+// Update Quick Access Hotkey
+void update_quick_access_hotkey(bool enabled, PowerToysSettings::HotkeyObject hotkey);
// Callback type to be called by the tray icon loop
typedef void (*main_loop_callback_function)(PVOID);
// Calls a callback in _callback
diff --git a/src/settings-ui/QuickAccess.UI/Helpers/ModuleGpoHelper.cs b/src/settings-ui/QuickAccess.UI/Helpers/ModuleGpoHelper.cs
new file mode 100644
index 0000000000..25f32e191b
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/Helpers/ModuleGpoHelper.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using global::PowerToys.GPOWrapper;
+using ManagedCommon;
+using Microsoft.PowerToys.Settings.UI.Library;
+
+namespace Microsoft.PowerToys.QuickAccess.Helpers;
+
+internal static class ModuleGpoHelper
+{
+ public static GpoRuleConfigured GetModuleGpoConfiguration(ModuleType moduleType)
+ {
+ return moduleType switch
+ {
+ ModuleType.AdvancedPaste => GPOWrapper.GetConfiguredAdvancedPasteEnabledValue(),
+ ModuleType.AlwaysOnTop => GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue(),
+ ModuleType.Awake => GPOWrapper.GetConfiguredAwakeEnabledValue(),
+ ModuleType.CmdPal => GPOWrapper.GetConfiguredCmdPalEnabledValue(),
+ ModuleType.ColorPicker => GPOWrapper.GetConfiguredColorPickerEnabledValue(),
+ ModuleType.CropAndLock => GPOWrapper.GetConfiguredCropAndLockEnabledValue(),
+ ModuleType.CursorWrap => GPOWrapper.GetConfiguredCursorWrapEnabledValue(),
+ ModuleType.EnvironmentVariables => GPOWrapper.GetConfiguredEnvironmentVariablesEnabledValue(),
+ ModuleType.FancyZones => GPOWrapper.GetConfiguredFancyZonesEnabledValue(),
+ ModuleType.FileLocksmith => GPOWrapper.GetConfiguredFileLocksmithEnabledValue(),
+ ModuleType.FindMyMouse => GPOWrapper.GetConfiguredFindMyMouseEnabledValue(),
+ ModuleType.Hosts => GPOWrapper.GetConfiguredHostsFileEditorEnabledValue(),
+ ModuleType.ImageResizer => GPOWrapper.GetConfiguredImageResizerEnabledValue(),
+ ModuleType.KeyboardManager => GPOWrapper.GetConfiguredKeyboardManagerEnabledValue(),
+ ModuleType.MouseHighlighter => GPOWrapper.GetConfiguredMouseHighlighterEnabledValue(),
+ ModuleType.MouseJump => GPOWrapper.GetConfiguredMouseJumpEnabledValue(),
+ ModuleType.MousePointerCrosshairs => GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue(),
+ ModuleType.MouseWithoutBorders => GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue(),
+ ModuleType.NewPlus => GPOWrapper.GetConfiguredNewPlusEnabledValue(),
+ ModuleType.Peek => GPOWrapper.GetConfiguredPeekEnabledValue(),
+ ModuleType.PowerRename => GPOWrapper.GetConfiguredPowerRenameEnabledValue(),
+ ModuleType.PowerLauncher => GPOWrapper.GetConfiguredPowerLauncherEnabledValue(),
+ ModuleType.PowerAccent => GPOWrapper.GetConfiguredQuickAccentEnabledValue(),
+ ModuleType.Workspaces => GPOWrapper.GetConfiguredWorkspacesEnabledValue(),
+ ModuleType.RegistryPreview => GPOWrapper.GetConfiguredRegistryPreviewEnabledValue(),
+ ModuleType.MeasureTool => GPOWrapper.GetConfiguredScreenRulerEnabledValue(),
+ ModuleType.ShortcutGuide => GPOWrapper.GetConfiguredShortcutGuideEnabledValue(),
+ ModuleType.PowerOCR => GPOWrapper.GetConfiguredTextExtractorEnabledValue(),
+ ModuleType.ZoomIt => GPOWrapper.GetConfiguredZoomItEnabledValue(),
+ _ => GpoRuleConfigured.Unavailable,
+ };
+ }
+}
diff --git a/src/settings-ui/QuickAccess.UI/Helpers/ResourceLoaderInstance.cs b/src/settings-ui/QuickAccess.UI/Helpers/ResourceLoaderInstance.cs
new file mode 100644
index 0000000000..b57d73015b
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/Helpers/ResourceLoaderInstance.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Windows.ApplicationModel.Resources;
+
+namespace Microsoft.PowerToys.QuickAccess.Helpers;
+
+internal static class ResourceLoaderInstance
+{
+ internal static ResourceLoader ResourceLoader { get; } = new("PowerToys.QuickAccess.pri");
+}
diff --git a/src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj b/src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj
new file mode 100644
index 0000000000..0c20d4d234
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj
@@ -0,0 +1,89 @@
+
+
+
+
+
+ WinExe
+ net9.0-windows10.0.26100.0
+ Microsoft.PowerToys.QuickAccess
+ PowerToys.QuickAccess
+ true
+ None
+ true
+ true
+ false
+ false
+ app.manifest
+ ..\..\..\$(Platform)\$(Configuration)\WinUI3Apps
+ false
+ false
+ enable
+ PowerToys.QuickAccess.pri
+
+
+
+ PowerToys.GPOWrapper
+ $(OutDir)
+
+
+
+
+
+
+
+
+
+
+
+
+ Resources\Styles\Button.xaml
+
+
+ Resources\Styles\TextBlock.xaml
+
+
+ Resources\Themes\Colors.xaml
+
+
+ Resources\Themes\Generic.xaml
+
+
+
+
+
+ Strings\en-us\Resources.resw
+
+
+
+
+
+ Assets\Settings\Icons\%(RecursiveDir)%(Filename)%(Extension)
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs b/src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs
new file mode 100644
index 0000000000..2b01947728
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.PowerToys.QuickAccess;
+
+public sealed record QuickAccessLaunchContext(string? ShowEventName, string? ExitEventName, string? RunnerPipeName, string? AppPipeName)
+{
+ public static QuickAccessLaunchContext Parse(string[] args)
+ {
+ string? showEvent = null;
+ string? exitEvent = null;
+ string? runnerPipe = null;
+ string? appPipe = null;
+
+ foreach (var arg in args)
+ {
+ if (TryReadValue(arg, "--show-event", out var value))
+ {
+ showEvent = value;
+ }
+ else if (TryReadValue(arg, "--exit-event", out value))
+ {
+ exitEvent = value;
+ }
+ else if (TryReadValue(arg, "--runner-pipe", out value))
+ {
+ runnerPipe = value;
+ }
+ else if (TryReadValue(arg, "--app-pipe", out value))
+ {
+ appPipe = value;
+ }
+ }
+
+ return new QuickAccessLaunchContext(showEvent, exitEvent, runnerPipe, appPipe);
+ }
+
+ private static bool TryReadValue(string candidate, string key, [NotNullWhen(true)] out string? value)
+ {
+ if (candidate.StartsWith(key, StringComparison.OrdinalIgnoreCase))
+ {
+ if (candidate.Length == key.Length)
+ {
+ value = null;
+ return false;
+ }
+
+ if (candidate[key.Length] == '=')
+ {
+ value = candidate[(key.Length + 1)..].Trim('"');
+ return true;
+ }
+ }
+
+ value = null;
+ return false;
+ }
+}
diff --git a/src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml
new file mode 100644
index 0000000000..ab7b3c250a
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml.cs b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml.cs
new file mode 100644
index 0000000000..b21fb04b24
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Microsoft.UI.Xaml;
+
+namespace Microsoft.PowerToys.QuickAccess;
+
+public partial class App : Application
+{
+ private static MainWindow? _window;
+
+ public App()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ var launchContext = QuickAccessLaunchContext.Parse(Environment.GetCommandLineArgs());
+ _window = new MainWindow(launchContext);
+ _window.Closed += OnWindowClosed;
+ _window.Activate();
+ }
+
+ private static void OnWindowClosed(object sender, WindowEventArgs args)
+ {
+ if (sender is MainWindow window)
+ {
+ window.Closed -= OnWindowClosed;
+ }
+
+ _window = null;
+ }
+}
diff --git a/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/AppsListPage.xaml b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/AppsListPage.xaml
new file mode 100644
index 0000000000..c4d010560d
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/AppsListPage.xaml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/AppsListPage.xaml.cs b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/AppsListPage.xaml.cs
new file mode 100644
index 0000000000..1212855fe4
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/AppsListPage.xaml.cs
@@ -0,0 +1,64 @@
+// 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.PowerToys.QuickAccess.ViewModels;
+using Microsoft.PowerToys.Settings.UI.Library;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Media.Animation;
+using Microsoft.UI.Xaml.Navigation;
+
+namespace Microsoft.PowerToys.QuickAccess.Flyout;
+
+public sealed partial class AppsListPage : Page
+{
+ private FlyoutNavigationContext? _context;
+
+ public AppsListPage()
+ {
+ InitializeComponent();
+ }
+
+ public AllAppsViewModel ViewModel { get; private set; } = default!;
+
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ base.OnNavigatedTo(e);
+
+ if (e.Parameter is FlyoutNavigationContext context)
+ {
+ _context = context;
+ ViewModel = context.AllAppsViewModel;
+ DataContext = ViewModel;
+ ViewModel.RefreshSettings();
+ }
+ }
+
+ private void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (_context == null || Frame == null)
+ {
+ return;
+ }
+
+ Frame.Navigate(typeof(LaunchPage), _context, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromLeft });
+ }
+
+ private void SortAlphabetical_Click(object sender, RoutedEventArgs e)
+ {
+ if (ViewModel != null)
+ {
+ ViewModel.DashboardSortOrder = DashboardSortOrder.Alphabetical;
+ }
+ }
+
+ private void SortByStatus_Click(object sender, RoutedEventArgs e)
+ {
+ if (ViewModel != null)
+ {
+ ViewModel.DashboardSortOrder = DashboardSortOrder.ByStatus;
+ }
+ }
+}
diff --git a/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/FlyoutNavigationContext.cs b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/FlyoutNavigationContext.cs
new file mode 100644
index 0000000000..3aab3ca334
--- /dev/null
+++ b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/FlyoutNavigationContext.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.
+
+using Microsoft.PowerToys.QuickAccess.Services;
+using Microsoft.PowerToys.QuickAccess.ViewModels;
+
+namespace Microsoft.PowerToys.QuickAccess.Flyout;
+
+internal sealed record FlyoutNavigationContext(
+ LauncherViewModel LauncherViewModel,
+ AllAppsViewModel AllAppsViewModel,
+ IQuickAccessCoordinator Coordinator);
diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/LaunchPage.xaml
similarity index 64%
rename from src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml
rename to src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/LaunchPage.xaml
index 8dea006b08..f2f53b57d4 100644
--- a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml
+++ b/src/settings-ui/QuickAccess.UI/QuickAccessXAML/Flyout/LaunchPage.xaml
@@ -1,5 +1,5 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleList.xaml.cs b/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleList.xaml.cs
new file mode 100644
index 0000000000..f9d36a7e69
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleList.xaml.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+
+namespace Microsoft.PowerToys.Settings.UI.Controls
+{
+ public sealed partial class ModuleList : UserControl
+ {
+ public ModuleList()
+ {
+ this.InitializeComponent();
+ }
+
+ public Thickness DividerThickness
+ {
+ get => (Thickness)GetValue(DividerThicknessProperty);
+ set => SetValue(DividerThicknessProperty, value);
+ }
+
+ public static readonly DependencyProperty DividerThicknessProperty = DependencyProperty.Register(nameof(DividerThickness), typeof(Thickness), typeof(ModuleList), new PropertyMetadata(new Thickness(0, 1, 0, 0)));
+
+ public bool IsItemClickable
+ {
+ get => (bool)GetValue(IsItemClickableProperty);
+ set => SetValue(IsItemClickableProperty, value);
+ }
+
+ public static readonly DependencyProperty IsItemClickableProperty = DependencyProperty.Register(nameof(IsItemClickable), typeof(bool), typeof(ModuleList), new PropertyMetadata(true));
+
+ public object ItemsSource
+ {
+ get => (object)GetValue(ItemsSourceProperty);
+ set => SetValue(ItemsSourceProperty, value);
+ }
+
+ public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(ModuleList), new PropertyMetadata(null));
+
+ public ModuleListSortOption SortOption
+ {
+ get => (ModuleListSortOption)GetValue(SortOptionProperty);
+ set => SetValue(SortOptionProperty, value);
+ }
+
+ public static readonly DependencyProperty SortOptionProperty = DependencyProperty.Register(nameof(SortOption), typeof(ModuleListSortOption), typeof(ModuleList), new PropertyMetadata(ModuleListSortOption.Alphabetical));
+
+ private void OnSettingsCardClick(object sender, RoutedEventArgs e)
+ {
+ if (sender is FrameworkElement element && element.Tag is ModuleListItem item)
+ {
+ item.ClickCommand?.Execute(item.Tag);
+ }
+ }
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListItem.cs b/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListItem.cs
new file mode 100644
index 0000000000..ef92e3e592
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListItem.cs
@@ -0,0 +1,119 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows.Input;
+
+namespace Microsoft.PowerToys.Settings.UI.Controls
+{
+ public class ModuleListItem : INotifyPropertyChanged
+ {
+ private bool _isEnabled;
+ private string _label = string.Empty;
+ private string _icon = string.Empty;
+ private bool _isNew;
+ private bool _isLocked;
+ private object? _tag;
+ private ICommand? _clickCommand;
+
+ public virtual string Label
+ {
+ get => _label;
+ set
+ {
+ if (_label != value)
+ {
+ _label = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public virtual string Icon
+ {
+ get => _icon;
+ set
+ {
+ if (_icon != value)
+ {
+ _icon = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public virtual bool IsNew
+ {
+ get => _isNew;
+ set
+ {
+ if (_isNew != value)
+ {
+ _isNew = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public virtual bool IsLocked
+ {
+ get => _isLocked;
+ set
+ {
+ if (_isLocked != value)
+ {
+ _isLocked = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public virtual bool IsEnabled
+ {
+ get => _isEnabled;
+ set
+ {
+ if (_isEnabled != value)
+ {
+ _isEnabled = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public virtual object? Tag
+ {
+ get => _tag;
+ set
+ {
+ if (_tag != value)
+ {
+ _tag = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public virtual ICommand? ClickCommand
+ {
+ get => _clickCommand;
+ set
+ {
+ if (_clickCommand != value)
+ {
+ _clickCommand = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListSortOption.cs b/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListSortOption.cs
new file mode 100644
index 0000000000..233c891d6b
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/ModuleList/ModuleListSortOption.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.PowerToys.Settings.UI.Controls
+{
+ public enum ModuleListSortOption
+ {
+ Alphabetical,
+ ByStatus,
+ }
+}
diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/Card.xaml b/src/settings-ui/Settings.UI.Controls/Primitives/Card.xaml
similarity index 97%
rename from src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/Card.xaml
rename to src/settings-ui/Settings.UI.Controls/Primitives/Card.xaml
index 9563bfceb3..508d94b7ca 100644
--- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/Card.xaml
+++ b/src/settings-ui/Settings.UI.Controls/Primitives/Card.xaml
@@ -21,11 +21,11 @@
BorderThickness="{x:Bind BorderThickness, Mode=OneWay}"
CornerRadius="{x:Bind CornerRadius, Mode=OneWay}">
-
+
-
+
diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/Card.xaml.cs b/src/settings-ui/Settings.UI.Controls/Primitives/Card.xaml.cs
similarity index 94%
rename from src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/Card.xaml.cs
rename to src/settings-ui/Settings.UI.Controls/Primitives/Card.xaml.cs
index 1959a70445..ebf04c4e89 100644
--- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/Card.xaml.cs
+++ b/src/settings-ui/Settings.UI.Controls/Primitives/Card.xaml.cs
@@ -11,9 +11,9 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
{
public static readonly DependencyProperty TitleContentProperty = DependencyProperty.Register(nameof(TitleContent), typeof(object), typeof(Card), new PropertyMetadata(defaultValue: null, OnVisualPropertyChanged));
- public object TitleContent
+ public object? TitleContent
{
- get => (object)GetValue(TitleContentProperty);
+ get => (object?)GetValue(TitleContentProperty);
set => SetValue(TitleContentProperty, value);
}
@@ -34,7 +34,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
set => SetValue(ContentProperty, value);
}
- public static readonly DependencyProperty DividerVisibilityProperty = DependencyProperty.Register(nameof(DividerVisibility), typeof(Visibility), typeof(Card), new PropertyMetadata(defaultValue: null));
+ public static readonly DependencyProperty DividerVisibilityProperty = DependencyProperty.Register(nameof(DividerVisibility), typeof(Visibility), typeof(Card), new PropertyMetadata(defaultValue: Visibility.Visible));
public Visibility DividerVisibility
{
@@ -66,7 +66,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
else
{
VisualStateManager.GoToState(this, "TitleGridVisible", true);
- DividerVisibility = Visibility.Visible;
}
}
}
diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/FlyoutMenuButton.xaml.cs b/src/settings-ui/Settings.UI.Controls/Primitives/FlyoutMenuButton.cs
similarity index 96%
rename from src/settings-ui/Settings.UI/SettingsXAML/Controls/FlyoutMenuButton.xaml.cs
rename to src/settings-ui/Settings.UI.Controls/Primitives/FlyoutMenuButton.cs
index bb1767e01e..f313506f23 100644
--- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/FlyoutMenuButton.xaml.cs
+++ b/src/settings-ui/Settings.UI.Controls/Primitives/FlyoutMenuButton.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation
+// 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.
diff --git a/src/settings-ui/Settings.UI.Controls/QuickAccess/IQuickAccessLauncher.cs b/src/settings-ui/Settings.UI.Controls/QuickAccess/IQuickAccessLauncher.cs
new file mode 100644
index 0000000000..8c35889c94
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/QuickAccess/IQuickAccessLauncher.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.
+
+using ManagedCommon;
+
+namespace Microsoft.PowerToys.Settings.UI.Controls
+{
+ public interface IQuickAccessLauncher
+ {
+ bool Launch(ModuleType moduleType);
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessItem.cs b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessItem.cs
new file mode 100644
index 0000000000..b639d87802
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessItem.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Windows.Input;
+using Microsoft.PowerToys.Settings.UI.Library.Helpers;
+using Microsoft.UI.Xaml;
+
+namespace Microsoft.PowerToys.Settings.UI.Controls
+{
+ public sealed class QuickAccessItem : Observable
+ {
+ private string _title = string.Empty;
+
+ public string Title
+ {
+ get => _title;
+ set => Set(ref _title, value);
+ }
+
+ private string _description = string.Empty;
+
+ public string Description
+ {
+ get => _description;
+ set => Set(ref _description, value);
+ }
+
+ private string _icon = string.Empty;
+
+ public string Icon
+ {
+ get => _icon;
+ set => Set(ref _icon, value);
+ }
+
+ private ICommand? _command;
+
+ public ICommand? Command
+ {
+ get => _command;
+ set => Set(ref _command, value);
+ }
+
+ private object? _commandParameter;
+
+ public object? CommandParameter
+ {
+ get => _commandParameter;
+ set => Set(ref _commandParameter, value);
+ }
+
+ private bool _visible = true;
+
+ public bool Visible
+ {
+ get => _visible;
+ set => Set(ref _visible, value);
+ }
+
+ private object? _tag;
+
+ public object? Tag
+ {
+ get => _tag;
+ set => Set(ref _tag, value);
+ }
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessLauncher.cs b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessLauncher.cs
new file mode 100644
index 0000000000..4799ff624f
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessLauncher.cs
@@ -0,0 +1,121 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Threading;
+using ManagedCommon;
+using Microsoft.PowerToys.Settings.UI.Library;
+using PowerToys.Interop;
+
+namespace Microsoft.PowerToys.Settings.UI.Controls
+{
+ public class QuickAccessLauncher : IQuickAccessLauncher
+ {
+ private readonly bool _isElevated;
+
+ public QuickAccessLauncher(bool isElevated)
+ {
+ _isElevated = isElevated;
+ }
+
+ public virtual bool Launch(ModuleType moduleType)
+ {
+ switch (moduleType)
+ {
+ case ModuleType.ColorPicker:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowColorPickerSharedEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ case ModuleType.EnvironmentVariables:
+ {
+ bool launchAdmin = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator;
+ string eventName = !_isElevated && launchAdmin
+ ? Constants.ShowEnvironmentVariablesAdminSharedEvent()
+ : Constants.ShowEnvironmentVariablesSharedEvent();
+
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
+ {
+ eventHandle.Set();
+ }
+ }
+
+ return true;
+ case ModuleType.FancyZones:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.FZEToggleEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ case ModuleType.Hosts:
+ {
+ bool launchAdmin = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator;
+ string eventName = !_isElevated && launchAdmin
+ ? Constants.ShowHostsAdminSharedEvent()
+ : Constants.ShowHostsSharedEvent();
+
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
+ {
+ eventHandle.Set();
+ }
+ }
+
+ return true;
+ case ModuleType.PowerLauncher:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.PowerLauncherSharedEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ case ModuleType.PowerOCR:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowPowerOCRSharedEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ case ModuleType.RegistryPreview:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RegistryPreviewTriggerEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ case ModuleType.MeasureTool:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.MeasureToolTriggerEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ case ModuleType.ShortcutGuide:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShortcutGuideTriggerEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ case ModuleType.CmdPal:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowCmdPalEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ case ModuleType.Workspaces:
+ using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.WorkspacesLaunchEditorEvent()))
+ {
+ eventHandle.Set();
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessList.xaml b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessList.xaml
new file mode 100644
index 0000000000..415ae22684
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessList.xaml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessList.xaml.cs b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessList.xaml.cs
new file mode 100644
index 0000000000..34c4bad013
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessList.xaml.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+
+namespace Microsoft.PowerToys.Settings.UI.Controls
+{
+ public sealed partial class QuickAccessList : UserControl
+ {
+ public QuickAccessList()
+ {
+ this.InitializeComponent();
+ }
+
+ public object ItemsSource
+ {
+ get => (object)GetValue(ItemsSourceProperty);
+ set => SetValue(ItemsSourceProperty, value);
+ }
+
+ public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(QuickAccessList), new PropertyMetadata(null));
+ }
+}
diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessViewModel.cs
similarity index 50%
rename from src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs
rename to src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessViewModel.cs
index 7adc4bb933..5531df1ee5 100644
--- a/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs
+++ b/src/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessViewModel.cs
@@ -1,44 +1,64 @@
-// Copyright (c) Microsoft Corporation
+// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.ObjectModel;
-
-using global::PowerToys.GPOWrapper;
using ManagedCommon;
-using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
+using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
+using Microsoft.UI.Dispatching;
using Microsoft.Windows.ApplicationModel.Resources;
-namespace Microsoft.PowerToys.Settings.UI.ViewModels
+namespace Microsoft.PowerToys.Settings.UI.Controls
{
- public partial class LauncherViewModel : Observable
+ public partial class QuickAccessViewModel : Observable
{
- public bool IsUpdateAvailable { get; set; }
+ private readonly ISettingsRepository _settingsRepository;
+ private readonly IQuickAccessLauncher _launcher;
+ private readonly Func _isModuleGpoDisabled;
+ private readonly ResourceLoader _resourceLoader;
+ private readonly DispatcherQueue _dispatcherQueue;
+ private GeneralSettings _generalSettings;
- public ObservableCollection FlyoutMenuItems { get; set; }
+ public ObservableCollection Items { get; } = new();
- private GeneralSettings generalSettingsConfig;
- private UpdatingSettings updatingSettingsConfig;
- private ISettingsRepository _settingsRepository;
- private ResourceLoader resourceLoader;
-
- private Func SendIPCMessage { get; }
-
- public LauncherViewModel(ISettingsRepository settingsRepository, Func ipcMSGCallBackFunc)
+ public QuickAccessViewModel(
+ ISettingsRepository settingsRepository,
+ IQuickAccessLauncher launcher,
+ Func isModuleGpoDisabled,
+ ResourceLoader resourceLoader)
{
_settingsRepository = settingsRepository;
- generalSettingsConfig = settingsRepository.SettingsConfig;
- generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
+ _launcher = launcher;
+ _isModuleGpoDisabled = isModuleGpoDisabled;
+ _resourceLoader = resourceLoader;
+ _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
- // set the callback functions value to handle outgoing IPC message.
- SendIPCMessage = ipcMSGCallBackFunc;
- resourceLoader = ResourceLoaderInstance.ResourceLoader;
- FlyoutMenuItems = new ObservableCollection();
+ _generalSettings = _settingsRepository.SettingsConfig;
+ _generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
+ _settingsRepository.SettingsChanged += OnSettingsChanged;
+ InitializeItems();
+ }
+
+ private void OnSettingsChanged(GeneralSettings newSettings)
+ {
+ if (_dispatcherQueue != null)
+ {
+ _dispatcherQueue.TryEnqueue(() =>
+ {
+ _generalSettings = newSettings;
+ _generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
+ RefreshItemsVisibility();
+ });
+ }
+ }
+
+ private void InitializeItems()
+ {
AddFlyoutMenuItem(ModuleType.ColorPicker);
AddFlyoutMenuItem(ModuleType.CmdPal);
AddFlyoutMenuItem(ModuleType.EnvironmentVariables);
@@ -50,40 +70,50 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
AddFlyoutMenuItem(ModuleType.MeasureTool);
AddFlyoutMenuItem(ModuleType.ShortcutGuide);
AddFlyoutMenuItem(ModuleType.Workspaces);
-
- updatingSettingsConfig = UpdatingSettings.LoadSettings();
- if (updatingSettingsConfig == null)
- {
- updatingSettingsConfig = new UpdatingSettings();
- }
-
- if (updatingSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToInstall || updatingSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToDownload)
- {
- IsUpdateAvailable = true;
- }
- else
- {
- IsUpdateAvailable = false;
- }
}
private void AddFlyoutMenuItem(ModuleType moduleType)
{
- if (ModuleHelper.GetModuleGpoConfiguration(moduleType) == GpoRuleConfigured.Disabled)
+ if (_isModuleGpoDisabled(moduleType))
{
return;
}
- FlyoutMenuItems.Add(new FlyoutMenuItem()
+ Items.Add(new QuickAccessItem
{
- Label = resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)),
+ Title = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType)),
Tag = moduleType,
- Visible = ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType),
- ToolTip = GetModuleToolTip(moduleType),
- Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
+ Visible = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType),
+ Description = GetModuleToolTip(moduleType),
+ Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType),
+ Command = new RelayCommand(() => _launcher.Launch(moduleType)),
});
}
+ private void ModuleEnabledChanged()
+ {
+ if (_dispatcherQueue != null)
+ {
+ _dispatcherQueue.TryEnqueue(() =>
+ {
+ _generalSettings = _settingsRepository.SettingsConfig;
+ _generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
+ RefreshItemsVisibility();
+ });
+ }
+ }
+
+ private void RefreshItemsVisibility()
+ {
+ foreach (var item in Items)
+ {
+ if (item.Tag is ModuleType moduleType)
+ {
+ item.Visible = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType);
+ }
+ }
+ }
+
private string GetModuleToolTip(ModuleType moduleType)
{
return moduleType switch
@@ -99,16 +129,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
};
}
- private void ModuleEnabledChanged()
- {
- generalSettingsConfig = _settingsRepository.SettingsConfig;
- generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
- foreach (FlyoutMenuItem item in FlyoutMenuItems)
- {
- item.Visible = ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, item.Tag);
- }
- }
-
private string GetShortcutGuideToolTip()
{
var shortcutGuideSettings = SettingsRepository.GetInstance(SettingsUtils.Default).SettingsConfig;
@@ -116,15 +136,5 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
? "Win"
: shortcutGuideSettings.Properties.OpenShortcutGuide.ToString();
}
-
- internal void StartBugReport()
- {
- SendIPCMessage("{\"bugreport\": 0 }");
- }
-
- internal void KillRunner()
- {
- SendIPCMessage("{\"killrunner\": 0 }");
- }
}
}
diff --git a/src/settings-ui/Settings.UI.Controls/Settings.UI.Controls.csproj b/src/settings-ui/Settings.UI.Controls/Settings.UI.Controls.csproj
new file mode 100644
index 0000000000..fac43d56a4
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/Settings.UI.Controls.csproj
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Library
+ net9.0-windows10.0.26100.0
+ Microsoft.PowerToys.Settings.UI.Controls
+ PowerToys.Settings.UI.Controls
+ true
+ true
+ true
+ PowerToys.Settings.UI.Controls.pri
+ enable
+ x64;ARM64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/settings-ui/Settings.UI.Controls/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI.Controls/Strings/en-us/Resources.resw
new file mode 100644
index 0000000000..6664b14936
--- /dev/null
+++ b/src/settings-ui/Settings.UI.Controls/Strings/en-us/Resources.resw
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Sort utilities
+
+
+ Alphabetically
+
+
+ By status
+
+
+ Sort utilities
+
+
+ NEW
+
+
+ This setting is managed by your organization
+
+
\ No newline at end of file
diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/FlyoutMenuButton.xaml b/src/settings-ui/Settings.UI.Controls/Themes/Generic.xaml
similarity index 95%
rename from src/settings-ui/Settings.UI/SettingsXAML/Controls/FlyoutMenuButton.xaml
rename to src/settings-ui/Settings.UI.Controls/Themes/Generic.xaml
index 50c0e4007c..466ad4475b 100644
--- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/FlyoutMenuButton.xaml
+++ b/src/settings-ui/Settings.UI.Controls/Themes/Generic.xaml
@@ -1,14 +1,9 @@
-
-
-
-
-
-