#pragma once #include "json.h" namespace PowerToysSettings { class HotkeyObject; class Settings { public: Settings( const HINSTANCE hinstance, // Module handle of the PowerToy DLL 'IMAGE_DOS_HEADER __ImageBase' std::wstring_view powertoy_name); // Add additional general information to the PowerToy settings. void set_description(UINT resource_id); void set_description(std::wstring_view description); void set_icon_key(std::wstring_view icon_key); void set_overview_link(std::wstring_view overview_link); void set_video_link(std::wstring_view video_link); // Add properties to the PowerToy settings. void add_bool_toggle(std::wstring_view name, UINT description_resource_id, bool value); void add_bool_toggle(std::wstring_view name, std::wstring_view description, bool value); void add_int_spinner(std::wstring_view name, UINT description_resource_id, int value, int min, int max, int step); void add_int_spinner(std::wstring_view name, std::wstring_view description, int value, int min, int max, int step); void add_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value); void add_string(std::wstring_view name, std::wstring_view description, std::wstring_view value); void add_multiline_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value); void add_multiline_string(std::wstring_view name, std::wstring_view description, std::wstring_view value); void add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value); void add_color_picker(std::wstring_view name, std::wstring_view description, std::wstring_view value); void add_hotkey(std::wstring_view name, UINT description_resource_id, const HotkeyObject& hotkey); void add_hotkey(std::wstring_view name, std::wstring_view description, const HotkeyObject& hotkey); void add_choice_group(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids); void add_choice_group(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts); void add_dropdown(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector>& keys_and_text_ids); void add_dropdown(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector>& keys_and_texts); void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id); void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value); void add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value); void add_header_szLarge(std::wstring_view name, std::wstring_view description, std::wstring_view value); // Serialize the internal json to a string. std::wstring serialize(); // Serialize the internal json to the input buffer. bool serialize_to_buffer(wchar_t* buffer, int* buffer_size); private: json::JsonObject m_json; int m_curr_priority = 0; // For keeping order when adding elements. HINSTANCE m_instance; std::wstring get_resource(UINT resource_id); }; class PowerToyValues { public: PowerToyValues(std::wstring_view powertoy_name); static PowerToyValues from_json_string(std::wstring_view json); static PowerToyValues load_from_settings_file(std::wstring_view powertoy_name); template inline void add_property(std::wstring_view name, T value) { json::JsonObject prop_value; prop_value.SetNamedValue(L"value", json::value(value)); m_json.GetNamedObject(L"properties").SetNamedValue(name, prop_value); } std::optional get_bool_value(std::wstring_view property_name); std::optional get_int_value(std::wstring_view property_name); std::optional get_string_value(std::wstring_view property_name); std::optional get_json(std::wstring_view property_name); std::wstring serialize(); void save_to_settings_file(); private: const std::wstring m_version = L"1.0"; void set_version(); json::JsonObject m_json; std::wstring _name; PowerToyValues() {} }; class CustomActionObject { public: static CustomActionObject from_json_string(std::wstring_view json) { return CustomActionObject(json::JsonValue::Parse(json).GetObjectW()); } std::wstring get_name() { return m_json.GetNamedString(L"action_name").c_str(); } std::wstring get_value() { return m_json.GetNamedString(L"value").c_str(); } protected: CustomActionObject(json::JsonObject action_json) : m_json(std::move(action_json)){}; json::JsonObject m_json; }; class HotkeyObject { public: static HotkeyObject from_json(json::JsonObject json) { return HotkeyObject(std::move(json)); } static HotkeyObject from_json_string(std::wstring_view json) { return HotkeyObject(json::JsonValue::Parse(json).GetObjectW()); } static HotkeyObject from_settings(bool win_pressed, bool ctrl_pressed, bool alt_pressed, bool shift_pressed, UINT vk_code) { json::JsonObject json; json.SetNamedValue(L"win", json::value(win_pressed)); json.SetNamedValue(L"ctrl", json::value(ctrl_pressed)); json.SetNamedValue(L"alt", json::value(alt_pressed)); json.SetNamedValue(L"shift", json::value(shift_pressed)); json.SetNamedValue(L"code", json::value(vk_code)); json.SetNamedValue(L"key", json::value(key_from_code(vk_code))); return std::move(json); } const json::JsonObject& get_json() const { return m_json; } std::wstring get_key() const { return m_json.GetNamedString(L"key").c_str(); } UINT get_code() const { return static_cast(m_json.GetNamedNumber(L"code")); } bool win_pressed() const { return m_json.GetNamedBoolean(L"win"); } bool ctrl_pressed() const { return m_json.GetNamedBoolean(L"ctrl"); } bool alt_pressed() const { return m_json.GetNamedBoolean(L"alt"); } bool shift_pressed() const { return m_json.GetNamedBoolean(L"shift"); } UINT get_modifiers_repeat() const { return (win_pressed() ? MOD_WIN : 0) | (ctrl_pressed() ? MOD_CONTROL : 0) | (alt_pressed() ? MOD_ALT : 0) | (shift_pressed() ? MOD_SHIFT : 0); } UINT get_modifiers() const { return get_modifiers_repeat() | MOD_NOREPEAT; } protected: static std::wstring key_from_code(UINT key_code) { auto layout = GetKeyboardLayout(0); auto scan_code = MapVirtualKeyExW(key_code, MAPVK_VK_TO_VSC_EX, layout); // Determinate if vk is an extended key. Unfortunately MAPVK_VK_TO_VSC_EX // does not return correct values. static std::vector extended_keys = { VK_APPS, VK_CANCEL, VK_SNAPSHOT, VK_DIVIDE, VK_NUMLOCK, VK_LWIN, VK_RWIN, VK_RMENU, VK_RCONTROL, VK_RSHIFT, VK_RETURN, VK_INSERT, VK_DELETE, VK_PRIOR, VK_NEXT, VK_HOME, VK_END, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, }; if (find(begin(extended_keys), end(extended_keys), key_code) != end(extended_keys)) { scan_code |= 0x100; } std::array key_states{}; // Zero-initialize std::array output; auto output_bytes = ToUnicodeEx(key_code, scan_code, key_states.data(), output.data(), (int)output.size() - 1, 0, layout); if (output_bytes <= 0) { // If ToUnicodeEx fails (e.g. for F1-F12 keys) use GetKeyNameTextW output_bytes = GetKeyNameTextW(scan_code << 16, output.data(), static_cast(output.size())); } if (output_bytes > 0) { output[output_bytes] = 0; if (output_bytes == 1 && output[0] >= 'a' && output[0] <= 'z') { // Make Latin letters keys capital, as it looks better output[0] = toupper(output[0]); } return output.data(); } return L"(Key " + std::to_wstring(key_code) + L")"; } HotkeyObject(json::JsonObject hotkey_json) : m_json(std::move(hotkey_json)) { if (get_key() == L"~" && get_modifiers_repeat() == MOD_WIN) { m_json.SetNamedValue(L"key", json::value(key_from_code(get_code()))); } }; json::JsonObject m_json; }; }