settings_objects.h
1 #pragma once 2 3 #include "../utils/json.h" 4 5 #include <cwctype> 6 7 namespace PowerToysSettings 8 { 9 class HotkeyObject; 10 11 class Settings 12 { 13 public: 14 Settings( 15 const HINSTANCE hinstance, // Module handle of the PowerToy DLL 'IMAGE_DOS_HEADER __ImageBase' 16 std::wstring_view powertoy_name); 17 18 // Add additional general information to the PowerToy settings. 19 void set_description(UINT resource_id); 20 void set_description(std::wstring_view description); 21 22 void set_icon_key(std::wstring_view icon_key); 23 void set_overview_link(std::wstring_view overview_link); 24 void set_video_link(std::wstring_view video_link); 25 26 // Add properties to the PowerToy settings. 27 void add_bool_toggle(std::wstring_view name, UINT description_resource_id, bool value); 28 void add_bool_toggle(std::wstring_view name, std::wstring_view description, bool value); 29 30 void add_int_spinner(std::wstring_view name, UINT description_resource_id, int value, int min, int max, int step); 31 void add_int_spinner(std::wstring_view name, std::wstring_view description, int value, int min, int max, int step); 32 33 void add_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value); 34 void add_string(std::wstring_view name, std::wstring_view description, std::wstring_view value); 35 36 void add_multiline_string(std::wstring_view name, UINT description_resource_id, std::wstring_view value); 37 void add_multiline_string(std::wstring_view name, std::wstring_view description, std::wstring_view value); 38 39 void add_color_picker(std::wstring_view name, UINT description_resource_id, std::wstring_view value); 40 void add_color_picker(std::wstring_view name, std::wstring_view description, std::wstring_view value); 41 42 void add_hotkey(std::wstring_view name, UINT description_resource_id, const HotkeyObject& hotkey); 43 void add_hotkey(std::wstring_view name, std::wstring_view description, const HotkeyObject& hotkey); 44 45 void add_choice_group(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector<std::pair<std::wstring, UINT>>& keys_and_text_ids); 46 void add_choice_group(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector<std::pair<std::wstring, std::wstring>>& keys_and_texts); 47 48 void add_dropdown(std::wstring_view name, UINT description_resource_id, std::wstring_view value, const std::vector<std::pair<std::wstring, UINT>>& keys_and_text_ids); 49 void add_dropdown(std::wstring_view name, std::wstring_view description, std::wstring_view value, const std::vector<std::pair<std::wstring, std::wstring>>& keys_and_texts); 50 51 void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, UINT ext_description_resource_id); 52 void add_custom_action(std::wstring_view name, UINT description_resource_id, UINT button_text_resource_id, std::wstring_view value); 53 void add_custom_action(std::wstring_view name, std::wstring_view description, std::wstring_view button_text, std::wstring_view value); 54 55 void add_header_szLarge(std::wstring_view name, std::wstring_view description, std::wstring_view value); 56 // Serialize the internal json to a string. 57 std::wstring serialize(); 58 // Serialize the internal json to the input buffer. 59 bool serialize_to_buffer(wchar_t* buffer, int* buffer_size); 60 61 private: 62 json::JsonObject m_json; 63 int m_curr_priority = 0; // For keeping order when adding elements. 64 HINSTANCE m_instance; 65 66 std::wstring get_resource(UINT resource_id); 67 }; 68 69 class PowerToyValues 70 { 71 public: 72 PowerToyValues(std::wstring_view powertoy_name, std::wstring_view powertoy_key); 73 static PowerToyValues from_json_string(std::wstring_view json, std::wstring_view powertoy_key); 74 static PowerToyValues load_from_settings_file(std::wstring_view powertoy_key); 75 76 template<typename T> 77 inline void add_property(std::wstring_view name, T value) 78 { 79 json::JsonObject prop_value; 80 prop_value.SetNamedValue(L"value", json::value(value)); 81 m_json.GetNamedObject(L"properties").SetNamedValue(name, prop_value); 82 } 83 84 std::optional<bool> get_bool_value(std::wstring_view property_name) const; 85 std::optional<int> get_int_value(std::wstring_view property_name) const; 86 std::optional<unsigned int> get_uint_value(std::wstring_view property_name) const; 87 std::optional<std::wstring> get_string_value(std::wstring_view property_name) const; 88 std::optional<json::JsonObject> get_json(std::wstring_view property_name) const; 89 json::JsonObject get_raw_json(); 90 91 std::wstring serialize(); 92 void save_to_settings_file(); 93 94 private: 95 const std::wstring m_version = L"1.0"; 96 void set_version(); 97 json::JsonObject m_json; 98 std::wstring _key; 99 PowerToyValues() {} 100 }; 101 102 class CustomActionObject 103 { 104 public: 105 static CustomActionObject from_json_string(std::wstring_view json) 106 { 107 return CustomActionObject(json::JsonValue::Parse(json).GetObjectW()); 108 } 109 110 std::wstring get_name() { return m_json.GetNamedString(L"action_name").c_str(); } 111 std::wstring get_value() { return m_json.GetNamedString(L"value").c_str(); } 112 113 protected: 114 CustomActionObject(json::JsonObject action_json) : 115 m_json(std::move(action_json)){}; 116 json::JsonObject m_json; 117 }; 118 119 class HotkeyObject 120 { 121 public: 122 HotkeyObject() : 123 m_json(json::JsonObject()) 124 { 125 m_json.SetNamedValue(L"win", json::value(false)); 126 m_json.SetNamedValue(L"ctrl", json::value(false)); 127 m_json.SetNamedValue(L"alt", json::value(false)); 128 m_json.SetNamedValue(L"shift", json::value(false)); 129 m_json.SetNamedValue(L"code", json::value(0)); 130 m_json.SetNamedValue(L"key", json::value(L"")); 131 } 132 static HotkeyObject from_json(json::JsonObject json) 133 { 134 return HotkeyObject(std::move(json)); 135 } 136 static HotkeyObject from_json_string(std::wstring_view json) 137 { 138 return HotkeyObject(json::JsonValue::Parse(json).GetObjectW()); 139 } 140 static HotkeyObject from_settings(bool win_pressed, bool ctrl_pressed, bool alt_pressed, bool shift_pressed, UINT vk_code) 141 { 142 json::JsonObject json; 143 json.SetNamedValue(L"win", json::value(win_pressed)); 144 json.SetNamedValue(L"ctrl", json::value(ctrl_pressed)); 145 json.SetNamedValue(L"alt", json::value(alt_pressed)); 146 json.SetNamedValue(L"shift", json::value(shift_pressed)); 147 json.SetNamedValue(L"code", json::value(vk_code)); 148 json.SetNamedValue(L"key", json::value(key_from_code(vk_code))); 149 return std::move(json); 150 } 151 const json::JsonObject& get_json() const { return m_json; } 152 153 std::wstring get_key() const { return m_json.GetNamedString(L"key").c_str(); } 154 UINT get_code() const { return static_cast<UINT>(m_json.GetNamedNumber(L"code")); } 155 bool win_pressed() const { return m_json.GetNamedBoolean(L"win"); } 156 bool ctrl_pressed() const { return m_json.GetNamedBoolean(L"ctrl"); } 157 bool alt_pressed() const { return m_json.GetNamedBoolean(L"alt"); } 158 bool shift_pressed() const { return m_json.GetNamedBoolean(L"shift"); } 159 UINT get_modifiers_repeat() const 160 { 161 return (win_pressed() ? MOD_WIN : 0) | 162 (ctrl_pressed() ? MOD_CONTROL : 0) | 163 (alt_pressed() ? MOD_ALT : 0) | 164 (shift_pressed() ? MOD_SHIFT : 0); 165 } 166 UINT get_modifiers() const 167 { 168 return get_modifiers_repeat() | MOD_NOREPEAT; 169 } 170 171 std::wstring to_string() 172 { 173 std::wstring result = L""; 174 if (shift_pressed()) 175 { 176 result += L"shift+"; 177 } 178 179 if (ctrl_pressed()) 180 { 181 result += L"ctrl+"; 182 } 183 184 if (win_pressed()) 185 { 186 result += L"win+"; 187 } 188 189 if (alt_pressed()) 190 { 191 result += L"alt+"; 192 } 193 194 result += key_from_code(get_code()); 195 return result; 196 } 197 198 static std::wstring key_from_code(UINT key_code) 199 { 200 auto layout = GetKeyboardLayout(0); 201 auto scan_code = MapVirtualKeyExW(key_code, MAPVK_VK_TO_VSC_EX, layout); 202 // Determinate if vk is an extended key. Unfortunately MAPVK_VK_TO_VSC_EX 203 // does not return correct values. 204 static std::vector<UINT> extended_keys = { 205 VK_APPS, 206 VK_CANCEL, 207 VK_SNAPSHOT, 208 VK_DIVIDE, 209 VK_NUMLOCK, 210 VK_LWIN, 211 VK_RWIN, 212 VK_RMENU, 213 VK_RCONTROL, 214 VK_RSHIFT, 215 VK_RETURN, 216 VK_INSERT, 217 VK_DELETE, 218 VK_PRIOR, 219 VK_NEXT, 220 VK_HOME, 221 VK_END, 222 VK_UP, 223 VK_DOWN, 224 VK_LEFT, 225 VK_RIGHT, 226 }; 227 if (find(begin(extended_keys), end(extended_keys), key_code) != end(extended_keys)) 228 { 229 scan_code |= 0x100; 230 } 231 std::array<BYTE, 256> key_states{}; // Zero-initialize 232 std::array<wchar_t, 256> output; 233 const UINT wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer) 234 auto output_bytes = ToUnicodeEx(key_code, scan_code, key_states.data(), output.data(), static_cast<int>(output.size()) - 1, wFlags, layout); 235 if (output_bytes <= 0) 236 { 237 // If ToUnicodeEx fails (e.g. for F1-F12 keys) use GetKeyNameTextW 238 output_bytes = GetKeyNameTextW(scan_code << 16, output.data(), static_cast<int>(output.size())); 239 } 240 if (output_bytes > 0) 241 { 242 output[output_bytes] = 0; 243 if (output_bytes == 1 && output[0] >= 'a' && output[0] <= 'z') 244 { 245 // Make Latin letters keys capital, as it looks better 246 output[0] = std::towupper(output[0]); 247 } 248 return output.data(); 249 } 250 return L"(Key " + std::to_wstring(key_code) + L")"; 251 } 252 253 protected: 254 HotkeyObject(json::JsonObject hotkey_json) : 255 m_json(std::move(hotkey_json)) 256 { 257 if (get_key() == L"~" && get_modifiers_repeat() == MOD_WIN) 258 { 259 m_json.SetNamedValue(L"key", json::value(key_from_code(get_code()))); 260 } 261 }; 262 json::JsonObject m_json; 263 }; 264 265 }