From f45d54abdf60ba4f76e56f37f621c4a854dc5065 Mon Sep 17 00:00:00 2001 From: Jaylyn Barbee <51131738+Jaylyn-Barbee@users.noreply.github.com> Date: Mon, 20 Oct 2025 18:57:03 -0400 Subject: [PATCH] [Light Switch] Hotfixes (#42434) ## Summary of the Pull Request Adds new "Off" mode for the schedule mode options which disable the schedule. Adds explicit function to disable light switch by default. ## PR Checklist Closes: - #42402 New behavior: "Off" mode added. when off, the regular service loop stops and all actions are event driven to either resume the loop or listen for hotkey. - #42386 New behavior: Disabled explicitly by default - #42389 New behavior: When switching from dark to light mode the system theme will remove the accent color. - #42513 New behavior: Manual mode no longer gets reset. It was being overridden by the sun calculations that were invertedly running when in manual mode. Todo: - [ ] **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 @alvinashcraft we will need to add this new mode to the documentation. ## Validation Steps Performed - Removed all default settings and tested new logic. Light Switch is set to off by default. - Updated UI and tested new "Off" mode, logs indicate mode switched and ticker stopped. Polling resumes on mode change. (need to check that the shortcut still works) --------- Co-authored-by: Niels Laute Co-authored-by: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com> --- .../ThemeHelper.cpp | 25 + .../LightSwitchModuleInterface/dllmain.cpp | 84 ++- .../LightSwitchService/LightSwitchService.cpp | 129 ++++- .../LightSwitchSettings.cpp | 19 +- .../LightSwitchService/LightSwitchSettings.h | 11 +- .../LightSwitchService/ThemeHelper.cpp | 30 + .../LightSwitchProperties.cs | 2 +- .../Converters/EnumToVisibilityConverter.cs | 37 -- .../SettingsXAML/Views/LightSwitchPage.xaml | 537 ++++++++++-------- .../Views/LightSwitchPage.xaml.cs | 40 +- .../Settings.UI/Strings/en-us/Resources.resw | 8 +- .../ViewModels/LightSwitchViewModel.cs | 1 + 12 files changed, 584 insertions(+), 339 deletions(-) delete mode 100644 src/settings-ui/Settings.UI/Converters/EnumToVisibilityConverter.cs diff --git a/src/modules/LightSwitch/LightSwitchModuleInterface/ThemeHelper.cpp b/src/modules/LightSwitch/LightSwitchModuleInterface/ThemeHelper.cpp index dff2a67669..3593a5bbae 100644 --- a/src/modules/LightSwitch/LightSwitchModuleInterface/ThemeHelper.cpp +++ b/src/modules/LightSwitch/LightSwitchModuleInterface/ThemeHelper.cpp @@ -3,6 +3,26 @@ #include "ThemeHelper.h" // Controls changing the themes. +static void ResetColorPrevalence() +{ + HKEY hKey; + if (RegOpenKeyEx(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + 0, + KEY_SET_VALUE, + &hKey) == ERROR_SUCCESS) + { + DWORD value = 0; // back to default value + RegSetValueEx(hKey, L"ColorPrevalence", 0, REG_DWORD, reinterpret_cast(&value), sizeof(value)); + RegCloseKey(hKey); + + SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr); + + SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr); + + SendMessageTimeout(HWND_BROADCAST, WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr); + } +} void SetAppsTheme(bool mode) { @@ -36,6 +56,11 @@ void SetSystemTheme(bool mode) RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast(&value), sizeof(value)); RegCloseKey(hKey); + if (mode) // if are changing to light mode + { + ResetColorPrevalence(); + } + SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr); SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr); diff --git a/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp b/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp index 4e95555a5a..170dde5b0a 100644 --- a/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp +++ b/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp @@ -47,6 +47,7 @@ const static wchar_t* MODULE_DESC = L"This is a module that allows you to contro enum class ScheduleMode { + Off, FixedHours, SunsetToSunrise, // add more later @@ -59,8 +60,9 @@ inline std::wstring ToString(ScheduleMode mode) case ScheduleMode::SunsetToSunrise: return L"SunsetToSunrise"; case ScheduleMode::FixedHours: - default: return L"FixedHours"; + default: + return L"Off"; } } @@ -68,7 +70,9 @@ inline ScheduleMode FromString(const std::wstring& str) { if (str == L"SunsetToSunrise") return ScheduleMode::SunsetToSunrise; - return ScheduleMode::FixedHours; + if (str == L"FixedHours") + return ScheduleMode::FixedHours; + return ScheduleMode::Off; } // These are the properties shown in the Settings page. @@ -76,7 +80,7 @@ struct ModuleSettings { bool m_changeSystem = true; bool m_changeApps = true; - ScheduleMode m_scheduleMode = ScheduleMode::FixedHours; + ScheduleMode m_scheduleMode = ScheduleMode::Off; int m_lightTime = 480; int m_darkTime = 1200; int m_sunrise_offset = 0; @@ -161,7 +165,8 @@ public: L"scheduleMode", L"Theme schedule mode", ToString(g_settings.m_scheduleMode), - { { L"FixedHours", L"Set hours manually" }, + { { L"Off", L"Disable the schedule" }, + { L"FixedHours", L"Set hours manually" }, { L"SunsetToSunrise", L"Use sunrise/sunset times" } }); // Integer spinners @@ -284,9 +289,20 @@ public: g_settings.m_changeApps = *v; } + auto previousMode = g_settings.m_scheduleMode; + if (auto v = values.get_string_value(L"scheduleMode")) { - g_settings.m_scheduleMode = FromString(*v); + auto newMode = FromString(*v); + if (newMode != g_settings.m_scheduleMode) + { + Logger::info(L"[LightSwitchInterface] Schedule mode changed from {} to {}", + ToString(g_settings.m_scheduleMode), + ToString(newMode)); + g_settings.m_scheduleMode = newMode; + + start_service_if_needed(); + } } if (auto v = values.get_int_value(L"lightTime")) @@ -304,7 +320,7 @@ public: g_settings.m_sunrise_offset = *v; } - if (auto v = values.get_int_value(L"m_sunset_offset")) + if (auto v = values.get_int_value(L"sunset_offset")) { g_settings.m_sunset_offset = *v; } @@ -326,6 +342,47 @@ public: } } + virtual void start_service_if_needed() + { + if (!m_process || WaitForSingleObject(m_process, 0) != WAIT_TIMEOUT) + { + Logger::info(L"[LightSwitchInterface] Starting LightSwitchService due to active schedule mode."); + enable(); + } + else + { + Logger::debug(L"[LightSwitchInterface] Service already running, skipping start."); + } + } + + /*virtual void stop_worker_only() + { + if (m_process) + { + Logger::info(L"[LightSwitchInterface] Stopping LightSwitchService (worker only)."); + constexpr DWORD timeout_ms = 1500; + DWORD result = WaitForSingleObject(m_process, timeout_ms); + + if (result == WAIT_TIMEOUT) + { + Logger::warn("Light Switch: Process didn't exit in time. Forcing termination."); + TerminateProcess(m_process, 0); + } + + CloseHandle(m_process); + m_process = nullptr; + } + }*/ + + /*virtual void stop_service_if_running() + { + if (m_process) + { + Logger::info(L"[LightSwitchInterface] Stopping LightSwitchService due to schedule OFF."); + stop_worker_only(); + } + }*/ + virtual void enable() { m_enabled = true; @@ -413,6 +470,12 @@ public: return m_enabled; } + // Returns whether the PowerToys should be enabled by default + virtual bool is_enabled_by_default() const override + { + return false; + } + void parse_hotkey(PowerToysSettings::PowerToyValues& settings) { auto settingsObject = settings.get_raw_json(); @@ -471,6 +534,15 @@ public: SetAppsTheme(!GetCurrentAppsTheme()); } + if (!m_manual_override_event_handle) + { + m_manual_override_event_handle = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE"); + if (!m_manual_override_event_handle) + { + m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE"); + } + } + if (m_manual_override_event_handle) { SetEvent(m_manual_override_event_handle); diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchService.cpp b/src/modules/LightSwitch/LightSwitchService/LightSwitchService.cpp index 15c268fc84..69fe38248f 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchService.cpp +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchService.cpp @@ -186,61 +186,138 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) { if (settings.changeSystem && !isSystemCurrentlyLight) SetSystemTheme(true); + Logger::info(L"[LightSwitchService] Changing system theme to light mode."); if (settings.changeApps && !isAppsCurrentlyLight) SetAppsTheme(true); + Logger::info(L"[LightSwitchService] Changing apps theme to light mode."); } else { if (settings.changeSystem && isSystemCurrentlyLight) SetSystemTheme(false); + Logger::info(L"[LightSwitchService] Changing system theme to dark mode."); if (settings.changeApps && isAppsCurrentlyLight) SetAppsTheme(false); + Logger::info(L"[LightSwitchService] Changing apps theme to light mode."); } }; - // --- At service start: immediately honor the schedule --- + // --- Initial settings load --- + LightSwitchSettings::instance().LoadSettings(); + auto& settings = LightSwitchSettings::instance().settings(); + + // --- Initial theme application (if schedule enabled) --- + if (settings.scheduleMode != ScheduleMode::Off) { SYSTEMTIME st; GetLocalTime(&st); int nowMinutes = st.wHour * 60 + st.wMinute; - - LightSwitchSettings::instance().LoadSettings(); - const auto& settings = LightSwitchSettings::instance().settings(); - applyTheme(nowMinutes, settings.lightTime + settings.sunrise_offset, settings.darkTime + settings.sunset_offset, settings); } + else + { + Logger::info(L"[LightSwitchService] Schedule mode is OFF - ticker suspended, waiting for manual action or mode change."); + } - // --- Main loop: wakes once per minute or stop/parent death --- + // --- Main loop --- for (;;) { HANDLE waits[2] = { g_ServiceStopEvent, hParent }; DWORD count = hParent ? 2 : 1; + LightSwitchSettings::instance().LoadSettings(); + const auto& settings = LightSwitchSettings::instance().settings(); + + // If schedule is off, idle but keep watching settings and manual override + if (settings.scheduleMode == ScheduleMode::Off) + { + Logger::info(L"[LightSwitchService] Schedule mode OFF - suspending scheduler but keeping service alive."); + + if (!hManualOverride) + { + hManualOverride = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE"); + } + + HANDLE waits[4]; + DWORD count = 0; + waits[count++] = g_ServiceStopEvent; + if (hParent) + waits[count++] = hParent; + if (hManualOverride) + waits[count++] = hManualOverride; + waits[count++] = LightSwitchSettings::instance().GetSettingsChangedEvent(); + + for (;;) + { + DWORD wait = WaitForMultipleObjects(count, waits, FALSE, INFINITE); + + // --- Handle exit signals --- + if (wait == WAIT_OBJECT_0) // stop event + { + Logger::info(L"[LightSwitchService] Stop event triggered - exiting worker loop."); + break; + } + if (hParent && wait == WAIT_OBJECT_0 + 1) + { + Logger::info(L"[LightSwitchService] Parent exited - stopping service."); + break; + } + + // --- Manual override triggered --- + if (wait == WAIT_OBJECT_0 + (hParent ? 2 : 1)) + { + Logger::info(L"[LightSwitchService] Manual override received while schedule OFF."); + ResetEvent(hManualOverride); + continue; + } + + // --- Settings file changed --- + if (wait == WAIT_OBJECT_0 + (hParent ? 3 : 2)) + { + Logger::trace(L"[LightSwitchService] Settings change event triggered, reloading settings..."); + + ResetEvent(LightSwitchSettings::instance().GetSettingsChangedEvent()); + + LightSwitchSettings::instance().LoadSettings(); + const auto& newSettings = LightSwitchSettings::instance().settings(); + + if (newSettings.scheduleMode != ScheduleMode::Off) + { + Logger::info(L"[LightSwitchService] Schedule re-enabled, resuming normal loop."); + break; + } + } + } + } + + + // --- When schedule is active, run once per minute --- SYSTEMTIME st; GetLocalTime(&st); int nowMinutes = st.wHour * 60 + st.wMinute; - LightSwitchSettings::instance().LoadSettings(); - const auto& settings = LightSwitchSettings::instance().settings(); - // Refresh suntimes at day boundary - if (g_lastUpdatedDay != st.wDay) + if ((g_lastUpdatedDay != st.wDay) && (settings.scheduleMode == ScheduleMode::SunsetToSunrise)) { update_sun_times(settings); g_lastUpdatedDay = st.wDay; - Logger::info(L"[LightSwitchService] Recalculated sun times at new day boundary."); } + // Have to do this again in case settings got updated in the refresh suntimes chunk + LightSwitchSettings::instance().LoadSettings(); + const auto& currentSettings = LightSwitchSettings::instance().settings(); + wchar_t msg[160]; swprintf_s(msg, - L"[LightSwitchService] now=%02d:%02d | light=%02d:%02d | dark=%02d:%02d", + L"[LightSwitchService] now=%02d:%02d | light=%02d:%02d | dark=%02d:%02d | mode=%d", st.wHour, st.wMinute, - settings.lightTime / 60, - settings.lightTime % 60, - settings.darkTime / 60, - settings.darkTime % 60); + currentSettings.lightTime / 60, + currentSettings.lightTime % 60, + currentSettings.darkTime / 60, + currentSettings.darkTime % 60, + static_cast(currentSettings.scheduleMode)); Logger::info(msg); // --- Manual override check --- @@ -252,22 +329,20 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) if (manualOverrideActive) { - // Did we hit a scheduled boundary? (reset override at boundary) - if (nowMinutes == (settings.lightTime + settings.sunrise_offset) % 1440 || - nowMinutes == (settings.darkTime + settings.sunset_offset) % 1440) + if (nowMinutes == (currentSettings.lightTime + currentSettings.sunrise_offset) % 1440 || + nowMinutes == (currentSettings.darkTime + currentSettings.sunset_offset) % 1440) { ResetEvent(hManualOverride); - Logger::info(L"[LightSwitchService] Manual override cleared at boundary\n"); + Logger::info(L"[LightSwitchService] Manual override cleared at boundary"); } else { - Logger::info(L"[LightSwitchService] Skipping schedule due to manual override\n"); + Logger::info(L"[LightSwitchService] Skipping schedule due to manual override"); goto sleep_until_next_minute; } } - // Apply theme logic (only runs if no manual override or override just cleared) - applyTheme(nowMinutes, settings.lightTime + settings.sunrise_offset, settings.darkTime + settings.sunset_offset, settings); + applyTheme(nowMinutes, currentSettings.lightTime + currentSettings.sunrise_offset, currentSettings.darkTime + currentSettings.sunset_offset, currentSettings); sleep_until_next_minute: GetLocalTime(&st); @@ -278,15 +353,14 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) DWORD wait = WaitForMultipleObjects(count, waits, FALSE, msToNextMinute); if (wait == WAIT_OBJECT_0) { - Logger::info(L"[LightSwitchService] Stop event triggered — exiting worker loop."); + Logger::info(L"[LightSwitchService] Stop event triggered - exiting worker loop."); break; } - if (hParent && wait == WAIT_OBJECT_0 + 1) // parent process exited + if (hParent && wait == WAIT_OBJECT_0 + 1) { - Logger::info(L"[LightSwitchService] Parent process exited — stopping service."); + Logger::info(L"[LightSwitchService] Parent process exited - stopping service."); break; } - } if (hManualOverride) @@ -297,6 +371,7 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) return 0; } + int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) { if (powertoys_gpo::getConfiguredLightSwitchEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled) diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp b/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp index 5bd5a1fe92..a7f44cca6d 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace std; @@ -27,10 +28,20 @@ std::wstring LightSwitchSettings::GetSettingsFileName() void LightSwitchSettings::InitFileWatcher() { - const std::wstring& settingsFileName = GetSettingsFileName(); - m_settingsFileWatcher = std::make_unique(settingsFileName, [&]() { - PostMessageW(HWND_BROADCAST, WM_PRIV_SETTINGS_CHANGED, NULL, NULL); - }); + if (!m_settingsChangedEvent) + { + m_settingsChangedEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); + } + + if (!m_settingsFileWatcher) + { + m_settingsFileWatcher = std::make_unique( + GetSettingsFileName(), + [this]() { + Logger::info(L"[LightSwitchSettings] Settings file changed, signaling event."); + SetEvent(m_settingsChangedEvent); + }); + } } void LightSwitchSettings::AddObserver(SettingsObserver& observer) diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.h b/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.h index 51f0988eda..32d011313f 100644 --- a/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.h +++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.h @@ -14,6 +14,7 @@ class SettingsObserver; enum class ScheduleMode { + Off, FixedHours, SunsetToSunrise // Add more in the future @@ -28,7 +29,7 @@ inline std::wstring ToString(ScheduleMode mode) case ScheduleMode::SunsetToSunrise: return L"SunsetToSunrise"; default: - return L"FixedHours"; + return L"Off"; } } @@ -36,8 +37,10 @@ inline ScheduleMode FromString(const std::wstring& str) { if (str == L"SunsetToSunrise") return ScheduleMode::SunsetToSunrise; - else + if (str == L"FixedHours") return ScheduleMode::FixedHours; + else + return ScheduleMode::Off; } struct LightSwitchConfig @@ -76,6 +79,8 @@ public: void LoadSettings(); + HANDLE GetSettingsChangedEvent() const { return m_settingsChangedEvent; } + private: LightSwitchSettings(); ~LightSwitchSettings() = default; @@ -85,4 +90,6 @@ private: std::unordered_set m_observers; void NotifyObservers(SettingId id) const; + + HANDLE m_settingsChangedEvent = nullptr; }; diff --git a/src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp b/src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp index b0a57cf468..9633ab2fde 100644 --- a/src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp +++ b/src/modules/LightSwitch/LightSwitchService/ThemeHelper.cpp @@ -1,8 +1,32 @@ #include +#include +#include +#include #include "ThemeHelper.h" // Controls changing the themes. +static void ResetColorPrevalence() +{ + HKEY hKey; + if (RegOpenKeyEx(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + 0, + KEY_SET_VALUE, + &hKey) == ERROR_SUCCESS) + { + DWORD value = 0; // back to default value + RegSetValueEx(hKey, L"ColorPrevalence", 0, REG_DWORD, reinterpret_cast(&value), sizeof(value)); + RegCloseKey(hKey); + + SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr); + + SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr); + + SendMessageTimeout(HWND_BROADCAST, WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr); + } +} + void SetAppsTheme(bool mode) { HKEY hKey; @@ -35,6 +59,12 @@ void SetSystemTheme(bool mode) RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast(&value), sizeof(value)); RegCloseKey(hKey); + if (mode) // if are changing to light mode + { + ResetColorPrevalence(); + Logger::info(L"[LightSwitchService] Reset ColorPrevalence to default when switching to light mode."); + } + SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr); SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr); diff --git a/src/settings-ui/Settings.UI.Library/LightSwitchProperties.cs b/src/settings-ui/Settings.UI.Library/LightSwitchProperties.cs index a58022d4a6..8f5bf88a19 100644 --- a/src/settings-ui/Settings.UI.Library/LightSwitchProperties.cs +++ b/src/settings-ui/Settings.UI.Library/LightSwitchProperties.cs @@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library public const int DefaultSunsetOffset = 0; public const string DefaultLatitude = "0.0"; public const string DefaultLongitude = "0.0"; - public const string DefaultScheduleMode = "FixedHours"; + public const string DefaultScheduleMode = "Off"; public static readonly HotkeySettings DefaultToggleThemeHotkey = new HotkeySettings(true, true, false, true, 0x44); // Ctrl+Win+Shift+D public LightSwitchProperties() diff --git a/src/settings-ui/Settings.UI/Converters/EnumToVisibilityConverter.cs b/src/settings-ui/Settings.UI/Converters/EnumToVisibilityConverter.cs deleted file mode 100644 index 5cb7a5e0ed..0000000000 --- a/src/settings-ui/Settings.UI/Converters/EnumToVisibilityConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Data; - -namespace Microsoft.PowerToys.Settings.UI.Converters -{ - public partial class EnumToVisibilityConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, string language) - { - if (value == null || parameter == null) - { - return Visibility.Collapsed; - } - - string enumString = value.ToString(); - string targetString = parameter.ToString(); - - return enumString.Equals(targetString, StringComparison.OrdinalIgnoreCase) - ? Visibility.Visible - : Visibility.Collapsed; - } - - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml index dc2be9a9e6..7f55e31cd8 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml @@ -17,143 +17,175 @@ AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Margin="0,24,0,0" + HorizontalAlignment="Center" + Orientation="Vertical" + Spacing="32"> + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 d970700484..4a8e8905d7 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/LightSwitchPage.xaml.cs @@ -300,20 +300,20 @@ namespace Microsoft.PowerToys.Settings.UI.Views private void ModeSelector_SelectionChanged(object sender, SelectionChangedEventArgs e) { - SunriseModeChartState(); - } - - private void SunriseModeChartState() - { - if (ViewModel.Latitude == "0.0" && ViewModel.Longitude == "0.0" && ViewModel.ScheduleMode == "SunsetToSunrise") + switch (ViewModel.ScheduleMode) { - TimelineCard.Visibility = Visibility.Collapsed; - LocationWarningBar.Visibility = Visibility.Visible; - } - else - { - TimelineCard.Visibility = Visibility.Visible; - LocationWarningBar.Visibility = Visibility.Collapsed; + case "FixedHours": + VisualStateManager.GoToState(this, "ManualState", true); + TimelineCard.Visibility = Visibility.Visible; + break; + case "SunsetToSunrise": + VisualStateManager.GoToState(this, "SunsetToSunriseState", true); + SunriseModeChartState(); + break; + default: + VisualStateManager.GoToState(this, "OffState", true); + TimelineCard.Visibility = Visibility.Collapsed; + break; } } @@ -321,5 +321,19 @@ namespace Microsoft.PowerToys.Settings.UI.Views { await GetGeoLocation(); } + + private void SunriseModeChartState() + { + if (ViewModel.Latitude != "0.0" && ViewModel.Longitude != "0.0") + { + TimelineCard.Visibility = Visibility.Visible; + LocationWarningBar.Visibility = Visibility.Collapsed; + } + else + { + TimelineCard.Visibility = Visibility.Collapsed; + LocationWarningBar.Visibility = Visibility.Visible; + } + } } } 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 92ecca11f0..8384be49a1 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -5269,12 +5269,18 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m Determine when dark mode should be turned on + + Off + - Manual + Fixed hours Sunset to sunrise + + Scheduling is turned off. + Turn on dark mode diff --git a/src/settings-ui/Settings.UI/ViewModels/LightSwitchViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/LightSwitchViewModel.cs index 010bd68935..3f9ea48c18 100644 --- a/src/settings-ui/Settings.UI/ViewModels/LightSwitchViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/LightSwitchViewModel.cs @@ -39,6 +39,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels AvailableScheduleModes = new ObservableCollection { + "Off", "FixedHours", "SunsetToSunrise", };