Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
This commit is contained in:
Shawn Yuan (from Dev Box)
2025-12-08 16:21:44 +08:00
parent 6ba33b0d91
commit 2ca7531ae7
13 changed files with 210 additions and 3 deletions

View File

@@ -119,6 +119,16 @@ namespace PowerToysSettings
class HotkeyObject class HotkeyObject
{ {
public: 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) static HotkeyObject from_json(json::JsonObject json)
{ {
return HotkeyObject(std::move(json)); return HotkeyObject(std::move(json));

View File

@@ -2,6 +2,7 @@
#include "general_settings.h" #include "general_settings.h"
#include "auto_start_helper.h" #include "auto_start_helper.h"
#include "tray_icon.h" #include "tray_icon.h"
#include "quick_access_host.h"
#include "Generated files/resource.h" #include "Generated files/resource.h"
#include "hotkey_conflict_detector.h" #include "hotkey_conflict_detector.h"
@@ -71,6 +72,8 @@ static bool download_updates_automatically = true;
static bool show_whats_new_after_updates = true; static bool show_whats_new_after_updates = true;
static bool enable_experimentation = true; static bool enable_experimentation = true;
static bool enable_warnings_elevated_apps = 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 DashboardSortOrder dashboard_sort_order = DashboardSortOrder::Alphabetical;
static json::JsonObject ignored_conflict_properties = create_default_ignored_conflict_properties(); static json::JsonObject ignored_conflict_properties = create_default_ignored_conflict_properties();
@@ -104,6 +107,8 @@ json::JsonObject GeneralSettings::to_json()
result.SetNamedValue(L"dashboard_sort_order", json::value(static_cast<int>(dashboardSortOrder))); result.SetNamedValue(L"dashboard_sort_order", json::value(static_cast<int>(dashboardSortOrder)));
result.SetNamedValue(L"is_admin", json::value(isAdmin)); result.SetNamedValue(L"is_admin", json::value(isAdmin));
result.SetNamedValue(L"enable_warnings_elevated_apps", json::value(enableWarningsElevatedApps)); 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"theme", json::value(theme));
result.SetNamedValue(L"system_theme", json::value(systemTheme)); result.SetNamedValue(L"system_theme", json::value(systemTheme));
result.SetNamedValue(L"powertoys_version", json::value(powerToysVersion)); result.SetNamedValue(L"powertoys_version", json::value(powerToysVersion));
@@ -126,6 +131,11 @@ json::JsonObject load_general_settings()
show_whats_new_after_updates = loaded.GetNamedBoolean(L"show_whats_new_after_updates", true); show_whats_new_after_updates = loaded.GetNamedBoolean(L"show_whats_new_after_updates", true);
enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation", true); enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation", true);
enable_warnings_elevated_apps = loaded.GetNamedBoolean(L"enable_warnings_elevated_apps", 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); dashboard_sort_order = parse_dashboard_sort_order(loaded, dashboard_sort_order);
if (json::has(loaded, L"ignored_conflict_properties", json::JsonValueType::Object)) if (json::has(loaded, L"ignored_conflict_properties", json::JsonValueType::Object))
@@ -152,6 +162,8 @@ GeneralSettings get_general_settings()
.isRunElevated = run_as_elevated, .isRunElevated = run_as_elevated,
.isAdmin = is_user_admin, .isAdmin = is_user_admin,
.enableWarningsElevatedApps = enable_warnings_elevated_apps, .enableWarningsElevatedApps = enable_warnings_elevated_apps,
.enableQuickAccess = enable_quick_access,
.quickAccessShortcut = quick_access_shortcut,
.showNewUpdatesToastNotification = show_new_updates_toast_notification, .showNewUpdatesToastNotification = show_new_updates_toast_notification,
.downloadUpdatesAutomatically = download_updates_automatically && is_user_admin, .downloadUpdatesAutomatically = download_updates_automatically && is_user_admin,
.showWhatsNewAfterUpdates = show_whats_new_after_updates, .showWhatsNewAfterUpdates = show_whats_new_after_updates,
@@ -182,6 +194,36 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save)
enable_warnings_elevated_apps = general_configs.GetNamedBoolean(L"enable_warnings_elevated_apps", true); 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); 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); download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true);
@@ -404,3 +446,5 @@ void start_enabled_powertoys()
} }
} }
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <common/utils/json.h> #include <common/utils/json.h>
#include <common/SettingsAPI/settings_objects.h>
enum class DashboardSortOrder enum class DashboardSortOrder
{ {
@@ -18,6 +19,8 @@ struct GeneralSettings
bool isRunElevated; bool isRunElevated;
bool isAdmin; bool isAdmin;
bool enableWarningsElevatedApps; bool enableWarningsElevatedApps;
bool enableQuickAccess;
PowerToysSettings::HotkeyObject quickAccessShortcut;
bool showNewUpdatesToastNotification; bool showNewUpdatesToastNotification;
bool downloadUpdatesAutomatically; bool downloadUpdatesAutomatically;
bool showWhatsNewAfterUpdates; bool showWhatsNewAfterUpdates;

View File

@@ -106,7 +106,11 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
#endif #endif
Trace::RegisterProvider(); Trace::RegisterProvider();
start_tray_icon(isProcessElevated); start_tray_icon(isProcessElevated);
QuickAccessHost::start(); 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); set_tray_icon_visible(get_general_settings().showSystemTrayIcon);
CentralizedKeyboardHook::Start(); CentralizedKeyboardHook::Start();

View File

@@ -107,9 +107,11 @@ namespace QuickAccessHost
void start() void start()
{ {
Logger::info(L"QuickAccessHost::start() called");
std::scoped_lock lock(quick_access_mutex); std::scoped_lock lock(quick_access_mutex);
if (is_process_active_locked()) if (is_process_active_locked())
{ {
Logger::info(L"QuickAccessHost::start: process already active");
return; return;
} }
@@ -232,6 +234,7 @@ namespace QuickAccessHost
void stop() void stop()
{ {
Logger::info(L"QuickAccessHost::stop() called");
std::unique_lock lock(quick_access_mutex); std::unique_lock lock(quick_access_mutex);
if (exit_event) if (exit_event)
{ {
@@ -241,6 +244,7 @@ namespace QuickAccessHost
if (quick_access_process) if (quick_access_process)
{ {
const DWORD wait_result = WaitForSingleObject(quick_access_process.get(), 2000); const DWORD wait_result = WaitForSingleObject(quick_access_process.get(), 2000);
Logger::info(L"QuickAccessHost::stop: WaitForSingleObject result={}", wait_result);
if (wait_result == WAIT_TIMEOUT) if (wait_result == WAIT_TIMEOUT)
{ {
Logger::warn(L"QuickAccessHost: Quick Access process did not exit in time, terminating."); Logger::warn(L"QuickAccessHost: Quick Access process did not exit in time, terminating.");
@@ -250,6 +254,7 @@ namespace QuickAccessHost
} }
else else
{ {
Logger::info(L"QuickAccessHost: TerminateProcess succeeded.");
WaitForSingleObject(quick_access_process.get(), 5000); WaitForSingleObject(quick_access_process.get(), 5000);
} }
} }

View File

@@ -180,6 +180,8 @@ void dispatch_received_json(const std::wstring& json_to_parse)
return; return;
} }
Logger::info(L"dispatch_received_json: {}", json_to_parse);
for (const auto& base_element : j) for (const auto& base_element : j)
{ {
const auto name = base_element.Key(); const auto name = base_element.Key();

View File

@@ -125,7 +125,14 @@ void click_timer_elapsed()
double_click_timer_running = false; double_click_timer_running = false;
if (!double_clicked) if (!double_clicked)
{ {
open_quick_access_flyout_window(); if (get_general_settings().enableQuickAccess)
{
open_quick_access_flyout_window();
}
else
{
open_settings_window(std::nullopt);
}
} }
} }
@@ -345,4 +352,24 @@ void stop_tray_icon()
BugReportManager::instance().clear_callbacks(); BugReportManager::instance().clear_callbacks();
SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0); SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0);
} }
} }
void update_quick_access_hotkey(bool enabled, PowerToysSettings::HotkeyObject hotkey)
{
static PowerToysSettings::HotkeyObject current_hotkey;
static bool is_registered = false;
if (is_registered)
{
CentralizedHotkeys::UnregisterHotkeysForModule(L"QuickAccess");
is_registered = false;
}
if (enabled && hotkey.get_code() != 0)
{
CentralizedHotkeys::AddHotkeyAction({ static_cast<WORD>(hotkey.get_modifiers()), static_cast<WORD>(hotkey.get_code()) }, { L"QuickAccess", [](WORD, WORD) {
open_quick_access_flyout_window();
}});
current_hotkey = hotkey;
is_registered = true;
}
}

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <optional> #include <optional>
#include <string> #include <string>
#include <common/SettingsAPI/settings_objects.h>
// Start the Tray Icon // Start the Tray Icon
void start_tray_icon(bool isProcessElevated); void start_tray_icon(bool isProcessElevated);
@@ -10,6 +11,8 @@ void set_tray_icon_visible(bool shouldIconBeVisible);
void stop_tray_icon(); void stop_tray_icon();
// Open the Settings Window // Open the Settings Window
void open_settings_window(std::optional<std::wstring> settings_window); void open_settings_window(std::optional<std::wstring> 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 // Callback type to be called by the tray icon loop
typedef void (*main_loop_callback_function)(PVOID); typedef void (*main_loop_callback_function)(PVOID);
// Calls a callback in _callback // Calls a callback in _callback

View File

@@ -48,6 +48,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("enable_warnings_elevated_apps")] [JsonPropertyName("enable_warnings_elevated_apps")]
public bool EnableWarningsElevatedApps { get; set; } public bool EnableWarningsElevatedApps { get; set; }
// Gets or sets a value indicating whether Quick Access is enabled.
[JsonPropertyName("enable_quick_access")]
public bool EnableQuickAccess { get; set; }
// Gets or sets Quick Access shortcut.
[JsonPropertyName("quick_access_shortcut")]
public HotkeySettings QuickAccessShortcut { get; set; }
// Gets or sets theme Name. // Gets or sets theme Name.
[JsonPropertyName("theme")] [JsonPropertyName("theme")]
public string Theme { get; set; } public string Theme { get; set; }
@@ -94,6 +102,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
ShowSysTrayIcon = true; ShowSysTrayIcon = true;
IsAdmin = false; IsAdmin = false;
EnableWarningsElevatedApps = true; EnableWarningsElevatedApps = true;
EnableQuickAccess = true;
QuickAccessShortcut = new HotkeySettings();
IsElevated = false; IsElevated = false;
ShowNewUpdatesToastNotification = true; ShowNewUpdatesToastNotification = true;
AutoDownloadUpdates = false; AutoDownloadUpdates = false;

