mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-29 16:36:40 +01:00
Compare commits
54 Commits
shawn/fixe
...
shawn/quic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa262cdf20 | ||
|
|
bb3dee489b | ||
|
|
01aa491962 | ||
|
|
ee0d931f39 | ||
|
|
eceb55ca9f | ||
|
|
e941ea4ebf | ||
|
|
b1cf7fa9da | ||
|
|
346c99543a | ||
|
|
1211e2c92f | ||
|
|
9f99727bf5 | ||
|
|
fee146b084 | ||
|
|
91cceb70d5 | ||
|
|
b73908c413 | ||
|
|
062589b9f7 | ||
|
|
ff21b77b7b | ||
|
|
d6ae07c040 | ||
|
|
e038a09357 | ||
|
|
f6e6fe676a | ||
|
|
debf4322df | ||
|
|
4a823320c4 | ||
|
|
e89d6d8f5a | ||
|
|
2ca7531ae7 | ||
|
|
6ba33b0d91 | ||
|
|
273eebda25 | ||
|
|
fdd34bca09 | ||
|
|
dd9643dd38 | ||
|
|
eb69549320 | ||
|
|
8f6bd72679 | ||
|
|
4865b44de1 | ||
|
|
3381e82fc1 | ||
|
|
a40be6f9be | ||
|
|
90e4f1ca41 | ||
|
|
1fcb8e7515 | ||
|
|
3ed9641072 | ||
|
|
0428cf45af | ||
|
|
5fc0ae7e42 | ||
|
|
8cdd8574d6 | ||
|
|
f564cf9189 | ||
|
|
e826cfb491 | ||
|
|
45e94f2ce0 | ||
|
|
b3b99d6d11 | ||
|
|
bb180a166a | ||
|
|
7e62c76c18 | ||
|
|
68dd7a46f0 | ||
|
|
a67326d86d | ||
|
|
16733718c3 | ||
|
|
5bf797d9f6 | ||
|
|
8de7110918 | ||
|
|
ed254b60cc | ||
|
|
ea0a13be3a | ||
|
|
8e6f40ffe9 | ||
|
|
891dd41cc2 | ||
|
|
d7e727fa1a | ||
|
|
709ceed137 |
7
.github/actions/spell-check/expect.txt
vendored
7
.github/actions/spell-check/expect.txt
vendored
@@ -363,6 +363,7 @@ DEFAULTICON
|
||||
defaultlib
|
||||
DEFAULTONLY
|
||||
DEFAULTTONEAREST
|
||||
Defaulttonearest
|
||||
DEFAULTTONULL
|
||||
DEFAULTTOPRIMARY
|
||||
DEFERERASE
|
||||
@@ -856,6 +857,7 @@ lastcodeanalysissucceeded
|
||||
LASTEXITCODE
|
||||
LAYOUTRTL
|
||||
lbl
|
||||
Lbuttondown
|
||||
LCh
|
||||
lcid
|
||||
LCIDTo
|
||||
@@ -976,6 +978,7 @@ maxversiontested
|
||||
mber
|
||||
MBM
|
||||
MBR
|
||||
Mbuttondown
|
||||
MDICHILD
|
||||
MDL
|
||||
mdtext
|
||||
@@ -1428,6 +1431,7 @@ RAWINPUTHEADER
|
||||
RAWMODE
|
||||
RAWPATH
|
||||
rbhid
|
||||
Rbuttondown
|
||||
rclsid
|
||||
RCZOOMIT
|
||||
remotedesktop
|
||||
@@ -1727,6 +1731,7 @@ svgz
|
||||
SVSI
|
||||
SWFO
|
||||
SWP
|
||||
Swp
|
||||
SWPNOSIZE
|
||||
SWPNOZORDER
|
||||
SWRESTORE
|
||||
@@ -1746,6 +1751,7 @@ syskeydown
|
||||
SYSKEYUP
|
||||
SYSLIB
|
||||
SYSMENU
|
||||
Sysmenu
|
||||
systemai
|
||||
SYSTEMAPPS
|
||||
SYSTEMMODAL
|
||||
@@ -2071,6 +2077,7 @@ Wwanpp
|
||||
xap
|
||||
XAxis
|
||||
XButton
|
||||
Xbuttondown
|
||||
xclip
|
||||
xcopy
|
||||
XDeployment
|
||||
|
||||
@@ -976,6 +976,14 @@
|
||||
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
|
||||
</Folder>
|
||||
<Folder Name="/settings-ui/">
|
||||
<Project Path="src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/settings-ui/Settings.UI.Controls/Settings.UI.Controls.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
|
||||
@@ -36,5 +36,6 @@ namespace ManagedCommon
|
||||
PowerOCR,
|
||||
Workspaces,
|
||||
ZoomIt,
|
||||
GeneralSettings,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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<int>(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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/utils/json.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
|
||||
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;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <shellapi.h>
|
||||
#include "centralized_kb_hook.h"
|
||||
#include "centralized_hotkeys.h"
|
||||
#include "quick_access_host.h"
|
||||
#include "ai_detection.h"
|
||||
#include <common/utils/package.h>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
269
src/runner/quick_access_host.cpp
Normal file
269
src/runner/quick_access_host.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "pch.h"
|
||||
#include "quick_access_host.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <rpc.h>
|
||||
#include <new>
|
||||
#include <memory>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/interop/two_way_pipe_message_ipc.h>
|
||||
#include <wil/resource.h>
|
||||
|
||||
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<TwoWayPipeMessageIPC> 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<RPC_WSTR*>(&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<RPC_WSTR*>(&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<wchar_t> 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();
|
||||
}
|
||||
}
|
||||
12
src/runner/quick_access_host.h
Normal file
12
src/runner/quick_access_host.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <optional>
|
||||
|
||||
namespace QuickAccessHost
|
||||
{
|
||||
void start();
|
||||
void show();
|
||||
void stop();
|
||||
bool is_running();
|
||||
}
|
||||
@@ -70,6 +70,7 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="powertoy_module.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="quick_access_host.cpp" />
|
||||
<ClCompile Include="restart_elevated.cpp" />
|
||||
<ClCompile Include="centralized_kb_hook.cpp" />
|
||||
<ClCompile Include="settings_telemetry.cpp" />
|
||||
@@ -85,6 +86,7 @@
|
||||
<ClInclude Include="auto_start_helper.h" />
|
||||
<ClInclude Include="bug_report.h" />
|
||||
<ClInclude Include="centralized_hotkeys.h" />
|
||||
<ClInclude Include="quick_access_host.h" />
|
||||
<ClInclude Include="general_settings.h" />
|
||||
<ClInclude Include="hotkey_conflict_detector.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
<ClCompile Include="hotkey_conflict_detector.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="quick_access_host.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -102,6 +105,9 @@
|
||||
<ClInclude Include="hotkey_conflict_detector.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="quick_access_host.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Utils">
|
||||
|
||||
@@ -180,6 +180,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();
|
||||
@@ -188,12 +190,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")
|
||||
{
|
||||
@@ -403,7 +405,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<std::wstring> settings_window, bool show_flyout = false, const std::optional<POINT>& flyout_position = std::nullopt)
|
||||
void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional<std::wstring> settings_window)
|
||||
{
|
||||
g_isLaunchInProgress = true;
|
||||
|
||||
@@ -473,22 +475,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,
|
||||
@@ -498,9 +494,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())
|
||||
{
|
||||
@@ -508,14 +502,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
|
||||
@@ -666,39 +652,22 @@ void bring_settings_to_front()
|
||||
EnumWindows(callback, 0);
|
||||
}
|
||||
|
||||
void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout = false, const std::optional<POINT>& flyout_position)
|
||||
void open_settings_window(std::optional<std::wstring> 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\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -706,8 +675,8 @@ void open_settings_window(std::optional<std::wstring> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<std::wstring> settings_window, bool show_flyout, const std::optional<POINT>& flyout_position);
|
||||
void open_settings_window(std::optional<std::wstring> settings_window);
|
||||
void close_settings_window();
|
||||
|
||||
void open_oobe_window();
|
||||
void open_scoobe_window();
|
||||
void open_flyout();
|
||||
|
||||
@@ -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 <Windows.h>
|
||||
|
||||
#include <common/utils/resources.h>
|
||||
@@ -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<ESettingsWindowNames>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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<unsigned char>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
|
||||
// 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<std::wstring> settings_window, bool show_flyout, const std::optional<POINT>& flyout_position = std::nullopt);
|
||||
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
|
||||
typedef void (*main_loop_callback_function)(PVOID);
|
||||
// Calls a callback in _callback
|
||||
|
||||
49
src/settings-ui/QuickAccess.UI/Helpers/ModuleGpoHelper.cs
Normal file
49
src/settings-ui/QuickAccess.UI/Helpers/ModuleGpoHelper.cs
Normal file
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
89
src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj
Normal file
89
src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj
Normal file
@@ -0,0 +1,89 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.PowerToys.QuickAccess</RootNamespace>
|
||||
<AssemblyName>PowerToys.QuickAccess</AssemblyName>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<OutputPath>..\..\..\$(Platform)\$(Configuration)\WinUI3Apps</OutputPath>
|
||||
<EnableDefaultPageItems>false</EnableDefaultPageItems>
|
||||
<EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition>
|
||||
<Nullable>enable</Nullable>
|
||||
<ProjectPriFileName>PowerToys.QuickAccess.pri</ProjectPriFileName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CsWinRTIncludes>PowerToys.GPOWrapper</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="QuickAccessXaml\App.xaml" />
|
||||
<Page Include="QuickAccessXaml\MainWindow.xaml" />
|
||||
<Page Include="QuickAccessXaml\Flyout\ShellPage.xaml" />
|
||||
<Page Include="QuickAccessXaml\Flyout\LaunchPage.xaml" />
|
||||
<Page Include="QuickAccessXaml\Flyout\AppsListPage.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Include="..\Settings.UI\SettingsXAML\Styles\Button.xaml">
|
||||
<Link>Resources\Styles\Button.xaml</Link>
|
||||
</Page>
|
||||
<Page Include="..\Settings.UI\SettingsXAML\Styles\TextBlock.xaml">
|
||||
<Link>Resources\Styles\TextBlock.xaml</Link>
|
||||
</Page>
|
||||
<Page Include="..\Settings.UI\SettingsXAML\Themes\Colors.xaml">
|
||||
<Link>Resources\Themes\Colors.xaml</Link>
|
||||
</Page>
|
||||
<Page Include="..\Settings.UI\SettingsXAML\Themes\Generic.xaml">
|
||||
<Link>Resources\Themes\Generic.xaml</Link>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PRIResource Include="..\Settings.UI\Strings\en-us\Resources.resw">
|
||||
<Link>Strings\en-us\Resources.resw</Link>
|
||||
</PRIResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\Settings.UI\Assets\Settings\Icons\**\*">
|
||||
<Link>Assets\Settings\Icons\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Animations" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Controls\Settings.UI.Controls.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
62
src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs
Normal file
62
src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
60
src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml
Normal file
60
src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Application
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<ResourceDictionary Source="/Resources/Styles/Button.xaml" />
|
||||
<ResourceDictionary Source="/Resources/Styles/TextBlock.xaml" />
|
||||
<ResourceDictionary Source="/Resources/Themes/Colors.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<SolidColorBrush
|
||||
x:Key="LayerOnAcrylicFillColorDefaultBrush"
|
||||
Opacity="0.7"
|
||||
Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="CardStrokeColorDefaultBrush" Color="#0F000000" />
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush" Color="#B3FFFFFF" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush
|
||||
x:Key="LayerOnAcrylicFillColorDefaultBrush"
|
||||
Opacity="0.7"
|
||||
Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="CardStrokeColorDefaultBrush" Color="#0F000000" />
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush" Color="#B3FFFFFF" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush
|
||||
x:Key="LayerOnAcrylicFillColorDefaultBrush"
|
||||
Opacity="0.6"
|
||||
Color="#FF000000" />
|
||||
<SolidColorBrush x:Key="CardStrokeColorDefaultBrush" Color="#0FFFFFFF" />
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush" Color="#0DFFFFFF" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="LayerOnAcrylicFillColorDefaultBrush" Color="{ThemeResource SystemColorWindowColor}" />
|
||||
<SolidColorBrush x:Key="CardStrokeColorDefaultBrush" Color="{ThemeResource SystemColorWindowTextColor}" />
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush" Color="{ThemeResource SystemColorWindowColor}" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="ReverseBoolToVisibilityConverter"
|
||||
FalseValue="Visible"
|
||||
TrueValue="Collapsed" />
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="BoolToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
TrueValue="Visible" />
|
||||
<tkconverters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
<tkconverters:StringVisibilityConverter x:Key="StringVisibilityConverter" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
36
src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml.cs
Normal file
36
src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.Flyout.AppsListPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Controls.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Microsoft.PowerToys.QuickAccess.Flyout"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:Microsoft.PowerToys.QuickAccess.ViewModels"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
|
||||
</Page.Resources>
|
||||
<Grid Background="{ThemeResource LayerOnAcrylicFillColorDefaultBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Padding="24,32,24,0">
|
||||
<TextBlock
|
||||
x:Uid="AllAppsTxt"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<Button
|
||||
x:Uid="Dashboard_SortBy"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="Dashboard_SortBy_ToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout Placement="BottomEdgeAlignedRight">
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Uid="Dashboard_SortAlphabetical"
|
||||
Click="SortAlphabetical_Click"
|
||||
IsChecked="{x:Bind ViewModel.DashboardSortOrder, Mode=OneWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Alphabetical}" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Uid="Dashboard_SortByStatus"
|
||||
Click="SortByStatus_Click"
|
||||
IsChecked="{x:Bind ViewModel.DashboardSortOrder, Mode=OneWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=ByStatus}" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Button
|
||||
x:Uid="BackBtn"
|
||||
Padding="8,4,8,4"
|
||||
VerticalAlignment="Center"
|
||||
Click="BackButton_Click">
|
||||
<Button.Content>
|
||||
<StackPanel
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
<FontIcon
|
||||
Margin="0,2,0,0"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="BackLabel" Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<controls:ModuleList
|
||||
Margin="8,12,12,12"
|
||||
DividerThickness="0,0,0,0"
|
||||
IsItemClickable="False"
|
||||
ItemsSource="{x:Bind ViewModel.FlyoutMenuItems, Mode=OneWay}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -1,5 +1,5 @@
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Flyout.LaunchPage"
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.Flyout.LaunchPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:animatedVisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
|
||||
@@ -9,7 +9,7 @@
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
xmlns:viewModels="using:Microsoft.PowerToys.Settings.UI.ViewModels"
|
||||
xmlns:viewModels="using:Microsoft.PowerToys.QuickAccess.ViewModels"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<Style
|
||||
@@ -52,7 +52,6 @@
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
|
||||
<TextBlock x:Uid="MoreLabel" Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
<FontIcon
|
||||
Margin="0,2,0,0"
|
||||
@@ -64,45 +63,12 @@
|
||||
</Grid>
|
||||
<Grid Grid.Row="1">
|
||||
<ScrollViewer>
|
||||
<ItemsControl
|
||||
<controls:QuickAccessList
|
||||
Margin="12,26,12,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
ItemsSource="{x:Bind ViewModel.FlyoutMenuItems}"
|
||||
TabNavigation="Local">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<tkcontrols:WrapPanel HorizontalAlignment="Stretch" VerticalSpacing="12" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewModels:FlyoutMenuItem">
|
||||
<controls:FlyoutMenuButton
|
||||
AutomationProperties.Name="{x:Bind Label}"
|
||||
Click="ModuleButton_Click"
|
||||
Tag="{x:Bind Tag}"
|
||||
Visibility="{x:Bind Visible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<controls:FlyoutMenuButton.Content>
|
||||
<TextBlock
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Label}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:FlyoutMenuButton.Content>
|
||||
<controls:FlyoutMenuButton.Icon>
|
||||
<Image>
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind Icon, Mode=OneWay}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
</controls:FlyoutMenuButton.Icon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{x:Bind ToolTip}" Visibility="{x:Bind ToolTip, Converter={StaticResource StringVisibilityConverter}}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</controls:FlyoutMenuButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
TabNavigation="Local" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -150,7 +116,7 @@
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="SettingsTooltip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<AnimatedIcon x:Name="SearchAnimatedIcon">
|
||||
<AnimatedIcon x:Name="SettingsAnimatedIcon">
|
||||
<AnimatedIcon.Source>
|
||||
<animatedVisuals:AnimatedSettingsVisualSource />
|
||||
</AnimatedIcon.Source>
|
||||
@@ -159,14 +125,6 @@
|
||||
</AnimatedIcon.FallbackIconSource>
|
||||
</AnimatedIcon>
|
||||
</Button>
|
||||
<!--<AppBarSeparator />
|
||||
<Button
|
||||
x:Name="QuitBtn"
|
||||
Style="{StaticResource FlyoutButtonStyle}"
|
||||
ToolTipService.ToolTip="Quit"
|
||||
Click="QuitButton_Click">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
</Button>-->
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using PowerToys.Interop;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Flyout;
|
||||
|
||||
public sealed partial class LaunchPage : Page
|
||||
{
|
||||
private AllAppsViewModel? _allAppsViewModel;
|
||||
private IQuickAccessCoordinator? _coordinator;
|
||||
|
||||
public LaunchPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public LauncherViewModel ViewModel { get; private set; } = default!;
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is FlyoutNavigationContext context)
|
||||
{
|
||||
ViewModel = context.LauncherViewModel;
|
||||
_allAppsViewModel = context.AllAppsViewModel;
|
||||
_coordinator = context.Coordinator;
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_coordinator?.OpenSettings();
|
||||
}
|
||||
|
||||
private async void DocsBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_coordinator == null || !await _coordinator.ShowDocumentationAsync())
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://aka.ms/PowerToysOverview"));
|
||||
}
|
||||
}
|
||||
|
||||
private void AllAppButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Frame == null || _allAppsViewModel == null || ViewModel == null || _coordinator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var context = new FlyoutNavigationContext(ViewModel, _allAppsViewModel, _coordinator);
|
||||
Frame.Navigate(typeof(AppsListPage), context, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
}
|
||||
|
||||
public void ReportBugBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_coordinator?.ReportBug();
|
||||
}
|
||||
|
||||
private void UpdateInfoBar_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
_coordinator?.OpenGeneralSettingsForUpdates();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Flyout.ShellPage"
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.Flyout.ShellPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Flyout;
|
||||
|
||||
/// <summary>
|
||||
/// Hosts the flyout navigation frame.
|
||||
/// </summary>
|
||||
public sealed partial class ShellPage : Page
|
||||
{
|
||||
private LauncherViewModel? _launcherViewModel;
|
||||
private AllAppsViewModel? _allAppsViewModel;
|
||||
private IQuickAccessCoordinator? _coordinator;
|
||||
|
||||
public ShellPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void Initialize(IQuickAccessCoordinator coordinator, LauncherViewModel launcherViewModel, AllAppsViewModel allAppsViewModel)
|
||||
{
|
||||
_coordinator = coordinator;
|
||||
_launcherViewModel = launcherViewModel;
|
||||
_allAppsViewModel = allAppsViewModel;
|
||||
}
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_launcherViewModel == null || _allAppsViewModel == null || _coordinator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ContentFrame.Content is LaunchPage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var context = new FlyoutNavigationContext(_launcherViewModel, _allAppsViewModel, _coordinator);
|
||||
ContentFrame.Navigate(typeof(LaunchPage), context, new SuppressNavigationTransitionInfo());
|
||||
}
|
||||
|
||||
internal void NavigateToLaunch()
|
||||
{
|
||||
if (_launcherViewModel == null || _allAppsViewModel == null || _coordinator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var context = new FlyoutNavigationContext(_launcherViewModel, _allAppsViewModel, _coordinator);
|
||||
ContentFrame.Navigate(typeof(LaunchPage), context, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromLeft });
|
||||
}
|
||||
|
||||
internal void RefreshIfAppsList()
|
||||
{
|
||||
if (ContentFrame.Content is AppsListPage appsListPage)
|
||||
{
|
||||
appsListPage.ViewModel?.RefreshSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
<winuiex:WindowEx
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.FlyoutWindow"
|
||||
<winuiEx:WindowEx
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:flyout="using:Microsoft.PowerToys.Settings.UI.Flyout"
|
||||
xmlns:local="using:Microsoft.PowerToys.Settings.UI"
|
||||
xmlns:flyout="using:Microsoft.PowerToys.QuickAccess.Flyout"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winuiex="using:WinUIEx"
|
||||
Title="PowerToys Settings"
|
||||
xmlns:winuiEx="using:WinUIEx"
|
||||
Title="PowerToys Quick Access (Preview)"
|
||||
Width="400"
|
||||
Height="516"
|
||||
MinWidth="400"
|
||||
MinHeight="516"
|
||||
IsAlwaysOnTop="True"
|
||||
IsMaximizable="False"
|
||||
IsMinimizable="False"
|
||||
@@ -15,8 +18,8 @@
|
||||
IsShownInSwitchers="False"
|
||||
IsTitleBarVisible="False"
|
||||
mc:Ignorable="d">
|
||||
<winuiex:WindowEx.Backdrop>
|
||||
<winuiex:AcrylicSystemBackdrop
|
||||
<winuiEx:WindowEx.Backdrop>
|
||||
<winuiEx:AcrylicSystemBackdrop
|
||||
DarkFallbackColor="#1c1c1c"
|
||||
DarkLuminosityOpacity="0.96"
|
||||
DarkTintColor="#202020"
|
||||
@@ -25,8 +28,9 @@
|
||||
LightLuminosityOpacity="0.90"
|
||||
LightTintColor="#F3F3F3"
|
||||
LightTintOpacity="0" />
|
||||
</winuiex:WindowEx.Backdrop>
|
||||
</winuiEx:WindowEx.Backdrop>
|
||||
|
||||
<Grid>
|
||||
<flyout:ShellPage x:Name="FlyoutShellPage" />
|
||||
<flyout:ShellPage x:Name="ShellHost" />
|
||||
</Grid>
|
||||
</winuiex:WindowEx>
|
||||
</winuiEx:WindowEx>
|
||||
@@ -0,0 +1,766 @@
|
||||
// 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.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.QuickAccess.Flyout;
|
||||
using Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess;
|
||||
|
||||
public sealed partial class MainWindow : WindowEx, IDisposable
|
||||
{
|
||||
private readonly QuickAccessLaunchContext _launchContext;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private readonly IntPtr _hwnd;
|
||||
private readonly AppWindow? _appWindow;
|
||||
private readonly LauncherViewModel _launcherViewModel;
|
||||
private readonly AllAppsViewModel _allAppsViewModel;
|
||||
private readonly QuickAccessCoordinator _coordinator;
|
||||
private bool _disposed;
|
||||
private EventWaitHandle? _showEvent;
|
||||
private EventWaitHandle? _exitEvent;
|
||||
private ManualResetEventSlim? _listenerShutdownEvent;
|
||||
private Thread? _showListenerThread;
|
||||
private Thread? _exitListenerThread;
|
||||
private bool _isWindowCloaked;
|
||||
private bool _initialActivationHandled;
|
||||
private bool _isPrimed;
|
||||
|
||||
// Prevent auto-hide until the window actually gained focus once.
|
||||
private bool _hasSeenInteractiveActivation;
|
||||
private bool _isVisible;
|
||||
private IntPtr _mouseHook;
|
||||
private LowLevelMouseProc? _mouseHookDelegate;
|
||||
private CancellationTokenSource? _trimCts;
|
||||
|
||||
private const int DefaultWidth = 320;
|
||||
private const int DefaultHeight = 480;
|
||||
private const int DwmWaCloak = 13;
|
||||
private const int GwlStyle = -16;
|
||||
private const int GwlExStyle = -20;
|
||||
private const int SwHide = 0;
|
||||
private const int SwShow = 5;
|
||||
private const int SwShowNoActivate = 8;
|
||||
private const uint SwpShowWindow = 0x0040;
|
||||
private const uint SwpNoZorder = 0x0004;
|
||||
private const uint SwpNoSize = 0x0001;
|
||||
private const uint SwpNoMove = 0x0002;
|
||||
private const uint SwpNoActivate = 0x0010;
|
||||
private const uint SwpFrameChanged = 0x0020;
|
||||
private const long WsSysmenu = 0x00080000L;
|
||||
private const long WsMinimizeBox = 0x00020000L;
|
||||
private const long WsMaximizeBox = 0x00010000L;
|
||||
private const long WsExToolWindow = 0x00000080L;
|
||||
private const uint MonitorDefaulttonearest = 0x00000002;
|
||||
private static readonly IntPtr HwndTopmost = new(-1);
|
||||
private static readonly IntPtr HwndBottom = new(1);
|
||||
|
||||
public MainWindow(QuickAccessLaunchContext launchContext)
|
||||
{
|
||||
InitializeComponent();
|
||||
_launchContext = launchContext;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_hwnd = WindowNative.GetWindowHandle(this);
|
||||
_appWindow = InitializeAppWindow(_hwnd);
|
||||
Title = "PowerToys Quick Access (Preview)";
|
||||
|
||||
_coordinator = new QuickAccessCoordinator(this, _launchContext);
|
||||
_launcherViewModel = new LauncherViewModel(_coordinator);
|
||||
_allAppsViewModel = new AllAppsViewModel(_coordinator);
|
||||
ShellHost.Initialize(_coordinator, _launcherViewModel, _allAppsViewModel);
|
||||
|
||||
CustomizeWindowChrome();
|
||||
HideFromTaskbar();
|
||||
HideWindow();
|
||||
InitializeEventListeners();
|
||||
Closed += OnClosed;
|
||||
Activated += OnActivated;
|
||||
}
|
||||
|
||||
private AppWindow? InitializeAppWindow(IntPtr hwnd)
|
||||
{
|
||||
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd);
|
||||
return AppWindow.GetFromWindowId(windowId);
|
||||
}
|
||||
|
||||
private void HideWindow()
|
||||
{
|
||||
if (_hwnd != IntPtr.Zero)
|
||||
{
|
||||
var cloaked = CloakWindow();
|
||||
|
||||
if (!ShowWindowNative(_hwnd, SwHide) && _appWindow != null)
|
||||
{
|
||||
_appWindow.Hide();
|
||||
}
|
||||
|
||||
if (cloaked)
|
||||
{
|
||||
ShowWindowNative(_hwnd, SwShowNoActivate);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowPosNative(_hwnd, HwndBottom, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoActivate);
|
||||
}
|
||||
}
|
||||
else if (_appWindow != null)
|
||||
{
|
||||
_appWindow.Hide();
|
||||
}
|
||||
|
||||
_isVisible = false;
|
||||
RemoveGlobalMouseHook();
|
||||
|
||||
ScheduleMemoryTrim();
|
||||
}
|
||||
|
||||
internal void RequestHide()
|
||||
{
|
||||
if (_dispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
HideWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(HideWindow);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScheduleMemoryTrim()
|
||||
{
|
||||
CancelMemoryTrim();
|
||||
_trimCts = new CancellationTokenSource();
|
||||
var token = _trimCts.Token;
|
||||
|
||||
// Delay the trim to avoid aggressive GC during quick toggles
|
||||
Task.Delay(2000, token).ContinueWith(
|
||||
_ =>
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TrimMemory();
|
||||
},
|
||||
token,
|
||||
TaskContinuationOptions.None,
|
||||
TaskScheduler.Default);
|
||||
}
|
||||
|
||||
private void CancelMemoryTrim()
|
||||
{
|
||||
_trimCts?.Cancel();
|
||||
_trimCts?.Dispose();
|
||||
_trimCts = null;
|
||||
}
|
||||
|
||||
private void TrimMemory()
|
||||
{
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
|
||||
}
|
||||
|
||||
private void InitializeEventListeners()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_launchContext.ShowEventName))
|
||||
{
|
||||
try
|
||||
{
|
||||
_showEvent = EventWaitHandle.OpenExisting(_launchContext.ShowEventName!);
|
||||
EnsureListenerInfrastructure();
|
||||
StartShowListenerThread();
|
||||
}
|
||||
catch (WaitHandleCannotBeOpenedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_launchContext.ExitEventName))
|
||||
{
|
||||
try
|
||||
{
|
||||
_exitEvent = EventWaitHandle.OpenExisting(_launchContext.ExitEventName!);
|
||||
EnsureListenerInfrastructure();
|
||||
StartExitListenerThread();
|
||||
}
|
||||
catch (WaitHandleCannotBeOpenedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowWindow()
|
||||
{
|
||||
CancelMemoryTrim();
|
||||
|
||||
if (_hwnd != IntPtr.Zero)
|
||||
{
|
||||
UncloakWindow();
|
||||
|
||||
ShowWindowNative(_hwnd, SwShow);
|
||||
|
||||
var flags = SwpNoSize | SwpShowWindow;
|
||||
var targetX = 0;
|
||||
var targetY = 0;
|
||||
|
||||
var windowSize = _appWindow?.Size;
|
||||
var windowWidth = windowSize?.Width ?? DefaultWidth;
|
||||
var windowHeight = windowSize?.Height ?? DefaultHeight;
|
||||
|
||||
GetCursorPos(out var cursorPosition);
|
||||
var monitorHandle = MonitorFromPointNative(cursorPosition, MonitorDefaulttonearest);
|
||||
if (monitorHandle != IntPtr.Zero)
|
||||
{
|
||||
var monitorInfo = new MonitorInfo { CbSize = Marshal.SizeOf<MonitorInfo>() };
|
||||
if (GetMonitorInfoNative(monitorHandle, ref monitorInfo))
|
||||
{
|
||||
targetX = monitorInfo.RcWork.Right - windowWidth;
|
||||
targetY = monitorInfo.RcWork.Bottom - windowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
SetWindowPosNative(_hwnd, HwndTopmost, targetX, targetY, 0, 0, flags);
|
||||
BringToForeground(_hwnd);
|
||||
}
|
||||
|
||||
_hasSeenInteractiveActivation = true;
|
||||
_initialActivationHandled = true;
|
||||
Activate();
|
||||
_isVisible = true;
|
||||
EnsureGlobalMouseHook();
|
||||
ShellHost.RefreshIfAppsList();
|
||||
}
|
||||
|
||||
private void OnActivated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
if (args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
if (!_hasSeenInteractiveActivation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HideWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
_hasSeenInteractiveActivation = true;
|
||||
|
||||
if (_initialActivationHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_initialActivationHandled = true;
|
||||
PrimeWindow();
|
||||
HideWindow();
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, WindowEventArgs e)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void PrimeWindow()
|
||||
{
|
||||
if (_isPrimed || _hwnd == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isPrimed = true;
|
||||
|
||||
if (_appWindow != null)
|
||||
{
|
||||
var currentPosition = _appWindow.Position;
|
||||
_appWindow.MoveAndResize(new RectInt32(currentPosition.X, currentPosition.Y, DefaultWidth, DefaultHeight));
|
||||
}
|
||||
|
||||
// Warm up the window while cloaked so the first summon does not pay XAML initialization cost.
|
||||
var cloaked = CloakWindow();
|
||||
if (cloaked)
|
||||
{
|
||||
ShowWindowNative(_hwnd, SwShowNoActivate);
|
||||
}
|
||||
}
|
||||
|
||||
private void HideFromTaskbar()
|
||||
{
|
||||
if (_appWindow == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_appWindow.IsShownInSwitchers = false;
|
||||
}
|
||||
|
||||
private bool CloakWindow()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_isWindowCloaked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int cloak = 1;
|
||||
var result = DwmSetWindowAttribute(_hwnd, DwmWaCloak, ref cloak, sizeof(int));
|
||||
if (result == 0)
|
||||
{
|
||||
_isWindowCloaked = true;
|
||||
SetWindowPosNative(_hwnd, HwndBottom, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoActivate);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UncloakWindow()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero || !_isWindowCloaked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int cloak = 0;
|
||||
var result = DwmSetWindowAttribute(_hwnd, DwmWaCloak, ref cloak, sizeof(int));
|
||||
if (result == 0)
|
||||
{
|
||||
_isWindowCloaked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
StopEventListeners();
|
||||
|
||||
_showEvent?.Dispose();
|
||||
_showEvent = null;
|
||||
|
||||
_exitEvent?.Dispose();
|
||||
_exitEvent = null;
|
||||
|
||||
if (_hwnd != IntPtr.Zero && IsWindow(_hwnd))
|
||||
{
|
||||
UncloakWindow();
|
||||
}
|
||||
|
||||
RemoveGlobalMouseHook();
|
||||
|
||||
_coordinator.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool IsWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
|
||||
private static extern bool ShowWindowNative(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", SetLastError = true)]
|
||||
private static extern nint GetWindowLongPtrNative(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
|
||||
private static extern nint SetWindowLongPtrNative(IntPtr hWnd, int nIndex, nint dwNewLong);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos", SetLastError = true)]
|
||||
private static extern bool SetWindowPosNative(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetForegroundWindow", SetLastError = true)]
|
||||
private static extern bool SetForegroundWindowNative(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetForegroundWindow", SetLastError = true)]
|
||||
private static extern IntPtr GetForegroundWindowNative();
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true)]
|
||||
private static extern uint GetWindowThreadProcessIdNative(IntPtr hWnd, IntPtr lpdwProcessId);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "AttachThreadInput", SetLastError = true)]
|
||||
private static extern bool AttachThreadInputNative(uint idAttach, uint idAttachTo, bool fAttach);
|
||||
|
||||
[DllImport("dwmapi.dll", EntryPoint = "DwmSetWindowAttribute", SetLastError = true)]
|
||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "MonitorFromPoint", SetLastError = true)]
|
||||
private static extern IntPtr MonitorFromPointNative(NativePoint pt, uint dwFlags);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetMonitorInfoW", SetLastError = true)]
|
||||
private static extern bool GetMonitorInfoNative(IntPtr hMonitor, ref MonitorInfo lpmi);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowsHookExW", SetLastError = true)]
|
||||
private static extern IntPtr SetWindowsHookExNative(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "UnhookWindowsHookEx", SetLastError = true)]
|
||||
private static extern bool UnhookWindowsHookExNative(IntPtr hhk);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "CallNextHookEx", SetLastError = true)]
|
||||
private static extern IntPtr CallNextHookExNative(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "GetModuleHandleW", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr GetModuleHandleNative([MarshalAs(UnmanagedType.LPWStr)] string? lpModuleName);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowRect", SetLastError = true)]
|
||||
private static extern bool GetWindowRectNative(IntPtr hWnd, out Rect rect);
|
||||
|
||||
private static void BringToForeground(IntPtr hwnd)
|
||||
{
|
||||
if (hwnd == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetForegroundWindowNative(hwnd);
|
||||
|
||||
var foreground = GetForegroundWindowNative();
|
||||
if (foreground == hwnd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var windowThread = GetWindowThreadProcessIdNative(hwnd, IntPtr.Zero);
|
||||
var foregroundThread = foreground != IntPtr.Zero ? GetWindowThreadProcessIdNative(foreground, IntPtr.Zero) : 0;
|
||||
|
||||
if (windowThread != 0 && foregroundThread != 0 && windowThread != foregroundThread)
|
||||
{
|
||||
if (AttachThreadInputNative(windowThread, foregroundThread, true))
|
||||
{
|
||||
SetForegroundWindowNative(hwnd);
|
||||
AttachThreadInputNative(windowThread, foregroundThread, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetForegroundWindowNative(hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureGlobalMouseHook()
|
||||
{
|
||||
if (_mouseHook != IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_mouseHookDelegate ??= LowLevelMouseHookCallback;
|
||||
var moduleHandle = GetModuleHandleNative(null);
|
||||
_mouseHook = SetWindowsHookExNative(WhMouseLl, _mouseHookDelegate, moduleHandle, 0);
|
||||
}
|
||||
|
||||
private void RemoveGlobalMouseHook()
|
||||
{
|
||||
if (_mouseHook == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnhookWindowsHookExNative(_mouseHook);
|
||||
_mouseHook = IntPtr.Zero;
|
||||
}
|
||||
|
||||
private IntPtr LowLevelMouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0 && _isVisible && lParam != IntPtr.Zero && IsMouseButtonDownMessage(wParam))
|
||||
{
|
||||
var data = Marshal.PtrToStructure<LowLevelMouseInput>(lParam);
|
||||
if (!IsPointInsideWindow(data.Point))
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
if (_isVisible)
|
||||
{
|
||||
HideWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return CallNextHookExNative(_mouseHook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private static bool IsMouseButtonDownMessage(IntPtr wParam)
|
||||
{
|
||||
var message = wParam.ToInt32();
|
||||
return message == WmLbuttondown || message == WmRbuttondown || message == WmMbuttondown || message == WmXbuttondown;
|
||||
}
|
||||
|
||||
private bool IsPointInsideWindow(NativePoint point)
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetWindowRectNative(_hwnd, out var rect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return point.X >= rect.Left && point.X <= rect.Right && point.Y >= rect.Top && point.Y <= rect.Bottom;
|
||||
}
|
||||
|
||||
private void EnsureListenerInfrastructure()
|
||||
{
|
||||
_listenerShutdownEvent ??= new ManualResetEventSlim(false);
|
||||
}
|
||||
|
||||
private void StartShowListenerThread()
|
||||
{
|
||||
if (_showEvent == null || _listenerShutdownEvent == null || _showListenerThread != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_showListenerThread = new Thread(ListenForShowEvents)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "QuickAccess-ShowEventListener",
|
||||
};
|
||||
_showListenerThread.Start();
|
||||
}
|
||||
|
||||
private void StartExitListenerThread()
|
||||
{
|
||||
if (_exitEvent == null || _listenerShutdownEvent == null || _exitListenerThread != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_exitListenerThread = new Thread(ListenForExitEvents)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "QuickAccess-ExitEventListener",
|
||||
};
|
||||
_exitListenerThread.Start();
|
||||
}
|
||||
|
||||
private void ListenForShowEvents()
|
||||
{
|
||||
if (_showEvent == null || _listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var handles = new WaitHandle[] { _showEvent, _listenerShutdownEvent.WaitHandle };
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var index = WaitHandle.WaitAny(handles);
|
||||
if (index == 0)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(ShowWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void ListenForExitEvents()
|
||||
{
|
||||
if (_exitEvent == null || _listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var handles = new WaitHandle[] { _exitEvent, _listenerShutdownEvent.WaitHandle };
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var index = WaitHandle.WaitAny(handles);
|
||||
if (index == 0)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(Close);
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void StopEventListeners()
|
||||
{
|
||||
if (_listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_listenerShutdownEvent.Set();
|
||||
|
||||
JoinListenerThread(ref _showListenerThread);
|
||||
JoinListenerThread(ref _exitListenerThread);
|
||||
|
||||
_listenerShutdownEvent.Dispose();
|
||||
_listenerShutdownEvent = null;
|
||||
}
|
||||
|
||||
private static void JoinListenerThread(ref Thread? thread)
|
||||
{
|
||||
if (thread == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!thread.Join(TimeSpan.FromMilliseconds(250)))
|
||||
{
|
||||
thread.Interrupt();
|
||||
thread.Join(TimeSpan.FromMilliseconds(250));
|
||||
}
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadStateException)
|
||||
{
|
||||
}
|
||||
|
||||
thread = null;
|
||||
}
|
||||
|
||||
private void CustomizeWindowChrome()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var windowAttributesChanged = false;
|
||||
|
||||
var stylePtr = GetWindowLongPtrNative(_hwnd, GwlStyle);
|
||||
var styleError = Marshal.GetLastWin32Error();
|
||||
if (!(stylePtr == nint.Zero && styleError != 0))
|
||||
{
|
||||
var styleValue = (long)stylePtr;
|
||||
var newStyleValue = styleValue & ~(WsSysmenu | WsMinimizeBox | WsMaximizeBox);
|
||||
|
||||
if (newStyleValue != styleValue)
|
||||
{
|
||||
SetWindowLongPtrNative(_hwnd, GwlStyle, (nint)newStyleValue);
|
||||
windowAttributesChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
var exStylePtr = GetWindowLongPtrNative(_hwnd, GwlExStyle);
|
||||
var exStyleError = Marshal.GetLastWin32Error();
|
||||
if (!(exStylePtr == nint.Zero && exStyleError != 0))
|
||||
{
|
||||
var exStyleValue = (long)exStylePtr;
|
||||
var newExStyleValue = exStyleValue | WsExToolWindow;
|
||||
if (newExStyleValue != exStyleValue)
|
||||
{
|
||||
SetWindowLongPtrNative(_hwnd, GwlExStyle, (nint)newExStyleValue);
|
||||
windowAttributesChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (windowAttributesChanged)
|
||||
{
|
||||
// Apply the new chrome immediately so caption buttons disappear right away and the tool-window flag takes effect.
|
||||
SetWindowPosNative(_hwnd, IntPtr.Zero, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoZorder | SwpNoActivate | SwpFrameChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private const int WhMouseLl = 14;
|
||||
private const int WmLbuttondown = 0x0201;
|
||||
private const int WmRbuttondown = 0x0204;
|
||||
private const int WmMbuttondown = 0x0207;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool GetCursorPos(out NativePoint lpPoint);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool SetProcessWorkingSetSize(IntPtr hProcess, int dwMinimumWorkingSetSize, int dwMaximumWorkingSetSize);
|
||||
|
||||
private const int WmXbuttondown = 0x020B;
|
||||
|
||||
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
private struct Rect
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct LowLevelMouseInput
|
||||
{
|
||||
public NativePoint Point;
|
||||
public int MouseData;
|
||||
public int Flags;
|
||||
public int Time;
|
||||
public IntPtr DwExtraInfo;
|
||||
}
|
||||
|
||||
private struct NativePoint
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct MonitorInfo
|
||||
{
|
||||
public int CbSize;
|
||||
public Rect RcMonitor;
|
||||
public Rect RcWork;
|
||||
public uint DwFlags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Services;
|
||||
|
||||
public interface IQuickAccessCoordinator
|
||||
{
|
||||
bool IsRunnerElevated { get; }
|
||||
|
||||
void HideFlyout();
|
||||
|
||||
void OpenSettings();
|
||||
|
||||
void OpenGeneralSettingsForUpdates();
|
||||
|
||||
Task<bool> ShowDocumentationAsync();
|
||||
|
||||
void NotifyUserSettingsInteraction();
|
||||
|
||||
bool UpdateModuleEnabled(ModuleType moduleType, bool isEnabled);
|
||||
|
||||
void ReportBug();
|
||||
|
||||
void OnModuleLaunched(ModuleType moduleType);
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
// 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.Threading.Tasks;
|
||||
using Common.UI;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using PowerToys.Interop;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Services;
|
||||
|
||||
internal sealed class QuickAccessCoordinator : IQuickAccessCoordinator, IDisposable
|
||||
{
|
||||
private readonly MainWindow _window;
|
||||
private readonly QuickAccessLaunchContext _launchContext;
|
||||
private readonly SettingsUtils _settingsUtils = SettingsUtils.Default;
|
||||
private readonly object _generalSettingsLock = new();
|
||||
private readonly object _ipcLock = new();
|
||||
private TwoWayPipeMessageIPCManaged? _ipcManager;
|
||||
private bool _ipcUnavailableLogged;
|
||||
|
||||
public QuickAccessCoordinator(MainWindow window, QuickAccessLaunchContext launchContext)
|
||||
{
|
||||
_window = window;
|
||||
_launchContext = launchContext;
|
||||
InitializeIpc();
|
||||
}
|
||||
|
||||
public bool IsRunnerElevated => false; // TODO: wire up real elevation state.
|
||||
|
||||
public void HideFlyout()
|
||||
{
|
||||
_window.RequestHide();
|
||||
}
|
||||
|
||||
public void OpenSettings()
|
||||
{
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.Dashboard, true);
|
||||
_window.RequestHide();
|
||||
}
|
||||
|
||||
public void OpenGeneralSettingsForUpdates()
|
||||
{
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.Overview, true);
|
||||
_window.RequestHide();
|
||||
}
|
||||
|
||||
public Task<bool> ShowDocumentationAsync()
|
||||
{
|
||||
Logger.LogInfo("QuickAccessCoordinator.ShowDocumentationAsync is not yet connected.");
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
public void NotifyUserSettingsInteraction()
|
||||
{
|
||||
Logger.LogDebug("QuickAccessCoordinator.NotifyUserSettingsInteraction invoked.");
|
||||
}
|
||||
|
||||
public bool UpdateModuleEnabled(ModuleType moduleType, bool isEnabled)
|
||||
{
|
||||
GeneralSettings? updatedSettings = null;
|
||||
lock (_generalSettingsLock)
|
||||
{
|
||||
var repository = SettingsRepository<GeneralSettings>.GetInstance(_settingsUtils);
|
||||
var generalSettings = repository.SettingsConfig;
|
||||
var current = ModuleHelper.GetIsModuleEnabled(generalSettings, moduleType);
|
||||
if (current == isEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ModuleHelper.SetIsModuleEnabled(generalSettings, moduleType, isEnabled);
|
||||
_settingsUtils.SaveSettings(generalSettings.ToJsonString());
|
||||
Logger.LogInfo($"QuickAccess updated module '{moduleType}' enabled state to {isEnabled}.");
|
||||
updatedSettings = generalSettings;
|
||||
}
|
||||
|
||||
if (updatedSettings != null)
|
||||
{
|
||||
SendGeneralSettingsUpdate(updatedSettings);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ReportBug()
|
||||
{
|
||||
if (!TrySendIpcMessage("{\"bugreport\": 0 }", "bug report request"))
|
||||
{
|
||||
Logger.LogWarning("QuickAccessCoordinator: failed to dispatch bug report request; IPC unavailable.");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnModuleLaunched(ModuleType moduleType)
|
||||
{
|
||||
Logger.LogInfo($"QuickAccessLauncher invoked module {moduleType}.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeIpc();
|
||||
}
|
||||
|
||||
private void InitializeIpc()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_launchContext.RunnerPipeName) || string.IsNullOrEmpty(_launchContext.AppPipeName))
|
||||
{
|
||||
Logger.LogWarning("QuickAccessCoordinator: IPC pipe names not provided. Runner will not receive updates.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ipcManager = new TwoWayPipeMessageIPCManaged(_launchContext.AppPipeName, _launchContext.RunnerPipeName, OnIpcMessageReceived);
|
||||
_ipcManager.Start();
|
||||
_ipcUnavailableLogged = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("QuickAccessCoordinator: failed to start IPC channel to runner.", ex);
|
||||
DisposeIpc();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnIpcMessageReceived(string message)
|
||||
{
|
||||
Logger.LogDebug($"QuickAccessCoordinator received IPC payload: {message}");
|
||||
}
|
||||
|
||||
private void SendGeneralSettingsUpdate(GeneralSettings updatedSettings)
|
||||
{
|
||||
string payload;
|
||||
try
|
||||
{
|
||||
payload = new OutGoingGeneralSettings(updatedSettings).ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("QuickAccessCoordinator: failed to serialize general settings payload.", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
TrySendIpcMessage(payload, "general settings update");
|
||||
}
|
||||
|
||||
private bool TrySendIpcMessage(string payload, string operationDescription)
|
||||
{
|
||||
lock (_ipcLock)
|
||||
{
|
||||
if (_ipcManager == null)
|
||||
{
|
||||
if (!_ipcUnavailableLogged)
|
||||
{
|
||||
_ipcUnavailableLogged = true;
|
||||
Logger.LogWarning($"QuickAccessCoordinator: unable to send {operationDescription} because IPC is not available.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ipcManager.Send(payload);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"QuickAccessCoordinator: failed to send {operationDescription}.", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeIpc()
|
||||
{
|
||||
lock (_ipcLock)
|
||||
{
|
||||
if (_ipcManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ipcManager.End();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"QuickAccessCoordinator: exception while shutting down IPC. {ex.Message}");
|
||||
}
|
||||
|
||||
_ipcManager.Dispose();
|
||||
_ipcManager = null;
|
||||
_ipcUnavailableLogged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.Interop;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Services
|
||||
{
|
||||
public class QuickAccessLauncher : Microsoft.PowerToys.Settings.UI.Controls.QuickAccessLauncher
|
||||
{
|
||||
private readonly IQuickAccessCoordinator? _coordinator;
|
||||
|
||||
public QuickAccessLauncher(IQuickAccessCoordinator? coordinator)
|
||||
: base(coordinator?.IsRunnerElevated ?? false)
|
||||
{
|
||||
_coordinator = coordinator;
|
||||
}
|
||||
|
||||
public override bool Launch(ModuleType moduleType)
|
||||
{
|
||||
bool moduleRun = base.Launch(moduleType);
|
||||
|
||||
if (moduleRun)
|
||||
{
|
||||
_coordinator?.OnModuleLaunched(moduleType);
|
||||
}
|
||||
|
||||
_coordinator?.HideFlyout();
|
||||
|
||||
return moduleRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
172
src/settings-ui/QuickAccess.UI/ViewModels/AllAppsViewModel.cs
Normal file
172
src/settings-ui/QuickAccess.UI/ViewModels/AllAppsViewModel.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
// 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.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Helpers;
|
||||
using Microsoft.PowerToys.QuickAccess.Services;
|
||||
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.QuickAccess.ViewModels;
|
||||
|
||||
public sealed class AllAppsViewModel : Observable
|
||||
{
|
||||
private readonly IQuickAccessCoordinator _coordinator;
|
||||
private readonly ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private readonly SettingsUtils _settingsUtils;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private GeneralSettings _generalSettings;
|
||||
|
||||
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; }
|
||||
|
||||
public DashboardSortOrder DashboardSortOrder
|
||||
{
|
||||
get => _generalSettings.DashboardSortOrder;
|
||||
set
|
||||
{
|
||||
if (_generalSettings.DashboardSortOrder != value)
|
||||
{
|
||||
_generalSettings.DashboardSortOrder = value;
|
||||
_settingsUtils.SaveSettings(_generalSettings.ToJsonString(), _generalSettings.GetModuleName());
|
||||
OnPropertyChanged();
|
||||
RefreshFlyoutMenuItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AllAppsViewModel(IQuickAccessCoordinator coordinator)
|
||||
{
|
||||
_coordinator = coordinator;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_settingsUtils = SettingsUtils.Default;
|
||||
_settingsRepository = SettingsRepository<GeneralSettings>.GetInstance(_settingsUtils);
|
||||
_generalSettings = _settingsRepository.SettingsConfig;
|
||||
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
|
||||
_settingsRepository.SettingsChanged += OnSettingsChanged;
|
||||
|
||||
_resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
FlyoutMenuItems = new ObservableCollection<FlyoutMenuItem>();
|
||||
|
||||
RefreshFlyoutMenuItems();
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(GeneralSettings newSettings)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
_generalSettings = newSettings;
|
||||
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
|
||||
OnPropertyChanged(nameof(DashboardSortOrder));
|
||||
RefreshFlyoutMenuItems();
|
||||
});
|
||||
}
|
||||
|
||||
public void RefreshSettings()
|
||||
{
|
||||
if (_settingsRepository.ReloadSettings())
|
||||
{
|
||||
OnSettingsChanged(_settingsRepository.SettingsConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshFlyoutMenuItems()
|
||||
{
|
||||
var desiredItems = new List<FlyoutMenuItem>();
|
||||
|
||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||
{
|
||||
if (moduleType == ModuleType.GeneralSettings)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var gpo = Helpers.ModuleGpoHelper.GetModuleGpoConfiguration(moduleType);
|
||||
var isLocked = gpo is GpoRuleConfigured.Enabled or GpoRuleConfigured.Disabled;
|
||||
var isEnabled = gpo == GpoRuleConfigured.Enabled || (!isLocked && Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType));
|
||||
|
||||
var existingItem = FlyoutMenuItems.FirstOrDefault(x => x.Tag == moduleType);
|
||||
|
||||
if (existingItem != null)
|
||||
{
|
||||
existingItem.Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType));
|
||||
existingItem.IsLocked = isLocked;
|
||||
existingItem.Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType);
|
||||
|
||||
if (existingItem.IsEnabled != isEnabled)
|
||||
{
|
||||
var callback = existingItem.EnabledChangedCallback;
|
||||
existingItem.EnabledChangedCallback = null;
|
||||
existingItem.IsEnabled = isEnabled;
|
||||
existingItem.EnabledChangedCallback = callback;
|
||||
}
|
||||
|
||||
desiredItems.Add(existingItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
desiredItems.Add(new FlyoutMenuItem
|
||||
{
|
||||
Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType)),
|
||||
IsEnabled = isEnabled,
|
||||
IsLocked = isLocked,
|
||||
Tag = moduleType,
|
||||
Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType),
|
||||
EnabledChangedCallback = EnabledChangedOnUI,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var sortedItems = DashboardSortOrder switch
|
||||
{
|
||||
DashboardSortOrder.ByStatus => desiredItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label).ToList(),
|
||||
_ => desiredItems.OrderBy(x => x.Label).ToList(),
|
||||
};
|
||||
|
||||
for (int i = FlyoutMenuItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!sortedItems.Contains(FlyoutMenuItems[i]))
|
||||
{
|
||||
FlyoutMenuItems.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < sortedItems.Count; i++)
|
||||
{
|
||||
var item = sortedItems[i];
|
||||
var oldIndex = FlyoutMenuItems.IndexOf(item);
|
||||
|
||||
if (oldIndex < 0)
|
||||
{
|
||||
FlyoutMenuItems.Insert(i, item);
|
||||
}
|
||||
else if (oldIndex != i)
|
||||
{
|
||||
FlyoutMenuItems.Move(oldIndex, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnabledChangedOnUI(FlyoutMenuItem item)
|
||||
{
|
||||
if (_coordinator.UpdateModuleEnabled(item.Tag, item.IsEnabled))
|
||||
{
|
||||
_coordinator.NotifyUserSettingsInteraction();
|
||||
}
|
||||
}
|
||||
|
||||
private void ModuleEnabledChangedOnSettingsPage()
|
||||
{
|
||||
RefreshFlyoutMenuItems();
|
||||
}
|
||||
}
|
||||
52
src/settings-ui/QuickAccess.UI/ViewModels/FlyoutMenuItem.cs
Normal file
52
src/settings-ui/QuickAccess.UI/ViewModels/FlyoutMenuItem.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
|
||||
public sealed class FlyoutMenuItem : ModuleListItem
|
||||
{
|
||||
private bool _visible;
|
||||
|
||||
public string ToolTip { get; set; } = string.Empty;
|
||||
|
||||
public new ModuleType Tag
|
||||
{
|
||||
get => (ModuleType)(base.Tag ?? ModuleType.PowerLauncher);
|
||||
set => base.Tag = value;
|
||||
}
|
||||
|
||||
public override bool IsEnabled
|
||||
{
|
||||
get => base.IsEnabled;
|
||||
set
|
||||
{
|
||||
if (base.IsEnabled != value)
|
||||
{
|
||||
base.IsEnabled = value;
|
||||
EnabledChangedCallback?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action<FlyoutMenuItem>? EnabledChangedCallback { get; set; }
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
if (_visible != value)
|
||||
{
|
||||
_visible = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.ObjectModel;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
|
||||
public sealed class LauncherViewModel : Observable
|
||||
{
|
||||
private readonly IQuickAccessCoordinator _coordinator;
|
||||
private readonly ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private readonly QuickAccessViewModel _quickAccessViewModel;
|
||||
|
||||
public ObservableCollection<QuickAccessItem> FlyoutMenuItems => _quickAccessViewModel.Items;
|
||||
|
||||
public bool IsUpdateAvailable { get; private set; }
|
||||
|
||||
public LauncherViewModel(IQuickAccessCoordinator coordinator)
|
||||
{
|
||||
_coordinator = coordinator;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
var settingsUtils = SettingsUtils.Default;
|
||||
_settingsRepository = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils);
|
||||
|
||||
_resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
|
||||
_quickAccessViewModel = new QuickAccessViewModel(
|
||||
_settingsRepository,
|
||||
new Microsoft.PowerToys.QuickAccess.Services.QuickAccessLauncher(_coordinator),
|
||||
moduleType => Helpers.ModuleGpoHelper.GetModuleGpoConfiguration(moduleType) == GpoRuleConfigured.Disabled,
|
||||
_resourceLoader);
|
||||
var updatingSettings = UpdatingSettings.LoadSettings() ?? new UpdatingSettings();
|
||||
IsUpdateAvailable = updatingSettings.State is UpdatingSettings.UpdatingState.ReadyToInstall or UpdatingSettings.UpdatingState.ReadyToDownload;
|
||||
}
|
||||
}
|
||||
16
src/settings-ui/QuickAccess.UI/app.manifest
Normal file
16
src/settings-ui/QuickAccess.UI/app.manifest
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="PowerToys.QuickAccess.app" />
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
@@ -1,11 +1,11 @@
|
||||
// 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 Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls.Converters
|
||||
{
|
||||
public partial class EnumToBooleanConverter : IValueConverter
|
||||
{
|
||||
@@ -20,7 +20,7 @@ namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
var enumString = value.ToString();
|
||||
var parameterString = parameter.ToString();
|
||||
|
||||
return enumString.Equals(parameterString, StringComparison.OrdinalIgnoreCase);
|
||||
return enumString!.Equals(parameterString, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls.Converters
|
||||
{
|
||||
public partial class ModuleListSortOptionToBooleanConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is ModuleListSortOption sortOption && parameter is string paramString)
|
||||
{
|
||||
if (Enum.TryParse(typeof(ModuleListSortOption), paramString, out object? result) && result != null)
|
||||
{
|
||||
return sortOption == (ModuleListSortOption)result;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool isChecked && isChecked && parameter is string paramString)
|
||||
{
|
||||
if (Enum.TryParse(typeof(ModuleListSortOption), paramString, out object? result) && result != null)
|
||||
{
|
||||
return (ModuleListSortOption)result;
|
||||
}
|
||||
}
|
||||
|
||||
return ModuleListSortOption.Alphabetical;
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/settings-ui/Settings.UI.Controls/ModuleList/ModuleList.xaml
Normal file
107
src/settings-ui/Settings.UI.Controls/ModuleList/ModuleList.xaml
Normal file
@@ -0,0 +1,107 @@
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.ModuleList"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Controls.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tk="using:CommunityToolkit.WinUI"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<converters:ModuleListSortOptionToBooleanConverter x:Key="ModuleListSortOptionToBooleanConverter" />
|
||||
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="BoolToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
TrueValue="Visible" />
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="ReverseBoolToVisibilityConverter"
|
||||
FalseValue="Visible"
|
||||
TrueValue="Collapsed" />
|
||||
<tkconverters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
|
||||
<Style x:Key="NewInfoBadgeStyle" TargetType="InfoBadge">
|
||||
<Setter Property="Padding" Value="5,1,5,2" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="InfoBadge">
|
||||
<Border
|
||||
x:Name="RootGrid"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.InfoBadgeCornerRadius}">
|
||||
<TextBlock
|
||||
x:Uid="NewInfoBadge"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="10" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<ItemsRepeater x:Name="DashboardView" ItemsSource="{x:Bind ItemsSource, Mode=OneWay}">
|
||||
<ItemsRepeater.Layout>
|
||||
<StackLayout Orientation="Vertical" Spacing="0" />
|
||||
</ItemsRepeater.Layout>
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate x:DataType="controls:ModuleListItem">
|
||||
<tkcontrols:SettingsCard
|
||||
MinWidth="0"
|
||||
MinHeight="0"
|
||||
Padding="12,4,12,4"
|
||||
tk:FrameworkElementExtensions.AncestorType="controls:ModuleList"
|
||||
Background="Transparent"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="{Binding (tk:FrameworkElementExtensions.Ancestor).DividerThickness, RelativeSource={RelativeSource Self}}"
|
||||
Click="OnSettingsCardClick"
|
||||
CornerRadius="0"
|
||||
IsClickEnabled="{Binding (tk:FrameworkElementExtensions.Ancestor).IsItemClickable, RelativeSource={RelativeSource Self}}"
|
||||
Tag="{x:Bind}">
|
||||
<tkcontrols:SettingsCard.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{x:Bind Label, Mode=OneWay}" />
|
||||
<!-- InfoBadge -->
|
||||
<InfoBadge
|
||||
x:Name="NewInfoBadge"
|
||||
Margin="4,0,0,0"
|
||||
Style="{StaticResource NewInfoBadgeStyle}"
|
||||
Visibility="{x:Bind IsNew, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<FontIcon
|
||||
Width="20"
|
||||
Margin="4,0,0,0"
|
||||
FontSize="16"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind IsLocked, Converter={StaticResource ReverseBoolToVisibilityConverter}, ConverterParameter=True}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="GPOWarning" TextWrapping="WrapWholeWords" />
|
||||
</ToolTipService.ToolTip>
|
||||
</FontIcon>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Header>
|
||||
|
||||
<tkcontrols:SettingsCard.HeaderIcon>
|
||||
<ImageIcon>
|
||||
<ImageIcon.Source>
|
||||
<BitmapImage UriSource="{x:Bind Icon, Mode=OneWay}" />
|
||||
</ImageIcon.Source>
|
||||
</ImageIcon>
|
||||
</tkcontrols:SettingsCard.HeaderIcon>
|
||||
|
||||
<ToggleSwitch
|
||||
HorizontalAlignment="Right"
|
||||
AutomationProperties.Name="{x:Bind Label, Mode=OneWay}"
|
||||
IsEnabled="{x:Bind IsLocked, Converter={StaticResource BoolNegationConverter}, ConverterParameter=True, Mode=OneWay}"
|
||||
IsOn="{x:Bind IsEnabled, Mode=TwoWay}"
|
||||
OffContent=""
|
||||
OnContent="" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</UserControl>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -21,11 +21,11 @@
|
||||
BorderThickness="{x:Bind BorderThickness, Mode=OneWay}"
|
||||
CornerRadius="{x:Bind CornerRadius, Mode=OneWay}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" MinHeight="44" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="TitleGrid">
|
||||
<Grid x:Name="TitleGrid" MinHeight="44">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<EnvironmentVariablesSettings>.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<HostsSettings>.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.QuickAccessList"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters">
|
||||
|
||||
<UserControl.Resources>
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="BoolToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
TrueValue="Visible" />
|
||||
<tkconverters:StringVisibilityConverter x:Key="StringVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<ItemsControl ItemsSource="{x:Bind ItemsSource, Mode=OneWay}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<tkcontrols:WrapPanel HorizontalAlignment="Stretch" VerticalSpacing="12" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:QuickAccessItem">
|
||||
<local:FlyoutMenuButton
|
||||
AutomationProperties.Name="{x:Bind Title}"
|
||||
Command="{x:Bind Command}"
|
||||
CommandParameter="{x:Bind CommandParameter}"
|
||||
Visibility="{x:Bind Visible, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<local:FlyoutMenuButton.Content>
|
||||
<TextBlock
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Title}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</local:FlyoutMenuButton.Content>
|
||||
<local:FlyoutMenuButton.Icon>
|
||||
<Image>
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind Icon}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
</local:FlyoutMenuButton.Icon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{x:Bind Description}" Visibility="{x:Bind Description, Converter={StaticResource StringVisibilityConverter}}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</local:FlyoutMenuButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</UserControl>
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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<GeneralSettings> _settingsRepository;
|
||||
private readonly IQuickAccessLauncher _launcher;
|
||||
private readonly Func<ModuleType, bool> _isModuleGpoDisabled;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private GeneralSettings _generalSettings;
|
||||
|
||||
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; set; }
|
||||
public ObservableCollection<QuickAccessItem> Items { get; } = new();
|
||||
|
||||
private GeneralSettings generalSettingsConfig;
|
||||
private UpdatingSettings updatingSettingsConfig;
|
||||
private ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private Func<string, int> SendIPCMessage { get; }
|
||||
|
||||
public LauncherViewModel(ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc)
|
||||
public QuickAccessViewModel(
|
||||
ISettingsRepository<GeneralSettings> settingsRepository,
|
||||
IQuickAccessLauncher launcher,
|
||||
Func<ModuleType, bool> 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<FlyoutMenuItem>();
|
||||
_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<ShortcutGuideSettings>.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 }");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.PowerToys.Settings.UI.Controls</RootNamespace>
|
||||
<AssemblyName>PowerToys.Settings.UI.Controls</AssemblyName>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<GenerateLibraryLayout>true</GenerateLibraryLayout>
|
||||
<ProjectPriFileName>PowerToys.Settings.UI.Controls.pri</ProjectPriFileName>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>x64;ARM64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Dashboard_SortBy_ToolTip.Text" xml:space="preserve">
|
||||
<value>Sort utilities</value>
|
||||
</data>
|
||||
<data name="Dashboard_SortAlphabetical.Text" xml:space="preserve">
|
||||
<value>Alphabetically</value>
|
||||
</data>
|
||||
<data name="Dashboard_SortByStatus.Text" xml:space="preserve">
|
||||
<value>By status</value>
|
||||
</data>
|
||||
<data name="Dashboard_SortBy.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Sort utilities</value>
|
||||
</data>
|
||||
<data name="NewInfoBadge.Text" xml:space="preserve">
|
||||
<value>NEW</value>
|
||||
</data>
|
||||
<data name="GPOWarning.Text" xml:space="preserve">
|
||||
<value>This setting is managed by your organization</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,14 +1,9 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls">
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultFlyoutMenuButtonStyle}" TargetType="controls:FlyoutMenuButton" />
|
||||
|
||||
<Style x:Key="DefaultFlyoutMenuButtonStyle" TargetType="controls:FlyoutMenuButton">
|
||||
<Style TargetType="controls:FlyoutMenuButton">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Width" Value="116" />
|
||||
@@ -7,6 +7,7 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
using Settings.UI.Library.Attributes;
|
||||
@@ -19,7 +20,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
ByStatus,
|
||||
}
|
||||
|
||||
public class GeneralSettings : ISettingsConfig
|
||||
public class GeneralSettings : ISettingsConfig, IHotkeyConfig
|
||||
{
|
||||
// Gets or sets a value indicating whether run powertoys on start-up.
|
||||
[JsonPropertyName("startup")]
|
||||
@@ -48,6 +49,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("enable_warnings_elevated_apps")]
|
||||
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.
|
||||
[JsonPropertyName("theme")]
|
||||
public string Theme { get; set; }
|
||||
@@ -94,6 +103,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
ShowSysTrayIcon = true;
|
||||
IsAdmin = false;
|
||||
EnableWarningsElevatedApps = true;
|
||||
EnableQuickAccess = true;
|
||||
QuickAccessShortcut = new HotkeySettings();
|
||||
IsElevated = false;
|
||||
ShowNewUpdatesToastNotification = true;
|
||||
AutoDownloadUpdates = false;
|
||||
@@ -116,6 +127,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
IgnoredConflictProperties = new ShortcutConflictProperties();
|
||||
}
|
||||
|
||||
public HotkeyAccessor[] GetAllHotkeyAccessors()
|
||||
{
|
||||
return new HotkeyAccessor[]
|
||||
{
|
||||
new HotkeyAccessor(
|
||||
() => QuickAccessShortcut,
|
||||
(hotkey) => { QuickAccessShortcut = hotkey; },
|
||||
"GeneralPage_QuickAccessShortcut"),
|
||||
};
|
||||
}
|
||||
|
||||
public ModuleType GetModuleType()
|
||||
{
|
||||
return ModuleType.GeneralSettings;
|
||||
}
|
||||
|
||||
// converts the current to a json string.
|
||||
public string ToJsonString()
|
||||
{
|
||||
|
||||
121
src/settings-ui/Settings.UI.Library/Helpers/ModuleHelper.cs
Normal file
121
src/settings-ui/Settings.UI.Library/Helpers/ModuleHelper.cs
Normal file
@@ -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 ManagedCommon;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.Helpers
|
||||
{
|
||||
public static class ModuleHelper
|
||||
{
|
||||
public static string GetModuleLabelResourceName(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.Workspaces => "Workspaces/ModuleTitle",
|
||||
ModuleType.PowerAccent => "QuickAccent/ModuleTitle",
|
||||
ModuleType.PowerOCR => "TextExtractor/ModuleTitle",
|
||||
ModuleType.FindMyMouse => "MouseUtils_FindMyMouse/Header",
|
||||
ModuleType.MouseHighlighter => "MouseUtils_MouseHighlighter/Header",
|
||||
ModuleType.MouseJump => "MouseUtils_MouseJump/Header",
|
||||
ModuleType.MousePointerCrosshairs => "MouseUtils_MousePointerCrosshairs/Header",
|
||||
ModuleType.CursorWrap => "MouseUtils_CursorWrap/Header",
|
||||
ModuleType.GeneralSettings => "QuickAccessTitle/Title",
|
||||
_ => $"{moduleType}/ModuleTitle",
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetModuleTypeFluentIconName(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => "ms-appx:///Assets/Settings/Icons/AdvancedPaste.png",
|
||||
ModuleType.Workspaces => "ms-appx:///Assets/Settings/Icons/Workspaces.png",
|
||||
ModuleType.PowerOCR => "ms-appx:///Assets/Settings/Icons/TextExtractor.png",
|
||||
ModuleType.PowerAccent => "ms-appx:///Assets/Settings/Icons/QuickAccent.png",
|
||||
ModuleType.MousePointerCrosshairs => "ms-appx:///Assets/Settings/Icons/MouseCrosshairs.png",
|
||||
ModuleType.MeasureTool => "ms-appx:///Assets/Settings/Icons/ScreenRuler.png",
|
||||
ModuleType.PowerLauncher => "ms-appx:///Assets/Settings/Icons/PowerToysRun.png",
|
||||
ModuleType.GeneralSettings => "ms-appx:///Assets/Settings/Icons/PowerToys.png",
|
||||
_ => $"ms-appx:///Assets/Settings/Icons/{moduleType}.png",
|
||||
};
|
||||
}
|
||||
|
||||
public static bool GetIsModuleEnabled(GeneralSettings generalSettingsConfig, ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => generalSettingsConfig.Enabled.AdvancedPaste,
|
||||
ModuleType.AlwaysOnTop => generalSettingsConfig.Enabled.AlwaysOnTop,
|
||||
ModuleType.Awake => generalSettingsConfig.Enabled.Awake,
|
||||
ModuleType.CmdPal => generalSettingsConfig.Enabled.CmdPal,
|
||||
ModuleType.ColorPicker => generalSettingsConfig.Enabled.ColorPicker,
|
||||
ModuleType.CropAndLock => generalSettingsConfig.Enabled.CropAndLock,
|
||||
ModuleType.CursorWrap => generalSettingsConfig.Enabled.CursorWrap,
|
||||
ModuleType.EnvironmentVariables => generalSettingsConfig.Enabled.EnvironmentVariables,
|
||||
ModuleType.FancyZones => generalSettingsConfig.Enabled.FancyZones,
|
||||
ModuleType.FileLocksmith => generalSettingsConfig.Enabled.FileLocksmith,
|
||||
ModuleType.FindMyMouse => generalSettingsConfig.Enabled.FindMyMouse,
|
||||
ModuleType.Hosts => generalSettingsConfig.Enabled.Hosts,
|
||||
ModuleType.ImageResizer => generalSettingsConfig.Enabled.ImageResizer,
|
||||
ModuleType.KeyboardManager => generalSettingsConfig.Enabled.KeyboardManager,
|
||||
ModuleType.LightSwitch => generalSettingsConfig.Enabled.LightSwitch,
|
||||
ModuleType.MouseHighlighter => generalSettingsConfig.Enabled.MouseHighlighter,
|
||||
ModuleType.MouseJump => generalSettingsConfig.Enabled.MouseJump,
|
||||
ModuleType.MousePointerCrosshairs => generalSettingsConfig.Enabled.MousePointerCrosshairs,
|
||||
ModuleType.MouseWithoutBorders => generalSettingsConfig.Enabled.MouseWithoutBorders,
|
||||
ModuleType.NewPlus => generalSettingsConfig.Enabled.NewPlus,
|
||||
ModuleType.Peek => generalSettingsConfig.Enabled.Peek,
|
||||
ModuleType.PowerRename => generalSettingsConfig.Enabled.PowerRename,
|
||||
ModuleType.PowerLauncher => generalSettingsConfig.Enabled.PowerLauncher,
|
||||
ModuleType.PowerAccent => generalSettingsConfig.Enabled.PowerAccent,
|
||||
ModuleType.RegistryPreview => generalSettingsConfig.Enabled.RegistryPreview,
|
||||
ModuleType.MeasureTool => generalSettingsConfig.Enabled.MeasureTool,
|
||||
ModuleType.ShortcutGuide => generalSettingsConfig.Enabled.ShortcutGuide,
|
||||
ModuleType.PowerOCR => generalSettingsConfig.Enabled.PowerOcr,
|
||||
ModuleType.Workspaces => generalSettingsConfig.Enabled.Workspaces,
|
||||
ModuleType.ZoomIt => generalSettingsConfig.Enabled.ZoomIt,
|
||||
ModuleType.GeneralSettings => generalSettingsConfig.EnableQuickAccess,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public static void SetIsModuleEnabled(GeneralSettings generalSettingsConfig, ModuleType moduleType, bool isEnabled)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: generalSettingsConfig.Enabled.AdvancedPaste = isEnabled; break;
|
||||
case ModuleType.AlwaysOnTop: generalSettingsConfig.Enabled.AlwaysOnTop = isEnabled; break;
|
||||
case ModuleType.Awake: generalSettingsConfig.Enabled.Awake = isEnabled; break;
|
||||
case ModuleType.CmdPal: generalSettingsConfig.Enabled.CmdPal = isEnabled; break;
|
||||
case ModuleType.ColorPicker: generalSettingsConfig.Enabled.ColorPicker = isEnabled; break;
|
||||
case ModuleType.CropAndLock: generalSettingsConfig.Enabled.CropAndLock = isEnabled; break;
|
||||
case ModuleType.CursorWrap: generalSettingsConfig.Enabled.CursorWrap = isEnabled; break;
|
||||
case ModuleType.EnvironmentVariables: generalSettingsConfig.Enabled.EnvironmentVariables = isEnabled; break;
|
||||
case ModuleType.FancyZones: generalSettingsConfig.Enabled.FancyZones = isEnabled; break;
|
||||
case ModuleType.FileLocksmith: generalSettingsConfig.Enabled.FileLocksmith = isEnabled; break;
|
||||
case ModuleType.FindMyMouse: generalSettingsConfig.Enabled.FindMyMouse = isEnabled; break;
|
||||
case ModuleType.Hosts: generalSettingsConfig.Enabled.Hosts = isEnabled; break;
|
||||
case ModuleType.ImageResizer: generalSettingsConfig.Enabled.ImageResizer = isEnabled; break;
|
||||
case ModuleType.KeyboardManager: generalSettingsConfig.Enabled.KeyboardManager = isEnabled; break;
|
||||
case ModuleType.LightSwitch: generalSettingsConfig.Enabled.LightSwitch = isEnabled; break;
|
||||
case ModuleType.MouseHighlighter: generalSettingsConfig.Enabled.MouseHighlighter = isEnabled; break;
|
||||
case ModuleType.MouseJump: generalSettingsConfig.Enabled.MouseJump = isEnabled; break;
|
||||
case ModuleType.MousePointerCrosshairs: generalSettingsConfig.Enabled.MousePointerCrosshairs = isEnabled; break;
|
||||
case ModuleType.MouseWithoutBorders: generalSettingsConfig.Enabled.MouseWithoutBorders = isEnabled; break;
|
||||
case ModuleType.NewPlus: generalSettingsConfig.Enabled.NewPlus = isEnabled; break;
|
||||
case ModuleType.Peek: generalSettingsConfig.Enabled.Peek = isEnabled; break;
|
||||
case ModuleType.PowerRename: generalSettingsConfig.Enabled.PowerRename = isEnabled; break;
|
||||
case ModuleType.PowerLauncher: generalSettingsConfig.Enabled.PowerLauncher = isEnabled; break;
|
||||
case ModuleType.PowerAccent: generalSettingsConfig.Enabled.PowerAccent = isEnabled; break;
|
||||
case ModuleType.RegistryPreview: generalSettingsConfig.Enabled.RegistryPreview = isEnabled; break;
|
||||
case ModuleType.MeasureTool: generalSettingsConfig.Enabled.MeasureTool = isEnabled; break;
|
||||
case ModuleType.ShortcutGuide: generalSettingsConfig.Enabled.ShortcutGuide = isEnabled; break;
|
||||
case ModuleType.PowerOCR: generalSettingsConfig.Enabled.PowerOcr = isEnabled; break;
|
||||
case ModuleType.Workspaces: generalSettingsConfig.Enabled.Workspaces = isEnabled; break;
|
||||
case ModuleType.ZoomIt: generalSettingsConfig.Enabled.ZoomIt = isEnabled; break;
|
||||
case ModuleType.GeneralSettings: generalSettingsConfig.EnableQuickAccess = isEnabled; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
|
||||
{
|
||||
public interface ISettingsRepository<T>
|
||||
@@ -9,5 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
|
||||
T SettingsConfig { get; set; }
|
||||
|
||||
bool ReloadSettings();
|
||||
|
||||
event Action<T> SettingsChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,11 @@ namespace Microsoft.PowerToys.Settings.UI.Services
|
||||
if (settingsInstance != null)
|
||||
{
|
||||
var moduleName = settingsInstance.GetModuleName();
|
||||
if (string.IsNullOrEmpty(moduleName) && type == typeof(GeneralSettings))
|
||||
{
|
||||
moduleName = "GeneralSettings";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(moduleName))
|
||||
{
|
||||
settingsTypes[moduleName] = type;
|
||||
@@ -104,7 +109,13 @@ namespace Microsoft.PowerToys.Settings.UI.Services
|
||||
var genericMethod = getSettingsMethod?.MakeGenericMethod(settingsType);
|
||||
|
||||
// Call GetSettingsOrDefault<T>(moduleKey) to get fresh settings from file
|
||||
var freshSettings = genericMethod?.Invoke(_settingsUtils, new object[] { moduleKey, "settings.json" });
|
||||
string actualModuleKey = moduleKey;
|
||||
if (moduleKey == "GeneralSettings")
|
||||
{
|
||||
actualModuleKey = string.Empty;
|
||||
}
|
||||
|
||||
var freshSettings = genericMethod?.Invoke(_settingsUtils, new object[] { actualModuleKey, "settings.json" });
|
||||
|
||||
return freshSettings as IHotkeyConfig;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
// This Singleton class is a wrapper around the settings configurations that are accessed by viewmodels.
|
||||
// This class can have only one instance and therefore the settings configurations are common to all.
|
||||
public class SettingsRepository<T> : ISettingsRepository<T>
|
||||
public sealed class SettingsRepository<T> : ISettingsRepository<T>, IDisposable
|
||||
where T : class, ISettingsConfig, new()
|
||||
{
|
||||
private static readonly Lock _SettingsRepoLock = new Lock();
|
||||
@@ -22,6 +23,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
private T settingsConfig;
|
||||
|
||||
private FileSystemWatcher _watcher;
|
||||
|
||||
public event Action<T> SettingsChanged;
|
||||
|
||||
// Suppressing the warning as this is a singleton class and this method is
|
||||
// necessarily static
|
||||
#pragma warning disable CA1000 // Do not declare static members on generic types
|
||||
@@ -35,6 +40,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
settingsRepository = new SettingsRepository<T>();
|
||||
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
|
||||
settingsRepository.InitializeWatcher();
|
||||
}
|
||||
|
||||
return settingsRepository;
|
||||
@@ -46,12 +52,51 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
}
|
||||
|
||||
private void InitializeWatcher()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settingsItem = new T();
|
||||
var filePath = _settingsUtils.GetSettingsFilePath(settingsItem.GetModuleName());
|
||||
var directory = Path.GetDirectoryName(filePath);
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
_watcher = new FileSystemWatcher(directory, fileName);
|
||||
_watcher.NotifyFilter = NotifyFilters.LastWrite;
|
||||
_watcher.Changed += Watcher_Changed;
|
||||
_watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to initialize settings watcher for {typeof(T).Name}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Watcher_Changed(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
// Wait a bit for the file write to complete and retry if needed
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
if (ReloadSettings())
|
||||
{
|
||||
SettingsChanged?.Invoke(SettingsConfig);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReloadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
T settingsItem = new T();
|
||||
settingsConfig = _settingsUtils.GetSettingsOrDefault<T>(settingsItem.GetModuleName());
|
||||
settingsConfig = _settingsUtils.GetSettings<T>(settingsItem.GetModuleName());
|
||||
|
||||
SettingsConfig = settingsConfig;
|
||||
|
||||
@@ -85,5 +130,26 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StopWatching()
|
||||
{
|
||||
if (_watcher != null)
|
||||
{
|
||||
_watcher.EnableRaisingEvents = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartWatching()
|
||||
{
|
||||
if (_watcher != null)
|
||||
{
|
||||
_watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_watcher?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
// Main Settings Classes
|
||||
[JsonSerializable(typeof(GeneralSettings))]
|
||||
[JsonSerializable(typeof(OutGoingGeneralSettings))]
|
||||
[JsonSerializable(typeof(AdvancedPasteSettings))]
|
||||
[JsonSerializable(typeof(AlwaysOnTopSettings))]
|
||||
[JsonSerializable(typeof(AwakeSettings))]
|
||||
|
||||
@@ -30,6 +30,11 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility
|
||||
private readonly SettingsUtils _settingsUtils;
|
||||
private T _settingsConfig;
|
||||
|
||||
// Implements ISettingsRepository<T>.SettingsChanged
|
||||
#pragma warning disable CS0067
|
||||
public event System.Action<T> SettingsChanged;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public MockSettingsRepository(SettingsUtils settingsUtils)
|
||||
{
|
||||
_settingsUtils = settingsUtils;
|
||||
|
||||
@@ -28,6 +28,28 @@ namespace ViewModelTests
|
||||
mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<GeneralSettings>();
|
||||
}
|
||||
|
||||
private sealed class TestGeneralViewModel : GeneralViewModel
|
||||
{
|
||||
public TestGeneralViewModel(
|
||||
Microsoft.PowerToys.Settings.UI.Library.Interfaces.ISettingsRepository<GeneralSettings> settingsRepository,
|
||||
string runAsAdminText,
|
||||
string runAsUserText,
|
||||
bool isElevated,
|
||||
bool isAdmin,
|
||||
Func<string, int> ipcMSGCallBackFunc,
|
||||
Func<string, int> ipcMSGRestartAsAdminMSGCallBackFunc,
|
||||
Func<string, int> ipcMSGCheckForUpdatesCallBackFunc,
|
||||
string configFileSubfolder = "")
|
||||
: base(settingsRepository, runAsAdminText, runAsUserText, isElevated, isAdmin, ipcMSGCallBackFunc, ipcMSGRestartAsAdminMSGCallBackFunc, ipcMSGCheckForUpdatesCallBackFunc, configFileSubfolder)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Microsoft.UI.Dispatching.DispatcherQueue GetDispatcherQueue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("v0.18.2")]
|
||||
[DataRow("v0.19.2")]
|
||||
@@ -49,7 +71,7 @@ namespace ViewModelTests
|
||||
Func<string, int> sendMockIPCConfigMSG = msg => 0;
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => 0;
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => 0;
|
||||
var viewModel = new GeneralViewModel(
|
||||
var viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: generalSettingsRepository,
|
||||
runAsAdminText: "GeneralSettings_RunningAsAdminText",
|
||||
runAsUserText: "GeneralSettings_RunningAsUserText",
|
||||
@@ -78,7 +100,7 @@ namespace ViewModelTests
|
||||
Func<string, int> sendMockIPCConfigMSG = msg => { return 0; };
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
GeneralViewModel viewModel = new GeneralViewModel(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -114,7 +136,7 @@ namespace ViewModelTests
|
||||
// Arrange
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
GeneralViewModel viewModel = new GeneralViewModel(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -145,7 +167,7 @@ namespace ViewModelTests
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
|
||||
// Arrange
|
||||
GeneralViewModel viewModel = new GeneralViewModel(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -178,7 +200,7 @@ namespace ViewModelTests
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
viewModel = new GeneralViewModel(
|
||||
viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -208,7 +230,7 @@ namespace ViewModelTests
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
GeneralViewModel viewModel = new GeneralViewModel(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -238,7 +260,7 @@ namespace ViewModelTests
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
GeneralViewModel viewModel = new(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
public partial class EnumToModuleListSortOptionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is DashboardSortOrder sortOrder)
|
||||
{
|
||||
return sortOrder switch
|
||||
{
|
||||
DashboardSortOrder.Alphabetical => ModuleListSortOption.Alphabetical,
|
||||
DashboardSortOrder.ByStatus => ModuleListSortOption.ByStatus,
|
||||
_ => ModuleListSortOption.Alphabetical,
|
||||
};
|
||||
}
|
||||
|
||||
return ModuleListSortOption.Alphabetical;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is ModuleListSortOption sortOption)
|
||||
{
|
||||
return sortOption switch
|
||||
{
|
||||
ModuleListSortOption.Alphabetical => DashboardSortOrder.Alphabetical,
|
||||
ModuleListSortOption.ByStatus => DashboardSortOrder.ByStatus,
|
||||
_ => DashboardSortOrder.Alphabetical,
|
||||
};
|
||||
}
|
||||
|
||||
return DashboardSortOrder.Alphabetical;
|
||||
}
|
||||
}
|
||||
}
|
||||
91
src/settings-ui/Settings.UI/Helpers/ModuleGpoHelper.cs
Normal file
91
src/settings-ui/Settings.UI/Helpers/ModuleGpoHelper.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Helpers
|
||||
{
|
||||
internal sealed class ModuleGpoHelper
|
||||
{
|
||||
public static GpoRuleConfigured GetModuleGpoConfiguration(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: return GPOWrapper.GetConfiguredAdvancedPasteEnabledValue();
|
||||
case ModuleType.AlwaysOnTop: return GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue();
|
||||
case ModuleType.Awake: return GPOWrapper.GetConfiguredAwakeEnabledValue();
|
||||
case ModuleType.CmdPal: return GPOWrapper.GetConfiguredCmdPalEnabledValue();
|
||||
case ModuleType.ColorPicker: return GPOWrapper.GetConfiguredColorPickerEnabledValue();
|
||||
case ModuleType.CropAndLock: return GPOWrapper.GetConfiguredCropAndLockEnabledValue();
|
||||
case ModuleType.CursorWrap: return GPOWrapper.GetConfiguredCursorWrapEnabledValue();
|
||||
case ModuleType.EnvironmentVariables: return GPOWrapper.GetConfiguredEnvironmentVariablesEnabledValue();
|
||||
case ModuleType.FancyZones: return GPOWrapper.GetConfiguredFancyZonesEnabledValue();
|
||||
case ModuleType.FileLocksmith: return GPOWrapper.GetConfiguredFileLocksmithEnabledValue();
|
||||
case ModuleType.FindMyMouse: return GPOWrapper.GetConfiguredFindMyMouseEnabledValue();
|
||||
case ModuleType.Hosts: return GPOWrapper.GetConfiguredHostsFileEditorEnabledValue();
|
||||
case ModuleType.ImageResizer: return GPOWrapper.GetConfiguredImageResizerEnabledValue();
|
||||
case ModuleType.KeyboardManager: return GPOWrapper.GetConfiguredKeyboardManagerEnabledValue();
|
||||
case ModuleType.MouseHighlighter: return GPOWrapper.GetConfiguredMouseHighlighterEnabledValue();
|
||||
case ModuleType.MouseJump: return GPOWrapper.GetConfiguredMouseJumpEnabledValue();
|
||||
case ModuleType.MousePointerCrosshairs: return GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue();
|
||||
case ModuleType.MouseWithoutBorders: return GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue();
|
||||
case ModuleType.NewPlus: return GPOWrapper.GetConfiguredNewPlusEnabledValue();
|
||||
case ModuleType.Peek: return GPOWrapper.GetConfiguredPeekEnabledValue();
|
||||
case ModuleType.PowerRename: return GPOWrapper.GetConfiguredPowerRenameEnabledValue();
|
||||
case ModuleType.PowerLauncher: return GPOWrapper.GetConfiguredPowerLauncherEnabledValue();
|
||||
case ModuleType.PowerAccent: return GPOWrapper.GetConfiguredQuickAccentEnabledValue();
|
||||
case ModuleType.Workspaces: return GPOWrapper.GetConfiguredWorkspacesEnabledValue();
|
||||
case ModuleType.RegistryPreview: return GPOWrapper.GetConfiguredRegistryPreviewEnabledValue();
|
||||
case ModuleType.MeasureTool: return GPOWrapper.GetConfiguredScreenRulerEnabledValue();
|
||||
case ModuleType.ShortcutGuide: return GPOWrapper.GetConfiguredShortcutGuideEnabledValue();
|
||||
case ModuleType.PowerOCR: return GPOWrapper.GetConfiguredTextExtractorEnabledValue();
|
||||
case ModuleType.ZoomIt: return GPOWrapper.GetConfiguredZoomItEnabledValue();
|
||||
default: return GpoRuleConfigured.Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
public static System.Type GetModulePageType(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => typeof(AdvancedPastePage),
|
||||
ModuleType.AlwaysOnTop => typeof(AlwaysOnTopPage),
|
||||
ModuleType.Awake => typeof(AwakePage),
|
||||
ModuleType.CmdPal => typeof(CmdPalPage),
|
||||
ModuleType.ColorPicker => typeof(ColorPickerPage),
|
||||
ModuleType.CropAndLock => typeof(CropAndLockPage),
|
||||
ModuleType.CursorWrap => typeof(MouseUtilsPage),
|
||||
ModuleType.LightSwitch => typeof(LightSwitchPage),
|
||||
ModuleType.EnvironmentVariables => typeof(EnvironmentVariablesPage),
|
||||
ModuleType.FancyZones => typeof(FancyZonesPage),
|
||||
ModuleType.FileLocksmith => typeof(FileLocksmithPage),
|
||||
ModuleType.FindMyMouse => typeof(MouseUtilsPage),
|
||||
ModuleType.GeneralSettings => typeof(GeneralPage),
|
||||
ModuleType.Hosts => typeof(HostsPage),
|
||||
ModuleType.ImageResizer => typeof(ImageResizerPage),
|
||||
ModuleType.KeyboardManager => typeof(KeyboardManagerPage),
|
||||
ModuleType.MouseHighlighter => typeof(MouseUtilsPage),
|
||||
ModuleType.MouseJump => typeof(MouseUtilsPage),
|
||||
ModuleType.MousePointerCrosshairs => typeof(MouseUtilsPage),
|
||||
ModuleType.MouseWithoutBorders => typeof(MouseWithoutBordersPage),
|
||||
ModuleType.NewPlus => typeof(NewPlusPage),
|
||||
ModuleType.Peek => typeof(PeekPage),
|
||||
ModuleType.PowerRename => typeof(PowerRenamePage),
|
||||
ModuleType.PowerLauncher => typeof(PowerLauncherPage),
|
||||
ModuleType.PowerAccent => typeof(PowerAccentPage),
|
||||
ModuleType.Workspaces => typeof(WorkspacesPage),
|
||||
ModuleType.RegistryPreview => typeof(RegistryPreviewPage),
|
||||
ModuleType.MeasureTool => typeof(MeasureToolPage),
|
||||
ModuleType.ShortcutGuide => typeof(ShortcutGuidePage),
|
||||
ModuleType.PowerOCR => typeof(PowerOcrPage),
|
||||
ModuleType.ZoomIt => typeof(ZoomItPage),
|
||||
_ => typeof(DashboardPage), // never called, all values listed above
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,196 +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 global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Helpers
|
||||
{
|
||||
internal sealed class ModuleHelper
|
||||
{
|
||||
public static string GetModuleLabelResourceName(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.Workspaces: return "Workspaces/ModuleTitle";
|
||||
case ModuleType.PowerAccent: return "QuickAccent/ModuleTitle";
|
||||
case ModuleType.PowerOCR: return "TextExtractor/ModuleTitle";
|
||||
case ModuleType.FindMyMouse:
|
||||
case ModuleType.MouseHighlighter:
|
||||
case ModuleType.MouseJump:
|
||||
case ModuleType.MousePointerCrosshairs:
|
||||
case ModuleType.CursorWrap: return $"MouseUtils_{moduleType}/Header";
|
||||
default: return $"{moduleType}/ModuleTitle";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetModuleTypeFluentIconName(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: return "ms-appx:///Assets/Settings/Icons/AdvancedPaste.png";
|
||||
case ModuleType.Workspaces: return "ms-appx:///Assets/Settings/Icons/Workspaces.png";
|
||||
case ModuleType.PowerOCR: return "ms-appx:///Assets/Settings/Icons/TextExtractor.png";
|
||||
case ModuleType.PowerAccent: return "ms-appx:///Assets/Settings/Icons/QuickAccent.png";
|
||||
case ModuleType.MousePointerCrosshairs: return "ms-appx:///Assets/Settings/Icons/MouseCrosshairs.png";
|
||||
case ModuleType.MeasureTool: return "ms-appx:///Assets/Settings/Icons/ScreenRuler.png";
|
||||
case ModuleType.PowerLauncher: return $"ms-appx:///Assets/Settings/Icons/PowerToysRun.png";
|
||||
default: return $"ms-appx:///Assets/Settings/Icons/{moduleType}.png";
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetIsModuleEnabled(Library.GeneralSettings generalSettingsConfig, ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: return generalSettingsConfig.Enabled.AdvancedPaste;
|
||||
case ModuleType.AlwaysOnTop: return generalSettingsConfig.Enabled.AlwaysOnTop;
|
||||
case ModuleType.Awake: return generalSettingsConfig.Enabled.Awake;
|
||||
case ModuleType.CmdPal: return generalSettingsConfig.Enabled.CmdPal;
|
||||
case ModuleType.ColorPicker: return generalSettingsConfig.Enabled.ColorPicker;
|
||||
case ModuleType.CropAndLock: return generalSettingsConfig.Enabled.CropAndLock;
|
||||
case ModuleType.CursorWrap: return generalSettingsConfig.Enabled.CursorWrap;
|
||||
case ModuleType.LightSwitch: return generalSettingsConfig.Enabled.LightSwitch;
|
||||
case ModuleType.EnvironmentVariables: return generalSettingsConfig.Enabled.EnvironmentVariables;
|
||||
case ModuleType.FancyZones: return generalSettingsConfig.Enabled.FancyZones;
|
||||
case ModuleType.FileLocksmith: return generalSettingsConfig.Enabled.FileLocksmith;
|
||||
case ModuleType.FindMyMouse: return generalSettingsConfig.Enabled.FindMyMouse;
|
||||
case ModuleType.Hosts: return generalSettingsConfig.Enabled.Hosts;
|
||||
case ModuleType.ImageResizer: return generalSettingsConfig.Enabled.ImageResizer;
|
||||
case ModuleType.KeyboardManager: return generalSettingsConfig.Enabled.KeyboardManager;
|
||||
case ModuleType.MouseHighlighter: return generalSettingsConfig.Enabled.MouseHighlighter;
|
||||
case ModuleType.MouseJump: return generalSettingsConfig.Enabled.MouseJump;
|
||||
case ModuleType.MousePointerCrosshairs: return generalSettingsConfig.Enabled.MousePointerCrosshairs;
|
||||
case ModuleType.MouseWithoutBorders: return generalSettingsConfig.Enabled.MouseWithoutBorders;
|
||||
case ModuleType.NewPlus: return generalSettingsConfig.Enabled.NewPlus;
|
||||
case ModuleType.Peek: return generalSettingsConfig.Enabled.Peek;
|
||||
case ModuleType.PowerRename: return generalSettingsConfig.Enabled.PowerRename;
|
||||
case ModuleType.PowerLauncher: return generalSettingsConfig.Enabled.PowerLauncher;
|
||||
case ModuleType.PowerAccent: return generalSettingsConfig.Enabled.PowerAccent;
|
||||
case ModuleType.Workspaces: return generalSettingsConfig.Enabled.Workspaces;
|
||||
case ModuleType.RegistryPreview: return generalSettingsConfig.Enabled.RegistryPreview;
|
||||
case ModuleType.MeasureTool: return generalSettingsConfig.Enabled.MeasureTool;
|
||||
case ModuleType.ShortcutGuide: return generalSettingsConfig.Enabled.ShortcutGuide;
|
||||
case ModuleType.PowerOCR: return generalSettingsConfig.Enabled.PowerOcr;
|
||||
case ModuleType.ZoomIt: return generalSettingsConfig.Enabled.ZoomIt;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetIsModuleEnabled(GeneralSettings generalSettingsConfig, ModuleType moduleType, bool isEnabled)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: generalSettingsConfig.Enabled.AdvancedPaste = isEnabled; break;
|
||||
case ModuleType.AlwaysOnTop: generalSettingsConfig.Enabled.AlwaysOnTop = isEnabled; break;
|
||||
case ModuleType.Awake: generalSettingsConfig.Enabled.Awake = isEnabled; break;
|
||||
case ModuleType.CmdPal: generalSettingsConfig.Enabled.CmdPal = isEnabled; break;
|
||||
case ModuleType.ColorPicker: generalSettingsConfig.Enabled.ColorPicker = isEnabled; break;
|
||||
case ModuleType.CropAndLock: generalSettingsConfig.Enabled.CropAndLock = isEnabled; break;
|
||||
case ModuleType.CursorWrap: generalSettingsConfig.Enabled.CursorWrap = isEnabled; break;
|
||||
case ModuleType.LightSwitch: generalSettingsConfig.Enabled.LightSwitch = isEnabled; break;
|
||||
case ModuleType.EnvironmentVariables: generalSettingsConfig.Enabled.EnvironmentVariables = isEnabled; break;
|
||||
case ModuleType.FancyZones: generalSettingsConfig.Enabled.FancyZones = isEnabled; break;
|
||||
case ModuleType.FileLocksmith: generalSettingsConfig.Enabled.FileLocksmith = isEnabled; break;
|
||||
case ModuleType.FindMyMouse: generalSettingsConfig.Enabled.FindMyMouse = isEnabled; break;
|
||||
case ModuleType.Hosts: generalSettingsConfig.Enabled.Hosts = isEnabled; break;
|
||||
case ModuleType.ImageResizer: generalSettingsConfig.Enabled.ImageResizer = isEnabled; break;
|
||||
case ModuleType.KeyboardManager: generalSettingsConfig.Enabled.KeyboardManager = isEnabled; break;
|
||||
case ModuleType.MouseHighlighter: generalSettingsConfig.Enabled.MouseHighlighter = isEnabled; break;
|
||||
case ModuleType.MouseJump: generalSettingsConfig.Enabled.MouseJump = isEnabled; break;
|
||||
case ModuleType.MousePointerCrosshairs: generalSettingsConfig.Enabled.MousePointerCrosshairs = isEnabled; break;
|
||||
case ModuleType.MouseWithoutBorders: generalSettingsConfig.Enabled.MouseWithoutBorders = isEnabled; break;
|
||||
case ModuleType.NewPlus: generalSettingsConfig.Enabled.NewPlus = isEnabled; break;
|
||||
case ModuleType.Peek: generalSettingsConfig.Enabled.Peek = isEnabled; break;
|
||||
case ModuleType.PowerRename: generalSettingsConfig.Enabled.PowerRename = isEnabled; break;
|
||||
case ModuleType.PowerLauncher: generalSettingsConfig.Enabled.PowerLauncher = isEnabled; break;
|
||||
case ModuleType.PowerAccent: generalSettingsConfig.Enabled.PowerAccent = isEnabled; break;
|
||||
case ModuleType.Workspaces: generalSettingsConfig.Enabled.Workspaces = isEnabled; break;
|
||||
case ModuleType.RegistryPreview: generalSettingsConfig.Enabled.RegistryPreview = isEnabled; break;
|
||||
case ModuleType.MeasureTool: generalSettingsConfig.Enabled.MeasureTool = isEnabled; break;
|
||||
case ModuleType.ShortcutGuide: generalSettingsConfig.Enabled.ShortcutGuide = isEnabled; break;
|
||||
case ModuleType.PowerOCR: generalSettingsConfig.Enabled.PowerOcr = isEnabled; break;
|
||||
case ModuleType.ZoomIt: generalSettingsConfig.Enabled.ZoomIt = isEnabled; break;
|
||||
}
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetModuleGpoConfiguration(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: return GPOWrapper.GetConfiguredAdvancedPasteEnabledValue();
|
||||
case ModuleType.AlwaysOnTop: return GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue();
|
||||
case ModuleType.Awake: return GPOWrapper.GetConfiguredAwakeEnabledValue();
|
||||
case ModuleType.CmdPal: return GPOWrapper.GetConfiguredCmdPalEnabledValue();
|
||||
case ModuleType.ColorPicker: return GPOWrapper.GetConfiguredColorPickerEnabledValue();
|
||||
case ModuleType.CropAndLock: return GPOWrapper.GetConfiguredCropAndLockEnabledValue();
|
||||
case ModuleType.CursorWrap: return GPOWrapper.GetConfiguredCursorWrapEnabledValue();
|
||||
case ModuleType.EnvironmentVariables: return GPOWrapper.GetConfiguredEnvironmentVariablesEnabledValue();
|
||||
case ModuleType.FancyZones: return GPOWrapper.GetConfiguredFancyZonesEnabledValue();
|
||||
case ModuleType.FileLocksmith: return GPOWrapper.GetConfiguredFileLocksmithEnabledValue();
|
||||
case ModuleType.FindMyMouse: return GPOWrapper.GetConfiguredFindMyMouseEnabledValue();
|
||||
case ModuleType.Hosts: return GPOWrapper.GetConfiguredHostsFileEditorEnabledValue();
|
||||
case ModuleType.ImageResizer: return GPOWrapper.GetConfiguredImageResizerEnabledValue();
|
||||
case ModuleType.KeyboardManager: return GPOWrapper.GetConfiguredKeyboardManagerEnabledValue();
|
||||
case ModuleType.MouseHighlighter: return GPOWrapper.GetConfiguredMouseHighlighterEnabledValue();
|
||||
case ModuleType.MouseJump: return GPOWrapper.GetConfiguredMouseJumpEnabledValue();
|
||||
case ModuleType.MousePointerCrosshairs: return GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue();
|
||||
case ModuleType.MouseWithoutBorders: return GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue();
|
||||
case ModuleType.NewPlus: return GPOWrapper.GetConfiguredNewPlusEnabledValue();
|
||||
case ModuleType.Peek: return GPOWrapper.GetConfiguredPeekEnabledValue();
|
||||
case ModuleType.PowerRename: return GPOWrapper.GetConfiguredPowerRenameEnabledValue();
|
||||
case ModuleType.PowerLauncher: return GPOWrapper.GetConfiguredPowerLauncherEnabledValue();
|
||||
case ModuleType.PowerAccent: return GPOWrapper.GetConfiguredQuickAccentEnabledValue();
|
||||
case ModuleType.Workspaces: return GPOWrapper.GetConfiguredWorkspacesEnabledValue();
|
||||
case ModuleType.RegistryPreview: return GPOWrapper.GetConfiguredRegistryPreviewEnabledValue();
|
||||
case ModuleType.MeasureTool: return GPOWrapper.GetConfiguredScreenRulerEnabledValue();
|
||||
case ModuleType.ShortcutGuide: return GPOWrapper.GetConfiguredShortcutGuideEnabledValue();
|
||||
case ModuleType.PowerOCR: return GPOWrapper.GetConfiguredTextExtractorEnabledValue();
|
||||
case ModuleType.ZoomIt: return GPOWrapper.GetConfiguredZoomItEnabledValue();
|
||||
default: return GpoRuleConfigured.Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
public static System.Type GetModulePageType(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => typeof(AdvancedPastePage),
|
||||
ModuleType.AlwaysOnTop => typeof(AlwaysOnTopPage),
|
||||
ModuleType.Awake => typeof(AwakePage),
|
||||
ModuleType.CmdPal => typeof(CmdPalPage),
|
||||
ModuleType.ColorPicker => typeof(ColorPickerPage),
|
||||
ModuleType.CropAndLock => typeof(CropAndLockPage),
|
||||
ModuleType.CursorWrap => typeof(MouseUtilsPage),
|
||||
ModuleType.LightSwitch => typeof(LightSwitchPage),
|
||||
ModuleType.EnvironmentVariables => typeof(EnvironmentVariablesPage),
|
||||
ModuleType.FancyZones => typeof(FancyZonesPage),
|
||||
ModuleType.FileLocksmith => typeof(FileLocksmithPage),
|
||||
ModuleType.FindMyMouse => typeof(MouseUtilsPage),
|
||||
ModuleType.Hosts => typeof(HostsPage),
|
||||
ModuleType.ImageResizer => typeof(ImageResizerPage),
|
||||
ModuleType.KeyboardManager => typeof(KeyboardManagerPage),
|
||||
ModuleType.MouseHighlighter => typeof(MouseUtilsPage),
|
||||
ModuleType.MouseJump => typeof(MouseUtilsPage),
|
||||
ModuleType.MousePointerCrosshairs => typeof(MouseUtilsPage),
|
||||
ModuleType.MouseWithoutBorders => typeof(MouseWithoutBordersPage),
|
||||
ModuleType.NewPlus => typeof(NewPlusPage),
|
||||
ModuleType.Peek => typeof(PeekPage),
|
||||
ModuleType.PowerRename => typeof(PowerRenamePage),
|
||||
ModuleType.PowerLauncher => typeof(PowerLauncherPage),
|
||||
ModuleType.PowerAccent => typeof(PowerAccentPage),
|
||||
ModuleType.Workspaces => typeof(WorkspacesPage),
|
||||
ModuleType.RegistryPreview => typeof(RegistryPreviewPage),
|
||||
ModuleType.MeasureTool => typeof(MeasureToolPage),
|
||||
ModuleType.ShortcutGuide => typeof(ShortcutGuidePage),
|
||||
ModuleType.PowerOCR => typeof(PowerOcrPage),
|
||||
ModuleType.ZoomIt => typeof(ZoomItPage),
|
||||
_ => typeof(DashboardPage), // never called, all values listed above
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,7 @@
|
||||
<ProjectReference Include="..\..\common\LanguageModelProvider\LanguageModelProvider.csproj" />
|
||||
<ProjectReference Include="..\..\modules\MouseUtils\MouseJump.Common\MouseJump.Common.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Controls\Settings.UI.Controls.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- XamlIndexBuilder now outputs directly to Assets\Settings -->
|
||||
|
||||
@@ -41,16 +41,14 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
IsUserAdmin,
|
||||
ShowOobeWindow,
|
||||
ShowScoobeWindow,
|
||||
ShowFlyout,
|
||||
ContainsSettingsWindow,
|
||||
ContainsFlyoutPosition,
|
||||
}
|
||||
|
||||
private const int RequiredArgumentsSetSettingQty = 4;
|
||||
private const int RequiredArgumentsSetAdditionalSettingsQty = 4;
|
||||
private const int RequiredArgumentsGetSettingQty = 3;
|
||||
|
||||
private const int RequiredArgumentsLaunchedFromRunnerQty = 12;
|
||||
private const int RequiredArgumentsLaunchedFromRunnerQty = 10;
|
||||
|
||||
// Create an instance of the IPC wrapper.
|
||||
private static TwoWayPipeMessageIPCManaged ipcmanager;
|
||||
@@ -63,8 +61,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
public bool ShowOobe { get; set; }
|
||||
|
||||
public bool ShowFlyout { get; set; }
|
||||
|
||||
public bool ShowScoobe { get; set; }
|
||||
|
||||
public Type StartupPage { get; set; } = typeof(Views.DashboardPage);
|
||||
@@ -194,9 +190,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
IsUserAnAdmin = cmdArgs[(int)Arguments.IsUserAdmin] == "true";
|
||||
ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true";
|
||||
ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true";
|
||||
ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true";
|
||||
bool containsSettingsWindow = cmdArgs[(int)Arguments.ContainsSettingsWindow] == "true";
|
||||
bool containsFlyoutPosition = cmdArgs[(int)Arguments.ContainsFlyoutPosition] == "true";
|
||||
|
||||
// To keep track of variable arguments
|
||||
int currentArgumentIndex = RequiredArgumentsLaunchedFromRunnerQty;
|
||||
@@ -209,15 +203,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
currentArgumentIndex++;
|
||||
}
|
||||
|
||||
int flyout_x = 0;
|
||||
int flyout_y = 0;
|
||||
if (containsFlyoutPosition)
|
||||
{
|
||||
// get the flyout position arguments
|
||||
_ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x);
|
||||
_ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y);
|
||||
}
|
||||
|
||||
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
|
||||
{
|
||||
Environment.Exit(0);
|
||||
@@ -238,7 +223,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (!ShowOobe && !ShowScoobe && !ShowFlyout)
|
||||
if (!ShowOobe && !ShowScoobe)
|
||||
{
|
||||
settingsWindow = new MainWindow();
|
||||
settingsWindow.Activate();
|
||||
@@ -278,16 +263,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
|
||||
SetOobeWindow(scoobeWindow);
|
||||
}
|
||||
else if (ShowFlyout)
|
||||
{
|
||||
POINT? p = null;
|
||||
if (containsFlyoutPosition)
|
||||
{
|
||||
p = new POINT(flyout_x, flyout_y);
|
||||
}
|
||||
|
||||
ShellPage.OpenFlyoutCallback(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +339,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
private static MainWindow settingsWindow;
|
||||
private static OobeWindow oobeWindow;
|
||||
private static FlyoutWindow flyoutWindow;
|
||||
|
||||
public static void ClearSettingsWindow()
|
||||
{
|
||||
@@ -381,31 +355,16 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
return oobeWindow;
|
||||
}
|
||||
|
||||
public static FlyoutWindow GetFlyoutWindow()
|
||||
{
|
||||
return flyoutWindow;
|
||||
}
|
||||
|
||||
public static void SetOobeWindow(OobeWindow window)
|
||||
{
|
||||
oobeWindow = window;
|
||||
}
|
||||
|
||||
public static void SetFlyoutWindow(FlyoutWindow window)
|
||||
{
|
||||
flyoutWindow = window;
|
||||
}
|
||||
|
||||
public static void ClearOobeWindow()
|
||||
{
|
||||
oobeWindow = null;
|
||||
}
|
||||
|
||||
public static void ClearFlyoutWindow()
|
||||
{
|
||||
flyoutWindow = null;
|
||||
}
|
||||
|
||||
public static Type GetPage(string settingWindow)
|
||||
{
|
||||
switch (settingWindow)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard
|
||||
settingsCard.DataContext is ModuleHotkeyData moduleData)
|
||||
{
|
||||
var moduleType = moduleData.ModuleType;
|
||||
NavigationService.Navigate(ModuleHelper.GetModulePageType(moduleType));
|
||||
NavigationService.Navigate(ModuleGpoHelper.GetModulePageType(moduleType));
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Flyout.AppsListPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:Microsoft.PowerToys.Settings.UI.ViewModels"
|
||||
mc:Ignorable="d">
|
||||
<Grid Background="{ThemeResource LayerOnAcrylicFillColorDefaultBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Padding="24,32,24,0">
|
||||
<TextBlock
|
||||
x:Uid="AllAppsTxt"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<Button
|
||||
x:Uid="BackBtn"
|
||||
Padding="8,4,8,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Click="BackButton_Click">
|
||||
<Button.Content>
|
||||
<StackPanel
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
<FontIcon
|
||||
Margin="0,2,0,0"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="BackLabel" Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</Grid>
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Margin="0,16,0,0"
|
||||
ItemsSource="{x:Bind ViewModel.FlyoutMenuItems}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Padding="0,0,0,16" Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewModels:FlyoutMenuItem">
|
||||
<Grid Height="40" Padding="24,0,24,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!--<ViewBox VerticalAlignment="Center">-->
|
||||
<Image
|
||||
Width="20"
|
||||
Margin="0,0,16,0"
|
||||
VerticalAlignment="Center">
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind Icon, Mode=OneWay}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<!--</ViewBox>-->
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Label, Mode=OneWay}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<FontIcon
|
||||
Grid.Column="2"
|
||||
Width="20"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind IsLocked, Converter={StaticResource ReverseBoolToVisibilityConverter}, ConverterParameter=True, Mode=OneWay}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="GPO_SettingIsManaged_ToolTip" TextWrapping="WrapWholeWords" />
|
||||
</ToolTipService.ToolTip>
|
||||
</FontIcon>
|
||||
<ToggleSwitch
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="{x:Bind Label, Mode=OneWay}"
|
||||
IsEnabled="{x:Bind IsLocked, Converter={StaticResource BoolNegationConverter}, ConverterParameter=True, Mode=OneWay}"
|
||||
IsOn="{x:Bind IsEnabled, Mode=TwoWay}"
|
||||
OffContent=""
|
||||
OnContent=""
|
||||
Style="{StaticResource RightAlignedCompactToggleSwitchStyle}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,37 +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.ObjectModel;
|
||||
using System.Threading;
|
||||
|
||||
using global::Windows.System;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using PowerToys.Interop;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Flyout
|
||||
{
|
||||
public sealed partial class AppsListPage : Page
|
||||
{
|
||||
private AllAppsViewModel ViewModel { get; set; }
|
||||
|
||||
public AppsListPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
var settingsUtils = SettingsUtils.Default;
|
||||
ViewModel = new AllAppsViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), Views.ShellPage.SendDefaultIPCMessage);
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
|
||||
private void BackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Frame.Navigate(typeof(LaunchPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,197 +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.Threading;
|
||||
|
||||
using global::Windows.System;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
|
||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using PowerToys.Interop;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Flyout
|
||||
{
|
||||
public sealed partial class LaunchPage : Page
|
||||
{
|
||||
private LauncherViewModel ViewModel { get; set; }
|
||||
|
||||
public LaunchPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
var settingsUtils = SettingsUtils.Default;
|
||||
ViewModel = new LauncherViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), Views.ShellPage.SendDefaultIPCMessage);
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
|
||||
private void ModuleButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FlyoutMenuButton selectedModuleBtn = sender as FlyoutMenuButton;
|
||||
bool moduleRun = true;
|
||||
|
||||
// Closing manually the flyout to workaround focus gain problems
|
||||
App.GetFlyoutWindow()?.Hide();
|
||||
|
||||
switch ((ModuleType)selectedModuleBtn.Tag)
|
||||
{
|
||||
case ModuleType.ColorPicker: // Launch ColorPicker
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowColorPickerSharedEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
case ModuleType.EnvironmentVariables: // Launch Environment Variables
|
||||
{
|
||||
bool launchAdmin = SettingsRepository<EnvironmentVariablesSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator;
|
||||
string eventName = !App.IsElevated && launchAdmin
|
||||
? Constants.ShowEnvironmentVariablesAdminSharedEvent()
|
||||
: Constants.ShowEnvironmentVariablesSharedEvent();
|
||||
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ModuleType.FancyZones: // Launch FancyZones Editor
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.FZEToggleEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ModuleType.Hosts: // Launch Hosts
|
||||
{
|
||||
bool launchAdmin = SettingsRepository<HostsSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator;
|
||||
string eventName = !App.IsElevated && launchAdmin
|
||||
? Constants.ShowHostsAdminSharedEvent()
|
||||
: Constants.ShowHostsSharedEvent();
|
||||
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ModuleType.RegistryPreview: // Launch Registry Preview
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RegistryPreviewTriggerEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
case ModuleType.MeasureTool: // Launch Screen Ruler
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.MeasureToolTriggerEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ModuleType.PowerLauncher: // Launch Run
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.PowerLauncherSharedEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ModuleType.PowerOCR: // Launch Text Extractor
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowPowerOCRSharedEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ModuleType.Workspaces: // Launch Workspaces Editor
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.WorkspacesLaunchEditorEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ModuleType.ShortcutGuide: // Launch Shortcut Guide
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShortcutGuideTriggerEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ModuleType.CmdPal: // Show CmdPal
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowCmdPalEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
moduleRun = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (moduleRun)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutModuleRunEvent() { ModuleName = ((ModuleType)selectedModuleBtn.Tag).ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.OpenSettingsWindow(null, true);
|
||||
}
|
||||
|
||||
private async void DocsBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://aka.ms/PowerToysOverview"));
|
||||
}
|
||||
|
||||
private void AllAppButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Frame.Navigate(typeof(AppsListPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
}
|
||||
|
||||
private void QuitButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.KillRunner();
|
||||
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
|
||||
{
|
||||
Application.Current.Exit();
|
||||
});
|
||||
}
|
||||
|
||||
internal void ReportBugBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.StartBugReport();
|
||||
|
||||
// Closing manually the flyout since no window will steal the focus
|
||||
App.GetFlyoutWindow()?.Hide();
|
||||
}
|
||||
|
||||
private void UpdateInfoBar_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
|
||||
{
|
||||
// Hide the flyout before opening settings window
|
||||
App.GetFlyoutWindow()?.Hide();
|
||||
|
||||
// Open Settings window directly to General page where update controls are located
|
||||
App.OpenSettingsWindow(typeof(GeneralPage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +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 Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Flyout
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class ShellPage : Page
|
||||
{
|
||||
public ShellPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
internal void SwitchToLaunchPage()
|
||||
{
|
||||
ContentFrame.Navigate(typeof(LaunchPage), null, new SuppressNavigationTransitionInfo());
|
||||
}
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SwitchToLaunchPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +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 Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
|
||||
using Microsoft.PowerToys.Settings.UI.ViewModels.Flyout;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Windows.Graphics;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class FlyoutWindow : WindowEx
|
||||
{
|
||||
private const int WindowWidth = 386;
|
||||
private const int WindowHeight = 486;
|
||||
private const int WindowMargin = 12;
|
||||
|
||||
public FlyoutViewModel ViewModel { get; set; }
|
||||
|
||||
public POINT? FlyoutAppearPosition { get; set; }
|
||||
|
||||
public FlyoutWindow(POINT? initialPosition)
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// Remove the caption style from the window style. Windows App SDK 1.6 added it, which made the title bar and borders appear for the Flyout. This code removes it.
|
||||
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
var windowStyle = NativeMethods.GetWindowLong(hwnd, NativeMethods.GWL_STYLE);
|
||||
windowStyle &= ~NativeMethods.WS_CAPTION;
|
||||
_ = NativeMethods.SetWindowLong(hwnd, NativeMethods.GWL_STYLE, windowStyle);
|
||||
|
||||
this.Activated += FlyoutWindow_Activated;
|
||||
FlyoutAppearPosition = initialPosition;
|
||||
ViewModel = new FlyoutViewModel();
|
||||
}
|
||||
|
||||
private void FlyoutWindow_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutActivatedEvent());
|
||||
if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.CodeActivated)
|
||||
{
|
||||
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
|
||||
if (!FlyoutAppearPosition.HasValue)
|
||||
{
|
||||
DisplayArea displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest);
|
||||
double dpiScale = (float)this.GetDpiForWindow() / 96;
|
||||
double x = displayArea.WorkArea.Width - (dpiScale * (WindowWidth + WindowMargin));
|
||||
double y = displayArea.WorkArea.Height - (dpiScale * (WindowHeight + WindowMargin));
|
||||
this.MoveAndResize(x, y, WindowWidth, WindowHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayArea displayArea = DisplayArea.GetFromPoint(new PointInt32(FlyoutAppearPosition.Value.X, FlyoutAppearPosition.Value.Y), DisplayAreaFallback.Nearest);
|
||||
|
||||
// Move the window to the correct screen as a little blob, so we can get the accurate dpi for the screen to calculate the best position to show it.
|
||||
this.MoveAndResize(FlyoutAppearPosition.Value.X, FlyoutAppearPosition.Value.Y, 1, 1);
|
||||
double dpiScale = (float)this.GetDpiForWindow() / 96;
|
||||
|
||||
// Position the window so that it's inside the display are closest to the point.
|
||||
POINT newPosition = new POINT(FlyoutAppearPosition.Value.X - (int)(dpiScale * WindowWidth / 2), FlyoutAppearPosition.Value.Y - (int)(dpiScale * WindowHeight / 2));
|
||||
if (newPosition.X < displayArea.WorkArea.X)
|
||||
{
|
||||
newPosition.X = displayArea.WorkArea.X;
|
||||
}
|
||||
|
||||
if (newPosition.Y < displayArea.WorkArea.Y)
|
||||
{
|
||||
newPosition.Y = displayArea.WorkArea.Y;
|
||||
}
|
||||
|
||||
if (newPosition.X + (dpiScale * WindowWidth) > displayArea.WorkArea.X + displayArea.WorkArea.Width)
|
||||
{
|
||||
newPosition.X = (int)(displayArea.WorkArea.X + displayArea.WorkArea.Width - (dpiScale * WindowWidth));
|
||||
}
|
||||
|
||||
if (newPosition.Y + (dpiScale * WindowHeight) > displayArea.WorkArea.Y + displayArea.WorkArea.Height)
|
||||
{
|
||||
newPosition.Y = (int)(displayArea.WorkArea.Y + displayArea.WorkArea.Height - (dpiScale * WindowHeight));
|
||||
}
|
||||
|
||||
this.MoveAndResize(newPosition.X, newPosition.Y, WindowWidth, WindowHeight);
|
||||
}
|
||||
|
||||
FlyoutShellPage.SwitchToLaunchPage();
|
||||
}
|
||||
|
||||
if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.Deactivated)
|
||||
{
|
||||
if (ViewModel.CanHide)
|
||||
{
|
||||
this.Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,12 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerLauncher.Telemetry;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI;
|
||||
@@ -89,11 +91,17 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
{
|
||||
ModuleHelper.SetIsModuleEnabled(generalSettingsConfig, moduleType, isEnabled);
|
||||
var outgoing = new OutGoingGeneralSettings(generalSettingsConfig);
|
||||
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
|
||||
|
||||
// Save settings to file
|
||||
SettingsUtils.Default.SaveSettings(generalSettingsConfig.ToJsonString());
|
||||
|
||||
// Send IPC message asynchronously to avoid blocking UI and potential recursive calls
|
||||
Task.Run(() =>
|
||||
{
|
||||
ShellPage.SendDefaultIPCMessage(outgoing.ToString());
|
||||
ShellPage.ShellHandler?.SignalGeneralDataUpdate();
|
||||
});
|
||||
|
||||
ShellPage.ShellHandler?.SignalGeneralDataUpdate();
|
||||
}
|
||||
|
||||
return needToUpdate;
|
||||
@@ -125,40 +133,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
App.GetOobeWindow().Activate();
|
||||
});
|
||||
|
||||
// open flyout
|
||||
ShellPage.SetOpenFlyoutCallback((POINT? p) =>
|
||||
{
|
||||
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
|
||||
{
|
||||
if (App.GetFlyoutWindow() == null)
|
||||
{
|
||||
App.SetFlyoutWindow(new FlyoutWindow(p));
|
||||
}
|
||||
|
||||
FlyoutWindow flyout = App.GetFlyoutWindow();
|
||||
flyout.FlyoutAppearPosition = p;
|
||||
flyout.Activate();
|
||||
|
||||
// https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground
|
||||
// Need to call SetForegroundWindow to actually gain focus.
|
||||
WindowHelpers.BringToForeground(flyout.GetWindowHandle());
|
||||
});
|
||||
});
|
||||
|
||||
// disable flyout hiding
|
||||
ShellPage.SetDisableFlyoutHidingCallback(() =>
|
||||
{
|
||||
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
|
||||
{
|
||||
if (App.GetFlyoutWindow() == null)
|
||||
{
|
||||
App.SetFlyoutWindow(new FlyoutWindow(null));
|
||||
}
|
||||
|
||||
App.GetFlyoutWindow().ViewModel.DisableHiding();
|
||||
});
|
||||
});
|
||||
|
||||
this.InitializeComponent();
|
||||
SetAppTitleBar();
|
||||
|
||||
|
||||
@@ -4,6 +4,5 @@
|
||||
<ResourceDictionary Source="ms-appx:///SettingsXAML/Controls/SettingsGroup/SettingsGroup.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///SettingsXAML/Controls/GPOInfoControl.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///SettingsXAML/Controls/IsEnabledTextBlock.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///SettingsXAML/Controls/FlyoutMenuButton.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
@@ -3,6 +3,7 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Lib="using:Microsoft.PowerToys.Settings.UI.Library"
|
||||
xmlns:controlConverters="using:Microsoft.PowerToys.Settings.UI.Controls.Converters"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@@ -19,7 +20,8 @@
|
||||
x:Key="ModuleItemTemplateSelector"
|
||||
ActivationTemplate="{StaticResource ModuleItemActivationTemplate}"
|
||||
ShortcutTemplate="{StaticResource ModuleItemShortcutTemplate}" />
|
||||
<converters:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
|
||||
<controlConverters:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
|
||||
<converters:EnumToModuleListSortOptionConverter x:Key="EnumToModuleListSortOptionConverter" />
|
||||
<DataTemplate x:Key="ModuleItemShortcutTemplate" x:DataType="viewmodels:DashboardModuleShortcutItem">
|
||||
<Grid MinHeight="36" ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -159,64 +161,17 @@
|
||||
</Grid.RowDefinitions>
|
||||
<controls:Card x:Uid="QuickAccessTitle" VerticalAlignment="Top">
|
||||
<Grid>
|
||||
<ItemsControl
|
||||
<controls:QuickAccessList
|
||||
x:Name="QuickAccessItemsControl"
|
||||
Margin="8,0,12,12"
|
||||
ItemsSource="{x:Bind ViewModel.ActionModules, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.ActionModules.Count, Mode=OneWay, Converter={StaticResource DoubleToVisibilityConverter}}">
|
||||
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<tkcontrols:WrapPanel
|
||||
Padding="12"
|
||||
HorizontalSpacing="16"
|
||||
VerticalSpacing="24" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:DashboardListItem">
|
||||
<ItemsControl IsTabStop="False" ItemsSource="{x:Bind DashboardModuleItems, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:DashboardModuleButtonItem">
|
||||
<controls:FlyoutMenuButton AutomationProperties.Name="{x:Bind ButtonTitle}" Click="{x:Bind ButtonClickHandler}">
|
||||
<controls:FlyoutMenuButton.Content>
|
||||
<TextBlock
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ButtonTitle}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:FlyoutMenuButton.Content>
|
||||
<controls:FlyoutMenuButton.Icon>
|
||||
<Image Width="24">
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind ButtonGlyph}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
</controls:FlyoutMenuButton.Icon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{x:Bind ButtonDescription}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</controls:FlyoutMenuButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="0" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
ItemsSource="{x:Bind ViewModel.QuickAccessItems, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.QuickAccessItems.Count, Mode=OneWay, Converter={StaticResource DoubleToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
x:Uid="NoActionsToShow"
|
||||
Margin="12"
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Visibility="{x:Bind ViewModel.ActionModules.Count, Mode=OneWay, Converter={StaticResource DoubleToInvertedVisibilityConverter}}" />
|
||||
Visibility="{x:Bind ViewModel.QuickAccessItems.Count, Mode=OneWay, Converter={StaticResource DoubleToInvertedVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</controls:Card>
|
||||
<controls:Card
|
||||
@@ -270,7 +225,6 @@
|
||||
</Grid>
|
||||
</controls:Card>
|
||||
<controls:Card
|
||||
x:Name="ModulesCard"
|
||||
x:Uid="UtilitiesHeader"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
@@ -304,68 +258,11 @@
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</controls:Card.TitleContent>
|
||||
|
||||
<ItemsRepeater
|
||||
x:Name="DashboardView"
|
||||
<controls:ModuleList
|
||||
x:Name="ModulesCard"
|
||||
Grid.Row="1"
|
||||
ItemsSource="{x:Bind ViewModel.AllModules, Mode=OneWay}">
|
||||
<ItemsRepeater.Layout>
|
||||
<StackLayout Orientation="Vertical" Spacing="0" />
|
||||
</ItemsRepeater.Layout>
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:DashboardListItem">
|
||||
<tkcontrols:SettingsCard
|
||||
MinHeight="0"
|
||||
Padding="12,4,12,4"
|
||||
AutomationProperties.Name="{x:Bind Label}"
|
||||
Background="Transparent"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
Click="DashboardListItemClick"
|
||||
CornerRadius="0"
|
||||
IsClickEnabled="True"
|
||||
Tag="{x:Bind Tag}">
|
||||
<tkcontrols:SettingsCard.Resources>
|
||||
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
|
||||
<x:Double x:Key="SettingsCardHeaderIconMaxSize">16</x:Double>
|
||||
</tkcontrols:SettingsCard.Resources>
|
||||
<tkcontrols:SettingsCard.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{x:Bind Label}" />
|
||||
<InfoBadge
|
||||
Margin="4,0,0,0"
|
||||
Style="{StaticResource NewInfoBadge}"
|
||||
Visibility="{x:Bind IsNew, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<FontIcon
|
||||
Grid.Column="2"
|
||||
Width="20"
|
||||
Margin="0,0,-12,0"
|
||||
FontSize="16"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind IsLocked, Converter={StaticResource ReverseBoolToVisibilityConverter}, ConverterParameter=True}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="GPO_SettingIsManaged_ToolTip" TextWrapping="WrapWholeWords" />
|
||||
</ToolTipService.ToolTip>
|
||||
</FontIcon>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Header>
|
||||
<tkcontrols:SettingsCard.HeaderIcon>
|
||||
<ImageIcon>
|
||||
<ImageIcon.Source>
|
||||
<BitmapImage UriSource="{x:Bind Icon}" />
|
||||
</ImageIcon.Source>
|
||||
</ImageIcon>
|
||||
</tkcontrols:SettingsCard.HeaderIcon>
|
||||
<ToggleSwitch
|
||||
AutomationProperties.Name="{x:Bind Label}"
|
||||
IsEnabled="{x:Bind IsLocked, Converter={StaticResource BoolNegationConverter}, ConverterParameter=True, Mode=OneWay}"
|
||||
IsOn="{x:Bind IsEnabled, Mode=TwoWay}"
|
||||
OffContent=""
|
||||
OnContent="" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
ItemsSource="{x:Bind ViewModel.AllModules, Mode=OneWay}"
|
||||
SortOption="{x:Bind ViewModel.DashboardSortOrder, Mode=TwoWay, Converter={StaticResource EnumToModuleListSortOptionConverter}}" />
|
||||
</controls:Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -48,11 +48,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
ViewModel.ModuleEnabledChangedOnSettingsPage();
|
||||
}
|
||||
|
||||
private void DashboardListItemClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.DashboardListItemClick(sender);
|
||||
}
|
||||
|
||||
private void WhatsNewButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (App.GetOobeWindow() == null)
|
||||
|
||||
@@ -291,6 +291,28 @@
|
||||
Toggled="ShowSystemTrayIcon_Toggled" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:GPOInfoControl>
|
||||
|
||||
<tkcontrols:SettingsExpander
|
||||
Name="GeneralPageEnableQuickAccess"
|
||||
x:Uid="GeneralPage_EnableQuickAccess"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
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}">
|
||||
<controls:ShortcutControl HotkeySettings="{x:Bind ViewModel.QuickAccessShortcut, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard Visibility="Collapsed" />
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="General_SettingsBackupAndRestoreTitle" Visibility="Visible">
|
||||
|
||||
@@ -6,7 +6,6 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Flyout;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||
@@ -94,6 +93,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
CheckBugReportStatus();
|
||||
|
||||
doRefreshBackupRestoreStatus(100);
|
||||
|
||||
this.Loaded += (s, e) => ViewModel.OnPageLoaded();
|
||||
}
|
||||
|
||||
private void OpenColorsSettings_Click(object sender, RoutedEventArgs e)
|
||||
@@ -176,8 +177,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
private void BugReportToolClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Start bug report
|
||||
var launchPage = new LaunchPage();
|
||||
launchPage.ReportBugBtn_Click(sender, e);
|
||||
ShellPage.SendDefaultIPCMessage("{\"bugreport\": 0 }");
|
||||
|
||||
ViewModel.IsBugReportRunning = true;
|
||||
|
||||
|
||||
@@ -58,16 +58,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
/// </summary>
|
||||
public delegate void WhatIsNewOpeningCallback();
|
||||
|
||||
/// <summary>
|
||||
/// Declaration for opening flyout window callback function.
|
||||
/// </summary>
|
||||
public delegate void FlyoutOpeningCallback(POINT? point);
|
||||
|
||||
/// <summary>
|
||||
/// Declaration for disabling hide of flyout window callback function.
|
||||
/// </summary>
|
||||
public delegate void DisablingFlyoutHidingCallback();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a shell handler to be used to update contents of the shell dynamically from page within the frame.
|
||||
/// </summary>
|
||||
@@ -108,16 +98,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
/// </summary>
|
||||
public static WhatIsNewOpeningCallback OpenWhatIsNewWindowCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets callback function for opening flyout window
|
||||
/// </summary>
|
||||
public static FlyoutOpeningCallback OpenFlyoutCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets callback function for disabling hide of flyout window
|
||||
/// </summary>
|
||||
public static DisablingFlyoutHidingCallback DisableFlyoutHidingCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets view model.
|
||||
/// </summary>
|
||||
@@ -261,24 +241,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
OpenWhatIsNewWindowCallback = implementation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set flyout opening callback function
|
||||
/// </summary>
|
||||
/// <param name="implementation">delegate function implementation.</param>
|
||||
public static void SetOpenFlyoutCallback(FlyoutOpeningCallback implementation)
|
||||
{
|
||||
OpenFlyoutCallback = implementation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set disable flyout hiding callback function
|
||||
/// </summary>
|
||||
/// <param name="implementation">delegate function implementation.</param>
|
||||
public static void SetDisableFlyoutHidingCallback(DisablingFlyoutHidingCallback implementation)
|
||||
{
|
||||
DisableFlyoutHidingCallback = implementation;
|
||||
}
|
||||
|
||||
public static void SetElevationStatus(bool isElevated)
|
||||
{
|
||||
IsElevated = isElevated;
|
||||
@@ -399,25 +361,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
IJsonValue whatToShowJson;
|
||||
if (json.TryGetValue("ShowYourself", out whatToShowJson))
|
||||
{
|
||||
if (whatToShowJson.ValueType == JsonValueType.String && whatToShowJson.GetString().Equals("flyout", StringComparison.Ordinal))
|
||||
{
|
||||
POINT? p = null;
|
||||
|
||||
IJsonValue flyoutPointX;
|
||||
IJsonValue flyoutPointY;
|
||||
if (json.TryGetValue("x_position", out flyoutPointX) && json.TryGetValue("y_position", out flyoutPointY))
|
||||
{
|
||||
if (flyoutPointX.ValueType == JsonValueType.Number && flyoutPointY.ValueType == JsonValueType.Number)
|
||||
{
|
||||
int flyout_x = (int)flyoutPointX.GetNumber();
|
||||
int flyout_y = (int)flyoutPointY.GetNumber();
|
||||
p = new POINT(flyout_x, flyout_y);
|
||||
}
|
||||
}
|
||||
|
||||
OpenFlyoutCallback(p);
|
||||
}
|
||||
else if (whatToShowJson.ValueType == JsonValueType.String)
|
||||
if (whatToShowJson.ValueType == JsonValueType.String)
|
||||
{
|
||||
OpenMainWindowCallback(App.GetPage(whatToShowJson.GetString()));
|
||||
}
|
||||
|
||||
@@ -5760,6 +5760,15 @@ 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>
|
||||
<comment>Fluent Design is a product name, do not loc</comment>
|
||||
</data>
|
||||
<data name="GeneralPage_EnableQuickAccess.Header" xml:space="preserve">
|
||||
<value>Quick Access flyout</value>
|
||||
</data>
|
||||
<data name="GeneralPage_EnableQuickAccess.Description" xml:space="preserve">
|
||||
<value>Fast access to quick actions and module toggles</value>
|
||||
</data>
|
||||
<data name="GeneralPage_QuickAccessShortcut.Header" xml:space="preserve">
|
||||
<value>Activation shortcut</value>
|
||||
</data>
|
||||
<data name="LightSwitch_ModeFollowNightLight.Content" xml:space="preserve">
|
||||
<value>Follow Night Light</value>
|
||||
</data>
|
||||
@@ -5769,4 +5778,4 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
|
||||
<data name="LightSwitch_FollowNightLightCardMessage.Text" xml:space="preserve">
|
||||
<value>Following Night Light settings.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@@ -8,44 +8,39 @@ using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.UI;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public partial class DashboardListItem : INotifyPropertyChanged
|
||||
public partial class DashboardListItem : ModuleListItem
|
||||
{
|
||||
private bool _visible;
|
||||
private bool _isEnabled;
|
||||
|
||||
public string Label { get; set; }
|
||||
|
||||
public bool IsNew { get; set; }
|
||||
|
||||
public string Icon { get; set; }
|
||||
|
||||
public string ToolTip { get; set; }
|
||||
|
||||
public ModuleType Tag { get; set; }
|
||||
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
public bool IsEnabled
|
||||
public new ModuleType Tag
|
||||
{
|
||||
get => _isEnabled;
|
||||
get => (ModuleType)base.Tag!;
|
||||
set => base.Tag = value;
|
||||
}
|
||||
|
||||
public Action<DashboardListItem> EnabledChangedCallback { get; set; }
|
||||
|
||||
public override bool IsEnabled
|
||||
{
|
||||
get => base.IsEnabled;
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
if (base.IsEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnPropertyChanged();
|
||||
base.IsEnabled = value;
|
||||
EnabledChangedCallback?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action<DashboardListItem> EnabledChangedCallback { get; set; }
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
@@ -59,13 +54,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public ObservableCollection<DashboardModuleItem> DashboardModuleItems { get; set; } = new ObservableCollection<DashboardModuleItem>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Windows.Threading;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
@@ -40,11 +41,16 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
public ObservableCollection<DashboardListItem> ActionModules { get; set; } = new ObservableCollection<DashboardListItem>();
|
||||
|
||||
public ObservableCollection<QuickAccessItem> QuickAccessItems => _quickAccessViewModel.Items;
|
||||
|
||||
private readonly QuickAccessViewModel _quickAccessViewModel;
|
||||
|
||||
// Master list of module items that is sorted and projected into AllModules.
|
||||
private List<DashboardListItem> _moduleItems = new List<DashboardListItem>();
|
||||
|
||||
// Flag to prevent circular updates when a UI toggle triggers settings changes.
|
||||
private bool _isUpdatingFromUI;
|
||||
private bool _isUpdatingFromSettings;
|
||||
|
||||
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
|
||||
|
||||
@@ -79,6 +85,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
generalSettingsConfig.DashboardSortOrder = value;
|
||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettingsConfig);
|
||||
|
||||
// Save settings to file
|
||||
SettingsUtils.Default.SaveSettings(generalSettingsConfig.ToJsonString());
|
||||
|
||||
SendConfigMSG(outgoing.ToString());
|
||||
SortModuleList();
|
||||
}
|
||||
@@ -95,6 +105,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
_settingsRepository = settingsRepository;
|
||||
generalSettingsConfig = settingsRepository.SettingsConfig;
|
||||
generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
|
||||
_settingsRepository.SettingsChanged += OnSettingsChanged;
|
||||
|
||||
// Initialize dashboard sort order from settings
|
||||
_dashboardSortOrder = generalSettingsConfig.DashboardSortOrder;
|
||||
@@ -102,11 +113,27 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
// set the callback functions value to handle outgoing IPC message.
|
||||
SendConfigMSG = ipcMSGCallBackFunc;
|
||||
|
||||
_quickAccessViewModel = new QuickAccessViewModel(
|
||||
_settingsRepository,
|
||||
new Microsoft.PowerToys.Settings.UI.Controls.QuickAccessLauncher(App.IsElevated),
|
||||
moduleType => Helpers.ModuleGpoHelper.GetModuleGpoConfiguration(moduleType) == global::PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
|
||||
resourceLoader);
|
||||
|
||||
BuildModuleList();
|
||||
SortModuleList();
|
||||
RefreshShortcutModules();
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(GeneralSettings newSettings)
|
||||
{
|
||||
dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
generalSettingsConfig = newSettings;
|
||||
generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
|
||||
ModuleEnabledChangedOnSettingsPage();
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
|
||||
{
|
||||
dispatcher.BeginInvoke(() =>
|
||||
@@ -146,7 +173,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||
{
|
||||
GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(moduleType);
|
||||
if (moduleType == ModuleType.GeneralSettings)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GpoRuleConfigured gpo = ModuleGpoHelper.GetModuleGpoConfiguration(moduleType);
|
||||
var newItem = new DashboardListItem()
|
||||
{
|
||||
Tag = moduleType,
|
||||
@@ -156,6 +188,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
|
||||
IsNew = moduleType == ModuleType.CursorWrap,
|
||||
DashboardModuleItems = GetModuleItems(moduleType),
|
||||
ClickCommand = new RelayCommand<object>(DashboardListItemClick),
|
||||
};
|
||||
newItem.EnabledChangedCallback = EnabledChangedOnUI;
|
||||
_moduleItems.Add(newItem);
|
||||
@@ -211,7 +244,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
foreach (var item in _moduleItems)
|
||||
{
|
||||
GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(item.Tag);
|
||||
GpoRuleConfigured gpo = ModuleGpoHelper.GetModuleGpoConfiguration(item.Tag);
|
||||
|
||||
// GPO can force-enable (Enabled) or force-disable (Disabled) a module.
|
||||
// If Enabled: module is on and the user cannot disable it.
|
||||
@@ -244,6 +277,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
/// </summary>
|
||||
private void EnabledChangedOnUI(DashboardListItem dashboardListItem)
|
||||
{
|
||||
if (_isUpdatingFromSettings)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isUpdatingFromUI = true;
|
||||
try
|
||||
{
|
||||
@@ -287,6 +325,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
_isUpdatingFromSettings = true;
|
||||
try
|
||||
{
|
||||
RefreshModuleList();
|
||||
@@ -301,6 +340,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
Logger.LogError($"Updating active/disabled modules list failed: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isUpdatingFromSettings = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -694,10 +737,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
internal void DashboardListItemClick(object sender)
|
||||
{
|
||||
if (sender is SettingsCard card && card.Tag is ModuleType moduleType)
|
||||
if (sender is ModuleType moduleType)
|
||||
{
|
||||
NavigationService.Navigate(ModuleHelper.GetModulePageType(moduleType));
|
||||
NavigationService.Navigate(ModuleGpoHelper.GetModulePageType(moduleType));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
if (_settingsRepository != null)
|
||||
{
|
||||
_settingsRepository.SettingsChanged -= OnSettingsChanged;
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +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.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.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public partial class AllAppsViewModel : Observable
|
||||
{
|
||||
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; set; }
|
||||
|
||||
private ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private GeneralSettings generalSettingsConfig;
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private Func<string, int> SendConfigMSG { get; }
|
||||
|
||||
public AllAppsViewModel(ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc)
|
||||
{
|
||||
_settingsRepository = settingsRepository;
|
||||
generalSettingsConfig = settingsRepository.SettingsConfig;
|
||||
generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
|
||||
|
||||
resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
FlyoutMenuItems = new ObservableCollection<FlyoutMenuItem>();
|
||||
|
||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||
{
|
||||
AddFlyoutMenuItem(moduleType);
|
||||
}
|
||||
|
||||
// set the callback functions value to handle outgoing IPC message.
|
||||
SendConfigMSG = ipcMSGCallBackFunc;
|
||||
}
|
||||
|
||||
private void AddFlyoutMenuItem(ModuleType moduleType)
|
||||
{
|
||||
GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(moduleType);
|
||||
FlyoutMenuItems.Add(new FlyoutMenuItem()
|
||||
{
|
||||
Label = resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)),
|
||||
IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)),
|
||||
IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled,
|
||||
Tag = moduleType,
|
||||
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
|
||||
EnabledChangedCallback = EnabledChangedOnUI,
|
||||
});
|
||||
}
|
||||
|
||||
private void EnabledChangedOnUI(FlyoutMenuItem flyoutMenuItem)
|
||||
{
|
||||
if (Views.ShellPage.UpdateGeneralSettingsCallback(flyoutMenuItem.Tag, flyoutMenuItem.IsEnabled))
|
||||
{
|
||||
Views.ShellPage.DisableFlyoutHidingCallback();
|
||||
}
|
||||
}
|
||||
|
||||
private void ModuleEnabledChangedOnSettingsPage()
|
||||
{
|
||||
generalSettingsConfig = _settingsRepository.SettingsConfig;
|
||||
generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
|
||||
foreach (FlyoutMenuItem item in FlyoutMenuItems)
|
||||
{
|
||||
item.IsEnabled = ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, item.Tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +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.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using ManagedCommon;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public partial class FlyoutMenuItem : INotifyPropertyChanged
|
||||
{
|
||||
private bool _visible;
|
||||
private bool _isEnabled;
|
||||
|
||||
public string Label { get; set; }
|
||||
|
||||
public string Icon { get; set; }
|
||||
|
||||
public string ToolTip { get; set; }
|
||||
|
||||
public ModuleType Tag { get; set; }
|
||||
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnPropertyChanged();
|
||||
EnabledChangedCallback?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action<FlyoutMenuItem> EnabledChangedCallback { get; set; }
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
if (_visible != value)
|
||||
{
|
||||
_visible = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +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.Timers;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels.Flyout
|
||||
{
|
||||
public partial class FlyoutViewModel : IDisposable
|
||||
{
|
||||
private Timer _hideTimer;
|
||||
private bool _disposed;
|
||||
|
||||
public bool CanHide { get; set; }
|
||||
|
||||
public FlyoutViewModel()
|
||||
{
|
||||
CanHide = true;
|
||||
_hideTimer = new Timer();
|
||||
_hideTimer.Elapsed += HideTimer_Elapsed;
|
||||
_hideTimer.Interval = 1000;
|
||||
_hideTimer.Enabled = false;
|
||||
}
|
||||
|
||||
private void HideTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
CanHide = true;
|
||||
_hideTimer.Stop();
|
||||
}
|
||||
|
||||
internal void DisableHiding()
|
||||
{
|
||||
CanHide = false;
|
||||
_hideTimer.Stop();
|
||||
_hideTimer.Start();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_hideTimer?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ using Windows.System.Profile;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public partial class GeneralViewModel : Observable
|
||||
public partial class GeneralViewModel : PageViewModelBase
|
||||
{
|
||||
public enum InstallScope
|
||||
{
|
||||
@@ -39,6 +39,16 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
PerUser,
|
||||
}
|
||||
|
||||
protected override string ModuleName => "GeneralSettings";
|
||||
|
||||
public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()
|
||||
{
|
||||
return new Dictionary<string, HotkeySettings[]>
|
||||
{
|
||||
{ ModuleName, new HotkeySettings[] { QuickAccessShortcut } },
|
||||
};
|
||||
}
|
||||
|
||||
private GeneralSettings GeneralSettingsConfig { get; set; }
|
||||
|
||||
private UpdatingSettings UpdatingSettingsConfig { get; set; }
|
||||
@@ -75,6 +85,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private string _settingsConfigFileFolder = string.Empty;
|
||||
|
||||
private ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private Microsoft.UI.Dispatching.DispatcherQueue _dispatcherQueue;
|
||||
|
||||
private IFileSystemWatcher _fileWatcher;
|
||||
|
||||
private Func<Task<string>> PickSingleFolderDialog { get; }
|
||||
@@ -100,6 +113,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
// To obtain the general settings configuration of PowerToys if it exists, else to create a new file and return the default configurations.
|
||||
ArgumentNullException.ThrowIfNull(settingsRepository);
|
||||
|
||||
_settingsRepository = settingsRepository;
|
||||
_settingsRepository.SettingsChanged += OnSettingsChanged;
|
||||
_dispatcherQueue = GetDispatcherQueue();
|
||||
|
||||
GeneralSettingsConfig = settingsRepository.SettingsConfig;
|
||||
UpdatingSettingsConfig = UpdatingSettings.LoadSettings();
|
||||
if (UpdatingSettingsConfig == null)
|
||||
@@ -155,6 +172,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
_isElevated = isElevated;
|
||||
_runElevated = GeneralSettingsConfig.RunElevated;
|
||||
_enableWarningsElevatedApps = GeneralSettingsConfig.EnableWarningsElevatedApps;
|
||||
_enableQuickAccess = GeneralSettingsConfig.EnableQuickAccess;
|
||||
_quickAccessShortcut = GeneralSettingsConfig.QuickAccessShortcut;
|
||||
if (_quickAccessShortcut != null)
|
||||
{
|
||||
_quickAccessShortcut.PropertyChanged += QuickAccessShortcut_PropertyChanged;
|
||||
}
|
||||
|
||||
RunningAsUserDefaultText = runAsUserText;
|
||||
RunningAsAdminDefaultText = runAsAdminText;
|
||||
@@ -236,6 +259,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private bool _runElevated;
|
||||
private bool _isAdmin;
|
||||
private bool _enableWarningsElevatedApps;
|
||||
private bool _enableQuickAccess;
|
||||
private HotkeySettings _quickAccessShortcut;
|
||||
private int _themeIndex;
|
||||
|
||||
private bool _showNewUpdatesToastNotification;
|
||||
@@ -480,6 +505,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
|
||||
{
|
||||
get
|
||||
@@ -1434,5 +1510,35 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
Process.Start("explorer.exe", etwDirPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(GeneralSettings newSettings)
|
||||
{
|
||||
_dispatcherQueue?.TryEnqueue(() =>
|
||||
{
|
||||
GeneralSettingsConfig = newSettings;
|
||||
|
||||
if (_enableQuickAccess != newSettings.EnableQuickAccess)
|
||||
{
|
||||
_enableQuickAccess = newSettings.EnableQuickAccess;
|
||||
OnPropertyChanged(nameof(EnableQuickAccess));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
if (_settingsRepository != null)
|
||||
{
|
||||
_settingsRepository.SettingsChanged -= OnSettingsChanged;
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual Microsoft.UI.Dispatching.DispatcherQueue GetDispatcherQueue()
|
||||
{
|
||||
return Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ using System.Windows.Threading;
|
||||
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.HotkeyConflicts;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
@@ -244,6 +245,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
// Send IPC notification using the same format as other ViewModels
|
||||
SendConfigMSG(settingsConfig, moduleName);
|
||||
|
||||
// Request updated conflicts after changing a hotkey
|
||||
GlobalHotkeyConflictManager.Instance?.RequestAllConflicts();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -264,11 +268,22 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
? JsonSerializer.Serialize(settingsConfig, jsonTypeInfo)
|
||||
: JsonSerializer.Serialize(settingsConfig);
|
||||
|
||||
var ipcMessage = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{{ \"powertoys\": {{ \"{0}\": {1} }} }}",
|
||||
moduleName,
|
||||
serializedSettings);
|
||||
string ipcMessage;
|
||||
if (string.Equals(moduleName, "GeneralSettings", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ipcMessage = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{{ \"general\": {0} }}",
|
||||
serializedSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
ipcMessage = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{{ \"powertoys\": {{ \"{0}\": {1} }} }}",
|
||||
moduleName,
|
||||
serializedSettings);
|
||||
}
|
||||
|
||||
var result = _ipcMSGCallBackFunc(ipcMessage);
|
||||
System.Diagnostics.Debug.WriteLine($"Sent IPC notification for {moduleName}, result: {result}");
|
||||
|
||||
Reference in New Issue
Block a user