/ src / common / SettingsAPI / settings_objects.h
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  }