View File

@@ -40,6 +40,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
// Main Settings Classes // Main Settings Classes
[JsonSerializable(typeof(GeneralSettings))] [JsonSerializable(typeof(GeneralSettings))]
[JsonSerializable(typeof(OutGoingGeneralSettings))]
[JsonSerializable(typeof(AdvancedPasteSettings))] [JsonSerializable(typeof(AdvancedPasteSettings))]
[JsonSerializable(typeof(AlwaysOnTopSettings))] [JsonSerializable(typeof(AlwaysOnTopSettings))]
[JsonSerializable(typeof(AwakeSettings))] [JsonSerializable(typeof(AwakeSettings))]

View File

@@ -273,6 +273,32 @@
</ComboBox> </ComboBox>
</tkcontrols:SettingsCard> </tkcontrols:SettingsCard>
<tkcontrols:SettingsExpander
Name="GeneralPageEnableQuickAccess"
x:Uid="GeneralPage_EnableQuickAccess"
HeaderIcon="{ui:FontIcon Glyph=&#xE71D;}"
IsExpanded="True">
<ToggleSwitch
x:Uid="ToggleSwitch"
AutomationProperties.Name="{Binding ElementName=GeneralPageEnableQuickAccess, Path=Header}"
IsOn="{x:Bind ViewModel.EnableQuickAccess, Mode=TwoWay}" />
<tkcontrols:SettingsExpander.Items>
<!-- HACK: For some weird reason, a ShortcutControl does not work correctly if it's the first or last item in the expander, so we add an invisible card. -->
<tkcontrols:SettingsCard Visibility="Collapsed" />
<tkcontrols:SettingsCard
Name="QuickAccessShortcut"
x:Uid="GeneralPage_QuickAccessShortcut"
IsEnabled="{x:Bind ViewModel.EnableQuickAccess, Mode=OneWay}">
<tkcontrols:SettingsCard.Description>
<TextBlock x:Uid="GeneralPage_QuickAccessShortcut_Description" />
</tkcontrols:SettingsCard.Description>
<controls:ShortcutControl
HotkeySettings="{x:Bind ViewModel.QuickAccessShortcut, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard Visibility="Collapsed" />
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<tkcontrols:SettingsCard <tkcontrols:SettingsCard
Name="GeneralPageRunAtStartUp" Name="GeneralPageRunAtStartUp"
x:Uid="GeneralPage_RunAtStartUp" x:Uid="GeneralPage_RunAtStartUp"

