mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-31 01:16:59 +01:00
Compare commits
48 Commits
jay/DarkMo
...
shawn/Shor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37ecf5712a | ||
|
|
ade6b30a6f | ||
|
|
75088a9fed | ||
|
|
f94ca33a07 | ||
|
|
09eab7dd3a | ||
|
|
2d00566975 | ||
|
|
5c93a3ed5f | ||
|
|
13d6ec8058 | ||
|
|
fb28e3d184 | ||
|
|
0694c98972 | ||
|
|
988000744e | ||
|
|
9a975cb193 | ||
|
|
cc9b060a24 | ||
|
|
890a85bff6 | ||
|
|
a5f140df17 | ||
|
|
63257e928d | ||
|
|
875593e32e | ||
|
|
7078d05b77 | ||
|
|
36d568bfde | ||
|
|
5d37b29418 | ||
|
|
83d3b51fbd | ||
|
|
9b7cc006bb | ||
|
|
6995ebd8fd | ||
|
|
768c062068 | ||
|
|
567e431544 | ||
|
|
ff9e328102 | ||
|
|
2dc6bdb000 | ||
|
|
48b782a74a | ||
|
|
6c933f3c4e | ||
|
|
2dcf2020f5 | ||
|
|
5f748f8206 | ||
|
|
d1e702cf9b | ||
|
|
dbd2ea5231 | ||
|
|
3e14b3a8c2 | ||
|
|
cb3afc743f | ||
|
|
ab584188d4 | ||
|
|
40fc4561ee | ||
|
|
c1564db865 | ||
|
|
5457b27d54 | ||
|
|
e08ef9c3fe | ||
|
|
f162449850 | ||
|
|
ef51252d70 | ||
|
|
1d438426b3 | ||
|
|
0aff6426c0 | ||
|
|
d28ebcd3ce | ||
|
|
faab625a25 | ||
|
|
e1bcf62b84 | ||
|
|
72f62c5718 |
@@ -50,6 +50,7 @@ namespace
|
||||
const wchar_t JSON_KEY_CTRL[] = L"ctrl";
|
||||
const wchar_t JSON_KEY_SHIFT[] = L"shift";
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t JSON_KEY_PASTE_AS_PLAIN_HOTKEY[] = L"paste-as-plain-hotkey";
|
||||
const wchar_t JSON_KEY_ADVANCED_PASTE_UI_HOTKEY[] = L"advanced-paste-ui-hotkey";
|
||||
const wchar_t JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY[] = L"paste-as-markdown-hotkey";
|
||||
@@ -57,9 +58,29 @@ namespace
|
||||
const wchar_t JSON_KEY_IS_ADVANCED_AI_ENABLED[] = L"IsAdvancedAIEnabled";
|
||||
const wchar_t JSON_KEY_SHOW_CUSTOM_PREVIEW[] = L"ShowCustomPreview";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
const wchar_t JSON_KEY_IMAGE_TO_TEXT_HOTKEY[] = L"image-to-text";
|
||||
const wchar_t JSON_KEY_PASTE_AS_TXT_FILE_HOTKEY[] = L"paste-as-txt-file";
|
||||
const wchar_t JSON_KEY_PASTE_AS_PNG_FILE_HOTKEY[] = L"paste-as-png-file";
|
||||
const wchar_t JSON_KEY_PASTE_AS_HTML_FILE_HOTKEY[] = L"paste-as-html-file";
|
||||
const wchar_t JSON_KEY_TRANSCODE_TO_MP3_HOTKEY[] = L"transcode-to-mp3";
|
||||
const wchar_t JSON_KEY_TRANSCODE_TO_MP4_HOTKEY[] = L"transcode-to-mp4";
|
||||
|
||||
const wchar_t OPENAI_VAULT_RESOURCE[] = L"https://platform.openai.com/api-keys";
|
||||
const wchar_t OPENAI_VAULT_USERNAME[] = L"PowerToys_AdvancedPaste_OpenAIKey";
|
||||
|
||||
const wchar_t PASTE_AS_PLAIN_HOTKEY_NAME[] = L"PasteAsPlainTextShortcut";
|
||||
const wchar_t ADVANCED_PASTE_UI_HOTKEY_NAME[] = L"AdvancedPasteUIShortcut";
|
||||
const wchar_t PASTE_AS_MARKDOWN_HOTKEY_NAME[] = L"PasteAsMarkdownShortcut";
|
||||
const wchar_t PASTE_AS_JSON_HOTKEY_NAME[] = L"PasteAsJsonShortcut";
|
||||
// additional actions hotkeys
|
||||
const wchar_t IMAGE_TO_TEXT_HOTKEY_NAME[] = L"ImageToTextShortcut";
|
||||
const wchar_t PASTE_AS_TXT_FILE_HOTKEY_NAME[] = L"PasteAsTxtFileShortcut";
|
||||
const wchar_t PASTE_AS_PNG_FILE_HOTKEY_NAME[] = L"PasteAsPngFileShortcut";
|
||||
const wchar_t PASTE_AS_HTML_FILE_HOTKEY_NAME[] = L"PasteAsHtmlFileShortcut";
|
||||
const wchar_t TRANSCODE_TO_MP3_HOTKEY_NAME[] = L"TranscodeToMp3Shortcut";
|
||||
const wchar_t TRANSCODE_TO_MP4_HOTKEY_NAME[] = L"TranscodeToMp4Shortcut";
|
||||
|
||||
|
||||
}
|
||||
|
||||
class AdvancedPaste : public PowertoyModuleIface
|
||||
@@ -76,10 +97,10 @@ private:
|
||||
|
||||
static const constexpr int NUM_DEFAULT_HOTKEYS = 4;
|
||||
|
||||
Hotkey m_paste_as_plain_hotkey = { .win = true, .ctrl = true, .shift = false, .alt = true, .key = 'V' };
|
||||
Hotkey m_advanced_paste_ui_hotkey = { .win = true, .ctrl = false, .shift = true, .alt = false, .key = 'V' };
|
||||
Hotkey m_paste_as_markdown_hotkey{};
|
||||
Hotkey m_paste_as_json_hotkey{};
|
||||
Hotkey m_paste_as_plain_hotkey = { .win = true, .ctrl = true, .shift = false, .alt = true, .key = 'V', .name = PASTE_AS_PLAIN_HOTKEY_NAME };
|
||||
Hotkey m_advanced_paste_ui_hotkey = { .win = true, .ctrl = false, .shift = true, .alt = false, .key = 'V', .name = ADVANCED_PASTE_UI_HOTKEY_NAME };
|
||||
Hotkey m_paste_as_markdown_hotkey{ .name = PASTE_AS_MARKDOWN_HOTKEY_NAME };
|
||||
Hotkey m_paste_as_json_hotkey{ .name = PASTE_AS_JSON_HOTKEY_NAME };
|
||||
|
||||
template<class Id>
|
||||
struct ActionData
|
||||
@@ -93,6 +114,7 @@ private:
|
||||
|
||||
using CustomAction = ActionData<int>;
|
||||
std::vector<CustomAction> m_custom_actions;
|
||||
std::vector<std::wstring> m_custom_action_hotkey_names;
|
||||
|
||||
bool m_is_advanced_ai_enabled = false;
|
||||
bool m_preview_custom_format_output = true;
|
||||
@@ -102,7 +124,9 @@ private:
|
||||
try
|
||||
{
|
||||
const auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(keyName);
|
||||
return parse_single_hotkey(jsonHotkeyObject);
|
||||
Hotkey hotkey = parse_single_hotkey(jsonHotkeyObject);
|
||||
hotkey.name = get_hotkey_name(keyName);
|
||||
return hotkey;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -112,6 +136,38 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
const wchar_t* get_hotkey_name(const wchar_t* name)
|
||||
{
|
||||
if (wcscmp(name, JSON_KEY_PASTE_AS_PLAIN_HOTKEY) == 0)
|
||||
return PASTE_AS_PLAIN_HOTKEY_NAME;
|
||||
if (wcscmp(name, JSON_KEY_ADVANCED_PASTE_UI_HOTKEY) == 0)
|
||||
return ADVANCED_PASTE_UI_HOTKEY_NAME;
|
||||
if (wcscmp(name, JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY) == 0)
|
||||
return PASTE_AS_MARKDOWN_HOTKEY_NAME;
|
||||
if (wcscmp(name, JSON_KEY_PASTE_AS_JSON_HOTKEY) == 0)
|
||||
return PASTE_AS_JSON_HOTKEY_NAME;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const wchar_t* get_additional_action_hotkey_name(std::wstring name)
|
||||
{
|
||||
if (name == JSON_KEY_IMAGE_TO_TEXT_HOTKEY)
|
||||
return IMAGE_TO_TEXT_HOTKEY_NAME;
|
||||
if (name == JSON_KEY_PASTE_AS_TXT_FILE_HOTKEY)
|
||||
return PASTE_AS_TXT_FILE_HOTKEY_NAME;
|
||||
if (name == JSON_KEY_PASTE_AS_PNG_FILE_HOTKEY)
|
||||
return PASTE_AS_PNG_FILE_HOTKEY_NAME;
|
||||
if (name == JSON_KEY_PASTE_AS_HTML_FILE_HOTKEY)
|
||||
return PASTE_AS_HTML_FILE_HOTKEY_NAME;
|
||||
if (name == JSON_KEY_TRANSCODE_TO_MP3_HOTKEY)
|
||||
return TRANSCODE_TO_MP3_HOTKEY_NAME;
|
||||
if (name == JSON_KEY_TRANSCODE_TO_MP4_HOTKEY)
|
||||
return TRANSCODE_TO_MP4_HOTKEY_NAME;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Hotkey parse_single_hotkey(const winrt::Windows::Data::Json::JsonObject& jsonHotkeyObject)
|
||||
{
|
||||
try
|
||||
@@ -122,6 +178,7 @@ private:
|
||||
hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
hotkey.name = jsonHotkeyObject.GetNamedString(JSON_KEY_NAME).c_str();
|
||||
return hotkey;
|
||||
}
|
||||
catch (...)
|
||||
@@ -140,6 +197,7 @@ private:
|
||||
jsonObject.SetNamedValue(JSON_KEY_SHIFT, json::value(hotkey.shift));
|
||||
jsonObject.SetNamedValue(JSON_KEY_CTRL, json::value(hotkey.ctrl));
|
||||
jsonObject.SetNamedValue(JSON_KEY_CODE, json::value(hotkey.key));
|
||||
jsonObject.SetNamedValue(JSON_KEY_NAME, json::value(hotkey.name));
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
@@ -247,12 +305,13 @@ private:
|
||||
|
||||
if (action.HasKey(JSON_KEY_SHORTCUT))
|
||||
{
|
||||
const AdditionalAction additionalAction
|
||||
AdditionalAction additionalAction
|
||||
{
|
||||
actionName.c_str(),
|
||||
parse_single_hotkey(action.GetNamedObject(JSON_KEY_SHORTCUT))
|
||||
};
|
||||
|
||||
additionalAction.hotkey.name = get_additional_action_hotkey_name(additionalAction.id);
|
||||
m_additional_actions.push_back(additionalAction);
|
||||
}
|
||||
else
|
||||
@@ -334,12 +393,14 @@ private:
|
||||
|
||||
if (object.GetNamedBoolean(JSON_KEY_IS_SHOWN, false))
|
||||
{
|
||||
const CustomAction customActionData
|
||||
CustomAction customActionData
|
||||
{
|
||||
static_cast<int>(object.GetNamedNumber(JSON_KEY_ID)),
|
||||
parse_single_hotkey(object.GetNamedObject(JSON_KEY_SHORTCUT))
|
||||
};
|
||||
|
||||
m_custom_action_hotkey_names.push_back(L"CustomAction_" + std::to_wstring(customActionData.id));
|
||||
customActionData.hotkey.name = m_custom_action_hotkey_names.back().c_str();
|
||||
m_custom_actions.push_back(customActionData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ namespace
|
||||
const wchar_t JSON_KEY_REPARENT_HOTKEY[] = L"reparent-hotkey";
|
||||
const wchar_t JSON_KEY_THUMBNAIL_HOTKEY[] = L"thumbnail-hotkey";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t REPARENT_SHORTCUT_NAME[] = L"ReparentHotkey";
|
||||
const wchar_t THUMBNAIL_SHORTCUT_NAME[] = L"ThumbnailHotkey";
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE /*hModule*/,
|
||||
@@ -262,6 +265,7 @@ private:
|
||||
_temp_reparent.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
_temp_reparent.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
_temp_reparent.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
_temp_reparent.name = REPARENT_SHORTCUT_NAME;
|
||||
m_reparent_hotkey = _temp_reparent;
|
||||
}
|
||||
catch (...)
|
||||
@@ -277,6 +281,7 @@ private:
|
||||
_temp_thumbnail.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
_temp_thumbnail.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
_temp_thumbnail.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
_temp_thumbnail.name = THUMBNAIL_SHORTCUT_NAME;
|
||||
m_thumbnail_hotkey = _temp_thumbnail;
|
||||
}
|
||||
catch (...)
|
||||
@@ -319,8 +324,8 @@ private:
|
||||
HANDLE m_hProcess = nullptr;
|
||||
|
||||
// TODO: actual default hotkey setting in line with other PowerToys.
|
||||
Hotkey m_reparent_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'R' };
|
||||
Hotkey m_thumbnail_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'T' };
|
||||
Hotkey m_reparent_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'R', .name = REPARENT_SHORTCUT_NAME };
|
||||
Hotkey m_thumbnail_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'T', .name = THUMBNAIL_SHORTCUT_NAME };
|
||||
|
||||
HANDLE m_reparent_event_handle;
|
||||
HANDLE m_thumbnail_event_handle;
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace
|
||||
const wchar_t JSON_KEY_SHIFT[] = L"shift";
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"ActivationShortcut";
|
||||
}
|
||||
|
||||
class MeasureTool : public PowertoyModuleIface
|
||||
@@ -67,6 +69,7 @@ private:
|
||||
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -86,6 +89,7 @@ private:
|
||||
m_hotkey.alt = false;
|
||||
m_hotkey.shift = true;
|
||||
m_hotkey.key = 'M';
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ namespace
|
||||
const wchar_t JSON_KEY_SHAKING_INTERVAL_MS[] = L"shaking_interval_ms";
|
||||
const wchar_t JSON_KEY_SHAKING_FACTOR[] = L"shaking_factor";
|
||||
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"activation_shortcut";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"ActivationShortcut";
|
||||
}
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
@@ -484,6 +487,10 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
m_hotkey.modifiersMask = MOD_SHIFT | MOD_WIN;
|
||||
m_hotkey.vkCode = 0x46; // F key
|
||||
}
|
||||
if (m_hotkey.name == nullptr)
|
||||
{
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace
|
||||
const wchar_t JSON_KEY_HIGHLIGHT_FADE_DURATION_MS[] = L"highlight_fade_duration_ms";
|
||||
const wchar_t JSON_KEY_AUTO_ACTIVATE[] = L"auto_activate";
|
||||
const wchar_t JSON_KEY_SPOTLIGHT_MODE[] = L"spotlight_mode";
|
||||
const wchar_t ACTIVATION_HOTKEY_NAME[] = L"ActivationShortcut";
|
||||
}
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
@@ -389,6 +390,10 @@ public:
|
||||
m_hotkey.modifiersMask = MOD_SHIFT | MOD_WIN;
|
||||
m_hotkey.vkCode = 0x48; // H key
|
||||
}
|
||||
if (m_hotkey.name == nullptr)
|
||||
{
|
||||
m_hotkey.name = ACTIVATION_HOTKEY_NAME;
|
||||
}
|
||||
m_highlightSettings = highlightSettings;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,6 +44,8 @@ namespace
|
||||
const wchar_t JSON_KEY_SHIFT[] = L"shift";
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"activation_shortcut";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"ActivationShortcut";
|
||||
}
|
||||
|
||||
// Implement the PowerToy Module Interface and all the required methods.
|
||||
@@ -81,6 +83,7 @@ private:
|
||||
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -100,6 +103,7 @@ private:
|
||||
m_hotkey.shift = true;
|
||||
m_hotkey.ctrl = false;
|
||||
m_hotkey.key = 'D';
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace
|
||||
const wchar_t JSON_KEY_CROSSHAIRS_IS_FIXED_LENGTH_ENABLED[] = L"crosshairs_is_fixed_length_enabled";
|
||||
const wchar_t JSON_KEY_CROSSHAIRS_FIXED_LENGTH[] = L"crosshairs_fixed_length";
|
||||
const wchar_t JSON_KEY_AUTO_ACTIVATE[] = L"auto_activate";
|
||||
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"ActivationShortcut";
|
||||
}
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
@@ -394,6 +396,10 @@ public:
|
||||
m_hotkey.modifiersMask = MOD_WIN | MOD_ALT;
|
||||
m_hotkey.vkCode = 0x50; // P key
|
||||
}
|
||||
if (m_hotkey.name == nullptr)
|
||||
{
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
m_inclusiveCrosshairsSettings = inclusiveCrosshairsSettings;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
|
||||
HINSTANCE g_hInst_MouseWithoutBorders = 0;
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t SWITCH2ALLPC_SHORTCUT_NAME[] = L"HotKeySwitch2AllPC";
|
||||
const wchar_t LOCKMACHINE_SHORTCUT_NAME[] = L"HotKeyLockMachine";
|
||||
const wchar_t RECONNECT_SHORTCUT_NAME[] = L"HotKeyReconnect";
|
||||
const wchar_t TOGGLEEASYMOUSE_SHORTCUT_NAME[] = L"HotKeyToggleEasyMouse";
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
@@ -126,6 +134,32 @@ private:
|
||||
bool run_in_service_mode = false;
|
||||
PROCESS_INFORMATION p_info = {};
|
||||
|
||||
// Helper function to convert HotkeyObject to Hotkey struct
|
||||
Hotkey ConvertHotkeyObjectToHotkey(const PowerToysSettings::HotkeyObject& hotkeyObj, const wchar_t* name)
|
||||
{
|
||||
Hotkey hotkey;
|
||||
hotkey.win = hotkeyObj.win_pressed();
|
||||
hotkey.ctrl = hotkeyObj.ctrl_pressed();
|
||||
hotkey.alt = hotkeyObj.alt_pressed();
|
||||
hotkey.shift = hotkeyObj.shift_pressed();
|
||||
hotkey.key = static_cast<unsigned char>(hotkeyObj.get_code());
|
||||
hotkey.name = name;
|
||||
return hotkey;
|
||||
}
|
||||
|
||||
// Helper function to create a default disabled hotkey
|
||||
Hotkey CreateDisabledHotkey(const wchar_t* name)
|
||||
{
|
||||
Hotkey hotkey;
|
||||
hotkey.win = false;
|
||||
hotkey.ctrl = false;
|
||||
hotkey.alt = false;
|
||||
hotkey.shift = false;
|
||||
hotkey.key = 0;
|
||||
hotkey.name = name;
|
||||
return hotkey;
|
||||
}
|
||||
|
||||
bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
@@ -556,6 +590,77 @@ public:
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
|
||||
{
|
||||
constexpr size_t num_hotkeys = 4; // We have 4 hotkeys
|
||||
|
||||
if (hotkeys && buffer_size >= num_hotkeys)
|
||||
{
|
||||
try
|
||||
{
|
||||
PowerToysSettings::PowerToyValues values =
|
||||
PowerToysSettings::PowerToyValues::load_from_settings_file(MODULE_NAME);
|
||||
|
||||
// Cache the raw JSON object to avoid multiple parsing
|
||||
json::JsonObject root_json = values.get_raw_json();
|
||||
json::JsonObject properties_json = root_json.GetNamedObject(L"properties", json::JsonObject{});
|
||||
|
||||
size_t hotkey_index = 0;
|
||||
|
||||
// Helper lambda to extract hotkey from JSON properties
|
||||
auto extract_hotkey = [&](const wchar_t* property_name, const wchar_t* hotkey_name, bool default_win, bool default_ctrl, bool default_alt, bool default_shift, unsigned char default_key) -> Hotkey {
|
||||
if (properties_json.HasKey(property_name))
|
||||
{
|
||||
try
|
||||
{
|
||||
json::JsonObject hotkey_json = properties_json.GetNamedObject(property_name);
|
||||
|
||||
// Extract hotkey properties directly from JSON
|
||||
bool win = hotkey_json.GetNamedBoolean(L"win", default_win);
|
||||
bool ctrl = hotkey_json.GetNamedBoolean(L"ctrl", default_ctrl);
|
||||
bool alt = hotkey_json.GetNamedBoolean(L"alt", default_alt);
|
||||
bool shift = hotkey_json.GetNamedBoolean(L"shift", default_shift);
|
||||
unsigned char key = static_cast<unsigned char>(
|
||||
hotkey_json.GetNamedNumber(L"code", default_key));
|
||||
|
||||
return { win, ctrl, shift, alt, key, hotkey_name };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// If parsing individual hotkey fails, use defaults
|
||||
return { default_win, default_ctrl, default_shift, default_alt, default_key, hotkey_name };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Property doesn't exist, use defaults
|
||||
return { default_win, default_ctrl, default_shift, default_alt, default_key, hotkey_name };
|
||||
}
|
||||
};
|
||||
|
||||
// Extract all hotkeys using the optimized helper
|
||||
hotkeys[hotkey_index++] = extract_hotkey(L"ToggleEasyMouseShortcut", TOGGLEEASYMOUSE_SHORTCUT_NAME, false, true, false, true, 0x45); // Ctrl+Shift+E
|
||||
|
||||
hotkeys[hotkey_index++] = extract_hotkey(L"LockMachineShortcut", LOCKMACHINE_SHORTCUT_NAME, false, true, false, true, 0x4C); // Ctrl+Shift+L
|
||||
|
||||
hotkeys[hotkey_index++] = extract_hotkey(L"ReconnectShortcut", RECONNECT_SHORTCUT_NAME, true, true, true, false, 0x52); // Win+Ctrl+Alt+R
|
||||
|
||||
hotkeys[hotkey_index++] = extract_hotkey(L"Switch2AllPCShortcut", SWITCH2ALLPC_SHORTCUT_NAME, false, false, false, false, 0); // Disabled by default
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
// If settings file doesn't exist or is corrupted, use default hotkeys
|
||||
size_t hotkey_index = 0;
|
||||
hotkeys[hotkey_index++] = { false, true, false, true, 0x45, TOGGLEEASYMOUSE_SHORTCUT_NAME }; // Ctrl+Shift+E
|
||||
hotkeys[hotkey_index++] = { false, true, false, true, 0x4C, LOCKMACHINE_SHORTCUT_NAME }; // Ctrl+Shift+L
|
||||
hotkeys[hotkey_index++] = { true, true, true, false, 0x52, RECONNECT_SHORTCUT_NAME }; // Win+Ctrl+Alt+R
|
||||
hotkeys[hotkey_index++] = CreateDisabledHotkey(SWITCH2ALLPC_SHORTCUT_NAME); // Disabled
|
||||
}
|
||||
}
|
||||
|
||||
return num_hotkeys;
|
||||
}
|
||||
|
||||
void launch_add_firewall_process()
|
||||
{
|
||||
Logger::trace(L"Starting Process to add firewall rule");
|
||||
|
||||
@@ -44,6 +44,8 @@ namespace
|
||||
const wchar_t JSON_KEY_SHIFT[] = L"shift";
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"ActivationShortcut";
|
||||
}
|
||||
|
||||
struct ModuleSettings
|
||||
@@ -85,6 +87,7 @@ private:
|
||||
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -104,6 +107,7 @@ private:
|
||||
m_hotkey.shift = true;
|
||||
m_hotkey.ctrl = false;
|
||||
m_hotkey.key = 'T';
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
#include <common/utils/EventWaiter.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"OpenShortcutGuide";
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD /*ul_reason_for_call*/, LPVOID /*lpReserved*/)
|
||||
{
|
||||
return TRUE;
|
||||
@@ -370,6 +375,11 @@ private:
|
||||
m_hotkey.modifiersMask = MOD_SHIFT | MOD_WIN;
|
||||
m_hotkey.vkCode = VK_OEM_2;
|
||||
}
|
||||
|
||||
if (m_hotkey.name == nullptr)
|
||||
{
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace
|
||||
const wchar_t JSON_KEY_RUN_SNAPSHOT_TOOL_HOTKEY[] = L"run-snapshot-tool-hotkey";
|
||||
const wchar_t JSON_KEY_RUN_LAUNCHER_HOTKEY[] = L"run-launcher-hotkey";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
const wchar_t ACTIVATION_HOTKEY_NAME[] = L"Hotkey";
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
|
||||
@@ -307,6 +308,7 @@ private:
|
||||
}
|
||||
|
||||
m_hotkey.vkCode = static_cast<WORD>(hotkey.get_code());
|
||||
m_hotkey.name = ACTIVATION_HOTKEY_NAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_HOTKEY[] = L"hotkey";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"Hotkey";
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
|
||||
@@ -248,6 +250,7 @@ private:
|
||||
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -267,6 +270,7 @@ private:
|
||||
m_hotkey.shift = false;
|
||||
m_hotkey.ctrl = true;
|
||||
m_hotkey.key = 'T';
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Info: Ending the Explorer process isn't possible..
|
||||
/// Looks up a localized string similar to Info: Killing the Explorer process isn't possible..
|
||||
/// </summary>
|
||||
public static string windowwalker_ExplorerInfoTitle {
|
||||
get {
|
||||
@@ -133,7 +133,7 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to End task.
|
||||
/// Looks up a localized string similar to Kill process.
|
||||
/// </summary>
|
||||
public static string windowwalker_Kill {
|
||||
get {
|
||||
@@ -142,7 +142,7 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You are going to end the following process:.
|
||||
/// Looks up a localized string similar to Your are going to kill the following process:.
|
||||
/// </summary>
|
||||
public static string windowwalker_KillMessage {
|
||||
get {
|
||||
@@ -160,7 +160,7 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to End task confirmation.
|
||||
/// Looks up a localized string similar to Kill process confirmation.
|
||||
/// </summary>
|
||||
public static string windowwalker_KillMessageTitle {
|
||||
get {
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace
|
||||
const wchar_t JSON_KEY_SHIFT[] = L"shift";
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"ActivationShortcut";
|
||||
}
|
||||
|
||||
struct ModuleSettings
|
||||
@@ -82,6 +84,7 @@ private:
|
||||
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -101,6 +104,7 @@ private:
|
||||
m_hotkey.shift = true;
|
||||
m_hotkey.ctrl = false;
|
||||
m_hotkey.key = 'C';
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,14 +45,41 @@ public:
|
||||
bool shift = false;
|
||||
bool alt = false;
|
||||
unsigned char key = 0;
|
||||
const wchar_t* name = nullptr;
|
||||
|
||||
std::strong_ordering operator<=>(const Hotkey&) const = default;
|
||||
std::strong_ordering operator<=>(const Hotkey& other) const
|
||||
{
|
||||
// Compare bool fields first
|
||||
if (auto cmp = (win <=> other.win); cmp != 0)
|
||||
return cmp;
|
||||
if (auto cmp = (ctrl <=> other.ctrl); cmp != 0)
|
||||
return cmp;
|
||||
if (auto cmp = (shift <=> other.shift); cmp != 0)
|
||||
return cmp;
|
||||
if (auto cmp = (alt <=> other.alt); cmp != 0)
|
||||
return cmp;
|
||||
|
||||
// Compare key value only
|
||||
return key <=> other.key;
|
||||
|
||||
// Note: Deliberately NOT comparing 'name' field
|
||||
}
|
||||
|
||||
bool operator==(const Hotkey& other) const
|
||||
{
|
||||
return win == other.win &&
|
||||
ctrl == other.ctrl &&
|
||||
shift == other.shift &&
|
||||
alt == other.alt &&
|
||||
key == other.key;
|
||||
}
|
||||
};
|
||||
|
||||
struct HotkeyEx
|
||||
{
|
||||
WORD modifiersMask = 0;
|
||||
WORD vkCode = 0;
|
||||
const wchar_t* name = nullptr;
|
||||
};
|
||||
|
||||
/* Returns the localized name of the PowerToy*/
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_OPEN_POWERLAUNCHER[] = L"open_powerlauncher";
|
||||
const wchar_t JSON_KEY_USE_CENTRALIZED_KEYBOARD_HOOK[] = L"use_centralized_keyboard_hook";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"OpenPowerLauncher";
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
|
||||
@@ -390,6 +392,7 @@ void Microsoft_Launcher::parse_hotkey(PowerToysSettings::PowerToyValues& setting
|
||||
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -418,6 +421,7 @@ void Microsoft_Launcher::parse_hotkey(PowerToysSettings::PowerToyValues& setting
|
||||
m_hotkey.shift = false;
|
||||
m_hotkey.ctrl = false;
|
||||
m_hotkey.key = VK_SPACE;
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
|
||||
const wchar_t JSON_KEY_ALWAYS_RUN_NOT_ELEVATED[] = L"AlwaysRunNotElevated";
|
||||
const wchar_t JSON_KEY_NAME[] = L"hotkeyName";
|
||||
const wchar_t ACTIVATION_SHORTCUT_NAME[] = L"ActivationShortcut";
|
||||
}
|
||||
|
||||
// The PowerToy name that will be shown in the settings.
|
||||
@@ -127,6 +129,7 @@ private:
|
||||
m_hotkey.shift = false;
|
||||
m_hotkey.ctrl = true;
|
||||
m_hotkey.key = ' ';
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
|
||||
void parse_hotkey(winrt::Windows::Data::Json::JsonObject& jsonHotkeyObject)
|
||||
@@ -138,6 +141,7 @@ private:
|
||||
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -152,6 +156,7 @@ private:
|
||||
m_hotkey.shift = false;
|
||||
m_hotkey.ctrl = true;
|
||||
m_hotkey.key = ' ';
|
||||
m_hotkey.name = ACTIVATION_SHORTCUT_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "centralized_hotkeys.h"
|
||||
#include "hotkey_conflict_detector.h"
|
||||
|
||||
#include <map>
|
||||
#include <common/logger/logger.h>
|
||||
@@ -40,12 +41,15 @@ namespace CentralizedHotkeys
|
||||
return res;
|
||||
}
|
||||
|
||||
bool AddHotkeyAction(Shortcut shortcut, Action action)
|
||||
bool AddHotkeyAction(Shortcut shortcut, Action action, std::wstring moduleName, bool isEnabled)
|
||||
{
|
||||
if (!actions[shortcut].empty())
|
||||
HotkeyConflictDetector::HotkeyConflictManager& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
HotkeyConflictDetector::Hotkey hotkey = HotkeyConflictDetector::ShortcutToHotkey(shortcut);
|
||||
bool succeed = hkmng.AddHotkey(hotkey, moduleName.c_str(), shortcut.hotkeyName, isEnabled);
|
||||
|
||||
if (!succeed)
|
||||
{
|
||||
// It will only work if previous one is rewritten
|
||||
Logger::warn(L"{} shortcut is already registered", ToWstring(shortcut));
|
||||
Logger::warn(L"Shortcut conflict detected. Shortcut: {}, from module: {}", ToWstring(shortcut), moduleName);
|
||||
}
|
||||
|
||||
actions[shortcut].push_back(action);
|
||||
@@ -57,7 +61,6 @@ namespace CentralizedHotkeys
|
||||
static int nextId = 0;
|
||||
ids[shortcut] = nextId++;
|
||||
}
|
||||
|
||||
if (!RegisterHotKey(runnerWindow, ids[shortcut], shortcut.modifiersMask, shortcut.vkCode))
|
||||
{
|
||||
Logger::warn(L"Failed to add {} shortcut. {}", ToWstring(shortcut), get_last_error_or_default(GetLastError()));
|
||||
@@ -73,8 +76,18 @@ namespace CentralizedHotkeys
|
||||
|
||||
void UnregisterHotkeysForModule(std::wstring moduleName)
|
||||
{
|
||||
auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
for (auto it = actions.begin(); it != actions.end(); it++)
|
||||
{
|
||||
for (auto action : it->second)
|
||||
{
|
||||
if (action.moduleName == moduleName)
|
||||
{
|
||||
HotkeyConflictDetector::Hotkey hotkey = HotkeyConflictDetector::ShortcutToHotkey(it->first);
|
||||
hkmng.RemoveHotkey(hotkey, moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
auto val = std::find_if(it->second.begin(), it->second.end(), [moduleName](Action a) { return a.moduleName == moduleName; });
|
||||
if (val != it->second.end())
|
||||
{
|
||||
|
||||
@@ -20,11 +20,13 @@ namespace CentralizedHotkeys
|
||||
{
|
||||
WORD modifiersMask;
|
||||
WORD vkCode;
|
||||
const wchar_t* hotkeyName;
|
||||
|
||||
Shortcut(WORD modifiersMask = 0, WORD vkCode = 0)
|
||||
Shortcut(WORD modifiersMask = 0, WORD vkCode = 0, const wchar_t* hotkeyName = nullptr)
|
||||
{
|
||||
this->modifiersMask = modifiersMask;
|
||||
this->vkCode = vkCode;
|
||||
this->hotkeyName = hotkeyName;
|
||||
}
|
||||
|
||||
bool operator<(const Shortcut& key) const
|
||||
@@ -35,7 +37,7 @@ namespace CentralizedHotkeys
|
||||
|
||||
std::wstring ToWstring(const Shortcut& shortcut);
|
||||
|
||||
bool AddHotkeyAction(Shortcut shortcut, Action action);
|
||||
bool AddHotkeyAction(Shortcut shortcut, Action action, std::wstring moduleName, bool isEnabled);
|
||||
|
||||
void UnregisterHotkeysForModule(std::wstring moduleName);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "centralized_kb_hook.h"
|
||||
#include "hotkey_conflict_detector.h"
|
||||
#include <common/debug_control.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <common/logger/logger.h>
|
||||
@@ -187,10 +188,14 @@ namespace CentralizedKeyboardHook
|
||||
return CallNextHookEx(hHook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, std::function<bool()>&& action) noexcept
|
||||
void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, bool isEnabled, std::function<bool()>&& action) noexcept
|
||||
{
|
||||
Logger::trace(L"Register hotkey action for {}", moduleName);
|
||||
std::unique_lock lock{ mutex };
|
||||
|
||||
HotkeyConflictDetector::HotkeyConflictManager& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
hkmng.AddHotkey(hotkey, moduleName.c_str(), hotkey.name, isEnabled);
|
||||
|
||||
hotkeyDescriptors.insert({ .hotkey = hotkey, .moduleName = moduleName, .action = std::move(action) });
|
||||
}
|
||||
|
||||
@@ -238,6 +243,10 @@ namespace CentralizedKeyboardHook
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::info(L"Removing all hotkeys of {}", moduleName);
|
||||
HotkeyConflictDetector::HotkeyConflictManager& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
hkmng.RemoveHotkeyByModule(moduleName);
|
||||
}
|
||||
|
||||
void Start() noexcept
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace CentralizedKeyboardHook
|
||||
|
||||
void Start() noexcept;
|
||||
void Stop() noexcept;
|
||||
void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, std::function<bool()>&& action) noexcept;
|
||||
void SetHotkeyAction(const std::wstring& moduleName, const Hotkey& hotkey, bool isEnabled,std::function<bool()>&& action) noexcept;
|
||||
void AddPressedKeyAction(const std::wstring& moduleName, const DWORD vk, const UINT milliseconds, std::function<bool()>&& action) noexcept;
|
||||
void ClearModuleHotkeys(const std::wstring& moduleName) noexcept;
|
||||
void RegisterWindow(HWND hwnd) noexcept;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "auto_start_helper.h"
|
||||
#include "tray_icon.h"
|
||||
#include "Generated files/resource.h"
|
||||
#include "hotkey_conflict_detector.h"
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include "powertoy_module.h"
|
||||
@@ -204,11 +205,15 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save)
|
||||
{
|
||||
Logger::info(L"apply_general_settings: Enabling powertoy {}", name);
|
||||
powertoy->enable();
|
||||
auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
hkmng.EnableHotkeyByModule(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info(L"apply_general_settings: Disabling powertoy {}", name);
|
||||
powertoy->disable();
|
||||
auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
hkmng.DisableHotkeyByModule(name);
|
||||
}
|
||||
// Sync the hotkey state with the module state, so it can be removed for disabled modules.
|
||||
powertoy.UpdateHotkeyEx();
|
||||
@@ -315,7 +320,9 @@ void start_enabled_powertoys()
|
||||
{
|
||||
Logger::info(L"start_enabled_powertoys: Enabling powertoy {}", name);
|
||||
powertoy->enable();
|
||||
powertoy.UpdateHotkeyEx();
|
||||
auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
hkmng.EnableHotkeyByModule(name);
|
||||
// powertoy.UpdateHotkeyEx();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
526
src/runner/hotkey_conflict_detector.cpp
Normal file
526
src/runner/hotkey_conflict_detector.cpp
Normal file
@@ -0,0 +1,526 @@
|
||||
#include "pch.h"
|
||||
#include "hotkey_conflict_detector.h"
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <windows.h>
|
||||
#include <unordered_map>
|
||||
#include <cwchar>
|
||||
|
||||
namespace HotkeyConflictDetector
|
||||
{
|
||||
Hotkey ShortcutToHotkey(const CentralizedHotkeys::Shortcut& shortcut)
|
||||
{
|
||||
Hotkey hotkey;
|
||||
|
||||
hotkey.win = (shortcut.modifiersMask & MOD_WIN) != 0;
|
||||
hotkey.ctrl = (shortcut.modifiersMask & MOD_CONTROL) != 0;
|
||||
hotkey.shift = (shortcut.modifiersMask & MOD_SHIFT) != 0;
|
||||
hotkey.alt = (shortcut.modifiersMask & MOD_ALT) != 0;
|
||||
|
||||
hotkey.key = shortcut.vkCode > 255 ? 0 : static_cast<unsigned char>(shortcut.vkCode);
|
||||
|
||||
return hotkey;
|
||||
}
|
||||
|
||||
HotkeyConflictManager* HotkeyConflictManager::instance = nullptr;
|
||||
std::mutex HotkeyConflictManager::instanceMutex;
|
||||
|
||||
HotkeyConflictManager& HotkeyConflictManager::GetInstance()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(instanceMutex);
|
||||
if (instance == nullptr)
|
||||
{
|
||||
instance = new HotkeyConflictManager();
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
HotkeyConflictType HotkeyConflictManager::HasConflict(Hotkey const& _hotkey, const wchar_t* _moduleName, const wchar_t* _hotkeyName)
|
||||
{
|
||||
if (disabledHotkeys.find(_moduleName) != disabledHotkeys.end())
|
||||
{
|
||||
return HotkeyConflictType::NoConflict;
|
||||
}
|
||||
|
||||
uint16_t handle = GetHotkeyHandle(_hotkey);
|
||||
|
||||
if (handle == 0)
|
||||
{
|
||||
return HotkeyConflictType::NoConflict;
|
||||
}
|
||||
|
||||
// The order is important, first to check sys conflict and then inapp conflict
|
||||
if (sysConflictHotkeyMap.find(handle) != sysConflictHotkeyMap.end())
|
||||
{
|
||||
return HotkeyConflictType::SystemConflict;
|
||||
}
|
||||
|
||||
if (inAppConflictHotkeyMap.find(handle) != inAppConflictHotkeyMap.end())
|
||||
{
|
||||
return HotkeyConflictType::InAppConflict;
|
||||
}
|
||||
|
||||
auto it = hotkeyMap.find(handle);
|
||||
|
||||
if (it == hotkeyMap.end())
|
||||
{
|
||||
return HasConflictWithSystemHotkey(_hotkey) ?
|
||||
HotkeyConflictType::SystemConflict :
|
||||
HotkeyConflictType::NoConflict;
|
||||
}
|
||||
|
||||
if (wcscmp(it->second.moduleName.c_str(), _moduleName) == 0 && wcscmp(it->second.hotkeyName.c_str(), _hotkeyName) == 0)
|
||||
{
|
||||
// A shortcut matching its own assignment is not considered a conflict.
|
||||
return HotkeyConflictType::NoConflict;
|
||||
}
|
||||
|
||||
return HotkeyConflictType::InAppConflict;
|
||||
}
|
||||
|
||||
// This function should only be called when a conflict has already been identified.
|
||||
// It returns a list of all conflicting shortcuts.
|
||||
std::vector<HotkeyConflictInfo> HotkeyConflictManager::GetAllConflicts(Hotkey const& _hotkey)
|
||||
{
|
||||
std::vector<HotkeyConflictInfo> conflicts;
|
||||
uint16_t handle = GetHotkeyHandle(_hotkey);
|
||||
|
||||
// Check in-app conflicts first
|
||||
auto inAppIt = inAppConflictHotkeyMap.find(handle);
|
||||
if (inAppIt != inAppConflictHotkeyMap.end())
|
||||
{
|
||||
// Add all in-app conflicts
|
||||
for (const auto& conflict : inAppIt->second)
|
||||
{
|
||||
conflicts.push_back(conflict);
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
// Check system conflicts
|
||||
auto sysIt = sysConflictHotkeyMap.find(handle);
|
||||
if (sysIt != sysConflictHotkeyMap.end())
|
||||
{
|
||||
HotkeyConflictInfo systemConflict;
|
||||
systemConflict.hotkey = _hotkey;
|
||||
systemConflict.moduleName = L"System";
|
||||
systemConflict.hotkeyName = L"System Hotkey";
|
||||
|
||||
conflicts.push_back(systemConflict);
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
// Check if there's a successfully registered hotkey that would conflict
|
||||
auto registeredIt = hotkeyMap.find(handle);
|
||||
if (registeredIt != hotkeyMap.end())
|
||||
{
|
||||
conflicts.push_back(registeredIt->second);
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
// If all the above conditions are ruled out, a system-level conflict is the only remaining explanation.
|
||||
HotkeyConflictInfo systemConflict;
|
||||
systemConflict.hotkey = _hotkey;
|
||||
systemConflict.moduleName = L"System";
|
||||
systemConflict.hotkeyName = L"System Hotkey";
|
||||
conflicts.push_back(systemConflict);
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
bool HotkeyConflictManager::AddHotkey(Hotkey const& _hotkey, const wchar_t* _moduleName, const wchar_t* _hotkeyName, bool isEnabled)
|
||||
{
|
||||
if (!isEnabled)
|
||||
{
|
||||
disabledHotkeys[_moduleName].push_back({ _hotkey, _moduleName, _hotkeyName });
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t handle = GetHotkeyHandle(_hotkey);
|
||||
|
||||
if (handle == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HotkeyConflictType conflictType = HasConflict(_hotkey, _moduleName, _hotkeyName );
|
||||
if (conflictType != HotkeyConflictType::NoConflict)
|
||||
{
|
||||
if (conflictType == HotkeyConflictType::InAppConflict)
|
||||
{
|
||||
auto hotkeyFound = hotkeyMap.find(handle);
|
||||
inAppConflictHotkeyMap[handle].insert({ _hotkey, _moduleName, _hotkeyName });
|
||||
|
||||
if (hotkeyFound != hotkeyMap.end())
|
||||
{
|
||||
inAppConflictHotkeyMap[handle].insert(hotkeyFound->second);
|
||||
hotkeyMap.erase(hotkeyFound);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sysConflictHotkeyMap[handle].insert({ _hotkey, _moduleName, _hotkeyName });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HotkeyConflictInfo hotkeyInfo;
|
||||
hotkeyInfo.moduleName = _moduleName;
|
||||
hotkeyInfo.hotkeyName = _hotkeyName;
|
||||
hotkeyInfo.hotkey = _hotkey;
|
||||
hotkeyMap[handle] = hotkeyInfo;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HotkeyConflictManager::RemoveHotkey(Hotkey const& _hotkey, const std::wstring& moduleName)
|
||||
{
|
||||
uint16_t handle = GetHotkeyHandle(_hotkey);
|
||||
bool foundRecord = false;
|
||||
|
||||
if (disabledHotkeys.find(moduleName) != disabledHotkeys.end())
|
||||
{
|
||||
auto& hotkeys = disabledHotkeys[moduleName];
|
||||
for (auto it = hotkeys.begin(); it != hotkeys.end();)
|
||||
{
|
||||
if (it->hotkey == _hotkey)
|
||||
{
|
||||
it = hotkeys.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (hotkeys.empty())
|
||||
{
|
||||
disabledHotkeys.erase(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = hotkeyMap.find(handle);
|
||||
if (it != hotkeyMap.end() && it->second.moduleName == moduleName)
|
||||
{
|
||||
hotkeyMap.erase(it);
|
||||
foundRecord = true;
|
||||
}
|
||||
|
||||
auto it_sys = sysConflictHotkeyMap.find(handle);
|
||||
if (it_sys != sysConflictHotkeyMap.end())
|
||||
{
|
||||
auto& sysConflicts = it_sys->second;
|
||||
for (auto it_conf = sysConflicts.begin(); it_conf != sysConflicts.end();)
|
||||
{
|
||||
if (it_conf->moduleName == moduleName)
|
||||
{
|
||||
it_conf = sysConflicts.erase(it_conf);
|
||||
foundRecord = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++it_conf;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysConflicts.empty())
|
||||
{
|
||||
sysConflictHotkeyMap.erase(it_sys);
|
||||
}
|
||||
}
|
||||
|
||||
auto it_inApp = inAppConflictHotkeyMap.find(handle);
|
||||
if (it_inApp != inAppConflictHotkeyMap.end())
|
||||
{
|
||||
auto& inAppConflicts = it_inApp->second;
|
||||
for (auto it_conf = inAppConflicts.begin(); it_conf != inAppConflicts.end();)
|
||||
{
|
||||
if (it_conf->moduleName == moduleName)
|
||||
{
|
||||
it_conf = inAppConflicts.erase(it_conf);
|
||||
foundRecord = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++it_conf;
|
||||
}
|
||||
}
|
||||
|
||||
if (inAppConflicts.size() == 1)
|
||||
{
|
||||
const auto& onlyConflict = *inAppConflicts.begin();
|
||||
hotkeyMap[handle] = onlyConflict;
|
||||
inAppConflictHotkeyMap.erase(it_inApp);
|
||||
}
|
||||
if (inAppConflicts.empty())
|
||||
{
|
||||
inAppConflictHotkeyMap.erase(it_inApp);
|
||||
}
|
||||
}
|
||||
|
||||
return foundRecord;
|
||||
}
|
||||
|
||||
std::vector<HotkeyConflictInfo> HotkeyConflictManager::RemoveHotkeyByModule(const std::wstring& moduleName)
|
||||
{
|
||||
std::vector<HotkeyConflictInfo> removedHotkeys;
|
||||
|
||||
if (disabledHotkeys.find(moduleName) != disabledHotkeys.end())
|
||||
{
|
||||
disabledHotkeys.erase(moduleName);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(hotkeyMutex);
|
||||
bool foundRecord = false;
|
||||
|
||||
for (auto it = sysConflictHotkeyMap.begin(); it != sysConflictHotkeyMap.end();)
|
||||
{
|
||||
auto& conflictSet = it->second;
|
||||
for (auto setIt = conflictSet.begin(); setIt != conflictSet.end();)
|
||||
{
|
||||
if (setIt->moduleName == moduleName)
|
||||
{
|
||||
removedHotkeys.push_back(*setIt);
|
||||
setIt = conflictSet.erase(setIt);
|
||||
foundRecord = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++setIt;
|
||||
}
|
||||
}
|
||||
if (conflictSet.empty())
|
||||
{
|
||||
it = sysConflictHotkeyMap.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = inAppConflictHotkeyMap.begin(); it != inAppConflictHotkeyMap.end();)
|
||||
{
|
||||
auto& conflictSet = it->second;
|
||||
uint16_t handle = it->first;
|
||||
|
||||
for (auto setIt = conflictSet.begin(); setIt != conflictSet.end();)
|
||||
{
|
||||
if (setIt->moduleName == moduleName)
|
||||
{
|
||||
removedHotkeys.push_back(*setIt);
|
||||
setIt = conflictSet.erase(setIt);
|
||||
foundRecord = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++setIt;
|
||||
}
|
||||
}
|
||||
|
||||
if (conflictSet.empty())
|
||||
{
|
||||
it = inAppConflictHotkeyMap.erase(it);
|
||||
}
|
||||
else if (conflictSet.size() == 1)
|
||||
{
|
||||
// Move the only remaining conflict to main map
|
||||
const auto& onlyConflict = *conflictSet.begin();
|
||||
hotkeyMap[handle] = onlyConflict;
|
||||
it = inAppConflictHotkeyMap.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = hotkeyMap.begin(); it != hotkeyMap.end();)
|
||||
{
|
||||
if (it->second.moduleName == moduleName)
|
||||
{
|
||||
uint16_t handle = it->first;
|
||||
removedHotkeys.push_back(it->second);
|
||||
it = hotkeyMap.erase(it);
|
||||
foundRecord = true;
|
||||
|
||||
auto inAppIt = inAppConflictHotkeyMap.find(handle);
|
||||
if (inAppIt != inAppConflictHotkeyMap.end() && inAppIt->second.size() == 1)
|
||||
{
|
||||
// Move the only in-app conflict to main map
|
||||
const auto& onlyConflict = *inAppIt->second.begin();
|
||||
hotkeyMap[handle] = onlyConflict;
|
||||
inAppConflictHotkeyMap.erase(inAppIt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return removedHotkeys;
|
||||
}
|
||||
|
||||
void HotkeyConflictManager::EnableHotkeyByModule(const std::wstring& moduleName)
|
||||
{
|
||||
if (disabledHotkeys.find(moduleName) == disabledHotkeys.end())
|
||||
{
|
||||
return; // No disabled hotkeys for this module
|
||||
}
|
||||
|
||||
auto hotkeys = disabledHotkeys[moduleName];
|
||||
disabledHotkeys.erase(moduleName);
|
||||
|
||||
for (const auto& hotkeyInfo : hotkeys)
|
||||
{
|
||||
// Re-add the hotkey as enabled
|
||||
AddHotkey(hotkeyInfo.hotkey, moduleName.c_str(), hotkeyInfo.hotkeyName.c_str(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void HotkeyConflictManager::DisableHotkeyByModule(const std::wstring& moduleName)
|
||||
{
|
||||
auto hotkeys = RemoveHotkeyByModule(moduleName);
|
||||
disabledHotkeys[moduleName] = hotkeys;
|
||||
}
|
||||
|
||||
bool HotkeyConflictManager::HasConflictWithSystemHotkey(const Hotkey& hotkey)
|
||||
{
|
||||
// Convert PowerToys Hotkey format to Win32 RegisterHotKey format
|
||||
UINT modifiers = 0;
|
||||
if (hotkey.win)
|
||||
{
|
||||
modifiers |= MOD_WIN;
|
||||
}
|
||||
if (hotkey.ctrl)
|
||||
{
|
||||
modifiers |= MOD_CONTROL;
|
||||
}
|
||||
if (hotkey.alt)
|
||||
{
|
||||
modifiers |= MOD_ALT;
|
||||
}
|
||||
if (hotkey.shift)
|
||||
{
|
||||
modifiers |= MOD_SHIFT;
|
||||
}
|
||||
|
||||
// No modifiers or no key is not a valid hotkey
|
||||
if (modifiers == 0 || hotkey.key == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use a unique ID for this test registration
|
||||
const int hotkeyId = 0x0FFF; // Arbitrary ID for temporary registration
|
||||
|
||||
// Try to register the hotkey with Windows, using nullptr instead of a window handle
|
||||
if (!RegisterHotKey(nullptr, hotkeyId, modifiers, hotkey.key))
|
||||
{
|
||||
// If registration fails with ERROR_HOTKEY_ALREADY_REGISTERED, it means the hotkey
|
||||
// is already in use by the system or another application
|
||||
if (GetLastError() == ERROR_HOTKEY_ALREADY_REGISTERED)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If registration succeeds, unregister it immediately
|
||||
UnregisterHotKey(nullptr, hotkeyId);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
json::JsonObject HotkeyConflictManager::GetHotkeyConflictsAsJson()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(hotkeyMutex);
|
||||
|
||||
using namespace json;
|
||||
JsonObject root;
|
||||
|
||||
// Serialize hotkey to a unique string format for grouping
|
||||
auto serializeHotkey = [](const Hotkey& hotkey) -> JsonObject {
|
||||
JsonObject obj;
|
||||
obj.Insert(L"win", value(hotkey.win));
|
||||
obj.Insert(L"ctrl", value(hotkey.ctrl));
|
||||
obj.Insert(L"shift", value(hotkey.shift));
|
||||
obj.Insert(L"alt", value(hotkey.alt));
|
||||
obj.Insert(L"key", value(static_cast<int>(hotkey.key)));
|
||||
return obj;
|
||||
};
|
||||
|
||||
// New format: Group conflicts by hotkey
|
||||
JsonArray inAppConflictsArray;
|
||||
JsonArray sysConflictsArray;
|
||||
|
||||
// Process in-app conflicts - only include hotkeys that are actually in conflict
|
||||
for (const auto& [handle, conflicts] : inAppConflictHotkeyMap)
|
||||
{
|
||||
if (!conflicts.empty())
|
||||
{
|
||||
JsonObject conflictGroup;
|
||||
|
||||
// All entries have the same hotkey, so use the first one for the key
|
||||
conflictGroup.Insert(L"hotkey", serializeHotkey(conflicts.begin()->hotkey));
|
||||
|
||||
// Create an array of module info without repeating the hotkey
|
||||
JsonArray modules;
|
||||
for (const auto& info : conflicts)
|
||||
{
|
||||
JsonObject moduleInfo;
|
||||
moduleInfo.Insert(L"moduleName", value(info.moduleName));
|
||||
moduleInfo.Insert(L"hotkeyName", value(info.hotkeyName));
|
||||
modules.Append(moduleInfo);
|
||||
}
|
||||
|
||||
conflictGroup.Insert(L"modules", modules);
|
||||
inAppConflictsArray.Append(conflictGroup);
|
||||
}
|
||||
}
|
||||
|
||||
// Process system conflicts - only include hotkeys that are actually in conflict
|
||||
for (const auto& [handle, conflicts] : sysConflictHotkeyMap)
|
||||
{
|
||||
if (!conflicts.empty())
|
||||
{
|
||||
JsonObject conflictGroup;
|
||||
|
||||
// All entries have the same hotkey, so use the first one for the key
|
||||
conflictGroup.Insert(L"hotkey", serializeHotkey(conflicts.begin()->hotkey));
|
||||
|
||||
// Create an array of module info without repeating the hotkey
|
||||
JsonArray modules;
|
||||
for (const auto& info : conflicts)
|
||||
{
|
||||
JsonObject moduleInfo;
|
||||
moduleInfo.Insert(L"moduleName", value(info.moduleName));
|
||||
moduleInfo.Insert(L"hotkeyName", value(info.hotkeyName));
|
||||
modules.Append(moduleInfo);
|
||||
}
|
||||
|
||||
conflictGroup.Insert(L"modules", modules);
|
||||
sysConflictsArray.Append(conflictGroup);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the grouped conflicts to the root object
|
||||
root.Insert(L"inAppConflicts", inAppConflictsArray);
|
||||
root.Insert(L"sysConflicts", sysConflictsArray);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
uint16_t HotkeyConflictManager::GetHotkeyHandle(const Hotkey& hotkey)
|
||||
{
|
||||
uint16_t handle = hotkey.key;
|
||||
handle |= hotkey.win << 8;
|
||||
handle |= hotkey.ctrl << 9;
|
||||
handle |= hotkey.shift << 10;
|
||||
handle |= hotkey.alt << 11;
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
103
src/runner/hotkey_conflict_detector.h
Normal file
103
src/runner/hotkey_conflict_detector.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
#include "pch.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
|
||||
#include "../modules/interface/powertoy_module_interface.h"
|
||||
#include "centralized_hotkeys.h"
|
||||
#include "common/utils/json.h"
|
||||
|
||||
namespace HotkeyConflictDetector
|
||||
{
|
||||
using Hotkey = PowertoyModuleIface::Hotkey;
|
||||
using HotkeyEx = PowertoyModuleIface::HotkeyEx;
|
||||
using Shortcut = CentralizedHotkeys::Shortcut;
|
||||
|
||||
struct HotkeyConflictInfo
|
||||
{
|
||||
Hotkey hotkey;
|
||||
std::wstring moduleName;
|
||||
std::wstring hotkeyName;
|
||||
bool usingKBHook = true;
|
||||
|
||||
inline bool operator==(const HotkeyConflictInfo& other) const
|
||||
{
|
||||
return hotkey == other.hotkey &&
|
||||
moduleName == other.moduleName &&
|
||||
hotkeyName == other.hotkeyName;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
Hotkey ShortcutToHotkey(const CentralizedHotkeys::Shortcut& shortcut);
|
||||
|
||||
enum HotkeyConflictType
|
||||
{
|
||||
NoConflict = 0,
|
||||
SystemConflict = 1,
|
||||
InAppConflict = 2,
|
||||
};
|
||||
|
||||
class HotkeyConflictManager
|
||||
{
|
||||
public:
|
||||
static HotkeyConflictManager& GetInstance();
|
||||
|
||||
HotkeyConflictType HasConflict(const Hotkey& hotkey, const wchar_t* moduleName, const wchar_t* hotkeyName);
|
||||
std::vector<HotkeyConflictInfo> HotkeyConflictManager::GetAllConflicts(Hotkey const& _hotkey);
|
||||
bool AddHotkey(const Hotkey& hotkey, const wchar_t* moduleName, const wchar_t* hotkeyName, bool isEnabled);
|
||||
bool RemoveHotkey(const Hotkey& hotkey, const std::wstring& moduleName);
|
||||
std::vector<HotkeyConflictInfo> RemoveHotkeyByModule(const std::wstring& moduleName);
|
||||
|
||||
void EnableHotkeyByModule(const std::wstring& moduleName);
|
||||
void DisableHotkeyByModule(const std::wstring& moduleName);
|
||||
|
||||
json::JsonObject GetHotkeyConflictsAsJson();
|
||||
|
||||
private:
|
||||
static std::mutex instanceMutex;
|
||||
static HotkeyConflictManager* instance;
|
||||
|
||||
std::mutex hotkeyMutex;
|
||||
// Hotkey in hotkeyMap means the hotkey has been registered successfully
|
||||
std::unordered_map<uint16_t, HotkeyConflictInfo> hotkeyMap;
|
||||
// Hotkey in sysConflictHotkeyMap means the hotkey has conflict with system defined hotkeys
|
||||
std::unordered_map<uint16_t, std::unordered_set<HotkeyConflictInfo>> sysConflictHotkeyMap;
|
||||
// Hotkey in inAppConflictHotkeyMap means the hotkey has conflict with other modules
|
||||
std::unordered_map<uint16_t, std::unordered_set<HotkeyConflictInfo>> inAppConflictHotkeyMap;
|
||||
|
||||
std::unordered_map<std::wstring, std::vector<HotkeyConflictInfo>> disabledHotkeys;
|
||||
|
||||
uint16_t GetHotkeyHandle(const Hotkey&);
|
||||
bool HasConflictWithSystemHotkey(const Hotkey&);
|
||||
|
||||
HotkeyConflictManager() = default;
|
||||
};
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<HotkeyConflictDetector::HotkeyConflictInfo>
|
||||
{
|
||||
size_t operator()(const HotkeyConflictDetector::HotkeyConflictInfo& info) const
|
||||
{
|
||||
|
||||
size_t hotkeyHash =
|
||||
(info.hotkey.win ? 1ULL : 0ULL) |
|
||||
((info.hotkey.ctrl ? 1ULL : 0ULL) << 1) |
|
||||
((info.hotkey.shift ? 1ULL : 0ULL) << 2) |
|
||||
((info.hotkey.alt ? 1ULL : 0ULL) << 3) |
|
||||
(static_cast<size_t>(info.hotkey.key) << 4);
|
||||
|
||||
size_t moduleHash = std::hash<std::wstring>{}(info.moduleName);
|
||||
size_t nameHash = std::hash<std::wstring>{}(info.hotkeyName);
|
||||
|
||||
return hotkeyHash ^
|
||||
((moduleHash << 1) | (moduleHash >> (sizeof(size_t) * 8 - 1))) ^ // rotate left 1 bit
|
||||
((nameHash << 2) | (nameHash >> (sizeof(size_t) * 8 - 2))); // rotate left 2 bits
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -55,6 +55,15 @@ void PowertoyModule::update_hotkeys()
|
||||
{
|
||||
CentralizedKeyboardHook::ClearModuleHotkeys(pt_module->get_key());
|
||||
|
||||
if (pt_module->is_enabled())
|
||||
{
|
||||
Logger::trace(L"{} module is enabled, registering hotkeys", pt_module->get_key());
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::trace(L"{} module is disabled, unregistering hotkeys", pt_module->get_key());
|
||||
}
|
||||
|
||||
size_t hotkeyCount = pt_module->get_hotkeys(nullptr, 0);
|
||||
std::vector<PowertoyModuleIface::Hotkey> hotkeys(hotkeyCount);
|
||||
pt_module->get_hotkeys(hotkeys.data(), hotkeyCount);
|
||||
@@ -63,7 +72,7 @@ void PowertoyModule::update_hotkeys()
|
||||
|
||||
for (size_t i = 0; i < hotkeyCount; i++)
|
||||
{
|
||||
CentralizedKeyboardHook::SetHotkeyAction(pt_module->get_key(), hotkeys[i], [modulePtr, i] {
|
||||
CentralizedKeyboardHook::SetHotkeyAction(pt_module->get_key(), hotkeys[i], pt_module->is_enabled(), [modulePtr, i] {
|
||||
Logger::trace(L"{} hotkey is invoked from Centralized keyboard hook", modulePtr->get_key());
|
||||
return modulePtr->on_hotkey(i);
|
||||
});
|
||||
@@ -83,7 +92,7 @@ void PowertoyModule::UpdateHotkeyEx()
|
||||
modulePtr->OnHotkeyEx();
|
||||
};
|
||||
|
||||
CentralizedHotkeys::AddHotkeyAction({ hotkey.modifiersMask, hotkey.vkCode }, { pt_module->get_key(), action });
|
||||
CentralizedHotkeys::AddHotkeyAction({ hotkey.modifiersMask, hotkey.vkCode, hotkey.name }, { pt_module->get_key(), action }, pt_module->get_name(), pt_module->is_enabled());
|
||||
}
|
||||
|
||||
// HACK:
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<ClCompile Include="bug_report.cpp" />
|
||||
<ClCompile Include="centralized_hotkeys.cpp" />
|
||||
<ClCompile Include="general_settings.cpp" />
|
||||
<ClCompile Include="hotkey_conflict_detector.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
@@ -71,6 +72,7 @@
|
||||
<ClInclude Include="bug_report.h" />
|
||||
<ClInclude Include="centralized_hotkeys.h" />
|
||||
<ClInclude Include="general_settings.h" />
|
||||
<ClInclude Include="hotkey_conflict_detector.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="centralized_kb_hook.h" />
|
||||
<ClInclude Include="settings_telemetry.h" />
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
<ClCompile Include="bug_report.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="hotkey_conflict_detector.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -93,6 +96,9 @@
|
||||
<ClInclude Include="bug_report.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="hotkey_conflict_detector.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Utils">
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "UpdateUtils.h"
|
||||
#include "centralized_kb_hook.h"
|
||||
#include "Generated files/resource.h"
|
||||
#include "hotkey_conflict_detector.h"
|
||||
|
||||
#include <common/utils/json.h>
|
||||
#include <common/SettingsAPI/settings_helpers.cpp>
|
||||
@@ -249,6 +250,79 @@ void dispatch_received_json(const std::wstring& json_to_parse)
|
||||
const std::wstring save_file_location = PTSettingsHelper::get_root_save_folder_location() + language_filename;
|
||||
json::to_file(save_file_location, j);
|
||||
}
|
||||
else if (name == L"check_hotkey_conflict")
|
||||
{
|
||||
try
|
||||
{
|
||||
PowertoyModuleIface::Hotkey hotkey;
|
||||
hotkey.win = value.GetObjectW().GetNamedBoolean(L"win", false);
|
||||
hotkey.ctrl = value.GetObjectW().GetNamedBoolean(L"ctrl", false);
|
||||
hotkey.shift = value.GetObjectW().GetNamedBoolean(L"shift", false);
|
||||
hotkey.alt = value.GetObjectW().GetNamedBoolean(L"alt", false);
|
||||
hotkey.key = static_cast<unsigned char>(value.GetObjectW().GetNamedNumber(L"key", 0));
|
||||
|
||||
std::wstring requestId = value.GetObjectW().GetNamedString(L"request_id", L"").c_str();
|
||||
std::wstring moduleName = value.GetObjectW().GetNamedString(L"moduleName", L"").c_str();
|
||||
std::wstring hotkeyName = value.GetObjectW().GetNamedString(L"hotkeyName", L"").c_str();
|
||||
|
||||
auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
bool hasConflict = hkmng.HasConflict(hotkey, moduleName.c_str(), hotkeyName.c_str());
|
||||
|
||||
json::JsonObject response;
|
||||
response.SetNamedValue(L"response_type", json::JsonValue::CreateStringValue(L"hotkey_conflict_result"));
|
||||
response.SetNamedValue(L"request_id", json::JsonValue::CreateStringValue(requestId));
|
||||
response.SetNamedValue(L"has_conflict", json::JsonValue::CreateBooleanValue(hasConflict));
|
||||
|
||||
if (hasConflict)
|
||||
{
|
||||
auto conflicts = hkmng.GetAllConflicts(hotkey);
|
||||
if (!conflicts.empty())
|
||||
{
|
||||
// Include all conflicts in the response
|
||||
json::JsonArray allConflicts;
|
||||
for (const auto& conflict : conflicts)
|
||||
{
|
||||
json::JsonObject conflictObj;
|
||||
conflictObj.SetNamedValue(L"module", json::JsonValue::CreateStringValue(conflict.moduleName));
|
||||
conflictObj.SetNamedValue(L"hotkey_name", json::JsonValue::CreateStringValue(conflict.hotkeyName));
|
||||
allConflicts.Append(conflictObj);
|
||||
}
|
||||
response.SetNamedValue(L"all_conflicts", allConflicts);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock lock{ ipc_mutex };
|
||||
if (current_settings_ipc)
|
||||
{
|
||||
current_settings_ipc->send(response.Stringify().c_str());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"Failed to process hotkey conflict check request");
|
||||
}
|
||||
}
|
||||
else if (name == L"get_all_hotkey_conflicts")
|
||||
{
|
||||
try
|
||||
{
|
||||
auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
auto conflictsJson = hkmng.GetHotkeyConflictsAsJson();
|
||||
|
||||
// Add response type identifier
|
||||
conflictsJson.SetNamedValue(L"response_type", json::JsonValue::CreateStringValue(L"all_hotkey_conflicts"));
|
||||
|
||||
std::unique_lock lock{ ipc_mutex };
|
||||
if (current_settings_ipc)
|
||||
{
|
||||
current_settings_ipc->send(conflictsJson.Stringify().c_str());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"Failed to process get all hotkey conflicts request");
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library;
|
||||
public sealed partial class AdvancedPasteAdditionalAction : Observable, IAdvancedPasteAction
|
||||
{
|
||||
private HotkeySettings _shortcut = new();
|
||||
private bool _isShown = true;
|
||||
private bool _isShown;
|
||||
private bool _hasConflict;
|
||||
private string _tooltip;
|
||||
|
||||
[JsonPropertyName("shortcut")]
|
||||
public HotkeySettings Shortcut
|
||||
@@ -38,6 +40,20 @@ public sealed partial class AdvancedPasteAdditionalAction : Observable, IAdvance
|
||||
set => Set(ref _isShown, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool HasConflict
|
||||
{
|
||||
get => _hasConflict;
|
||||
set => Set(ref _hasConflict, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string Tooltip
|
||||
{
|
||||
get => _tooltip;
|
||||
set => Set(ref _tooltip, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<IAdvancedPasteAction> SubActions => [];
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
@@ -20,6 +20,8 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
|
||||
private bool _canMoveUp;
|
||||
private bool _canMoveDown;
|
||||
private bool _isValid;
|
||||
private bool _hasConflict;
|
||||
private string _tooltip;
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public int Id
|
||||
@@ -65,7 +67,8 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
|
||||
// We null-coalesce here rather than outside this branch as we want to raise PropertyChanged when the setter is called
|
||||
// with null; the ShortcutControl depends on this.
|
||||
_shortcut = value ?? new();
|
||||
|
||||
_shortcut.HotkeyName = $"CustomAction_{this.Id}";
|
||||
_shortcut.OwnerModuleName = AdvancedPasteSettings.ModuleName;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
@@ -99,6 +102,20 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
|
||||
private set => Set(ref _isValid, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool HasConflict
|
||||
{
|
||||
get => _hasConflict;
|
||||
set => Set(ref _hasConflict, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string Tooltip
|
||||
{
|
||||
get => _tooltip;
|
||||
set => Set(ref _tooltip, value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<IAdvancedPasteAction> SubActions => [];
|
||||
|
||||
@@ -118,6 +135,8 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
|
||||
IsShown = other.IsShown;
|
||||
CanMoveUp = other.CanMoveUp;
|
||||
CanMoveDown = other.CanMoveDown;
|
||||
HasConflict = other.HasConflict;
|
||||
Tooltip = other.Tooltip;
|
||||
}
|
||||
|
||||
private HotkeySettings GetShortcutClone()
|
||||
@@ -128,7 +147,10 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
|
||||
_ = HotkeySettings.TryParseFromCmd(shortcutString, out shortcut);
|
||||
}
|
||||
|
||||
return (shortcut as HotkeySettings) ?? new HotkeySettings();
|
||||
var result = (shortcut as HotkeySettings) ?? new HotkeySettings();
|
||||
result.HotkeyName = $"CustomAction_{this.Id}";
|
||||
result.OwnerModuleName = AdvancedPasteSettings.ModuleName;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void UpdateIsValid()
|
||||
|
||||
@@ -11,16 +11,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public class AdvancedPasteProperties
|
||||
{
|
||||
public static readonly HotkeySettings DefaultAdvancedPasteUIShortcut = new HotkeySettings(true, false, false, true, 0x56); // Win+Shift+V
|
||||
public static readonly HotkeySettings DefaultAdvancedPasteUIShortcut = new HotkeySettings(true, false, false, true, 0x56, "AdvancedPasteUIShortcut", AdvancedPasteSettings.ModuleName); // Win+Shift+V
|
||||
|
||||
public static readonly HotkeySettings DefaultPasteAsPlainTextShortcut = new HotkeySettings(true, true, true, false, 0x56); // Ctrl+Win+Alt+V
|
||||
public static readonly HotkeySettings DefaultPasteAsPlainTextShortcut = new HotkeySettings(true, true, true, false, 0x56, "PasteAsPlainTextShortcut", AdvancedPasteSettings.ModuleName); // Ctrl+Win+Alt+V
|
||||
|
||||
public AdvancedPasteProperties()
|
||||
{
|
||||
AdvancedPasteUIShortcut = DefaultAdvancedPasteUIShortcut;
|
||||
PasteAsPlainTextShortcut = DefaultPasteAsPlainTextShortcut;
|
||||
PasteAsMarkdownShortcut = new();
|
||||
PasteAsJsonShortcut = new();
|
||||
PasteAsMarkdownShortcut = new("PasteAsMarkdownShortcut", AdvancedPasteSettings.ModuleName);
|
||||
PasteAsJsonShortcut = new("PasteAsJsonShortcut", AdvancedPasteSettings.ModuleName);
|
||||
CustomActions = new();
|
||||
AdditionalActions = new();
|
||||
IsAdvancedAIEnabled = false;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
// Needs to be kept in sync with src\modules\alwaysontop\AlwaysOnTop\Settings.h
|
||||
public class AlwaysOnTopProperties
|
||||
{
|
||||
public static readonly HotkeySettings DefaultHotkeyValue = new HotkeySettings(true, true, false, false, 0x54);
|
||||
public static readonly HotkeySettings DefaultHotkeyValue = new HotkeySettings(true, true, false, false, 0x54, "Hotkey", AlwaysOnTopSettings.ModuleName);
|
||||
public const bool DefaultFrameEnabled = true;
|
||||
public const int DefaultFrameThickness = 15;
|
||||
public const string DefaultFrameColor = "#0099cc";
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class CmdPalProperties
|
||||
{
|
||||
// Default shortcut - Win + Alt + Space
|
||||
public static readonly HotkeySettings DefaultHotkeyValue = new HotkeySettings(true, false, true, false, 32);
|
||||
public static readonly HotkeySettings DefaultHotkeyValue = new HotkeySettings(true, false, true, false, 32, "Hotkey", "CmdPal");
|
||||
|
||||
#pragma warning disable SA1401 // Fields should be private
|
||||
#pragma warning disable CA1051 // Do not declare visible instance fields
|
||||
@@ -44,6 +44,17 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
if (doc.RootElement.TryGetProperty(nameof(Hotkey), out JsonElement hotkeyElement))
|
||||
{
|
||||
Hotkey = JsonSerializer.Deserialize<HotkeySettings>(hotkeyElement.GetRawText());
|
||||
|
||||
if (Hotkey == null)
|
||||
{
|
||||
Hotkey = DefaultHotkeyValue;
|
||||
}
|
||||
|
||||
if (Hotkey.HotkeyName == string.Empty)
|
||||
{
|
||||
Hotkey.HotkeyName = DefaultHotkeyValue.HotkeyName;
|
||||
Hotkey.OwnerModuleName = DefaultHotkeyValue.OwnerModuleName;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class ColorPickerProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x43);
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x43, "ActivationShortcut", ColorPickerSettings.ModuleName);
|
||||
|
||||
public ColorPickerProperties()
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public class ColorPickerPropertiesVersion1
|
||||
{
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x43);
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x43, "ActivationShortcut", ColorPickerSettings.ModuleName);
|
||||
|
||||
public ColorPickerPropertiesVersion1()
|
||||
{
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public class CropAndLockProperties
|
||||
{
|
||||
public static readonly HotkeySettings DefaultReparentHotkeyValue = new HotkeySettings(true, true, false, true, 0x52); // Ctrl+Win+Shift+R
|
||||
public static readonly HotkeySettings DefaultThumbnailHotkeyValue = new HotkeySettings(true, true, false, true, 0x54); // Ctrl+Win+Shift+T
|
||||
public static readonly HotkeySettings DefaultReparentHotkeyValue = new HotkeySettings(true, true, false, true, 0x52, "ReparentHotkey", CropAndLockSettings.ModuleName); // Ctrl+Win+Shift+R
|
||||
public static readonly HotkeySettings DefaultThumbnailHotkeyValue = new HotkeySettings(true, true, false, true, 0x54, "ThumbnailHotkey", CropAndLockSettings.ModuleName); // Ctrl+Win+Shift+T
|
||||
|
||||
public CropAndLockProperties()
|
||||
{
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public const int VkNext = 0x22;
|
||||
public const int VkPrior = 0x21;
|
||||
|
||||
public static readonly HotkeySettings DefaultEditorHotkeyValue = new HotkeySettings(true, false, false, true, VkOem3);
|
||||
public static readonly HotkeySettings DefaultNextTabHotkeyValue = new HotkeySettings(true, false, false, false, VkNext);
|
||||
public static readonly HotkeySettings DefaultPrevTabHotkeyValue = new HotkeySettings(true, false, false, false, VkPrior);
|
||||
public static readonly HotkeySettings DefaultEditorHotkeyValue = new HotkeySettings(true, false, false, true, VkOem3, "EditorHotkey", FancyZonesSettings.ModuleName);
|
||||
public static readonly HotkeySettings DefaultNextTabHotkeyValue = new HotkeySettings(true, false, false, false, VkNext, "NextTabHotkey", FancyZonesSettings.ModuleName);
|
||||
public static readonly HotkeySettings DefaultPrevTabHotkeyValue = new HotkeySettings(true, false, false, false, VkPrior, "PrevTabHotkey", FancyZonesSettings.ModuleName);
|
||||
|
||||
public FZConfigProperties()
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class FindMyMouseProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x46);
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x46, "ActivationShortcut", FindMyMouseSettings.ModuleName);
|
||||
|
||||
[JsonPropertyName("activation_method")]
|
||||
public IntProperty ActivationMethod { get; set; }
|
||||
|
||||
21
src/settings-ui/Settings.UI.Library/HotkeyConflictGroup.cs
Normal file
21
src/settings-ui/Settings.UI.Library/HotkeyConflictGroup.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public class HotkeyConflictGroup
|
||||
{
|
||||
public HotkeySettings Hotkey { get; set; } = new HotkeySettings();
|
||||
|
||||
public List<ModuleHotkeyInfo> Modules { get; set; } = new List<ModuleHotkeyInfo>();
|
||||
|
||||
public bool IsSystemConflict { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts
|
||||
{
|
||||
public class AllHotkeyConflictsData
|
||||
{
|
||||
public List<HotkeyConflictGroupData> InAppConflicts { get; set; } = new List<HotkeyConflictGroupData>();
|
||||
|
||||
public List<HotkeyConflictGroupData> SystemConflicts { get; set; } = new List<HotkeyConflictGroupData>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts
|
||||
{
|
||||
public class AllHotkeyConflictsEventArgs : EventArgs
|
||||
{
|
||||
public AllHotkeyConflictsData Conflicts { get; }
|
||||
|
||||
public AllHotkeyConflictsEventArgs(AllHotkeyConflictsData conflicts)
|
||||
{
|
||||
Conflicts = conflicts;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts
|
||||
{
|
||||
public class HotkeyConflictGroupData
|
||||
{
|
||||
public HotkeyData Hotkey { get; set; }
|
||||
|
||||
public bool IsSystemConflict { get; set; }
|
||||
|
||||
public List<ModuleHotkeyData> Modules { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts
|
||||
{
|
||||
public class HotkeyConflictInfo
|
||||
{
|
||||
public bool IsSystemConflict { get; set; }
|
||||
|
||||
public string ConflictingModuleName { get; set; }
|
||||
|
||||
public string ConflictingHotkeyName { get; set; }
|
||||
|
||||
public List<string> AllConflictingModules { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts
|
||||
{
|
||||
public class HotkeyData
|
||||
{
|
||||
public bool Win { get; set; }
|
||||
|
||||
public bool Ctrl { get; set; }
|
||||
|
||||
public bool Shift { get; set; }
|
||||
|
||||
public bool Alt { get; set; }
|
||||
|
||||
public int Key { get; set; }
|
||||
|
||||
public List<object> GetKeysList()
|
||||
{
|
||||
List<object> shortcutList = new List<object>();
|
||||
|
||||
if (Win)
|
||||
{
|
||||
shortcutList.Add(92); // The Windows key or button.
|
||||
}
|
||||
|
||||
if (Ctrl)
|
||||
{
|
||||
shortcutList.Add("Ctrl");
|
||||
}
|
||||
|
||||
if (Alt)
|
||||
{
|
||||
shortcutList.Add("Alt");
|
||||
}
|
||||
|
||||
if (Shift)
|
||||
{
|
||||
shortcutList.Add(16); // The Shift key or button.
|
||||
}
|
||||
|
||||
if (Key > 0)
|
||||
{
|
||||
switch (Key)
|
||||
{
|
||||
// https://learn.microsoft.com/uwp/api/windows.system.virtualkey?view=winrt-20348
|
||||
case 38: // The Up Arrow key or button.
|
||||
case 40: // The Down Arrow key or button.
|
||||
case 37: // The Left Arrow key or button.
|
||||
case 39: // The Right Arrow key or button.
|
||||
shortcutList.Add(Key);
|
||||
break;
|
||||
default:
|
||||
var localKey = Helper.GetKeyName((uint)Key);
|
||||
shortcutList.Add(localKey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return shortcutList;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
|
||||
if (Win)
|
||||
{
|
||||
output.Append("Win + ");
|
||||
}
|
||||
|
||||
if (Ctrl)
|
||||
{
|
||||
output.Append("Ctrl + ");
|
||||
}
|
||||
|
||||
if (Alt)
|
||||
{
|
||||
output.Append("Alt + ");
|
||||
}
|
||||
|
||||
if (Shift)
|
||||
{
|
||||
output.Append("Shift + ");
|
||||
}
|
||||
|
||||
if (Key > 0)
|
||||
{
|
||||
// For virtual key codes, we can display the key name
|
||||
// This follows the same pattern as HotkeySettings
|
||||
var keyName = Helper.GetKeyName((uint)Key);
|
||||
output.Append(keyName);
|
||||
}
|
||||
else if (output.Length >= 2)
|
||||
{
|
||||
// Remove the trailing " + " if there's no key
|
||||
output.Remove(output.Length - 2, 2);
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
private static string GetKeyName(uint keyCode)
|
||||
{
|
||||
// Simple mapping for common virtual key codes
|
||||
// This could be extended to use the Helper.GetKeyName method if available
|
||||
return keyCode switch
|
||||
{
|
||||
0x08 => "Backspace",
|
||||
0x09 => "Tab",
|
||||
0x0D => "Enter",
|
||||
0x1B => "Escape",
|
||||
0x20 => "Space",
|
||||
0x21 => "Page Up",
|
||||
0x22 => "Page Down",
|
||||
0x23 => "End",
|
||||
0x24 => "Home",
|
||||
0x25 => "Left",
|
||||
0x26 => "Up",
|
||||
0x27 => "Right",
|
||||
0x28 => "Down",
|
||||
0x2D => "Insert",
|
||||
0x2E => "Delete",
|
||||
>= 0x30 and <= 0x39 => ((char)keyCode).ToString(), // 0-9
|
||||
>= 0x41 and <= 0x5A => ((char)keyCode).ToString(), // A-Z
|
||||
0x70 => "F1",
|
||||
0x71 => "F2",
|
||||
0x72 => "F3",
|
||||
0x73 => "F4",
|
||||
0x74 => "F5",
|
||||
0x75 => "F6",
|
||||
0x76 => "F7",
|
||||
0x77 => "F8",
|
||||
0x78 => "F9",
|
||||
0x79 => "F10",
|
||||
0x7A => "F11",
|
||||
0x7B => "F12",
|
||||
_ => $"Key{keyCode}",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts
|
||||
{
|
||||
public class ModuleConflictsData
|
||||
{
|
||||
public List<HotkeyConflictGroupData> InAppConflicts { get; set; } = new List<HotkeyConflictGroupData>();
|
||||
|
||||
public List<HotkeyConflictGroupData> SystemConflicts { get; set; } = new List<HotkeyConflictGroupData>();
|
||||
|
||||
public bool HasConflicts => InAppConflicts.Count > 0 || SystemConflicts.Count > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts
|
||||
{
|
||||
public class ModuleHotkeyData : INotifyPropertyChanged
|
||||
{
|
||||
private string _moduleName;
|
||||
private string _hotkeyName;
|
||||
private HotkeySettings _hotkeySettings;
|
||||
private bool _isSystemConflict;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public string ModuleName
|
||||
{
|
||||
get => _moduleName;
|
||||
set
|
||||
{
|
||||
if (_moduleName != value)
|
||||
{
|
||||
_moduleName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string HotkeyName
|
||||
{
|
||||
get => _hotkeyName;
|
||||
set
|
||||
{
|
||||
if (_hotkeyName != value)
|
||||
{
|
||||
_hotkeyName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HotkeySettings HotkeySettings
|
||||
{
|
||||
get => _hotkeySettings;
|
||||
set
|
||||
{
|
||||
if (_hotkeySettings != value)
|
||||
{
|
||||
_hotkeySettings = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSystemConflict
|
||||
{
|
||||
get => _isSystemConflict;
|
||||
set
|
||||
{
|
||||
if (_isSystemConflict != value)
|
||||
{
|
||||
_isSystemConflict = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,29 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public record HotkeySettings : ICmdLineRepresentable
|
||||
public record HotkeySettings : ICmdLineRepresentable, INotifyPropertyChanged
|
||||
{
|
||||
private const int VKTAB = 0x09;
|
||||
private bool _hasConflict;
|
||||
private string _conflictDescription;
|
||||
private bool _isSystemConflict;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public HotkeySettings()
|
||||
{
|
||||
@@ -23,6 +35,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
Alt = false;
|
||||
Shift = false;
|
||||
Code = 0;
|
||||
|
||||
HasConflict = false;
|
||||
HotkeyName = string.Empty;
|
||||
OwnerModuleName = string.Empty;
|
||||
}
|
||||
|
||||
public HotkeySettings(string hotkeyName, string ownerModuleName)
|
||||
{
|
||||
Win = false;
|
||||
Ctrl = false;
|
||||
Alt = false;
|
||||
Shift = false;
|
||||
Code = 0;
|
||||
|
||||
HasConflict = false;
|
||||
HotkeyName = hotkeyName;
|
||||
OwnerModuleName = ownerModuleName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -33,13 +62,60 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
/// <param name="alt">Should Alt key be used</param>
|
||||
/// <param name="shift">Should Shift key be used</param>
|
||||
/// <param name="code">Go to https://learn.microsoft.com/windows/win32/inputdev/virtual-key-codes to see list of v-keys</param>
|
||||
public HotkeySettings(bool win, bool ctrl, bool alt, bool shift, int code)
|
||||
public HotkeySettings(bool win, bool ctrl, bool alt, bool shift, int code, string hotkeyName = "", string ownerModuleName = "", bool hasConflict = false)
|
||||
{
|
||||
Win = win;
|
||||
Ctrl = ctrl;
|
||||
Alt = alt;
|
||||
Shift = shift;
|
||||
Code = code;
|
||||
HasConflict = hasConflict;
|
||||
HotkeyName = hotkeyName;
|
||||
OwnerModuleName = ownerModuleName;
|
||||
}
|
||||
|
||||
public bool HasConflict
|
||||
{
|
||||
get => _hasConflict;
|
||||
set
|
||||
{
|
||||
if (_hasConflict != value)
|
||||
{
|
||||
_hasConflict = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ConflictDescription
|
||||
{
|
||||
get => _conflictDescription ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (_conflictDescription != value)
|
||||
{
|
||||
_conflictDescription = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSystemConflict
|
||||
{
|
||||
get => _isSystemConflict;
|
||||
set
|
||||
{
|
||||
if (_isSystemConflict != value)
|
||||
{
|
||||
_isSystemConflict = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void UpdateConflictStatus()
|
||||
{
|
||||
Logger.LogInfo($"{this.ToString()}");
|
||||
}
|
||||
|
||||
[JsonPropertyName("win")]
|
||||
@@ -57,6 +133,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; set; }
|
||||
|
||||
[JsonPropertyName("hotkeyName")]
|
||||
public string HotkeyName { get; set; }
|
||||
|
||||
[JsonPropertyName("ownerModuleName")]
|
||||
public string OwnerModuleName { get; set; }
|
||||
|
||||
// This is currently needed for FancyZones, we need to unify these two objects
|
||||
// see src\common\settings_objects.h
|
||||
[JsonPropertyName("key")]
|
||||
@@ -120,9 +202,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
if (Shift)
|
||||
{
|
||||
shortcutList.Add("Shift");
|
||||
|
||||
// shortcutList.Add(16); // The Shift key or button.
|
||||
shortcutList.Add(16); // The Shift key or button.
|
||||
}
|
||||
|
||||
if (Code > 0)
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class MeasureToolProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, true, false, true, 0x4D);
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, true, false, true, 0x4D, "ActivationShortcut", MeasureToolSettings.ModuleName);
|
||||
|
||||
public MeasureToolProperties()
|
||||
{
|
||||
|
||||
19
src/settings-ui/Settings.UI.Library/ModuleHotkeyInfo.cs
Normal file
19
src/settings-ui/Settings.UI.Library/ModuleHotkeyInfo.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public class ModuleHotkeyInfo
|
||||
{
|
||||
public string ModuleName { get; set; }
|
||||
|
||||
public string HotkeyName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class MouseHighlighterProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x48);
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x48, "ActivationShortcut", MouseHighlighterSettings.ModuleName);
|
||||
|
||||
[JsonPropertyName("activation_shortcut")]
|
||||
public HotkeySettings ActivationShortcut { get; set; }
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class MouseJumpProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x44);
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x44, "ActivationShortcut", MouseJumpSettings.ModuleName);
|
||||
|
||||
[JsonPropertyName("activation_shortcut")]
|
||||
public HotkeySettings ActivationShortcut
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class MousePointerCrosshairsProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, true, false, 0x50); // Win + Alt + P
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, true, false, 0x50, "ActivationShortcut", MousePointerCrosshairsSettings.ModuleName); // Win + Alt + P
|
||||
|
||||
[JsonPropertyName("activation_shortcut")]
|
||||
public HotkeySettings ActivationShortcut { get; set; }
|
||||
|
||||
@@ -26,16 +26,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class MouseWithoutBordersProperties : ICloneable
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultHotKeySwitch2AllPC => new HotkeySettings();
|
||||
public static HotkeySettings DefaultHotKeySwitch2AllPC => new HotkeySettings("HotKeySwitch2AllPC", MouseWithoutBordersSettings.ModuleName);
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultHotKeyLockMachine => new HotkeySettings(true, true, true, false, 0x4C);
|
||||
public static HotkeySettings DefaultHotKeyLockMachine => new HotkeySettings(true, true, true, false, 0x4C, "HotKeyLockMachine", MouseWithoutBordersSettings.ModuleName);
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultHotKeyReconnect => new HotkeySettings(true, true, true, false, 0x52);
|
||||
public static HotkeySettings DefaultHotKeyReconnect => new HotkeySettings(true, true, true, false, 0x52, "HotKeyReconnect", MouseWithoutBordersSettings.ModuleName);
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultHotKeyToggleEasyMouse => new HotkeySettings(true, true, true, false, 0x45);
|
||||
public static HotkeySettings DefaultHotKeyToggleEasyMouse => new HotkeySettings(true, true, true, false, 0x45, "HotKeyToggleEasyMouse", MouseWithoutBordersSettings.ModuleName);
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public StringProperty SecurityKey { get; set; }
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class PeekProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(false, true, false, false, 0x20);
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(false, true, false, false, 0x20, "ActivationShortcut", PeekSettings.ModuleName);
|
||||
|
||||
public PeekProperties()
|
||||
{
|
||||
|
||||
@@ -95,20 +95,20 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public bool GenerateThumbnailsFromFiles { get; set; }
|
||||
|
||||
[CmdConfigureIgnoreAttribute]
|
||||
public HotkeySettings DefaultOpenPowerLauncher => new HotkeySettings(false, false, true, false, 32);
|
||||
public HotkeySettings DefaultOpenPowerLauncher => new HotkeySettings(false, false, true, false, 32, "OpenPowerLauncher", PowerLauncherSettings.ModuleName);
|
||||
|
||||
[CmdConfigureIgnoreAttribute]
|
||||
public HotkeySettings DefaultOpenFileLocation => new HotkeySettings();
|
||||
public HotkeySettings DefaultOpenFileLocation => new HotkeySettings("OpenFileLocation", PowerLauncherSettings.ModuleName);
|
||||
|
||||
[CmdConfigureIgnoreAttribute]
|
||||
public HotkeySettings DefaultCopyPathLocation => new HotkeySettings();
|
||||
public HotkeySettings DefaultCopyPathLocation => new HotkeySettings("CopyPathLocation", PowerLauncherSettings.ModuleName);
|
||||
|
||||
public PowerLauncherProperties()
|
||||
{
|
||||
OpenPowerLauncher = DefaultOpenPowerLauncher;
|
||||
OpenFileLocation = DefaultOpenFileLocation;
|
||||
CopyPathLocation = DefaultCopyPathLocation;
|
||||
OpenConsole = new HotkeySettings();
|
||||
OpenConsole = new HotkeySettings("OpenConsole", PowerLauncherSettings.ModuleName);
|
||||
SearchResultPreference = "most_recently_used";
|
||||
SearchTypePreference = "application_name";
|
||||
IgnoreHotkeysInFullscreen = false;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class PowerOcrProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x54); // Win+Shift+T
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x54, "ActivationShortcut", PowerOcrSettings.ModuleName); // Win+Shift+T
|
||||
|
||||
public PowerOcrProperties()
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public class ShortcutGuideProperties
|
||||
{
|
||||
[CmdConfigureIgnore]
|
||||
public HotkeySettings DefaultOpenShortcutGuide => new HotkeySettings(true, false, false, true, 0xBF);
|
||||
public HotkeySettings DefaultOpenShortcutGuide => new HotkeySettings(true, false, false, true, 0xBF, "OpenShortcutGuide", ShortcutGuideSettings.ModuleName);
|
||||
|
||||
public ShortcutGuideProperties()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events
|
||||
{
|
||||
[EventData]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public class ShortcutConflictControlClickedEvent : EventBase, IEvent
|
||||
{
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
|
||||
public int ConflictCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events
|
||||
{
|
||||
[EventData]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public class ShortcutConflictResolvedEvent : EventBase, IEvent
|
||||
{
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
|
||||
public bool HasConflict { get; set; }
|
||||
|
||||
public string ModuleName { get; set; }
|
||||
|
||||
public string HotkeyName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events
|
||||
{
|
||||
[EventData]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public class ShortcutConflictTestedEvent : EventBase, IEvent
|
||||
{
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
|
||||
public bool ConflictFound { get; set; }
|
||||
|
||||
public string ModuleName { get; set; }
|
||||
|
||||
public string HotkeyName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
Name,
|
||||
}
|
||||
|
||||
public static readonly HotkeySettings DefaultHotkeyValue = new HotkeySettings(true, true, false, false, 0xC0);
|
||||
public static readonly HotkeySettings DefaultHotkeyValue = new HotkeySettings(true, true, false, false, 0xC0, "Hotkey", WorkspacesSettings.ModuleName);
|
||||
|
||||
public WorkspacesProperties()
|
||||
{
|
||||
|
||||
@@ -14,25 +14,25 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
}
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultToggleKey => new HotkeySettings(false, true, false, false, '1'); // Ctrl+1
|
||||
public static HotkeySettings DefaultToggleKey => new HotkeySettings(false, true, false, false, '1', "ToggleKey", ZoomItSettings.ModuleName); // Ctrl+1
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultLiveZoomToggleKey => new HotkeySettings(false, true, false, false, '4'); // Ctrl+4
|
||||
public static HotkeySettings DefaultLiveZoomToggleKey => new HotkeySettings(false, true, false, false, '4', "LiveZoomToggleKey", ZoomItSettings.ModuleName); // Ctrl+4
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultDrawToggleKey => new HotkeySettings(false, true, false, false, '2'); // Ctrl+2
|
||||
public static HotkeySettings DefaultDrawToggleKey => new HotkeySettings(false, true, false, false, '2', "DrawToggleKey", ZoomItSettings.ModuleName); // Ctrl+2
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultRecordToggleKey => new HotkeySettings(false, true, false, false, '5'); // Ctrl+5
|
||||
public static HotkeySettings DefaultRecordToggleKey => new HotkeySettings(false, true, false, false, '5', "RecordToggleKey", ZoomItSettings.ModuleName); // Ctrl+5
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultSnipToggleKey => new HotkeySettings(false, true, false, false, '6'); // Ctrl+6
|
||||
public static HotkeySettings DefaultSnipToggleKey => new HotkeySettings(false, true, false, false, '6', "SnipToggleKey", ZoomItSettings.ModuleName); // Ctrl+6
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultBreakTimerKey => new HotkeySettings(false, true, false, false, '3'); // Ctrl+3
|
||||
public static HotkeySettings DefaultBreakTimerKey => new HotkeySettings(false, true, false, false, '3', "BreakTimerKey", ZoomItSettings.ModuleName); // Ctrl+3
|
||||
|
||||
[CmdConfigureIgnore]
|
||||
public static HotkeySettings DefaultDemoTypeToggleKey => new HotkeySettings(false, true, false, false, '7'); // Ctrl+7
|
||||
public static HotkeySettings DefaultDemoTypeToggleKey => new HotkeySettings(false, true, false, false, '7', "DemoTypeToggleKey", ZoomItSettings.ModuleName); // Ctrl+7
|
||||
|
||||
public KeyboardKeysProperty ToggleKey { get; set; }
|
||||
|
||||
|
||||
BIN
src/settings-ui/Settings.UI/Assets/Settings/Icons/Windows.png
Normal file
BIN
src/settings-ui/Settings.UI/Assets/Settings/Icons/Windows.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,32 @@
|
||||
// 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
|
||||
{
|
||||
public partial class BoolNegationConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
return !boolValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
return !boolValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
public partial class BoolToConflictTypeConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool isSystemConflict)
|
||||
{
|
||||
return isSystemConflict ? "System Conflict" : "In-App Conflict";
|
||||
}
|
||||
|
||||
return "Unknown Conflict";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
// Add this file to: src/settings-ui/Settings.UI/Converters/BoolToVisibilityConverter.cs
|
||||
using System;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
public partial class BoolToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
bool shouldInvert = parameter != null && parameter.ToString().Equals("Invert", StringComparison.OrdinalIgnoreCase);
|
||||
bool result = shouldInvert ? !boolValue : boolValue;
|
||||
return result ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Visibility visibility)
|
||||
{
|
||||
return visibility == Visibility.Visible;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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
|
||||
{
|
||||
public partial class ConditionalStringConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (parameter is string paramString && paramString.Contains('|'))
|
||||
{
|
||||
string[] parts = paramString.Split('|');
|
||||
if (parts.Length == 2 && value is bool boolValue)
|
||||
{
|
||||
return boolValue ? parts[0] : parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
return value?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
public partial class ConflictStyleConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool hasConflict)
|
||||
{
|
||||
if (hasConflict)
|
||||
{
|
||||
return Application.Current.Resources["ConflictKeyVisualStyle"];
|
||||
}
|
||||
else
|
||||
{
|
||||
return Application.Current.Resources["AccentKeyVisualStyle"];
|
||||
}
|
||||
}
|
||||
|
||||
return Application.Current.Resources["AccentKeyVisualStyle"];
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
public partial class IntToInvertedVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is int intValue)
|
||||
{
|
||||
return intValue == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +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.Library;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
internal sealed partial class KeyVisualTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate KeyVisualTemplate { get; set; }
|
||||
|
||||
public DataTemplate CommaTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
var stringValue = item as string;
|
||||
return stringValue == KeysDataModel.CommaSeparator ? CommaTemplate : KeyVisualTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,23 +10,17 @@ namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
public partial class ModuleItemTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate TextTemplate { get; set; }
|
||||
|
||||
public DataTemplate ButtonTemplate { get; set; }
|
||||
|
||||
public DataTemplate ShortcutTemplate { get; set; }
|
||||
|
||||
public DataTemplate KBMTemplate { get; set; }
|
||||
public DataTemplate ActivationTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
switch (item)
|
||||
{
|
||||
case DashboardModuleButtonItem: return ButtonTemplate;
|
||||
case DashboardModuleShortcutItem: return ShortcutTemplate;
|
||||
case DashboardModuleTextItem: return TextTemplate;
|
||||
case DashboardModuleKBMItem: return KBMTemplate;
|
||||
default: return TextTemplate;
|
||||
case DashboardModuleActivationItem: return ActivationTemplate;
|
||||
default: return ActivationTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
src/settings-ui/Settings.UI/Helpers/HotkeyConflictHelper.cs
Normal file
75
src/settings-ui/Settings.UI/Helpers/HotkeyConflictHelper.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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.Text.Json.Nodes;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Helpers
|
||||
{
|
||||
public class HotkeyConflictHelper
|
||||
{
|
||||
public delegate void HotkeyConflictCheckCallback(bool hasConflict, HotkeyConflictResponse conflicts);
|
||||
|
||||
private static readonly Dictionary<string, HotkeyConflictCheckCallback> PendingHotkeyConflictChecks = new Dictionary<string, HotkeyConflictCheckCallback>();
|
||||
private static readonly object LockObject = new object();
|
||||
|
||||
public static void CheckHotkeyConflict(HotkeySettings hotkeySettings, Func<string, int> ipcMSGCallBackFunc, HotkeyConflictCheckCallback callback)
|
||||
{
|
||||
if (hotkeySettings == null || ipcMSGCallBackFunc == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string requestId = GenerateRequestId();
|
||||
|
||||
lock (LockObject)
|
||||
{
|
||||
PendingHotkeyConflictChecks[requestId] = callback;
|
||||
}
|
||||
|
||||
var hotkeyObj = new JsonObject
|
||||
{
|
||||
["request_id"] = requestId,
|
||||
["win"] = hotkeySettings.Win,
|
||||
["ctrl"] = hotkeySettings.Ctrl,
|
||||
["shift"] = hotkeySettings.Shift,
|
||||
["alt"] = hotkeySettings.Alt,
|
||||
["key"] = hotkeySettings.Code,
|
||||
["moduleName"] = hotkeySettings.OwnerModuleName,
|
||||
["hotkeyName"] = hotkeySettings.HotkeyName,
|
||||
};
|
||||
|
||||
var requestObject = new JsonObject
|
||||
{
|
||||
["check_hotkey_conflict"] = hotkeyObj,
|
||||
};
|
||||
|
||||
ipcMSGCallBackFunc(requestObject.ToString());
|
||||
}
|
||||
|
||||
public static void HandleHotkeyConflictResponse(HotkeyConflictResponse response)
|
||||
{
|
||||
if (response.AllConflicts.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HotkeyConflictCheckCallback callback = null;
|
||||
|
||||
lock (LockObject)
|
||||
{
|
||||
if (PendingHotkeyConflictChecks.TryGetValue(response.RequestId, out callback))
|
||||
{
|
||||
PendingHotkeyConflictChecks.Remove(response.RequestId);
|
||||
}
|
||||
}
|
||||
|
||||
callback?.Invoke(response.HasConflict, response);
|
||||
}
|
||||
|
||||
private static string GenerateRequestId() => Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Helpers
|
||||
{
|
||||
public class HotkeyConflictResponse
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool HasConflict { get; set; }
|
||||
|
||||
public List<ModuleHotkeyData> AllConflicts { get; set; } = new List<ModuleHotkeyData>();
|
||||
}
|
||||
}
|
||||
216
src/settings-ui/Settings.UI/Helpers/LocalizationHelper.cs
Normal file
216
src/settings-ui/Settings.UI/Helpers/LocalizationHelper.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Helpers
|
||||
{
|
||||
internal sealed class LocalizationHelper
|
||||
{
|
||||
private static readonly Dictionary<(string ModuleName, string HotkeyName), string> HotkeyToResourceKeyMap = new()
|
||||
{
|
||||
// AdvancedPaste module mappings
|
||||
{ ("advancedpaste", "AdvancedPasteUIShortcut"), "AdvancedPasteUI_Shortcut" },
|
||||
{ ("advancedpaste", "PasteAsPlainTextShortcut"), "PasteAsPlainText_Shortcut" },
|
||||
{ ("advancedpaste", "PasteAsMarkdownShortcut"), "PasteAsMarkdown_Shortcut" },
|
||||
{ ("advancedpaste", "PasteAsJsonShortcut"), "PasteAsJson_Shortcut" },
|
||||
{ ("advancedpaste", "ImageToTextShortcut"), "ImageToText" },
|
||||
{ ("advancedpaste", "PasteAsTxtFileShortcut"), "PasteAsTxtFile" },
|
||||
{ ("advancedpaste", "PasteAsPngFileShortcut"), "PasteAsPngFile" },
|
||||
{ ("advancedpaste", "PasteAsHtmlFileShortcut"), "PasteAsHtmlFile" },
|
||||
{ ("advancedpaste", "TranscodeToMp3Shortcut"), "TranscodeToMp3" },
|
||||
{ ("advancedpaste", "TranscodeToMp4Shortcut"), "TranscodeToMp4" },
|
||||
|
||||
// AlwaysOnTop module mappings
|
||||
{ ("alwaysontop", "Hotkey"), "AlwaysOnTop_ActivationShortcut" },
|
||||
|
||||
// ColorPicker module mappings
|
||||
{ ("colorpicker", "ActivationShortcut"), "Activation_Shortcut" },
|
||||
|
||||
// CropAndLock module mappings
|
||||
{ ("cropandlock", "ThumbnailHotkey"), "CropAndLock_ThumbnailActivation_Shortcut" },
|
||||
{ ("cropandlock", "ReparentHotkey"), "CropAndLock_ReparentActivation_Shortcut" },
|
||||
|
||||
// MeasureTool module mappings
|
||||
{ ("measuretool", "ActivationShortcut"), "MeasureTool_ActivationShortcut" },
|
||||
|
||||
// ShortcutGuide module mappings
|
||||
{ ("shortcutguide", "OpenShortcutGuide"), "Activation_Shortcut" },
|
||||
|
||||
// PowerOCR/TextExtractor module mappings
|
||||
{ ("textextractor", "ActivationShortcut"), "Activation_Shortcut" },
|
||||
|
||||
// Workspaces module mappings
|
||||
{ ("workspaces", "Hotkey"), "Workspaces_ActivationShortcut" },
|
||||
|
||||
// Peek module mappings
|
||||
{ ("peek", "ActivationShortcut"), "Activation_Shortcut" },
|
||||
|
||||
// PowerLauncher module mappings
|
||||
{ ("powerlauncher", "OpenPowerLauncher"), "PowerLauncher_OpenPowerLauncher" },
|
||||
|
||||
// MouseUtils module mappings
|
||||
{ ("mousehighlighter", "ActivationShortcut"), "MouseUtils_MouseHighlighter_ActivationShortcut" },
|
||||
{ ("mousejump", "ActivationShortcut"), "MouseUtils_MouseJump_ActivationShortcut" },
|
||||
{ ("mousepointercrosshairs", "ActivationShortcut"), "MouseUtils_MousePointerCrosshairs_ActivationShortcut" },
|
||||
{ ("findmymouse", "ActivationShortcut"), "MouseUtils_FindMyMouse_ActivationShortcut" },
|
||||
|
||||
// Mouse without borders module mappings
|
||||
{ ("mousewithoutborders", "HotKeySwitch2AllPC"), "MouseWithoutBorders_Switch2AllPcShortcut" },
|
||||
{ ("mousewithoutborders", "HotKeyLockMachine"), "MouseWithoutBorders_LockMachinesShortcut" },
|
||||
{ ("mousewithoutborders", "HotKeyReconnect"), "MouseWithoutBorders_ReconnectShortcut" },
|
||||
{ ("mousewithoutborders", "HotKeyToggleEasyMouse"), "MouseWithoutBorders_ToggleEasyMouseShortcut" },
|
||||
};
|
||||
|
||||
// Delegate for getting custom action names
|
||||
public static Func<string, int, string> GetCustomActionNameDelegate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the localized header text based on module name and hotkey name
|
||||
/// </summary>
|
||||
/// <param name="moduleName">The name of the module (case-insensitive)</param>
|
||||
/// <param name="hotkeyName">The name of the hotkey</param>
|
||||
/// <returns>The localized header text, or the hotkey name if no resource is found</returns>
|
||||
public static string GetLocalizedHotkeyHeader(string moduleName, string hotkeyName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(hotkeyName))
|
||||
{
|
||||
return hotkeyName ?? string.Empty;
|
||||
}
|
||||
|
||||
var key = (moduleName.ToLowerInvariant(), hotkeyName);
|
||||
|
||||
// Try to get from resource file using resource key mapping
|
||||
if (HotkeyToResourceKeyMap.TryGetValue(key, out string resourceKey))
|
||||
{
|
||||
var localizedText = GetLocalizedStringFromResource(resourceKey);
|
||||
if (!string.IsNullOrEmpty(localizedText))
|
||||
{
|
||||
return localizedText;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle custom actions for AdvancedPaste
|
||||
if (moduleName.Equals("advancedpaste", StringComparison.OrdinalIgnoreCase) &&
|
||||
hotkeyName.StartsWith("CustomAction_", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Try to get the custom action name using the delegate
|
||||
if (GetCustomActionNameDelegate != null &&
|
||||
int.TryParse(hotkeyName.AsSpan("CustomAction_".Length), out int actionId))
|
||||
{
|
||||
var customActionName = GetCustomActionNameDelegate(moduleName, actionId);
|
||||
if (!string.IsNullOrEmpty(customActionName))
|
||||
{
|
||||
return customActionName;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to resource
|
||||
var customActionText = GetLocalizedStringFromResource("PasteAsCustom_Shortcut");
|
||||
if (!string.IsNullOrEmpty(customActionText))
|
||||
{
|
||||
return customActionText;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to generate resource key from hotkey name
|
||||
var fallbackResourceKey = GenerateResourceKeyFromHotkeyName(moduleName, hotkeyName);
|
||||
var fallbackText = GetLocalizedStringFromResource(fallbackResourceKey);
|
||||
if (!string.IsNullOrEmpty(fallbackText))
|
||||
{
|
||||
return fallbackText;
|
||||
}
|
||||
|
||||
// Final fallback: return the hotkey name as-is
|
||||
return hotkeyName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a localized string from the resource file using ResourceLoaderInstance
|
||||
/// Tries multiple variations of the resource key to handle different naming conventions
|
||||
/// </summary>
|
||||
/// <param name="resourceKey">The resource key</param>
|
||||
/// <returns>The localized string, or null if not found</returns>
|
||||
private static string GetLocalizedStringFromResource(string resourceKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resourceKey))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||
if (resourceLoader != null)
|
||||
{
|
||||
// Try different variations of the resource key
|
||||
string[] keyVariations =
|
||||
{
|
||||
$"{resourceKey}.Header", // Try with .Header suffix first
|
||||
resourceKey, // Try the key as-is
|
||||
$"{resourceKey}/Header", // Try with /Header suffix (some resources use this format)
|
||||
$"{resourceKey}_Header", // Try with _Header suffix
|
||||
};
|
||||
|
||||
foreach (var keyVariation in keyVariations)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = resourceLoader.GetString(keyVariation);
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Continue to next variation
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If resource loading fails, return null to allow fallback
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a resource key from module name and hotkey name
|
||||
/// </summary>
|
||||
/// <param name="moduleName">The module name</param>
|
||||
/// <param name="hotkeyName">The hotkey name</param>
|
||||
/// <returns>Generated resource key</returns>
|
||||
private static string GenerateResourceKeyFromHotkeyName(string moduleName, string hotkeyName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(hotkeyName))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Clean module name - capitalize first letter and make rest lowercase
|
||||
var cleanModuleName = char.ToUpperInvariant(moduleName[0]) + moduleName.Substring(1).ToLowerInvariant();
|
||||
|
||||
// Clean hotkey name
|
||||
string cleanHotkeyName = hotkeyName;
|
||||
if (hotkeyName.EndsWith("Shortcut", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
cleanHotkeyName = hotkeyName.Substring(0, hotkeyName.Length - "Shortcut".Length);
|
||||
}
|
||||
else if (cleanHotkeyName.EndsWith("Hotkey", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
cleanHotkeyName = cleanHotkeyName.Substring(0, cleanHotkeyName.Length - "Hotkey".Length);
|
||||
}
|
||||
|
||||
// Generate resource key pattern: ModuleName_HotkeyName_Shortcut
|
||||
return $"{cleanModuleName}_{cleanHotkeyName}_Shortcut";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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 Microsoft.PowerToys.Settings.UI.Services;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Helpers
|
||||
{
|
||||
public static class ModuleNavigationHelper
|
||||
{
|
||||
private static readonly Dictionary<string, Type> ModulePageMapping = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "AdvancedPaste", typeof(AdvancedPastePage) },
|
||||
{ "AlwaysOnTop", typeof(AlwaysOnTopPage) },
|
||||
{ "ColorPicker", typeof(ColorPickerPage) },
|
||||
{ "CropAndLock", typeof(CropAndLockPage) },
|
||||
{ "MeasureTool", typeof(MeasureToolPage) },
|
||||
{ "MouseHighlighter", typeof(MouseUtilsPage) },
|
||||
{ "MouseJump", typeof(MouseUtilsPage) },
|
||||
{ "MousePointerCrosshairs", typeof(MouseUtilsPage) },
|
||||
{ "FindMyMouse", typeof(MouseUtilsPage) },
|
||||
{ "MouseWithoutBorders", typeof(MouseWithoutBordersPage) },
|
||||
{ "Peek", typeof(PeekPage) },
|
||||
{ "PowerLauncher", typeof(PowerLauncherPage) },
|
||||
{ "PowerOCR", typeof(PowerOcrPage) },
|
||||
{ "ShortcutGuide", typeof(ShortcutGuidePage) },
|
||||
{ "Workspaces", typeof(WorkspacesPage) },
|
||||
|
||||
// Shortcut conflict detection does not support the following modules.
|
||||
{ "ZoomIt", typeof(ZoomItPage) },
|
||||
{ "CmdPal", typeof(CmdPalPage) },
|
||||
{ "FancyZones", typeof(FancyZonesPage) },
|
||||
|
||||
// The following modules do not have any shortcuts
|
||||
{ "PowerPreview", typeof(PowerPreviewPage) },
|
||||
{ "PowerRename", typeof(PowerRenamePage) },
|
||||
{ "RegistryPreview", typeof(RegistryPreviewPage) },
|
||||
{ "PowerAccent", typeof(PowerAccentPage) },
|
||||
{ "NewPlus", typeof(NewPlusPage) },
|
||||
{ "EnvironmentVariables", typeof(EnvironmentVariablesPage) },
|
||||
{ "FileLocksmith", typeof(FileLocksmithPage) },
|
||||
{ "Hosts", typeof(HostsPage) },
|
||||
{ "ImageResizer", typeof(ImageResizerPage) },
|
||||
{ "KeyboardManager", typeof(KeyboardManagerPage) },
|
||||
{ "Awake", typeof(AwakePage) },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the settings page for the specified module
|
||||
/// </summary>
|
||||
/// <param name="moduleName">The name of the module</param>
|
||||
/// <returns>True if navigation was successful, false otherwise</returns>
|
||||
public static bool NavigateToModulePage(string moduleName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(moduleName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ModulePageMapping.TryGetValue(moduleName, out Type pageType))
|
||||
{
|
||||
return NavigationService.Navigate(pageType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the page type for the specified module
|
||||
/// </summary>
|
||||
/// <param name="moduleName">The name of the module</param>
|
||||
/// <returns>The page type if found, null otherwise</returns>
|
||||
public static Type GetModulePageType(string moduleName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(moduleName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ModulePageMapping.TryGetValue(moduleName, out Type pageType);
|
||||
return pageType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a module has a corresponding settings page
|
||||
/// </summary>
|
||||
/// <param name="moduleName">The name of the module</param>
|
||||
/// <returns>True if the module has a settings page, false otherwise</returns>
|
||||
public static bool HasModulePage(string moduleName)
|
||||
{
|
||||
return !string.IsNullOrEmpty(moduleName) && ModulePageMapping.ContainsKey(moduleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Settings\Modules\APDialog.dark.png" />
|
||||
<None Remove="Assets\Settings\Modules\APDialog.light.png" />
|
||||
<None Remove="SettingsXAML\Controls\Dashboard\CheckUpdateControl.xaml" />
|
||||
<None Remove="SettingsXAML\Controls\Dashboard\ShortcutConflictControl.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Remove="SettingsXAML\App.xaml" />
|
||||
@@ -132,6 +134,12 @@
|
||||
<None Update="Assets\Settings\Scripts\DisableModule.ps1">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Page Update="SettingsXAML\Controls\Dashboard\ShortcutConflictControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Update="SettingsXAML\Controls\Dashboard\CheckUpdateControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Services
|
||||
{
|
||||
public class GlobalHotkeyConflictManager
|
||||
{
|
||||
private readonly Func<string, int> _sendIPCMessage;
|
||||
|
||||
private static GlobalHotkeyConflictManager _instance;
|
||||
private AllHotkeyConflictsData _currentConflicts = new AllHotkeyConflictsData();
|
||||
|
||||
public static GlobalHotkeyConflictManager Instance => _instance;
|
||||
|
||||
public static void Initialize(Func<string, int> sendIPCMessage)
|
||||
{
|
||||
_instance = new GlobalHotkeyConflictManager(sendIPCMessage);
|
||||
}
|
||||
|
||||
private GlobalHotkeyConflictManager(Func<string, int> sendIPCMessage)
|
||||
{
|
||||
_sendIPCMessage = sendIPCMessage;
|
||||
|
||||
IPCResponseService.AllHotkeyConflictsReceived += OnAllHotkeyConflictsReceived;
|
||||
}
|
||||
|
||||
public event EventHandler<AllHotkeyConflictsEventArgs> ConflictsUpdated;
|
||||
|
||||
public void RequestAllConflicts()
|
||||
{
|
||||
var requestMessage = "{\"get_all_hotkey_conflicts\":{}}";
|
||||
_sendIPCMessage?.Invoke(requestMessage);
|
||||
}
|
||||
|
||||
private void OnAllHotkeyConflictsReceived(object sender, AllHotkeyConflictsEventArgs e)
|
||||
{
|
||||
_currentConflicts = e.Conflicts;
|
||||
ConflictsUpdated?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public bool HasConflictForHotkey(HotkeySettings hotkey, string moduleName = null, string hotkeyName = null)
|
||||
{
|
||||
if (hotkey == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var allConflictGroups = _currentConflicts.InAppConflicts.Concat(_currentConflicts.SystemConflicts);
|
||||
|
||||
foreach (var group in allConflictGroups)
|
||||
{
|
||||
if (IsHotkeyMatch(hotkey, group.Hotkey))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(moduleName) && !string.IsNullOrEmpty(hotkeyName))
|
||||
{
|
||||
var selfModule = group.Modules.FirstOrDefault(m =>
|
||||
m.ModuleName.Equals(moduleName, StringComparison.OrdinalIgnoreCase) &&
|
||||
m.HotkeyName.Equals(hotkeyName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (selfModule != null && group.Modules.Count == 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public HotkeyConflictInfo GetConflictInfo(HotkeySettings hotkey)
|
||||
{
|
||||
if (hotkey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var allConflictGroups = _currentConflicts.InAppConflicts.Concat(_currentConflicts.SystemConflicts);
|
||||
|
||||
foreach (var group in allConflictGroups)
|
||||
{
|
||||
if (IsHotkeyMatch(hotkey, group.Hotkey))
|
||||
{
|
||||
var conflictModules = group.Modules.Where(m => m != null).ToList();
|
||||
if (conflictModules.Count != 0)
|
||||
{
|
||||
var firstModule = conflictModules.First();
|
||||
return new HotkeyConflictInfo
|
||||
{
|
||||
IsSystemConflict = group.IsSystemConflict,
|
||||
ConflictingModuleName = firstModule.ModuleName,
|
||||
ConflictingHotkeyName = firstModule.HotkeyName,
|
||||
AllConflictingModules = conflictModules.Select(m => $"{m.ModuleName}:{m.HotkeyName}").ToList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsHotkeyMatch(HotkeySettings settings, HotkeyData data)
|
||||
{
|
||||
return settings.Win == data.Win &&
|
||||
settings.Ctrl == data.Ctrl &&
|
||||
settings.Shift == data.Shift &&
|
||||
settings.Alt == data.Alt &&
|
||||
settings.Code == data.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
199
src/settings-ui/Settings.UI/Services/IPCResponseService.cs
Normal file
199
src/settings-ui/Settings.UI/Services/IPCResponseService.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
// 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 Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Windows.Data.Json;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Services
|
||||
{
|
||||
public class IPCResponseService
|
||||
{
|
||||
private static IPCResponseService _instance;
|
||||
|
||||
public static IPCResponseService Instance => _instance ??= new IPCResponseService();
|
||||
|
||||
public static event EventHandler<AllHotkeyConflictsEventArgs> AllHotkeyConflictsReceived;
|
||||
|
||||
public void RegisterForIPC()
|
||||
{
|
||||
ShellPage.ShellHandler?.IPCResponseHandleList.Add(ProcessIPCMessage);
|
||||
}
|
||||
|
||||
public void UnregisterFromIPC()
|
||||
{
|
||||
ShellPage.ShellHandler?.IPCResponseHandleList.Remove(ProcessIPCMessage);
|
||||
}
|
||||
|
||||
private void ProcessIPCMessage(JsonObject json)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (json.TryGetValue("response_type", out IJsonValue responseTypeValue) &&
|
||||
responseTypeValue.ValueType == JsonValueType.String)
|
||||
{
|
||||
string responseType = responseTypeValue.GetString();
|
||||
|
||||
if (responseType.Equals("hotkey_conflict_result", StringComparison.Ordinal))
|
||||
{
|
||||
ProcessHotkeyConflictResult(json);
|
||||
}
|
||||
else if (responseType.Equals("all_hotkey_conflicts", StringComparison.Ordinal))
|
||||
{
|
||||
ProcessAllHotkeyConflicts(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessHotkeyConflictResult(JsonObject json)
|
||||
{
|
||||
string requestId = string.Empty;
|
||||
if (json.TryGetValue("request_id", out IJsonValue requestIdValue) &&
|
||||
requestIdValue.ValueType == JsonValueType.String)
|
||||
{
|
||||
requestId = requestIdValue.GetString();
|
||||
}
|
||||
|
||||
bool hasConflict = false;
|
||||
if (json.TryGetValue("has_conflict", out IJsonValue hasConflictValue) &&
|
||||
hasConflictValue.ValueType == JsonValueType.Boolean)
|
||||
{
|
||||
hasConflict = hasConflictValue.GetBoolean();
|
||||
}
|
||||
|
||||
var allConflicts = new List<ModuleHotkeyData>();
|
||||
|
||||
if (hasConflict)
|
||||
{
|
||||
// Parse the all_conflicts array
|
||||
if (json.TryGetValue("all_conflicts", out IJsonValue allConflictsValue) &&
|
||||
allConflictsValue.ValueType == JsonValueType.Array)
|
||||
{
|
||||
var conflictsArray = allConflictsValue.GetArray();
|
||||
foreach (var conflictItem in conflictsArray)
|
||||
{
|
||||
if (conflictItem.ValueType == JsonValueType.Object)
|
||||
{
|
||||
var conflictObj = conflictItem.GetObject();
|
||||
|
||||
string moduleName = string.Empty;
|
||||
string hotkeyName = string.Empty;
|
||||
|
||||
if (conflictObj.TryGetValue("module", out IJsonValue moduleValue) &&
|
||||
moduleValue.ValueType == JsonValueType.String)
|
||||
{
|
||||
moduleName = moduleValue.GetString();
|
||||
}
|
||||
|
||||
if (conflictObj.TryGetValue("hotkey_name", out IJsonValue hotkeyValue) &&
|
||||
hotkeyValue.ValueType == JsonValueType.String)
|
||||
{
|
||||
hotkeyName = hotkeyValue.GetString();
|
||||
}
|
||||
|
||||
allConflicts.Add(new ModuleHotkeyData
|
||||
{
|
||||
ModuleName = moduleName,
|
||||
HotkeyName = hotkeyName,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var response = new HotkeyConflictResponse
|
||||
{
|
||||
RequestId = requestId,
|
||||
HasConflict = hasConflict,
|
||||
AllConflicts = allConflicts,
|
||||
};
|
||||
|
||||
HotkeyConflictHelper.HandleHotkeyConflictResponse(response);
|
||||
}
|
||||
|
||||
private void ProcessAllHotkeyConflicts(JsonObject json)
|
||||
{
|
||||
var allConflicts = new AllHotkeyConflictsData();
|
||||
|
||||
if (json.TryGetValue("inAppConflicts", out IJsonValue inAppValue) &&
|
||||
inAppValue.ValueType == JsonValueType.Array)
|
||||
{
|
||||
var inAppArray = inAppValue.GetArray();
|
||||
foreach (var conflictGroup in inAppArray)
|
||||
{
|
||||
var conflictObj = conflictGroup.GetObject();
|
||||
var conflictData = ParseConflictGroup(conflictObj, false);
|
||||
if (conflictData != null)
|
||||
{
|
||||
allConflicts.InAppConflicts.Add(conflictData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (json.TryGetValue("sysConflicts", out IJsonValue sysValue) &&
|
||||
sysValue.ValueType == JsonValueType.Array)
|
||||
{
|
||||
var sysArray = sysValue.GetArray();
|
||||
foreach (var conflictGroup in sysArray)
|
||||
{
|
||||
var conflictObj = conflictGroup.GetObject();
|
||||
var conflictData = ParseConflictGroup(conflictObj, true);
|
||||
if (conflictData != null)
|
||||
{
|
||||
allConflicts.SystemConflicts.Add(conflictData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AllHotkeyConflictsReceived?.Invoke(this, new AllHotkeyConflictsEventArgs(allConflicts));
|
||||
}
|
||||
|
||||
private HotkeyConflictGroupData ParseConflictGroup(JsonObject conflictObj, bool isSystemConflict)
|
||||
{
|
||||
if (!conflictObj.TryGetValue("hotkey", out var hotkeyValue) ||
|
||||
!conflictObj.TryGetValue("modules", out var modulesValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var hotkeyObj = hotkeyValue.GetObject();
|
||||
bool win = hotkeyObj.TryGetValue("win", out var winVal) && winVal.GetBoolean();
|
||||
bool ctrl = hotkeyObj.TryGetValue("ctrl", out var ctrlVal) && ctrlVal.GetBoolean();
|
||||
bool shift = hotkeyObj.TryGetValue("shift", out var shiftVal) && shiftVal.GetBoolean();
|
||||
bool alt = hotkeyObj.TryGetValue("alt", out var altVal) && altVal.GetBoolean();
|
||||
int key = hotkeyObj.TryGetValue("key", out var keyVal) ? (int)keyVal.GetNumber() : 0;
|
||||
|
||||
var conflictGroup = new HotkeyConflictGroupData
|
||||
{
|
||||
Hotkey = new HotkeyData { Win = win, Ctrl = ctrl, Shift = shift, Alt = alt, Key = key },
|
||||
IsSystemConflict = isSystemConflict,
|
||||
Modules = new List<ModuleHotkeyData>(),
|
||||
};
|
||||
|
||||
var modulesArray = modulesValue.GetArray();
|
||||
foreach (var module in modulesArray)
|
||||
{
|
||||
var moduleObj = module.GetObject();
|
||||
string moduleName = moduleObj.TryGetValue("moduleName", out var modNameVal) ? modNameVal.GetString() : string.Empty;
|
||||
string hotkeyName = moduleObj.TryGetValue("hotkeyName", out var hotkeyNameVal) ? hotkeyNameVal.GetString() : string.Empty;
|
||||
|
||||
conflictGroup.Modules.Add(new ModuleHotkeyData
|
||||
{
|
||||
ModuleName = moduleName,
|
||||
HotkeyName = hotkeyName,
|
||||
});
|
||||
}
|
||||
|
||||
return conflictGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,19 @@
|
||||
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.Converters"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<ResourceDictionary Source="/SettingsXAML/Controls/KeyVisual/KeyVisual.xaml" />
|
||||
<ResourceDictionary Source="/SettingsXAML/Controls/KeyVisual.xaml" />
|
||||
<ResourceDictionary Source="/SettingsXAML/Styles/TextBlock.xaml" />
|
||||
<ResourceDictionary Source="/SettingsXAML/Styles/Button.xaml" />
|
||||
<ResourceDictionary Source="/SettingsXAML/Styles/InfoBadge.xaml" />
|
||||
<ResourceDictionary Source="/SettingsXAML/Themes/Colors.xaml" />
|
||||
<ResourceDictionary Source="/SettingsXAML/Themes/Generic.xaml" />
|
||||
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
@@ -26,19 +28,28 @@
|
||||
x:Key="BoolToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
TrueValue="Visible" />
|
||||
|
||||
<tkconverters:BoolToObjectConverter
|
||||
x:Key="BoolToComboBoxIndexConverter"
|
||||
FalseValue="0"
|
||||
TrueValue="1" />
|
||||
|
||||
<tkconverters:BoolToObjectConverter
|
||||
x:Key="ReverseBoolToComboBoxIndexConverter"
|
||||
FalseValue="1"
|
||||
TrueValue="0" />
|
||||
|
||||
<tkconverters:DoubleToVisibilityConverter
|
||||
x:Name="DoubleToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
GreaterThan="0"
|
||||
TrueValue="Visible" />
|
||||
<tkconverters:DoubleToVisibilityConverter
|
||||
x:Name="DoubleToInvertedVisibilityConverter"
|
||||
FalseValue="Visible"
|
||||
GreaterThan="0"
|
||||
TrueValue="Collapsed" />
|
||||
<tkconverters:StringFormatConverter x:Key="StringFormatConverter" />
|
||||
<tkconverters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
<converters:UpdateStateToBoolConverter x:Key="UpdateStateToBoolConverter" />
|
||||
|
||||
<x:Double x:Key="SettingsCardSpacing">2</x:Double>
|
||||
|
||||
<!-- Overrides -->
|
||||
@@ -46,6 +57,7 @@
|
||||
<Thickness x:Key="InfoBarContentRootPadding">16,0,0,0</Thickness>
|
||||
<x:Double x:Key="SettingActionControlMinWidth">240</x:Double>
|
||||
|
||||
<x:Double x:Key="PageMaxWidth">1000</x:Double>
|
||||
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="Margin" Value="0,0,0,2" />
|
||||
|
||||
@@ -232,6 +232,12 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
});
|
||||
ipcmanager.Start();
|
||||
|
||||
GlobalHotkeyConflictManager.Initialize(message =>
|
||||
{
|
||||
ipcmanager.Send(message);
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (!ShowOobe && !ShowScoobe && !ShowFlyout)
|
||||
{
|
||||
settingsWindow = new MainWindow();
|
||||
@@ -320,11 +326,20 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
|
||||
settingsWindow.Activate();
|
||||
settingsWindow.NavigateToSection(StartupPage);
|
||||
|
||||
// In DEBUG mode, we might not have IPC set up, so provide a dummy implementation
|
||||
GlobalHotkeyConflictManager.Initialize(message =>
|
||||
{
|
||||
// In debug mode, just log or do nothing
|
||||
System.Diagnostics.Debug.WriteLine($"IPC Message: {message}");
|
||||
return 0;
|
||||
});
|
||||
|
||||
ShowMessageDialog("The application is running in Debug mode.", "DEBUG");
|
||||
#else
|
||||
/* If we try to run Settings as a standalone app, it will start PowerToys.exe if not running and open Settings again through it in the Dashboard page. */
|
||||
Common.UI.SettingsDeepLink.OpenSettings(Common.UI.SettingsDeepLink.SettingsWindow.Dashboard, true);
|
||||
Exit();
|
||||
/* If we try to run Settings as a standalone app, it will start PowerToys.exe if not running and open Settings again through it in the Dashboard page. */
|
||||
Common.UI.SettingsDeepLink.OpenSettings(Common.UI.SettingsDeepLink.SettingsWindow.Dashboard, true);
|
||||
Exit();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.Card"
|
||||
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:local="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Padding="8"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}"
|
||||
mc:Ignorable="d">
|
||||
<Grid
|
||||
VerticalAlignment="{x:Bind VerticalContentAlignment, Mode=OneWay}"
|
||||
Background="{x:Bind Background, Mode=OneWay}"
|
||||
BorderBrush="{x:Bind BorderBrush, Mode=OneWay}"
|
||||
BorderThickness="{x:Bind BorderThickness, Mode=OneWay}"
|
||||
CornerRadius="{x:Bind CornerRadius, Mode=OneWay}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" MinHeight="44" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="TitleGrid">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="16,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind Title, Mode=OneWay}" />
|
||||
<ContentPresenter
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Bind TitleContent, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Rectangle
|
||||
x:Name="Divider"
|
||||
Grid.Row="1"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Fill="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
Visibility="{x:Bind DividerVisibility, Mode=OneWay}" />
|
||||
|
||||
<ContentPresenter
|
||||
Grid.Row="2"
|
||||
Margin="{x:Bind Padding, Mode=OneWay}"
|
||||
HorizontalAlignment="{x:Bind HorizontalContentAlignment, Mode=OneWay}"
|
||||
VerticalAlignment="{x:Bind VerticalContentAlignment, Mode=OneWay}"
|
||||
Content="{x:Bind Content, Mode=OneWay}" />
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="TitleGridVisibilityStates">
|
||||
<VisualState x:Name="TitleGridVisible" />
|
||||
<VisualState x:Name="TitleGridCollapsed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TitleGrid.Visibility" Value="Collapsed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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 Card : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty TitleContentProperty = DependencyProperty.Register(nameof(TitleContent), typeof(object), typeof(Card), new PropertyMetadata(defaultValue: null, OnVisualPropertyChanged));
|
||||
|
||||
public object TitleContent
|
||||
{
|
||||
get => (object)GetValue(TitleContentProperty);
|
||||
set => SetValue(TitleContentProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(Card), new PropertyMetadata(defaultValue: null, OnVisualPropertyChanged));
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => (string)GetValue(TitleProperty);
|
||||
set => SetValue(TitleProperty, value);
|
||||
}
|
||||
|
||||
public static new readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(object), typeof(Card), new PropertyMetadata(defaultValue: null));
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1061:Do not hide base class methods", Justification = "We need to hide the base class method")]
|
||||
public new object Content
|
||||
{
|
||||
get => (object)GetValue(ContentProperty);
|
||||
set => SetValue(ContentProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DividerVisibilityProperty = DependencyProperty.Register(nameof(DividerVisibility), typeof(Visibility), typeof(Card), new PropertyMetadata(defaultValue: null));
|
||||
|
||||
public Visibility DividerVisibility
|
||||
{
|
||||
get => (Visibility)GetValue(DividerVisibilityProperty);
|
||||
set => SetValue(DividerVisibilityProperty, value);
|
||||
}
|
||||
|
||||
public Card()
|
||||
{
|
||||
InitializeComponent();
|
||||
SetVisualStates();
|
||||
}
|
||||
|
||||
private static void OnVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is Card card)
|
||||
{
|
||||
card.SetVisualStates();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetVisualStates()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Title) && TitleContent == null)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "TitleGridCollapsed", true);
|
||||
DividerVisibility = Visibility.Collapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, "TitleGridVisible", true);
|
||||
DividerVisibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.CheckUpdateControl"
|
||||
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:local="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button
|
||||
Click="SWVersionButtonClicked"
|
||||
Style="{StaticResource SubtleButtonStyle}"
|
||||
Visibility="{x:Bind UpdateAvailable, Mode=OneWay}">
|
||||
<Grid ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Width="20"
|
||||
Height="20"
|
||||
CornerRadius="10">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0.5,1">
|
||||
<GradientStop Offset="0.0" Color="#239DE0" />
|
||||
<GradientStop Offset="1.0" Color="#037CD6" />
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
<FontIcon
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="11"
|
||||
Foreground="White"
|
||||
Glyph="" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical">
|
||||
<TextBlock x:Uid="UpdateAvailable_Text" FontWeight="SemiBold" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Run x:Uid="GeneralVersion" />
|
||||
<Run Text="{x:Bind UpdateSettingsConfig.NewVersion, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Button>
|
||||
<Grid
|
||||
Padding="0,0,4,0"
|
||||
VerticalAlignment="Center"
|
||||
ColumnSpacing="16"
|
||||
Visibility="{x:Bind UpdateAvailable, Converter={StaticResource ReverseBoolToVisibilityConverter}, Mode=OneWay}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Width="20"
|
||||
Height="20"
|
||||
CornerRadius="10">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0.5,1">
|
||||
<GradientStop Offset="0.0" Color="#6FB538" />
|
||||
<GradientStop Offset="1.0" Color="#397A24" />
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
<FontIcon
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="11"
|
||||
Foreground="White"
|
||||
Glyph="" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical">
|
||||
<TextBlock x:Uid="YoureUpToDate" FontWeight="SemiBold" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Run x:Uid="General_VersionLastChecked" />
|
||||
<Run Text="{x:Bind UpdateSettingsConfig.LastCheckedDateLocalized, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Services;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public sealed partial class CheckUpdateControl : UserControl
|
||||
{
|
||||
public bool UpdateAvailable { get; set; }
|
||||
|
||||
public UpdatingSettings UpdateSettingsConfig { get; set; }
|
||||
|
||||
public CheckUpdateControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
UpdateSettingsConfig = UpdatingSettings.LoadSettings();
|
||||
UpdateAvailable = UpdateSettingsConfig != null && (UpdateSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToInstall || UpdateSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToDownload);
|
||||
}
|
||||
|
||||
private void SWVersionButtonClicked(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
NavigationService.Navigate(typeof(GeneralPage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.ShortcutConflictControl"
|
||||
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:local="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Visibility="{x:Bind HasConflicts, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Button Click="ShortcutConflictBtn_Click" Style="{StaticResource SubtleButtonStyle}">
|
||||
<Grid ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<FontIcon
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="20"
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
Glyph="" />
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical">
|
||||
<TextBlock FontWeight="SemiBold" Text="Shortcut conflicts" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ConflictText, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Button>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
|
||||
using Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public sealed partial class ShortcutConflictControl : UserControl, INotifyPropertyChanged
|
||||
{
|
||||
private static readonly ResourceLoader ResourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
|
||||
public static readonly DependencyProperty AllHotkeyConflictsDataProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(AllHotkeyConflictsData),
|
||||
typeof(AllHotkeyConflictsData),
|
||||
typeof(ShortcutConflictControl),
|
||||
new PropertyMetadata(null, OnAllHotkeyConflictsDataChanged));
|
||||
|
||||
public AllHotkeyConflictsData AllHotkeyConflictsData
|
||||
{
|
||||
get => (AllHotkeyConflictsData)GetValue(AllHotkeyConflictsDataProperty);
|
||||
set => SetValue(AllHotkeyConflictsDataProperty, value);
|
||||
}
|
||||
|
||||
public int ConflictCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AllHotkeyConflictsData == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
if (AllHotkeyConflictsData.InAppConflicts != null)
|
||||
{
|
||||
count += AllHotkeyConflictsData.InAppConflicts.Count;
|
||||
}
|
||||
|
||||
if (AllHotkeyConflictsData.SystemConflicts != null)
|
||||
{
|
||||
count += AllHotkeyConflictsData.SystemConflicts.Count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public string ConflictText
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = ConflictCount;
|
||||
return count switch
|
||||
{
|
||||
// Todo: localization support
|
||||
0 => "No conflicts found",
|
||||
1 => "1 conflict found",
|
||||
_ => $"{count} conflicts found",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasConflicts => ConflictCount > 0;
|
||||
|
||||
private static void OnAllHotkeyConflictsDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ShortcutConflictControl control)
|
||||
{
|
||||
control.UpdateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void UpdateProperties()
|
||||
{
|
||||
OnPropertyChanged(nameof(ConflictCount));
|
||||
OnPropertyChanged(nameof(ConflictText));
|
||||
OnPropertyChanged(nameof(HasConflicts));
|
||||
|
||||
// Update visibility based on conflict count
|
||||
Visibility = HasConflicts ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public ShortcutConflictControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
|
||||
// Initially hide the control if no conflicts
|
||||
Visibility = HasConflicts ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ShortcutConflictBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AllHotkeyConflictsData == null || !HasConflicts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Log telemetry event when user clicks the shortcut conflict button
|
||||
PowerToysTelemetry.Log.WriteEvent(new ShortcutConflictControlClickedEvent()
|
||||
{
|
||||
ConflictCount = this.ConflictCount,
|
||||
});
|
||||
|
||||
// Create and show the new window instead of dialog
|
||||
var conflictWindow = new ShortcutConflictWindow();
|
||||
|
||||
// Show the window
|
||||
conflictWindow.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
<Window
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard.ShortcutConflictWindow"
|
||||
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.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hotkeyConflicts="using:Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop Kind="Base" />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid x:Name="RootGrid">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title Bar Area -->
|
||||
<Grid Grid.Row="0" Padding="6,6,6,6">
|
||||
|
||||
<TextBlock x:Uid="ShortcutConflictWindow_Title" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
Padding="24"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
ZoomMode="Disabled">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Description text -->
|
||||
<TextBlock
|
||||
x:Uid="ShortcutConflictWindow_Description"
|
||||
Grid.Row="0"
|
||||
Margin="0,0,0,24"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- Conflicts List -->
|
||||
<ItemsControl
|
||||
x:Name="ConflictItemsControl"
|
||||
Grid.Row="1"
|
||||
ItemsSource="{x:Bind ViewModel.ConflictItems, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="hotkeyConflicts:HotkeyConflictGroupData">
|
||||
<StackPanel Margin="0,0,0,32">
|
||||
<!-- Conflict Header -->
|
||||
<StackPanel Margin="0,0,0,12">
|
||||
<!-- Hotkey Header -->
|
||||
<ItemsControl
|
||||
Margin="0,0,0,8"
|
||||
HorizontalAlignment="Left"
|
||||
ItemsSource="{x:Bind Hotkey.GetKeysList()}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:KeyVisual
|
||||
Height="30"
|
||||
Padding="12,4,12,4"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{Binding}"
|
||||
FontWeight="ExtraBold"
|
||||
IsEnabled="True"
|
||||
Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Instruction text -->
|
||||
<TextBlock
|
||||
x:Uid="ShortcutConflictWindow_ModulesUsingShortcut"
|
||||
FontSize="13"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Module Cards and System Conflict Card -->
|
||||
<StackPanel>
|
||||
<!-- PowerToys Module Cards -->
|
||||
<ItemsControl ItemsSource="{x:Bind Modules}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="hotkeyConflicts:ModuleHotkeyData">
|
||||
<tkcontrols:SettingsCard
|
||||
Margin="0,0,0,4"
|
||||
Click="SettingsCard_Click"
|
||||
Description="{x:Bind ModuleName}"
|
||||
HeaderIcon="{ui:BitmapIcon}"
|
||||
IsClickEnabled="True"
|
||||
Loaded="SettingsCard_Loaded">
|
||||
|
||||
<!-- ShortcutControl with TwoWay binding and enabled for editing -->
|
||||
<controls:ShortcutControl
|
||||
x:Name="ShortcutControl"
|
||||
MinWidth="140"
|
||||
Margin="2"
|
||||
VerticalAlignment="Center"
|
||||
HasConflict="True"
|
||||
HotkeySettings="{x:Bind HotkeySettings, Mode=TwoWay}"
|
||||
IsEnabled="True" />
|
||||
|
||||
<!-- Tooltip for the entire card -->
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{x:Bind HotkeySettings.ConflictDescription, Mode=OneWay}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</tkcontrols:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- System Conflict Card (only show if it's a system conflict) -->
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="ShortcutConflictWindow_SystemCard"
|
||||
Margin="0,0,0,4"
|
||||
HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/Windows.png}"
|
||||
IsClickEnabled="False"
|
||||
Visibility="{x:Bind IsSystemConflict}">
|
||||
|
||||
<!-- System shortcut message -->
|
||||
<TextBlock
|
||||
x:Uid="ShortcutConflictWindow_SystemShortcutMessage"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontStyle="Italic"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextFillColorDisabledBrush}" />
|
||||
|
||||
<!-- Tooltip for system conflict -->
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Uid="ShortcutConflictWindow_SystemShortcutTooltip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</tkcontrols:SettingsCard>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Empty State (when no conflicts) -->
|
||||
<StackPanel
|
||||
x:Name="EmptyStatePanel"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="Collapsed">
|
||||
<FontIcon
|
||||
Margin="0,0,0,16"
|
||||
HorizontalAlignment="Center"
|
||||
FontFamily="Segoe Fluent Icons"
|
||||
FontSize="48"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
x:Uid="ShortcutConflictWindow_NoConflictsTitle"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock
|
||||
x:Uid="ShortcutConflictWindow_NoConflictsDescription"
|
||||
Margin="0,4,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,167 @@
|
||||
// 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.Linq;
|
||||
using System.Windows.Forms;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
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.ViewModels;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Automation;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.Graphics;
|
||||
using Windows.Web.AtomPub;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard
|
||||
{
|
||||
public sealed partial class ShortcutConflictWindow : Window
|
||||
{
|
||||
public ShortcutConflictViewModel DataContext { get; }
|
||||
|
||||
public ShortcutConflictViewModel ViewModel { get; private set; }
|
||||
|
||||
public ShortcutConflictWindow()
|
||||
{
|
||||
var settingsUtils = new SettingsUtils();
|
||||
ViewModel = new ShortcutConflictViewModel(
|
||||
settingsUtils,
|
||||
SettingsRepository<GeneralSettings>.GetInstance(settingsUtils),
|
||||
ShellPage.SendDefaultIPCMessage);
|
||||
|
||||
DataContext = ViewModel;
|
||||
InitializeComponent();
|
||||
|
||||
// Set up the custom action name delegate for LocalizationHelper
|
||||
LocalizationHelper.GetCustomActionNameDelegate = GetCustomActionName;
|
||||
|
||||
// Set localized window title
|
||||
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||
this.Title = resourceLoader.GetString("ShortcutConflictWindow_Title/Text");
|
||||
|
||||
// Configure window presenter to disable maximize button
|
||||
if (this.AppWindow.Presenter is OverlappedPresenter overlappedPresenter)
|
||||
{
|
||||
overlappedPresenter.IsMaximizable = false;
|
||||
overlappedPresenter.IsMinimizable = false;
|
||||
}
|
||||
|
||||
// Set window size using AppWindow API
|
||||
this.AppWindow.Resize(new SizeInt32(900, 1200));
|
||||
|
||||
// Set window properties
|
||||
this.AppWindow.SetIcon("Assets/Settings/Icons/PowerToys.ico");
|
||||
this.AppWindow.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||
this.AppWindow.TitleBar.ButtonBackgroundColor = Microsoft.UI.Colors.Transparent;
|
||||
this.AppWindow.TitleBar.ButtonInactiveBackgroundColor = Microsoft.UI.Colors.Transparent;
|
||||
|
||||
// Center the window on screen
|
||||
this.CenterOnScreen();
|
||||
|
||||
ViewModel.OnPageLoaded();
|
||||
|
||||
Closed += (s, e) =>
|
||||
{
|
||||
// Clean up the delegate when window is closed
|
||||
LocalizationHelper.GetCustomActionNameDelegate = null;
|
||||
ViewModel?.Dispose();
|
||||
};
|
||||
}
|
||||
|
||||
private void CenterOnScreen()
|
||||
{
|
||||
var displayArea = DisplayArea.GetFromWindowId(this.AppWindow.Id, DisplayAreaFallback.Nearest);
|
||||
if (displayArea != null)
|
||||
{
|
||||
var windowSize = this.AppWindow.Size;
|
||||
var centeredPosition = new PointInt32
|
||||
{
|
||||
X = (displayArea.WorkArea.Width - windowSize.Width) / 2,
|
||||
Y = (displayArea.WorkArea.Height - windowSize.Height) / 2,
|
||||
};
|
||||
this.AppWindow.Move(centeredPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsCard_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is SettingsCard settingsCard &&
|
||||
settingsCard.DataContext is ModuleHotkeyData moduleData)
|
||||
{
|
||||
var moduleName = moduleData.ModuleName;
|
||||
|
||||
// Navigate to the module's settings page
|
||||
if (ModuleNavigationHelper.NavigateToModulePage(moduleName))
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsCard_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is SettingsCard card && card.DataContext is ModuleHotkeyData moduleData)
|
||||
{
|
||||
var iconPath = GetModuleIconPath(moduleData.ModuleName);
|
||||
|
||||
// Setup header for SettingsCard
|
||||
card.Header = LocalizationHelper.GetLocalizedHotkeyHeader(moduleData.ModuleName, moduleData.HotkeyName);
|
||||
|
||||
card.HeaderIcon = new BitmapIcon
|
||||
{
|
||||
UriSource = new Uri(iconPath),
|
||||
ShowAsMonochrome = false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom action name for AdvancedPaste
|
||||
/// </summary>
|
||||
/// <param name="moduleName">The module name</param>
|
||||
/// <param name="actionId">The custom action ID</param>
|
||||
/// <returns>The custom action name, or null if not found</returns>
|
||||
private string GetCustomActionName(string moduleName, int actionId)
|
||||
{
|
||||
if (!moduleName.Equals("advancedpaste", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ViewModel?.GetAdvancedPasteCustomActionName(actionId);
|
||||
}
|
||||
|
||||
private string GetModuleIconPath(string moduleName)
|
||||
{
|
||||
return moduleName?.ToLowerInvariant() switch
|
||||
{
|
||||
"advancedpaste" => "ms-appx:///Assets/Settings/Icons/AdvancedPaste.png",
|
||||
"alwaysontop" => "ms-appx:///Assets/Settings/Icons/AlwaysOnTop.png",
|
||||
"colorpicker" => "ms-appx:///Assets/Settings/Icons/ColorPicker.png",
|
||||
"cropandlock" => "ms-appx:///Assets/Settings/Icons/CropAndLock.png",
|
||||
"fancyzones" => "ms-appx:///Assets/Settings/Icons/FancyZones.png",
|
||||
"mousehighlighter" => "ms-appx:///Assets/Settings/Icons/MouseHighlighter.png",
|
||||
"mousepointercrosshairs" => "ms-appx:///Assets/Settings/Icons/MouseCrosshairs.png",
|
||||
"findmymouse" => "ms-appx:///Assets/Settings/Icons/FindMyMouse.png",
|
||||
"mousejump" => "ms-appx:///Assets/Settings/Icons/MouseJump.png",
|
||||
"peek" => "ms-appx:///Assets/Settings/Icons/Peek.png",
|
||||
"powerlauncher" => "ms-appx:///Assets/Settings/Icons/PowerToysRun.png",
|
||||
"measuretool" => "ms-appx:///Assets/Settings/Icons/ScreenRuler.png",
|
||||
"shortcutguide" => "ms-appx:///Assets/Settings/Icons/ShortcutGuide.png",
|
||||
"powerocr" => "ms-appx:///Assets/Settings/Icons/TextExtractor.png",
|
||||
"workspaces" => "ms-appx:///Assets/Settings/Icons/Workspaces.png",
|
||||
"cmdpal" => "ms-appx:///Assets/Settings/Icons/CmdPal.png",
|
||||
"mousewithoutborders" => "ms-appx:///Assets/Settings/Icons/MouseWithoutBorders.png",
|
||||
"zoomit" => "ms-appx:///Assets/Settings/Icons/ZoomIt.png",
|
||||
"measure tool" => "ms-appx:///Assets/Settings/Icons/ScreenRuler.png",
|
||||
_ => "ms-appx:///Assets/Settings/Icons/PowerToys.png",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
250
src/settings-ui/Settings.UI/SettingsXAML/Controls/KeyVisual.xaml
Normal file
250
src/settings-ui/Settings.UI/SettingsXAML/Controls/KeyVisual.xaml
Normal file
@@ -0,0 +1,250 @@
|
||||
<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 DefaultKeyVisualStyle}" TargetType="controls:KeyVisual" />
|
||||
|
||||
<Style x:Key="DefaultKeyVisualStyle" TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="16" />
|
||||
<Setter Property="MinHeight" Value="16" />
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleFillColorTransparentBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource TextFillColorPrimaryBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ControlStrokeColorDefaultBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Padding" Value="4,0,4,0" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
<Setter Property="CornerRadius" Value="2" />
|
||||
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:KeyVisual">
|
||||
<ContentPresenter
|
||||
x:Name="KeyPresenter"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
TextLineBounds="Tight">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyPresenter.Background" Value="{ThemeResource SubtleFillColorTransparentBrush}" />
|
||||
<Setter Target="KeyPresenter.BorderBrush" Value="{ThemeResource CardStrokeColorDefaultSolidBrush}" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource ControlStrokeColorDefaultBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Invalid">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyPresenter.Background" Value="{ThemeResource SystemFillColorCriticalBackgroundBrush}" />
|
||||
<Setter Target="KeyPresenter.BorderBrush" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
<Setter Target="KeyPresenter.BorderThickness" Value="2" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SubtleKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleFillColorTransparentBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SubtleFillColorTransparentBrush}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:KeyVisual">
|
||||
<ContentPresenter
|
||||
x:Name="KeyPresenter"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
TextLineBounds="Tight">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Invalid">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="AccentKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="Background" Value="{ThemeResource AccentFillColorDefaultBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource TextOnAccentFillColorPrimaryBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource AccentControlElevationBorderBrush}" />
|
||||
<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:KeyVisual">
|
||||
<ContentPresenter
|
||||
x:Name="KeyPresenter"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
TextLineBounds="Tight">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyPresenter.Background" Value="{ThemeResource AccentButtonBackgroundDisabled}" />
|
||||
<Setter Target="KeyPresenter.BorderBrush" Value="{ThemeResource AccentButtonBorderBrushDisabled}" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource AccentButtonForegroundDisabled}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Invalid">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyPresenter.Background" Value="{ThemeResource SystemFillColorCriticalBackgroundBrush}" />
|
||||
<Setter Target="KeyPresenter.BorderBrush" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
<Setter Target="KeyPresenter.BorderThickness" Value="2" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="ConflictKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="Background" Value="{ThemeResource SystemFillColorCriticalBackgroundBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:KeyVisual">
|
||||
<ContentPresenter
|
||||
x:Name="KeyPresenter"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
TextLineBounds="Tight">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyPresenter.Background" Value="{ThemeResource SubtleFillColorTransparentBrush}" />
|
||||
<Setter Target="KeyPresenter.BorderBrush" Value="{ThemeResource CardStrokeColorDefaultSolidBrush}" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource ControlStrokeColorDefaultBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Invalid">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyPresenter.Background" Value="{ThemeResource SystemFillColorCriticalBackgroundBrush}" />
|
||||
<Setter Target="KeyPresenter.BorderBrush" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
<Setter Target="KeyPresenter.BorderThickness" Value="2" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -5,6 +5,7 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Markup;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
@@ -12,8 +13,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
[TemplatePart(Name = KeyPresenter, Type = typeof(ContentPresenter))]
|
||||
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Default", GroupName = "StateStates")]
|
||||
[TemplateVisualState(Name = "Error", GroupName = "StateStates")]
|
||||
[TemplateVisualState(Name = "Error", GroupName = "CommonStates")]
|
||||
public sealed partial class KeyVisual : Control
|
||||
{
|
||||
private const string KeyPresenter = "KeyPresenter";
|
||||
@@ -26,28 +26,19 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
set => SetValue(ContentProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(KeyVisual), new PropertyMetadata(default(string), OnContentChanged));
|
||||
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(object), typeof(KeyVisual), new PropertyMetadata(default(string), OnContentChanged));
|
||||
|
||||
public VisualType VisualType
|
||||
public bool IsInvalid
|
||||
{
|
||||
get => (VisualType)GetValue(VisualTypeProperty);
|
||||
set => SetValue(VisualTypeProperty, value);
|
||||
get => (bool)GetValue(IsInvalidProperty);
|
||||
set => SetValue(IsInvalidProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty VisualTypeProperty = DependencyProperty.Register("VisualType", typeof(VisualType), typeof(KeyVisual), new PropertyMetadata(default(VisualType), OnSizeChanged));
|
||||
|
||||
public bool IsError
|
||||
{
|
||||
get => (bool)GetValue(IsErrorProperty);
|
||||
set => SetValue(IsErrorProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register("IsError", typeof(bool), typeof(KeyVisual), new PropertyMetadata(false, OnIsErrorChanged));
|
||||
public static readonly DependencyProperty IsInvalidProperty = DependencyProperty.Register(nameof(IsInvalid), typeof(bool), typeof(KeyVisual), new PropertyMetadata(false, OnIsInvalidChanged));
|
||||
|
||||
public KeyVisual()
|
||||
{
|
||||
this.DefaultStyleKey = typeof(KeyVisual);
|
||||
this.Style = GetStyleSize("TextKeyVisualStyle");
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
@@ -56,48 +47,55 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
_keyVisual = (KeyVisual)this;
|
||||
_keyPresenter = (ContentPresenter)_keyVisual.GetTemplateChild(KeyPresenter);
|
||||
Update();
|
||||
SetEnabledState();
|
||||
SetErrorState();
|
||||
SetVisualStates();
|
||||
IsEnabledChanged += KeyVisual_IsEnabledChanged;
|
||||
base.OnApplyTemplate();
|
||||
}
|
||||
|
||||
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((KeyVisual)d).Update();
|
||||
((KeyVisual)d).SetVisualStates();
|
||||
}
|
||||
|
||||
private static void OnSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
private static void OnIsInvalidChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((KeyVisual)d).Update();
|
||||
((KeyVisual)d).SetVisualStates();
|
||||
}
|
||||
|
||||
private static void OnIsErrorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
private void SetVisualStates()
|
||||
{
|
||||
((KeyVisual)d).SetErrorState();
|
||||
if (_keyVisual != null)
|
||||
{
|
||||
if (_keyVisual.IsInvalid)
|
||||
{
|
||||
VisualStateManager.GoToState(_keyVisual, "Invalid", true);
|
||||
}
|
||||
else if (!_keyVisual.IsEnabled)
|
||||
{
|
||||
VisualStateManager.GoToState(_keyVisual, "Disabled", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(_keyVisual, "Normal", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_keyVisual == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_keyVisual.Content != null)
|
||||
if (_keyVisual != null && _keyVisual.Content != null && _keyPresenter != null)
|
||||
{
|
||||
if (_keyVisual.Content.GetType() == typeof(string))
|
||||
{
|
||||
_keyVisual.Style = GetStyleSize("TextKeyVisualStyle");
|
||||
_keyVisual._keyPresenter.Content = _keyVisual.Content;
|
||||
_keyVisual._keyPresenter.Margin = new Thickness(0, -1, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_keyVisual.Style = GetStyleSize("IconKeyVisualStyle");
|
||||
|
||||
_keyVisual._keyPresenter.FontSize = _keyVisual.FontSize * 0.8;
|
||||
_keyVisual._keyPresenter.FontFamily = new FontFamily("Segoe Fluent Icons, Segoe MDL2 Assets");
|
||||
switch ((int)_keyVisual.Content)
|
||||
{
|
||||
/* We can enable other glyphs in the future
|
||||
case 13: // The Enter key or button.
|
||||
_keyVisual._keyPresenter.Content = "\uE751"; break;
|
||||
|
||||
@@ -107,7 +105,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
case 16: // The right Shift key or button.
|
||||
case 160: // The left Shift key or button.
|
||||
case 161: // The Shift key or button.
|
||||
_keyVisual._keyPresenter.Content = "\uE752"; break; */
|
||||
_keyVisual._keyPresenter.Content = "\uE752"; break;
|
||||
|
||||
case 38: _keyVisual._keyPresenter.Content = "\uE0E4"; break; // The Up Arrow key or button.
|
||||
case 40: _keyVisual._keyPresenter.Content = "\uE0E5"; break; // The Down Arrow key or button.
|
||||
@@ -116,13 +114,13 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
|
||||
case 91: // The left Windows key
|
||||
case 92: // The right Windows key
|
||||
PathIcon winIcon = XamlReader.Load(@"<PathIcon xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" Data=""M683 1229H0V546h683v683zm819 0H819V546h683v683zm-819 819H0v-683h683v683zm819 0H819v-683h683v683z"" />") as PathIcon;
|
||||
PathIcon winIcon = XamlReader.Load(@"<PathIcon xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" Data=""M896 896H0V0h896v896zm1024 0h-896V0h896v896zM896 1920H0v-896h896v896zm1024 0h-896v-896h896v896z"" />") as PathIcon;
|
||||
Viewbox winIconContainer = new Viewbox();
|
||||
winIconContainer.Child = winIcon;
|
||||
winIconContainer.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
winIconContainer.VerticalAlignment = VerticalAlignment.Center;
|
||||
|
||||
double iconDimensions = GetIconSize();
|
||||
double iconDimensions = _keyVisual.FontSize * 0.8; // Adjust the size of the icon based on the font size
|
||||
winIconContainer.Height = iconDimensions;
|
||||
winIconContainer.Width = iconDimensions;
|
||||
_keyVisual._keyPresenter.Content = winIconContainer;
|
||||
@@ -133,59 +131,9 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public Style GetStyleSize(string styleName)
|
||||
{
|
||||
if (VisualType == VisualType.Small)
|
||||
{
|
||||
return (Style)App.Current.Resources["Small" + styleName];
|
||||
}
|
||||
else if (VisualType == VisualType.SmallOutline)
|
||||
{
|
||||
return (Style)App.Current.Resources["SmallOutline" + styleName];
|
||||
}
|
||||
else if (VisualType == VisualType.TextOnly)
|
||||
{
|
||||
return (Style)App.Current.Resources["Only" + styleName];
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Style)App.Current.Resources["Default" + styleName];
|
||||
}
|
||||
}
|
||||
|
||||
public double GetIconSize()
|
||||
{
|
||||
if (VisualType == VisualType.Small || VisualType == VisualType.SmallOutline)
|
||||
{
|
||||
return (double)App.Current.Resources["SmallIconSize"];
|
||||
}
|
||||
else
|
||||
{
|
||||
return (double)App.Current.Resources["DefaultIconSize"];
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyVisual_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
SetEnabledState();
|
||||
SetVisualStates();
|
||||
}
|
||||
|
||||
private void SetErrorState()
|
||||
{
|
||||
VisualStateManager.GoToState(this, IsError ? "Error" : "Default", true);
|
||||
}
|
||||
|
||||
private void SetEnabledState()
|
||||
{
|
||||
VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
public enum VisualType
|
||||
{
|
||||
Small,
|
||||
SmallOutline,
|
||||
TextOnly,
|
||||
Large,
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
<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">
|
||||
|
||||
<x:Double x:Key="DefaultIconSize">16</x:Double>
|
||||
<x:Double x:Key="SmallIconSize">12</x:Double>
|
||||
<Style x:Key="DefaultTextKeyVisualStyle" TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="56" />
|
||||
<Setter Property="MinHeight" Value="48" />
|
||||
<Setter Property="Background" Value="{ThemeResource AccentButtonBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource AccentButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource AccentButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="16,8,16,8" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="FontSize" Value="18" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:KeyVisual">
|
||||
<Grid>
|
||||
<Grid>
|
||||
<Rectangle
|
||||
x:Name="ContentHolder"
|
||||
Height="{TemplateBinding Height}"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
Fill="{TemplateBinding Background}"
|
||||
RadiusX="4"
|
||||
RadiusY="4"
|
||||
Stroke="{TemplateBinding BorderBrush}"
|
||||
StrokeThickness="{TemplateBinding BorderThickness}" />
|
||||
<ContentPresenter
|
||||
x:Name="KeyPresenter"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="Center"
|
||||
Content="{TemplateBinding Content}"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
Foreground="{TemplateBinding Foreground}" />
|
||||
</Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentHolder.Fill" Value="{ThemeResource AccentButtonBackgroundDisabled}" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource AccentButtonForegroundDisabled}" />
|
||||
<Setter Target="ContentHolder.Stroke" Value="{ThemeResource AccentButtonBorderBrushDisabled}" />
|
||||
<!--<Setter Target="ContentHolder.StrokeThickness" Value="{TemplateBinding BorderThickness}" />-->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="StateStates">
|
||||
<VisualState x:Name="Default" />
|
||||
<VisualState x:Name="Error">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentHolder.Fill" Value="{ThemeResource InfoBarErrorSeverityBackgroundBrush}" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource InfoBarErrorSeverityIconBackground}" />
|
||||
<Setter Target="ContentHolder.Stroke" Value="{ThemeResource InfoBarErrorSeverityIconBackground}" />
|
||||
<Setter Target="ContentHolder.StrokeThickness" Value="2" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SmallTextKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="40" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="12,0,12,2" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SmallOutlineTextKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="40" />
|
||||
<Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="8,0,8,2" />
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
|
||||
|
||||
<Style
|
||||
x:Key="DefaultIconKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="56" />
|
||||
<Setter Property="MinHeight" Value="48" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="Padding" Value="16,8,16,8" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SmallIconKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="40" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="FontSize" Value="10" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SmallOutlineIconKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="40" />
|
||||
<Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="FontSize" Value="9" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="OnlyTextKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinHeight" Value="12" />
|
||||
<Setter Property="MinWidth" Value="12" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="OnlyIconKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="controls:KeyVisual">
|
||||
<Setter Property="MinHeight" Value="10" />
|
||||
<Setter Property="MinWidth" Value="10" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="Padding" Value="0,0,0,3" />
|
||||
<!--<Setter Property="FontSize" Value="9" />-->
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -31,8 +31,7 @@
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Content="{Binding}"
|
||||
IsTabStop="False"
|
||||
VisualType="SmallOutline" />
|
||||
IsTabStop="False" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
@@ -9,17 +9,10 @@
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<x:Double x:Key="PageMaxWidth">1000</x:Double>
|
||||
<x:Double x:Key="PageHeaderMaxWidth">1020</x:Double>
|
||||
<tkconverters:DoubleToVisibilityConverter
|
||||
x:Name="doubleToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
GreaterThan="0"
|
||||
TrueValue="Visible" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Padding="20,0,0,0" RowSpacing="24">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -62,7 +55,7 @@
|
||||
MaxWidth="160"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
CornerRadius="4">
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Image AutomationProperties.AccessibilityView="Raw">
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind ModuleImageSource}" />
|
||||
@@ -113,7 +106,7 @@
|
||||
MaxWidth="{StaticResource PageMaxWidth}"
|
||||
AutomationProperties.Name="{x:Bind SecondaryLinksHeader}"
|
||||
Orientation="Vertical"
|
||||
Visibility="{x:Bind SecondaryLinks.Count, Converter={StaticResource doubleToVisibilityConverter}}">
|
||||
Visibility="{x:Bind SecondaryLinks.Count, Converter={StaticResource DoubleToVisibilityConverter}}">
|
||||
<TextBlock
|
||||
Margin="2,8,0,0"
|
||||
AutomationProperties.HeadingLevel="Level2"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user