From f822826cf136916d099d2335b95b7bc9be38f834 Mon Sep 17 00:00:00 2001 From: Jaylyn Barbee <51131738+Jaylyn-Barbee@users.noreply.github.com> Date: Wed, 10 Dec 2025 08:15:34 -0500 Subject: [PATCH] [Light Switch] Follow Night Light mode (#43683) ## Summary of the Pull Request Introduces a new mode that will have Light Switch follow Windows Night Light. ## PR Checklist - [x] Closes: https://github.com/microsoft/PowerToys/issues/42457 - [x] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [x] **Localization:** All end-user-facing strings can be localized - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments Strictly follows the state of Night Light. When NL is on, LS will be switch to dark mode and when NL is off, LS will switch to light mode (with respect to the users system/app selection). ## Validation Steps Performed Turn on Follow Night Light mode Change night light! ## Notes --- .github/actions/spell-check/expect.txt | 3 + .../LightSwitchModuleInterface/dllmain.cpp | 9 +- .../LightSwitchService/LightSwitchService.cpp | 67 ++++++++- .../LightSwitchService.vcxproj | 2 + .../LightSwitchService.vcxproj.filters | 6 + .../LightSwitchService/LightSwitchSettings.h | 7 +- .../LightSwitchStateManager.cpp | 56 ++++++-- .../LightSwitchStateManager.h | 4 + .../NightLightRegistryObserver.cpp | 1 + .../NightLightRegistryObserver.h | 134 ++++++++++++++++++ .../LightSwitchService/SettingsConstants.h | 5 +- .../LightSwitchService/ThemeHelper.cpp | 38 ++++- .../LightSwitchService/ThemeHelper.h | 1 + .../Settings.UI/Helpers/StartProcessHelper.cs | 1 + .../SettingsXAML/Views/LightSwitchPage.xaml | 43 ++++++ .../Views/LightSwitchPage.xaml.cs | 16 +++ .../Settings.UI/Strings/en-us/Resources.resw | 65 +++++---- .../ViewModels/LightSwitchViewModel.cs | 1 + 18 files changed, 413 insertions(+), 46 deletions(-) create mode 100644 src/modules/LightSwitch/LightSwitchService/NightLightRegistryObserver.cpp create mode 100644 src/modules/LightSwitch/LightSwitchService/NightLightRegistryObserver.h diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 4a3305217e..f839c5976c 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -144,6 +144,8 @@ BLENDFUNCTION blittable Blockquotes blt +bluelightreduction +bluelightreductionstate BLURBEHIND BLURREGION bmi @@ -1115,6 +1117,7 @@ NEWPLUSSHELLEXTENSIONWIN newrow nicksnettravels NIF +nightlight NLog NLSTEXT NMAKE diff --git a/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp b/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp index 170dde5b0a..a5973a396f 100644 --- a/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp +++ b/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp @@ -50,6 +50,7 @@ enum class ScheduleMode Off, FixedHours, SunsetToSunrise, + FollowNightLight, // add more later }; @@ -61,6 +62,8 @@ inline std::wstring ToString(ScheduleMode mode) return L"SunsetToSunrise"; case ScheduleMode::FixedHours: return L"FixedHours"; + case ScheduleMode::FollowNightLight: + return L"FollowNightLight"; default: return L"Off"; } @@ -72,6 +75,8 @@ inline ScheduleMode FromString(const std::wstring& str) return ScheduleMode::SunsetToSunrise; if (str == L"FixedHours") return ScheduleMode::FixedHours; + if (str == L"FollowNightLight") + return ScheduleMode::FollowNightLight; return ScheduleMode::Off; } @@ -167,7 +172,9 @@ public: ToString(g_settings.m_scheduleMode), { { L"Off", L"Disable the schedule" }, { L"FixedHours", L"Set hours manually" }, - { L"SunsetToSunrise", L"Use sunrise/sunset times" } }); + { L"SunsetToSunrise", L"Use sunrise/sunset times" }, + { L"FollowNightLight", L"Follow Windows Night Light state" } + }); // Integer spinners settings.add_int_spinner( diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchService.cpp b/src/modules/LightSwitch/LightSwitchService/LightSwitchService.cpp index 845e24fa93..b6684da54e 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchService.cpp +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchService.cpp @@ -13,10 +13,12 @@ #include #include "LightSwitchStateManager.h" #include +#include SERVICE_STATUS g_ServiceStatus = {}; SERVICE_STATUS_HANDLE g_StatusHandle = nullptr; HANDLE g_ServiceStopEvent = nullptr; +static LightSwitchStateManager* g_stateManagerPtr = nullptr; VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv); VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl); @@ -168,7 +170,15 @@ static void DetectAndHandleExternalThemeChange(LightSwitchStateManager& stateMan } // Use shared helper (handles wraparound logic) - bool shouldBeLight = ShouldBeLight(nowMinutes, effectiveLight, effectiveDark); + bool shouldBeLight = false; + if (s.scheduleMode == ScheduleMode::FollowNightLight) + { + shouldBeLight = !IsNightLightEnabled(); + } + else + { + shouldBeLight = ShouldBeLight(nowMinutes, effectiveLight, effectiveDark); + } // Compare current system/apps theme bool currentSystemLight = GetCurrentSystemTheme(); @@ -199,15 +209,40 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) // Initialization // ──────────────────────────────────────────────────────────────── static LightSwitchStateManager stateManager; + g_stateManagerPtr = &stateManager; LightSwitchSettings::instance().InitFileWatcher(); HANDLE hManualOverride = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE"); HANDLE hSettingsChanged = LightSwitchSettings::instance().GetSettingsChangedEvent(); + static std::unique_ptr g_nightLightWatcher; + LightSwitchSettings::instance().LoadSettings(); const auto& settings = LightSwitchSettings::instance().settings(); + // after loading settings: + bool nightLightNeeded = (settings.scheduleMode == ScheduleMode::FollowNightLight); + + if (nightLightNeeded && !g_nightLightWatcher) + { + Logger::info(L"[LightSwitchService] Starting Night Light registry watcher..."); + + g_nightLightWatcher = std::make_unique( + HKEY_CURRENT_USER, + NIGHT_LIGHT_REGISTRY_PATH, + []() { + if (g_stateManagerPtr) + g_stateManagerPtr->OnNightLightChange(); + }); + } + else if (!nightLightNeeded && g_nightLightWatcher) + { + Logger::info(L"[LightSwitchService] Stopping Night Light registry watcher..."); + g_nightLightWatcher->Stop(); + g_nightLightWatcher.reset(); + } + SYSTEMTIME st; GetLocalTime(&st); int nowMinutes = st.wHour * 60 + st.wMinute; @@ -274,6 +309,31 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) ResetEvent(hSettingsChanged); LightSwitchSettings::instance().LoadSettings(); stateManager.OnSettingsChanged(); + + const auto& settings = LightSwitchSettings::instance().settings(); + bool nightLightNeeded = (settings.scheduleMode == ScheduleMode::FollowNightLight); + + if (nightLightNeeded && !g_nightLightWatcher) + { + Logger::info(L"[LightSwitchService] Starting Night Light registry watcher..."); + + g_nightLightWatcher = std::make_unique( + HKEY_CURRENT_USER, + NIGHT_LIGHT_REGISTRY_PATH, + []() { + if (g_stateManagerPtr) + g_stateManagerPtr->OnNightLightChange(); + }); + + stateManager.OnNightLightChange(); + } + else if (!nightLightNeeded && g_nightLightWatcher) + { + Logger::info(L"[LightSwitchService] Stopping Night Light registry watcher..."); + g_nightLightWatcher->Stop(); + g_nightLightWatcher.reset(); + } + continue; } } @@ -285,6 +345,11 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) CloseHandle(hManualOverride); if (hParent) CloseHandle(hParent); + if (g_nightLightWatcher) + { + g_nightLightWatcher->Stop(); + g_nightLightWatcher.reset(); + } Logger::info(L"[LightSwitchService] Worker thread exiting cleanly."); return 0; diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj b/src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj index a3a505f897..e1c8052de6 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj @@ -76,6 +76,7 @@ + @@ -88,6 +89,7 @@ + diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj.filters b/src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj.filters index 795df99aba..55c7bde39b 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj.filters +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj.filters @@ -36,6 +36,9 @@ Source Files + + Source Files + @@ -62,6 +65,9 @@ Header Files + + Header Files + diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.h b/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.h index d4029d072d..1d1c7953fe 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.h +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.h @@ -19,7 +19,8 @@ enum class ScheduleMode { Off, FixedHours, - SunsetToSunrise + SunsetToSunrise, + FollowNightLight, // Add more in the future }; @@ -31,6 +32,8 @@ inline std::wstring ToString(ScheduleMode mode) return L"FixedHours"; case ScheduleMode::SunsetToSunrise: return L"SunsetToSunrise"; + case ScheduleMode::FollowNightLight: + return L"FollowNightLight"; default: return L"Off"; } @@ -42,6 +45,8 @@ inline ScheduleMode FromString(const std::wstring& str) return ScheduleMode::SunsetToSunrise; if (str == L"FixedHours") return ScheduleMode::FixedHours; + if (str == L"FollowNightLight") + return ScheduleMode::FollowNightLight; else return ScheduleMode::Off; } diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.cpp b/src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.cpp index 4fba4ae9a6..f562d38c41 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.cpp +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.cpp @@ -31,7 +31,10 @@ void LightSwitchStateManager::OnSettingsChanged() void LightSwitchStateManager::OnTick(int currentMinutes) { std::lock_guard lock(_stateMutex); - EvaluateAndApplyIfNeeded(); + if (_state.lastAppliedMode != ScheduleMode::FollowNightLight) + { + EvaluateAndApplyIfNeeded(); + } } // Called when manual override is triggered @@ -49,8 +52,38 @@ void LightSwitchStateManager::OnManualOverride() _state.isAppsLightActive = GetCurrentAppsTheme(); Logger::debug(L"[LightSwitchStateManager] Synced internal theme state to current system theme ({}) and apps theme ({}).", - (_state.isSystemLightActive ? L"light" : L"dark"), - (_state.isAppsLightActive ? L"light" : L"dark")); + (_state.isSystemLightActive ? L"light" : L"dark"), + (_state.isAppsLightActive ? L"light" : L"dark")); + } + + EvaluateAndApplyIfNeeded(); +} + +// Runs with the registry observer detects a change in Night Light settings. +void LightSwitchStateManager::OnNightLightChange() +{ + std::lock_guard lock(_stateMutex); + + bool newNightLightState = IsNightLightEnabled(); + + // In Follow Night Light mode, treat a Night Light toggle as a boundary + if (_state.lastAppliedMode == ScheduleMode::FollowNightLight && _state.isManualOverride) + { + Logger::info(L"[LightSwitchStateManager] Night Light changed while manual override active; " + L"treating as a boundary and clearing manual override."); + _state.isManualOverride = false; + } + + if (newNightLightState != _state.isNightLightActive) + { + Logger::info(L"[LightSwitchStateManager] Night Light toggled to {}", + newNightLightState ? L"ON" : L"OFF"); + + _state.isNightLightActive = newNightLightState; + } + else + { + Logger::debug(L"[LightSwitchStateManager] Night Light change event fired, but no actual change."); } EvaluateAndApplyIfNeeded(); @@ -77,9 +110,9 @@ void LightSwitchStateManager::SyncInitialThemeState() _state.isSystemLightActive = GetCurrentSystemTheme(); _state.isAppsLightActive = GetCurrentAppsTheme(); Logger::debug(L"[LightSwitchStateManager] Synced initial state to current system theme ({})", - _state.isSystemLightActive ? L"light" : L"dark"); + _state.isSystemLightActive ? L"light" : L"dark"); Logger::debug(L"[LightSwitchStateManager] Synced initial state to current apps theme ({})", - _state.isAppsLightActive ? L"light" : L"dark"); + _state.isAppsLightActive ? L"light" : L"dark"); } static std::pair update_sun_times(auto& settings) @@ -194,7 +227,15 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded() _state.lastAppliedMode = _currentSettings.scheduleMode; - bool shouldBeLight = ShouldBeLight(now, _state.effectiveLightMinutes, _state.effectiveDarkMinutes); + bool shouldBeLight = false; + if (_currentSettings.scheduleMode == ScheduleMode::FollowNightLight) + { + shouldBeLight = !_state.isNightLightActive; + } + else + { + shouldBeLight = ShouldBeLight(now, _state.effectiveLightMinutes, _state.effectiveDarkMinutes); + } bool appsNeedsToChange = _currentSettings.changeApps && (_state.isAppsLightActive != shouldBeLight); bool systemNeedsToChange = _currentSettings.changeSystem && (_state.isSystemLightActive != shouldBeLight); @@ -227,6 +268,3 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded() _state.lastTickMinutes = now; } - - - diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.h b/src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.h index 5c9bcc6e25..c4f39a2e9a 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.h +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchStateManager.h @@ -9,6 +9,7 @@ struct LightSwitchState bool isManualOverride = false; bool isSystemLightActive = false; bool isAppsLightActive = false; + bool isNightLightActive = false; int lastEvaluatedDay = -1; int lastTickMinutes = -1; @@ -32,6 +33,9 @@ public: // Called when manual override is toggled (via shortcut or system change). void OnManualOverride(); + // Called when night light changes in windows settings + void OnNightLightChange(); + // Initial sync at startup to align internal state with system theme void SyncInitialThemeState(); diff --git a/src/modules/LightSwitch/LightSwitchService/NightLightRegistryObserver.cpp b/src/modules/LightSwitch/LightSwitchService/NightLightRegistryObserver.cpp new file mode 100644 index 0000000000..8da19c6595 --- /dev/null +++ b/src/modules/LightSwitch/LightSwitchService/NightLightRegistryObserver.cpp @@ -0,0 +1 @@ +#include "NightLightRegistryObserver.h" diff --git a/src/modules/LightSwitch/LightSwitchService/NightLightRegistryObserver.h b/src/modules/LightSwitch/LightSwitchService/NightLightRegistryObserver.h new file mode 100644 index 0000000000..2806c28316 --- /dev/null +++ b/src/modules/LightSwitch/LightSwitchService/NightLightRegistryObserver.h @@ -0,0 +1,134 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +class NightLightRegistryObserver +{ +public: + NightLightRegistryObserver(HKEY root, const std::wstring& subkey, std::function callback) : + _root(root), _subkey(subkey), _callback(std::move(callback)), _stop(false) + { + _thread = std::thread([this]() { this->Run(); }); + } + + ~NightLightRegistryObserver() + { + Stop(); + } + + void Stop() + { + _stop = true; + + { + std::lock_guard lock(_mutex); + if (_event) + SetEvent(_event); + } + + if (_thread.joinable()) + _thread.join(); + + std::lock_guard lock(_mutex); + if (_hKey) + { + RegCloseKey(_hKey); + _hKey = nullptr; + } + + if (_event) + { + CloseHandle(_event); + _event = nullptr; + } + } + + +private: + void Run() + { + { + std::lock_guard lock(_mutex); + if (RegOpenKeyExW(_root, _subkey.c_str(), 0, KEY_NOTIFY, &_hKey) != ERROR_SUCCESS) + return; + + _event = CreateEventW(nullptr, TRUE, FALSE, nullptr); + if (!_event) + { + RegCloseKey(_hKey); + _hKey = nullptr; + return; + } + } + + while (!_stop) + { + HKEY hKeyLocal = nullptr; + HANDLE eventLocal = nullptr; + + { + std::lock_guard lock(_mutex); + if (_stop) + break; + + hKeyLocal = _hKey; + eventLocal = _event; + } + + if (!hKeyLocal || !eventLocal) + break; + + if (_stop) + break; + + if (RegNotifyChangeKeyValue(hKeyLocal, FALSE, REG_NOTIFY_CHANGE_LAST_SET, eventLocal, TRUE) != ERROR_SUCCESS) + break; + + DWORD wait = WaitForSingleObject(eventLocal, INFINITE); + if (_stop || wait == WAIT_FAILED) + break; + + ResetEvent(eventLocal); + + if (!_stop && _callback) + { + try + { + _callback(); + } + catch (...) + { + } + } + } + + { + std::lock_guard lock(_mutex); + if (_hKey) + { + RegCloseKey(_hKey); + _hKey = nullptr; + } + + if (_event) + { + CloseHandle(_event); + _event = nullptr; + } + } + } + + + HKEY _root; + std::wstring _subkey; + std::function _callback; + HANDLE _event = nullptr; + HKEY _hKey = nullptr; + std::thread _thread; + std::atomic _stop; + std::mutex _mutex; +}; \ No newline at end of file diff --git a/src/modules/LightSwitch/LightSwitchService/SettingsConstants.h b/src/modules/LightSwitch/LightSwitchService/SettingsConstants.h index 4872864eff..8015c9b3e6 100644 --- a/src/modules/LightSwitch/LightSwitchService/SettingsConstants.h +++ b/src/modules/LightSwitch/LightSwitchService/SettingsConstants.h @@ -11,4 +11,7 @@ enum class SettingId Sunset_Offset, ChangeSystem, ChangeApps -}; \ No newline at end of file +}; + +constexpr wchar_t PERSONALIZATION_REGISTRY_PATH[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr wchar_t NIGHT_LIGHT_REGISTRY_PATH[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\CloudStore\\Store\\DefaultAccount\\Current\\default$windows.data.bluelightreduction.bluelightreductionstate\\windows.data.bluelightreduction.bluelightreductionstate"; diff --git a/src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp b/src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp index 9633ab2fde..cfa858c636 100644 --- a/src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp +++ b/src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp @@ -3,6 +3,7 @@ #include #include #include "ThemeHelper.h" +#include // Controls changing the themes. @@ -10,7 +11,7 @@ static void ResetColorPrevalence() { HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + PERSONALIZATION_REGISTRY_PATH, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) @@ -31,7 +32,7 @@ void SetAppsTheme(bool mode) { HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + PERSONALIZATION_REGISTRY_PATH, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) @@ -50,7 +51,7 @@ void SetSystemTheme(bool mode) { HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + PERSONALIZATION_REGISTRY_PATH, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) @@ -79,7 +80,7 @@ bool GetCurrentSystemTheme() DWORD size = sizeof(value); if (RegOpenKeyEx(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + PERSONALIZATION_REGISTRY_PATH, 0, KEY_READ, &hKey) == ERROR_SUCCESS) @@ -98,7 +99,7 @@ bool GetCurrentAppsTheme() DWORD size = sizeof(value); if (RegOpenKeyEx(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + PERSONALIZATION_REGISTRY_PATH, 0, KEY_READ, &hKey) == ERROR_SUCCESS) @@ -109,3 +110,30 @@ bool GetCurrentAppsTheme() return value == 1; // true = light, false = dark } + +bool IsNightLightEnabled() +{ + HKEY hKey; + const wchar_t* path = NIGHT_LIGHT_REGISTRY_PATH; + + if (RegOpenKeyExW(HKEY_CURRENT_USER, path, 0, KEY_READ, &hKey) != ERROR_SUCCESS) + return false; + + // RegGetValueW will set size to the size of the data and we expect that to be at least 25 bytes (we need to access bytes 23 and 24) + DWORD size = 0; + if (RegGetValueW(hKey, nullptr, L"Data", RRF_RT_REG_BINARY, nullptr, nullptr, &size) != ERROR_SUCCESS || size < 25) + { + RegCloseKey(hKey); + return false; + } + + std::vector data(size); + if (RegGetValueW(hKey, nullptr, L"Data", RRF_RT_REG_BINARY, nullptr, data.data(), &size) != ERROR_SUCCESS) + { + RegCloseKey(hKey); + return false; + } + + RegCloseKey(hKey); + return data[23] == 0x10 && data[24] == 0x00; +} \ No newline at end of file diff --git a/src/modules/LightSwitch/LightSwitchService/ThemeHelper.h b/src/modules/LightSwitch/LightSwitchService/ThemeHelper.h index 5985fd95c8..e8d45e9c2a 100644 --- a/src/modules/LightSwitch/LightSwitchService/ThemeHelper.h +++ b/src/modules/LightSwitch/LightSwitchService/ThemeHelper.h @@ -3,3 +3,4 @@ void SetSystemTheme(bool dark); void SetAppsTheme(bool dark); bool GetCurrentSystemTheme(); bool GetCurrentAppsTheme(); +bool IsNightLightEnabled(); \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs b/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs index ce172b2aa2..05c5d7f66c 100644 --- a/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs +++ b/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs @@ -11,6 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers { public const string ColorsSettings = "ms-settings:colors"; public const string DiagnosticsAndFeedback = "ms-settings:privacy-feedback"; + public const string NightLightSettings = "ms-settings:nightlight"; public static string AnimationsSettings => OSVersionHelper.IsWindows11() ? "ms-settings:easeofaccess-visualeffects" diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml index ec61a0fcd5..a44d482a04 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml @@ -67,6 +67,10 @@ x:Uid="LightSwitch_ModeSunsetToSunrise" AutomationProperties.AutomationId="SunCBItem_LightSwitch" Tag="SunsetToSunrise" /> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs index dcd40fdbc7..1ee79a4010 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs @@ -355,6 +355,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views VisualStateManager.GoToState(this, "SunsetToSunriseState", true); this.SunriseModeChartState(); break; + case "FollowNightLight": + VisualStateManager.GoToState(this, "FollowNightLightState", true); + TimelineCard.Visibility = Visibility.Collapsed; + break; default: VisualStateManager.GoToState(this, "OffState", true); this.TimelineCard.Visibility = Visibility.Collapsed; @@ -362,6 +366,18 @@ namespace Microsoft.PowerToys.Settings.UI.Views } } + private void OpenNightLightSettings_Click(object sender, RoutedEventArgs e) + { + try + { + Helpers.StartProcessHelper.Start(Helpers.StartProcessHelper.NightLightSettings); + } + catch (Exception ex) + { + Logger.LogError("Error while trying to open the system night light settings", ex); + } + } + private void SunriseModeChartState() { if (this.ViewModel.Latitude != "0.0" && this.ViewModel.Longitude != "0.0") diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index f7fbeda08b..30535804b7 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -1,17 +1,17 @@ - @@ -5760,4 +5760,13 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m A modern UI built with Fluent Design Fluent Design is a product name, do not loc - + + Follow Night Light + + + Personalize your Night Light settings. + + + Following Night Light settings. + + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/ViewModels/LightSwitchViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/LightSwitchViewModel.cs index 621fa91d43..e9e744705f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/LightSwitchViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/LightSwitchViewModel.cs @@ -42,6 +42,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels "Off", "FixedHours", "SunsetToSunrise", + "FollowNightLight", }; _toggleThemeHotkey = _moduleSettings.Properties.ToggleThemeHotkey.Value;