View File

@@ -5763,4 +5763,17 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<value>A modern UI built with Fluent Design</value> <value>A modern UI built with Fluent Design</value>
<comment>Fluent Design is a product name, do not loc</comment> <comment>Fluent Design is a product name, do not loc</comment>
</data> </data>
<data name="GeneralPage_EnableQuickAccess.Header" xml:space="preserve">
<value>Enable Quick Access flyout</value>
</data>
<data name="GeneralPage_EnableQuickAccess.Description" xml:space="preserve">
<value>Keep the Quick Access process in memory for easy access to your favorite tools</value>
</data>
<data name="GeneralPage_QuickAccessShortcut.Header" xml:space="preserve">
<value>Activation Shortcut</value>
</data>
<data name="GeneralPage_QuickAccessShortcut_Description.Text" xml:space="preserve">
<value>Change the activation shortcut for Quick Access</value>
</data>
</root> </root>

View File

@@ -155,6 +155,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
_isElevated = isElevated; _isElevated = isElevated;
_runElevated = GeneralSettingsConfig.RunElevated; _runElevated = GeneralSettingsConfig.RunElevated;
_enableWarningsElevatedApps = GeneralSettingsConfig.EnableWarningsElevatedApps; _enableWarningsElevatedApps = GeneralSettingsConfig.EnableWarningsElevatedApps;
_enableQuickAccess = GeneralSettingsConfig.EnableQuickAccess;
_quickAccessShortcut = GeneralSettingsConfig.QuickAccessShortcut;
if (_quickAccessShortcut != null)
{
_quickAccessShortcut.PropertyChanged += QuickAccessShortcut_PropertyChanged;
}
RunningAsUserDefaultText = runAsUserText; RunningAsUserDefaultText = runAsUserText;
RunningAsAdminDefaultText = runAsAdminText; RunningAsAdminDefaultText = runAsAdminText;
@@ -236,6 +242,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private bool _runElevated; private bool _runElevated;
private bool _isAdmin; private bool _isAdmin;
private bool _enableWarningsElevatedApps; private bool _enableWarningsElevatedApps;
private bool _enableQuickAccess;
private HotkeySettings _quickAccessShortcut;
private int _themeIndex; private int _themeIndex;
private bool _showNewUpdatesToastNotification; private bool _showNewUpdatesToastNotification;
@@ -480,6 +488,57 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
} }
} }
public bool EnableQuickAccess
{
get
{
return _enableQuickAccess;
}
set
{
if (_enableQuickAccess != value)
{
_enableQuickAccess = value;
GeneralSettingsConfig.EnableQuickAccess = value;
NotifyPropertyChanged();
}
}
}
public HotkeySettings QuickAccessShortcut
{
get
{
return _quickAccessShortcut;
}
set
{
if (_quickAccessShortcut != value)
{
if (_quickAccessShortcut != null)
{
_quickAccessShortcut.PropertyChanged -= QuickAccessShortcut_PropertyChanged;
}
_quickAccessShortcut = value;
if (_quickAccessShortcut != null)
{
_quickAccessShortcut.PropertyChanged += QuickAccessShortcut_PropertyChanged;
}
GeneralSettingsConfig.QuickAccessShortcut = value;
NotifyPropertyChanged();
}
}
}
private void QuickAccessShortcut_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
NotifyPropertyChanged(nameof(QuickAccessShortcut));
}
public bool SomeUpdateSettingsAreGpoManaged public bool SomeUpdateSettingsAreGpoManaged
{ {
get get