Settings.cpp
1 #include "pch.h" 2 #include "Settings.h" 3 4 #include <ModuleConstants.h> 5 #include <SettingsObserver.h> 6 #include <WinHookEventIDs.h> 7 8 #include <common/SettingsAPI/settings_helpers.h> 9 #include <common/utils/string_utils.h> // trim 10 11 namespace NonLocalizable 12 { 13 const static wchar_t* SettingsFileName = L"settings.json"; 14 15 const static wchar_t* HotkeyID = L"hotkey"; 16 const static wchar_t* SoundEnabledID = L"sound-enabled"; 17 const static wchar_t* FrameEnabledID = L"frame-enabled"; 18 const static wchar_t* FrameThicknessID = L"frame-thickness"; 19 const static wchar_t* FrameColorID = L"frame-color"; 20 const static wchar_t* FrameOpacityID = L"frame-opacity"; 21 const static wchar_t* BlockInGameModeID = L"do-not-activate-on-game-mode"; 22 const static wchar_t* ExcludedAppsID = L"excluded-apps"; 23 const static wchar_t* FrameAccentColor = L"frame-accent-color"; 24 const static wchar_t* RoundCornersEnabledID = L"round-corners-enabled"; 25 } 26 27 // TODO: move to common utils 28 inline COLORREF HexToRGB(std::wstring_view hex, const COLORREF fallbackColor = RGB(255, 255, 255)) 29 { 30 hex = left_trim<wchar_t>(trim<wchar_t>(hex), L"#"); 31 32 try 33 { 34 const long long tmp = std::stoll(hex.data(), nullptr, 16); 35 const BYTE nR = static_cast<BYTE>((tmp & 0xFF0000) >> 16); 36 const BYTE nG = static_cast<BYTE>((tmp & 0xFF00) >> 8); 37 const BYTE nB = static_cast<BYTE>((tmp & 0xFF)); 38 return RGB(nR, nG, nB); 39 } 40 catch (const std::exception&) 41 { 42 return fallbackColor; 43 } 44 } 45 46 AlwaysOnTopSettings::AlwaysOnTopSettings() 47 { 48 m_uiSettings.ColorValuesChanged([&](winrt::Windows::UI::ViewManagement::UISettings const& settings, 49 winrt::Windows::Foundation::IInspectable const& args) 50 { 51 if (m_settings.frameAccentColor) 52 { 53 NotifyObservers(SettingId::FrameAccentColor); 54 } 55 }); 56 } 57 58 AlwaysOnTopSettings& AlwaysOnTopSettings::instance() 59 { 60 static AlwaysOnTopSettings instance; 61 return instance; 62 } 63 64 void AlwaysOnTopSettings::InitFileWatcher() 65 { 66 const std::wstring& settingsFileName = GetSettingsFileName(); 67 m_settingsFileWatcher = std::make_unique<FileWatcher>(settingsFileName, [&]() { 68 PostMessageW(HWND_BROADCAST, WM_PRIV_SETTINGS_CHANGED, NULL, NULL); 69 }); 70 } 71 72 std::wstring AlwaysOnTopSettings::GetSettingsFileName() 73 { 74 std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey); 75 return saveFolderPath + L"\\" + std::wstring(NonLocalizable::SettingsFileName); 76 } 77 78 void AlwaysOnTopSettings::AddObserver(SettingsObserver& observer) 79 { 80 m_observers.insert(&observer); 81 } 82 83 void AlwaysOnTopSettings::RemoveObserver(SettingsObserver& observer) 84 { 85 auto iter = m_observers.find(&observer); 86 if (iter != m_observers.end()) 87 { 88 m_observers.erase(iter); 89 } 90 } 91 92 void AlwaysOnTopSettings::LoadSettings() 93 { 94 try 95 { 96 PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::load_from_settings_file(NonLocalizable::ModuleKey); 97 98 if (const auto jsonVal = values.get_json(NonLocalizable::HotkeyID)) 99 { 100 auto val = PowerToysSettings::HotkeyObject::from_json(*jsonVal); 101 if (m_settings.hotkey.get_modifiers() != val.get_modifiers() || m_settings.hotkey.get_key() != val.get_key() || m_settings.hotkey.get_code() != val.get_code()) 102 { 103 m_settings.hotkey = val; 104 NotifyObservers(SettingId::Hotkey); 105 } 106 } 107 108 if (const auto jsonVal = values.get_bool_value(NonLocalizable::SoundEnabledID)) 109 { 110 auto val = *jsonVal; 111 if (m_settings.enableSound != val) 112 { 113 m_settings.enableSound = val; 114 NotifyObservers(SettingId::SoundEnabled); 115 } 116 } 117 118 if (const auto jsonVal = values.get_int_value(NonLocalizable::FrameThicknessID)) 119 { 120 auto val = *jsonVal; 121 if (m_settings.frameThickness != val) 122 { 123 m_settings.frameThickness = val; 124 NotifyObservers(SettingId::FrameThickness); 125 } 126 } 127 128 if (const auto jsonVal = values.get_string_value(NonLocalizable::FrameColorID)) 129 { 130 auto val = HexToRGB(*jsonVal); 131 if (m_settings.frameColor != val) 132 { 133 m_settings.frameColor = val; 134 NotifyObservers(SettingId::FrameColor); 135 } 136 } 137 138 if (const auto jsonVal = values.get_int_value(NonLocalizable::FrameOpacityID)) 139 { 140 auto val = *jsonVal; 141 if (m_settings.frameOpacity != val) 142 { 143 m_settings.frameOpacity = val; 144 NotifyObservers(SettingId::FrameOpacity); 145 } 146 } 147 148 if (const auto jsonVal = values.get_bool_value(NonLocalizable::FrameEnabledID)) 149 { 150 auto val = *jsonVal; 151 if (m_settings.enableFrame != val) 152 { 153 m_settings.enableFrame = val; 154 NotifyObservers(SettingId::FrameEnabled); 155 } 156 } 157 158 if (const auto jsonVal = values.get_bool_value(NonLocalizable::BlockInGameModeID)) 159 { 160 auto val = *jsonVal; 161 if (m_settings.blockInGameMode != val) 162 { 163 m_settings.blockInGameMode = val; 164 NotifyObservers(SettingId::BlockInGameMode); 165 } 166 } 167 168 if (const auto jsonVal = values.get_bool_value(NonLocalizable::RoundCornersEnabledID)) 169 { 170 auto val = *jsonVal; 171 if (m_settings.roundCornersEnabled != val) 172 { 173 m_settings.roundCornersEnabled = val; 174 NotifyObservers(SettingId::RoundCornersEnabled); 175 } 176 } 177 178 if (auto jsonVal = values.get_string_value(NonLocalizable::ExcludedAppsID)) 179 { 180 std::wstring apps = std::move(*jsonVal); 181 std::vector<std::wstring> excludedApps; 182 auto excludedUppercase = apps; 183 CharUpperBuffW(excludedUppercase.data(), static_cast<DWORD>(excludedUppercase.length())); 184 std::wstring_view view(excludedUppercase); 185 view = left_trim<wchar_t>(trim<wchar_t>(view)); 186 187 while (!view.empty()) 188 { 189 auto pos = (std::min)(view.find_first_of(L"\r\n"), view.length()); 190 excludedApps.emplace_back(view.substr(0, pos)); 191 view.remove_prefix(pos); 192 view = left_trim<wchar_t>(trim<wchar_t>(view)); 193 } 194 195 if (m_settings.excludedApps != excludedApps) 196 { 197 m_settings.excludedApps = excludedApps; 198 NotifyObservers(SettingId::ExcludeApps); 199 } 200 } 201 202 if (const auto jsonVal = values.get_bool_value(NonLocalizable::FrameAccentColor)) 203 { 204 auto val = *jsonVal; 205 if (m_settings.frameAccentColor != val) 206 { 207 m_settings.frameAccentColor = val; 208 NotifyObservers(SettingId::FrameAccentColor); 209 } 210 } 211 } 212 catch (...) 213 { 214 // Log error message and continue with default settings. 215 Logger::error("Failed to read settings"); 216 // TODO: show localized message 217 } 218 } 219 220 void AlwaysOnTopSettings::NotifyObservers(SettingId id) const 221 { 222 for (auto observer : m_observers) 223 { 224 if (observer->WantsToBeNotified(id)) 225 { 226 observer->SettingsUpdate(id); 227 } 228 } 229 }