[Light Switch] Hotfixes (#42434)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## 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.

<!-- Please review the items on the PR checklist before submitting-->
## 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 <niels.laute@live.nl>
Co-authored-by: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com>
This commit is contained in:
Jaylyn Barbee
2025-10-20 18:57:03 -04:00
committed by GitHub
parent f28d009131
commit f45d54abdf
12 changed files with 584 additions and 339 deletions

View File

@@ -3,6 +3,26 @@
#include "ThemeHelper.h" #include "ThemeHelper.h"
// Controls changing the themes. // 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<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(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) void SetAppsTheme(bool mode)
{ {
@@ -36,6 +56,11 @@ void SetSystemTheme(bool mode)
RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value)); RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey); RegCloseKey(hKey);
if (mode) // if are changing to light mode
{
ResetColorPrevalence();
}
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr); SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr); SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);

View File

@@ -47,6 +47,7 @@ const static wchar_t* MODULE_DESC = L"This is a module that allows you to contro
enum class ScheduleMode enum class ScheduleMode
{ {
Off,
FixedHours, FixedHours,
SunsetToSunrise, SunsetToSunrise,
// add more later // add more later
@@ -59,8 +60,9 @@ inline std::wstring ToString(ScheduleMode mode)
case ScheduleMode::SunsetToSunrise: case ScheduleMode::SunsetToSunrise:
return L"SunsetToSunrise"; return L"SunsetToSunrise";
case ScheduleMode::FixedHours: case ScheduleMode::FixedHours:
default:
return L"FixedHours"; return L"FixedHours";
default:
return L"Off";
} }
} }
@@ -68,7 +70,9 @@ inline ScheduleMode FromString(const std::wstring& str)
{ {
if (str == L"SunsetToSunrise") if (str == L"SunsetToSunrise")
return ScheduleMode::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. // These are the properties shown in the Settings page.
@@ -76,7 +80,7 @@ struct ModuleSettings
{ {
bool m_changeSystem = true; bool m_changeSystem = true;
bool m_changeApps = true; bool m_changeApps = true;
ScheduleMode m_scheduleMode = ScheduleMode::FixedHours; ScheduleMode m_scheduleMode = ScheduleMode::Off;
int m_lightTime = 480; int m_lightTime = 480;
int m_darkTime = 1200; int m_darkTime = 1200;
int m_sunrise_offset = 0; int m_sunrise_offset = 0;
@@ -161,7 +165,8 @@ public:
L"scheduleMode", L"scheduleMode",
L"Theme schedule mode", L"Theme schedule mode",
ToString(g_settings.m_scheduleMode), 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" } }); { L"SunsetToSunrise", L"Use sunrise/sunset times" } });
// Integer spinners // Integer spinners
@@ -284,9 +289,20 @@ public:
g_settings.m_changeApps = *v; g_settings.m_changeApps = *v;
} }
auto previousMode = g_settings.m_scheduleMode;
if (auto v = values.get_string_value(L"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")) if (auto v = values.get_int_value(L"lightTime"))
@@ -304,7 +320,7 @@ public:
g_settings.m_sunrise_offset = *v; 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; 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() virtual void enable()
{ {
m_enabled = true; m_enabled = true;
@@ -413,6 +470,12 @@ public:
return m_enabled; 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) void parse_hotkey(PowerToysSettings::PowerToyValues& settings)
{ {
auto settingsObject = settings.get_raw_json(); auto settingsObject = settings.get_raw_json();
@@ -471,6 +534,15 @@ public:
SetAppsTheme(!GetCurrentAppsTheme()); 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) if (m_manual_override_event_handle)
{ {
SetEvent(m_manual_override_event_handle); SetEvent(m_manual_override_event_handle);

View File

@@ -186,61 +186,138 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{ {
if (settings.changeSystem && !isSystemCurrentlyLight) if (settings.changeSystem && !isSystemCurrentlyLight)
SetSystemTheme(true); SetSystemTheme(true);
Logger::info(L"[LightSwitchService] Changing system theme to light mode.");
if (settings.changeApps && !isAppsCurrentlyLight) if (settings.changeApps && !isAppsCurrentlyLight)
SetAppsTheme(true); SetAppsTheme(true);
Logger::info(L"[LightSwitchService] Changing apps theme to light mode.");
} }
else else
{ {
if (settings.changeSystem && isSystemCurrentlyLight) if (settings.changeSystem && isSystemCurrentlyLight)
SetSystemTheme(false); SetSystemTheme(false);
Logger::info(L"[LightSwitchService] Changing system theme to dark mode.");
if (settings.changeApps && isAppsCurrentlyLight) if (settings.changeApps && isAppsCurrentlyLight)
SetAppsTheme(false); 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; SYSTEMTIME st;
GetLocalTime(&st); GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute; 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); 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 (;;) for (;;)
{ {
HANDLE waits[2] = { g_ServiceStopEvent, hParent }; HANDLE waits[2] = { g_ServiceStopEvent, hParent };
DWORD count = hParent ? 2 : 1; 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; SYSTEMTIME st;
GetLocalTime(&st); GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute; int nowMinutes = st.wHour * 60 + st.wMinute;
LightSwitchSettings::instance().LoadSettings();
const auto& settings = LightSwitchSettings::instance().settings();
// Refresh suntimes at day boundary // Refresh suntimes at day boundary
if (g_lastUpdatedDay != st.wDay) if ((g_lastUpdatedDay != st.wDay) && (settings.scheduleMode == ScheduleMode::SunsetToSunrise))
{ {
update_sun_times(settings); update_sun_times(settings);
g_lastUpdatedDay = st.wDay; g_lastUpdatedDay = st.wDay;
Logger::info(L"[LightSwitchService] Recalculated sun times at new day boundary."); 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]; wchar_t msg[160];
swprintf_s(msg, 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.wHour,
st.wMinute, st.wMinute,
settings.lightTime / 60, currentSettings.lightTime / 60,
settings.lightTime % 60, currentSettings.lightTime % 60,
settings.darkTime / 60, currentSettings.darkTime / 60,
settings.darkTime % 60); currentSettings.darkTime % 60,
static_cast<int>(currentSettings.scheduleMode));
Logger::info(msg); Logger::info(msg);
// --- Manual override check --- // --- Manual override check ---
@@ -252,22 +329,20 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
if (manualOverrideActive) if (manualOverrideActive)
{ {
// Did we hit a scheduled boundary? (reset override at boundary) if (nowMinutes == (currentSettings.lightTime + currentSettings.sunrise_offset) % 1440 ||
if (nowMinutes == (settings.lightTime + settings.sunrise_offset) % 1440 || nowMinutes == (currentSettings.darkTime + currentSettings.sunset_offset) % 1440)
nowMinutes == (settings.darkTime + settings.sunset_offset) % 1440)
{ {
ResetEvent(hManualOverride); ResetEvent(hManualOverride);
Logger::info(L"[LightSwitchService] Manual override cleared at boundary\n"); Logger::info(L"[LightSwitchService] Manual override cleared at boundary");
} }
else 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; goto sleep_until_next_minute;
} }
} }
// Apply theme logic (only runs if no manual override or override just cleared) applyTheme(nowMinutes, currentSettings.lightTime + currentSettings.sunrise_offset, currentSettings.darkTime + currentSettings.sunset_offset, currentSettings);
applyTheme(nowMinutes, settings.lightTime + settings.sunrise_offset, settings.darkTime + settings.sunset_offset, settings);
sleep_until_next_minute: sleep_until_next_minute:
GetLocalTime(&st); GetLocalTime(&st);
@@ -278,15 +353,14 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
DWORD wait = WaitForMultipleObjects(count, waits, FALSE, msToNextMinute); DWORD wait = WaitForMultipleObjects(count, waits, FALSE, msToNextMinute);
if (wait == WAIT_OBJECT_0) if (wait == WAIT_OBJECT_0)
{ {
Logger::info(L"[LightSwitchService] Stop event triggered <EFBFBD> exiting worker loop."); Logger::info(L"[LightSwitchService] Stop event triggered - exiting worker loop.");
break; 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 <EFBFBD> stopping service."); Logger::info(L"[LightSwitchService] Parent process exited - stopping service.");
break; break;
} }
} }
if (hManualOverride) if (hManualOverride)
@@ -297,6 +371,7 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
return 0; return 0;
} }
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{ {
if (powertoys_gpo::getConfiguredLightSwitchEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled) if (powertoys_gpo::getConfiguredLightSwitchEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)

View File

@@ -6,6 +6,7 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <WinHookEventIDs.h> #include <WinHookEventIDs.h>
#include <logger.h>
using namespace std; using namespace std;
@@ -27,10 +28,20 @@ std::wstring LightSwitchSettings::GetSettingsFileName()
void LightSwitchSettings::InitFileWatcher() void LightSwitchSettings::InitFileWatcher()
{ {
const std::wstring& settingsFileName = GetSettingsFileName(); if (!m_settingsChangedEvent)
m_settingsFileWatcher = std::make_unique<FileWatcher>(settingsFileName, [&]() { {
PostMessageW(HWND_BROADCAST, WM_PRIV_SETTINGS_CHANGED, NULL, NULL); m_settingsChangedEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
}); }
if (!m_settingsFileWatcher)
{
m_settingsFileWatcher = std::make_unique<FileWatcher>(
GetSettingsFileName(),
[this]() {
Logger::info(L"[LightSwitchSettings] Settings file changed, signaling event.");
SetEvent(m_settingsChangedEvent);
});
}
} }
void LightSwitchSettings::AddObserver(SettingsObserver& observer) void LightSwitchSettings::AddObserver(SettingsObserver& observer)

View File

@@ -14,6 +14,7 @@ class SettingsObserver;
enum class ScheduleMode enum class ScheduleMode
{ {
Off,
FixedHours, FixedHours,
SunsetToSunrise SunsetToSunrise
// Add more in the future // Add more in the future
@@ -28,7 +29,7 @@ inline std::wstring ToString(ScheduleMode mode)
case ScheduleMode::SunsetToSunrise: case ScheduleMode::SunsetToSunrise:
return L"SunsetToSunrise"; return L"SunsetToSunrise";
default: default:
return L"FixedHours"; return L"Off";
} }
} }
@@ -36,8 +37,10 @@ inline ScheduleMode FromString(const std::wstring& str)
{ {
if (str == L"SunsetToSunrise") if (str == L"SunsetToSunrise")
return ScheduleMode::SunsetToSunrise; return ScheduleMode::SunsetToSunrise;
else if (str == L"FixedHours")
return ScheduleMode::FixedHours; return ScheduleMode::FixedHours;
else
return ScheduleMode::Off;
} }
struct LightSwitchConfig struct LightSwitchConfig
@@ -76,6 +79,8 @@ public:
void LoadSettings(); void LoadSettings();
HANDLE GetSettingsChangedEvent() const { return m_settingsChangedEvent; }
private: private:
LightSwitchSettings(); LightSwitchSettings();
~LightSwitchSettings() = default; ~LightSwitchSettings() = default;
@@ -85,4 +90,6 @@ private:
std::unordered_set<SettingsObserver*> m_observers; std::unordered_set<SettingsObserver*> m_observers;
void NotifyObservers(SettingId id) const; void NotifyObservers(SettingId id) const;
HANDLE m_settingsChangedEvent = nullptr;
}; };

View File

@@ -1,8 +1,32 @@
#include <windows.h> #include <windows.h>
#include <logger/logger_settings.h>
#include <logger/logger.h>
#include <utils/logger_helper.h>
#include "ThemeHelper.h" #include "ThemeHelper.h"
// Controls changing the themes. // 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<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(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) void SetAppsTheme(bool mode)
{ {
HKEY hKey; HKEY hKey;
@@ -35,6 +59,12 @@ void SetSystemTheme(bool mode)
RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value)); RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey); 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<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr); SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr); SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);

View File

@@ -16,7 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public const int DefaultSunsetOffset = 0; public const int DefaultSunsetOffset = 0;
public const string DefaultLatitude = "0.0"; public const string DefaultLatitude = "0.0";
public const string DefaultLongitude = "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 static readonly HotkeySettings DefaultToggleThemeHotkey = new HotkeySettings(true, true, false, true, 0x44); // Ctrl+Win+Shift+D
public LightSwitchProperties() public LightSwitchProperties()

View File

@@ -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();
}
}
}

View File

@@ -17,143 +17,175 @@
AutomationProperties.LandmarkType="Main" AutomationProperties.LandmarkType="Main"
mc:Ignorable="d"> mc:Ignorable="d">
<Page.Resources> <Page.Resources>
<converters:EnumToVisibilityConverter x:Key="EnumToVisibilityConverter" />
<converters:TimeSpanToFriendlyTimeConverter x:Key="TimeSpanToFriendlyTimeConverter" /> <converters:TimeSpanToFriendlyTimeConverter x:Key="TimeSpanToFriendlyTimeConverter" />
</Page.Resources> </Page.Resources>
<controls:SettingsPageControl <Grid>
x:Uid="LightSwitch" <controls:SettingsPageControl
IsTabStop="False" x:Uid="LightSwitch"
ModuleImageSource="ms-appx:///Assets/Settings/Modules/LightSwitch.png"> IsTabStop="False"
<controls:SettingsPageControl.ModuleContent> ModuleImageSource="ms-appx:///Assets/Settings/Modules/LightSwitch.png">
<StackPanel Orientation="Vertical"> <controls:SettingsPageControl.ModuleContent>
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay}"> <StackPanel Orientation="Vertical">
<tkcontrols:SettingsCard <controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay}">
x:Uid="LightSwitch_EnableSettingsCard" <tkcontrols:SettingsCard
HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/LightSwitch.png}" x:Uid="LightSwitch_EnableSettingsCard"
IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/LightSwitch.png}"
<ToggleSwitch AutomationProperties.AutomationId="Toggle_LightSwitch" IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" /> IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
</tkcontrols:SettingsCard> <ToggleSwitch AutomationProperties.AutomationId="Toggle_LightSwitch" IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" />
</controls:GPOInfoControl> </tkcontrols:SettingsCard>
</controls:GPOInfoControl>
<controls:SettingsGroup x:Uid="LightSwitch_ShortcutsSettingsGroup" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}"> <controls:SettingsGroup x:Uid="LightSwitch_ShortcutsSettingsGroup" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<tkcontrols:SettingsCard x:Uid="LightSwitch_ThemeToggle_Shortcut" HeaderIcon="{ui:FontIcon Glyph=&#xE708;}"> <tkcontrols:SettingsCard x:Uid="LightSwitch_ThemeToggle_Shortcut" HeaderIcon="{ui:FontIcon Glyph=&#xE708;}">
<controls:ShortcutControl <controls:ShortcutControl
MinWidth="{StaticResource SettingActionControlMinWidth}" MinWidth="{StaticResource SettingActionControlMinWidth}"
AllowDisable="True" AllowDisable="True"
AutomationProperties.AutomationId="Shortcut_LightSwitch" AutomationProperties.AutomationId="Shortcut_LightSwitch"
HotkeySettings="{x:Bind Path=ViewModel.ToggleThemeActivationShortcut, Mode=TwoWay}" /> HotkeySettings="{x:Bind Path=ViewModel.ToggleThemeActivationShortcut, Mode=TwoWay}" />
</tkcontrols:SettingsCard> </tkcontrols:SettingsCard>
</controls:SettingsGroup> </controls:SettingsGroup>
<controls:SettingsGroup x:Uid="LightSwitch_ScheduleSettingsGroup" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}"> <controls:SettingsGroup x:Uid="LightSwitch_ScheduleSettingsGroup" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<tkcontrols:SettingsExpander
x:Uid="LightSwitch_ModeSettingsExpander"
HeaderIcon="{ui:FontIcon Glyph=&#xE823;}"
IsExpanded="True">
<ComboBox
x:Name="ModeSelector"
AutomationProperties.AutomationId="ModeSelection_LightSwitch"
SelectedValue="{x:Bind ViewModel.ScheduleMode, Mode=TwoWay}"
SelectedValuePath="Tag"
SelectionChanged="ModeSelector_SelectionChanged">
<ComboBoxItem
x:Uid="LightSwitch_ModeManual"
AutomationProperties.AutomationId="ManualCBItem_LightSwitch"
Tag="FixedHours" />
<ComboBoxItem
x:Uid="LightSwitch_ModeSunsetToSunrise"
AutomationProperties.AutomationId="SunCBItem_LightSwitch"
Tag="SunsetToSunrise" />
</ComboBox>
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard x:Uid="LightSwitch_TurnOnDarkMode" Visibility="{x:Bind ViewModel.ScheduleMode, Mode=OneWay, Converter={StaticResource EnumToVisibilityConverter}, ConverterParameter=FixedHours}">
<TimePicker AutomationProperties.AutomationId="DarkTimePicker" Time="{x:Bind ViewModel.DarkTimePickerValue, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard x:Uid="LightSwitch_TurnOffDarkMode" Visibility="{x:Bind ViewModel.ScheduleMode, Mode=OneWay, Converter={StaticResource EnumToVisibilityConverter}, ConverterParameter=FixedHours}">
<TimePicker AutomationProperties.AutomationId="LightTimePicker" Time="{x:Bind ViewModel.LightTimePickerValue, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard x:Uid="LightSwitch_LocationSettingsCard" Visibility="{x:Bind ViewModel.ScheduleMode, Mode=OneWay, Converter={StaticResource EnumToVisibilityConverter}, ConverterParameter=SunsetToSunrise}">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.SyncButtonInformation, Mode=OneWay}" />
<Button
Padding="8"
AutomationProperties.AutomationId="SetLocationButton_LightSwitch"
Click="SyncLocationButton_Click"
Content="{ui:FontIcon Glyph=&#xECAF;,
FontSize=16}" />
</StackPanel>
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard x:Uid="LightSwitch_OffsetSettingsCard" Visibility="{x:Bind ViewModel.ScheduleMode, Mode=OneWay, Converter={StaticResource EnumToVisibilityConverter}, ConverterParameter=SunsetToSunrise}">
<StackPanel Orientation="Horizontal" Spacing="20">
<StackPanel Orientation="Horizontal" Spacing="8">
<!--<FontIcon Glyph="&#xED39;" FontSize="16" />-->
<controls:IsEnabledTextBlock x:Uid="LightSwitch_SunriseText" VerticalAlignment="Center" />
<NumberBox
AutomationProperties.AutomationId="SunriseOffset_LightSwitch"
Maximum="60"
Minimum="-60"
SpinButtonPlacementMode="Compact"
Value="{x:Bind ViewModel.SunriseOffset, Mode=TwoWay}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8">
<controls:IsEnabledTextBlock x:Uid="LightSwitch_SunsetText" VerticalAlignment="Center" />
<NumberBox
AutomationProperties.AutomationId="SunsetOffset_LightSwitch"
Maximum="60"
Minimum="-60"
SpinButtonPlacementMode="Compact"
Value="{x:Bind ViewModel.SunsetOffset, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Name="TimelineCard"
HorizontalContentAlignment="Stretch"
ContentAlignment="Vertical">
<controls:Timeline
Margin="0,24,0,24"
AutomationProperties.AutomationId="Timeline_LightSwitch"
EndTime="{x:Bind ViewModel.DarkTimeTimeSpan, Mode=OneWay}"
StartTime="{x:Bind ViewModel.LightTimeTimeSpan, Mode=OneWay}"
Sunrise="{x:Bind ViewModel.SunriseTimeSpan, Mode=OneWay}"
Sunset="{x:Bind ViewModel.SunsetTimeSpan, Mode=OneWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<InfoBar
x:Name="LocationWarningBar"
x:Uid="LightSwitch_LocationWarningBar"
IsOpen="True"
Severity="Informational"
Visibility="Collapsed" />
<controls:SettingsGroup x:Uid="LightSwitch_BehaviorSettingsGroup" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<tkcontrols:SettingsExpander <tkcontrols:SettingsExpander
x:Uid="LightSwitch_ApplyDarkModeExpander" x:Uid="LightSwitch_ModeSettingsExpander"
HeaderIcon="{ui:FontIcon Glyph=&#xE790;}" HeaderIcon="{ui:FontIcon Glyph=&#xE823;}"
IsExpanded="True"> IsExpanded="True">
<ComboBox
x:Name="ModeSelector"
AutomationProperties.AutomationId="ModeSelection_LightSwitch"
SelectedValue="{x:Bind ViewModel.ScheduleMode, Mode=TwoWay}"
SelectedValuePath="Tag"
SelectionChanged="ModeSelector_SelectionChanged">
<ComboBoxItem
x:Uid="LightSwitch_ModeOff"
AutomationProperties.AutomationId="OffCBItem_LightSwitch"
Tag="Off" />
<ComboBoxItem
x:Uid="LightSwitch_ModeManual"
AutomationProperties.AutomationId="ManualCBItem_LightSwitch"
Tag="FixedHours" />
<ComboBoxItem
x:Uid="LightSwitch_ModeSunsetToSunrise"
AutomationProperties.AutomationId="SunCBItem_LightSwitch"
Tag="SunsetToSunrise" />
</ComboBox>
<tkcontrols:SettingsExpander.Items> <tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard HorizontalContentAlignment="Stretch" ContentAlignment="Left"> <tkcontrols:SettingsCard
<controls:CheckBoxWithDescriptionControl x:Name="Fixed_TurnOnCard"
x:Uid="LightSwitch_SystemCheckbox" x:Uid="LightSwitch_TurnOnDarkMode"
AutomationProperties.AutomationId="ChangeSystemCheckbox_LightSwitch" Visibility="Collapsed">
IsChecked="{x:Bind ViewModel.ChangeSystem, Mode=TwoWay}" /> <TimePicker AutomationProperties.AutomationId="DarkTimePicker" Time="{x:Bind ViewModel.DarkTimePickerValue, Mode=TwoWay}" />
</tkcontrols:SettingsCard> </tkcontrols:SettingsCard>
<tkcontrols:SettingsCard HorizontalContentAlignment="Stretch" ContentAlignment="Left"> <tkcontrols:SettingsCard
<controls:CheckBoxWithDescriptionControl x:Name="Fixed_TurnOffCard"
x:Uid="LightSwitch_AppsCheckbox" x:Uid="LightSwitch_TurnOffDarkMode"
AutomationProperties.AutomationId="ChangeAppsCheckbox_LightSwitch" Visibility="Collapsed">
IsChecked="{x:Bind ViewModel.ChangeApps, Mode=TwoWay}" /> <TimePicker AutomationProperties.AutomationId="LightTimePicker" Time="{x:Bind ViewModel.LightTimePickerValue, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Name="SunLocation_Card"
x:Uid="LightSwitch_LocationSettingsCard"
Visibility="Collapsed">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.SyncButtonInformation, Mode=OneWay}" />
<Button
Padding="8"
AutomationProperties.AutomationId="SetLocationButton_LightSwitch"
Click="SyncLocationButton_Click"
Content="{ui:FontIcon Glyph=&#xECAF;,
FontSize=16}" />
</StackPanel>
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Name="SunOffset_Card"
x:Uid="LightSwitch_OffsetSettingsCard"
Visibility="Collapsed">
<StackPanel Orientation="Horizontal" Spacing="20">
<StackPanel Orientation="Horizontal" Spacing="8">
<!--<FontIcon Glyph="&#xED39;" FontSize="16" />-->
<controls:IsEnabledTextBlock x:Uid="LightSwitch_SunriseText" VerticalAlignment="Center" />
<NumberBox
AutomationProperties.AutomationId="SunriseOffset_LightSwitch"
Maximum="60"
Minimum="-60"
SpinButtonPlacementMode="Compact"
Value="{x:Bind ViewModel.SunriseOffset, Mode=TwoWay}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8">
<controls:IsEnabledTextBlock x:Uid="LightSwitch_SunsetText" VerticalAlignment="Center" />
<NumberBox
AutomationProperties.AutomationId="SunsetOffset_LightSwitch"
Maximum="60"
Minimum="-60"
SpinButtonPlacementMode="Compact"
Value="{x:Bind ViewModel.SunsetOffset, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Name="TimelineCard"
HorizontalContentAlignment="Stretch"
ContentAlignment="Vertical"
Visibility="Collapsed">
<controls:Timeline
Margin="0,24,0,24"
AutomationProperties.AutomationId="Timeline_LightSwitch"
EndTime="{x:Bind ViewModel.DarkTimeTimeSpan, Mode=OneWay}"
StartTime="{x:Bind ViewModel.LightTimeTimeSpan, Mode=OneWay}"
Sunrise="{x:Bind ViewModel.SunriseTimeSpan, Mode=OneWay}"
Sunset="{x:Bind ViewModel.SunsetTimeSpan, Mode=OneWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Name="NoScheduleCard"
Padding="0"
HorizontalContentAlignment="Stretch"
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
ContentAlignment="Vertical"
Visibility="Visible">
<InfoBar
x:Uid="LightSwitch_ScheduleOffMessage"
Background="Transparent"
BorderThickness="0"
IsClosable="False"
IsOpen="True"
Severity="Informational" />
</tkcontrols:SettingsCard> </tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items> </tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander> </tkcontrols:SettingsExpander>
</controls:SettingsGroup> <InfoBar
<!-- Force mode buttons --> x:Name="LocationWarningBar"
<!--<tkcontrols:SettingsCard x:Uid="LightSwitch_LocationWarningBar"
IsOpen="True"
Severity="Informational"
Visibility="Collapsed" />
<controls:SettingsGroup x:Uid="LightSwitch_BehaviorSettingsGroup" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<tkcontrols:SettingsExpander
x:Uid="LightSwitch_ApplyDarkModeExpander"
HeaderIcon="{ui:FontIcon Glyph=&#xE790;}"
IsExpanded="True">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard HorizontalContentAlignment="Stretch" ContentAlignment="Left">
<controls:CheckBoxWithDescriptionControl
x:Uid="LightSwitch_SystemCheckbox"
AutomationProperties.AutomationId="ChangeSystemCheckbox_LightSwitch"
IsChecked="{x:Bind ViewModel.ChangeSystem, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard HorizontalContentAlignment="Stretch" ContentAlignment="Left">
<controls:CheckBoxWithDescriptionControl
x:Uid="LightSwitch_AppsCheckbox"
AutomationProperties.AutomationId="ChangeAppsCheckbox_LightSwitch"
IsChecked="{x:Bind ViewModel.ChangeApps, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>
<!-- Force mode buttons -->
<!--<tkcontrols:SettingsCard
Header="Force mode now" Header="Force mode now"
HeaderIcon="{ui:FontIcon Glyph=&#xE706;}" HeaderIcon="{ui:FontIcon Glyph=&#xE706;}"
Description="Apply light or dark mode immediately"> Description="Apply light or dark mode immediately">
@@ -167,21 +199,21 @@
</StackPanel> </StackPanel>
</tkcontrols:SettingsCard>--> </tkcontrols:SettingsCard>-->
<ContentDialog <ContentDialog
x:Name="LocationDialog" x:Name="LocationDialog"
x:Uid="LightSwitch_LocationDialog" x:Uid="LightSwitch_LocationDialog"
IsPrimaryButtonEnabled="True" IsPrimaryButtonEnabled="True"
IsSecondaryButtonEnabled="True" IsSecondaryButtonEnabled="True"
Opened="LocationDialog_Opened" Opened="LocationDialog_Opened"
PrimaryButtonClick="LocationDialog_PrimaryButtonClick" PrimaryButtonClick="LocationDialog_PrimaryButtonClick"
PrimaryButtonStyle="{StaticResource AccentButtonStyle}"> PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
<Grid RowSpacing="48"> <Grid RowSpacing="48">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock x:Uid="LightSwitch_LocationDialog_Description" Foreground="{ThemeResource TextFillColorSecondaryBrush}" /> <TextBlock x:Uid="LightSwitch_LocationDialog_Description" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<!--<AutoSuggestBox <!--<AutoSuggestBox
x:Name="CityAutoSuggestBox" x:Name="CityAutoSuggestBox"
Grid.Row="1" Grid.Row="1"
Margin="0,16,0,8" Margin="0,16,0,8"
@@ -208,113 +240,122 @@
</DataTemplate> </DataTemplate>
</AutoSuggestBox.ItemTemplate> </AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>--> </AutoSuggestBox>-->
<StackPanel <StackPanel
Grid.Row="2"
Margin="0,24,0,0"
HorizontalAlignment="Center"
Orientation="Vertical"
Spacing="32">
<Button
x:Name="SyncButton"
HorizontalAlignment="Stretch"
AutomationProperties.AutomationId="SyncLocationButton_LightSwitch"
Style="{StaticResource AccentButtonStyle}"
Visibility="Collapsed">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="14" Glyph="&#xECAF;" />
<TextBlock x:Uid="LightSwitch_GetCurrentLocation" />
</StackPanel>
</Button>
</StackPanel>
<ProgressRing
x:Name="SyncLoader"
Grid.Row="1"
Width="40"
Height="40"
VerticalAlignment="Center"
IsActive="False"
Visibility="Collapsed" />
<Grid
x:Name="LocationResultPanel"
Grid.Row="1"
VerticalAlignment="Bottom"
ColumnSpacing="16"
RowSpacing="12"
Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<FontIcon FontSize="16" Glyph="&#xECAF;">
<ToolTipService.ToolTip>
<TextBlock x:Uid="LightSwitch_LocationTooltip" />
</ToolTipService.ToolTip>
</FontIcon>
<TextBlock
Grid.Row="1"
AutomationProperties.AutomationId="LocationResultText_LightSwitch"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextAlignment="Center">
<Run Text="{x:Bind ViewModel.Latitude, Mode=OneWay}" /><Run Text="°, " />
<Run Text="{x:Bind ViewModel.Longitude, Mode=OneWay}" /><Run Text="°" />
</TextBlock>
<FontIcon
Grid.Column="1"
FontSize="20"
Glyph="&#xED39;">
<ToolTipService.ToolTip>
<TextBlock x:Uid="LightSwitch_SunriseTooltip" />
</ToolTipService.ToolTip>
</FontIcon>
<TextBlock
Grid.Row="1"
Grid.Column="1"
AutomationProperties.AutomationId="SunriseText_LightSwitch"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.LightTimeTimeSpan, Converter={StaticResource TimeSpanToFriendlyTimeConverter}, Mode=OneWay}"
TextAlignment="Center" />
<FontIcon
Grid.Column="2"
FontSize="20"
Glyph="&#xED3A;">
<ToolTipService.ToolTip>
<TextBlock x:Uid="LightSwitch_SunsetTooltip" />
</ToolTipService.ToolTip>
</FontIcon>
<TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="2" Margin="0,24,0,0"
AutomationProperties.AutomationId="SunsetText_LightSwitch" HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" Orientation="Vertical"
Text="{x:Bind ViewModel.DarkTimeTimeSpan, Converter={StaticResource TimeSpanToFriendlyTimeConverter}, Mode=OneWay}" Spacing="32">
TextAlignment="Center" /> <Button
</Grid> x:Name="SyncButton"
</Grid> HorizontalAlignment="Stretch"
</ContentDialog> AutomationProperties.AutomationId="SyncLocationButton_LightSwitch"
</controls:SettingsGroup> Style="{StaticResource AccentButtonStyle}"
Visibility="Collapsed">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="14" Glyph="&#xECAF;" />
<TextBlock x:Uid="LightSwitch_GetCurrentLocation" />
</StackPanel>
</Button>
</StackPanel>
</StackPanel> <ProgressRing
</controls:SettingsPageControl.ModuleContent> x:Name="SyncLoader"
<controls:SettingsPageControl.PrimaryLinks> Grid.Row="1"
<controls:PageLink x:Uid="LearnMore_LightSwitch" Link="https://aka.ms/PowerToysOverview_LightSwitch" /> Width="40"
</controls:SettingsPageControl.PrimaryLinks> Height="40"
</controls:SettingsPageControl> VerticalAlignment="Center"
<VisualStateManager.VisualStateGroups> IsActive="False"
<VisualStateGroup x:Name="LocationEnabledStates"> Visibility="Collapsed" />
<VisualState x:Name="LocationSet" />
<VisualState x:Name="LocationNotSet"> <Grid
<VisualState.Setters> x:Name="LocationResultPanel"
<Setter Target="TimelineCard.Visibility" Value="Collapsed" /> Grid.Row="1"
<Setter Target="LocationWarningBar.Visibility" Value="Visible" /> VerticalAlignment="Bottom"
</VisualState.Setters> ColumnSpacing="16"
</VisualState> RowSpacing="12"
</VisualStateGroup> Visibility="Collapsed">
</VisualStateManager.VisualStateGroups> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<FontIcon FontSize="16" Glyph="&#xECAF;">
<ToolTipService.ToolTip>
<TextBlock x:Uid="LightSwitch_LocationTooltip" />
</ToolTipService.ToolTip>
</FontIcon>
<TextBlock
Grid.Row="1"
AutomationProperties.AutomationId="LocationResultText_LightSwitch"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextAlignment="Center">
<Run Text="{x:Bind ViewModel.Latitude, Mode=OneWay}" /><Run Text="°, " />
<Run Text="{x:Bind ViewModel.Longitude, Mode=OneWay}" /><Run Text="°" />
</TextBlock>
<FontIcon
Grid.Column="1"
FontSize="20"
Glyph="&#xED39;">
<ToolTipService.ToolTip>
<TextBlock x:Uid="LightSwitch_SunriseTooltip" />
</ToolTipService.ToolTip>
</FontIcon>
<TextBlock
Grid.Row="1"
Grid.Column="1"
AutomationProperties.AutomationId="SunriseText_LightSwitch"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.LightTimeTimeSpan, Converter={StaticResource TimeSpanToFriendlyTimeConverter}, Mode=OneWay}"
TextAlignment="Center" />
<FontIcon
Grid.Column="2"
FontSize="20"
Glyph="&#xED3A;">
<ToolTipService.ToolTip>
<TextBlock x:Uid="LightSwitch_SunsetTooltip" />
</ToolTipService.ToolTip>
</FontIcon>
<TextBlock
Grid.Row="2"
Grid.Column="2"
AutomationProperties.AutomationId="SunsetText_LightSwitch"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.DarkTimeTimeSpan, Converter={StaticResource TimeSpanToFriendlyTimeConverter}, Mode=OneWay}"
TextAlignment="Center" />
</Grid>
</Grid>
</ContentDialog>
</controls:SettingsGroup>
</StackPanel>
</controls:SettingsPageControl.ModuleContent>
<controls:SettingsPageControl.PrimaryLinks>
<controls:PageLink x:Uid="LearnMore_LightSwitch" Link="https://aka.ms/PowerToysOverview_LightSwitch" />
</controls:SettingsPageControl.PrimaryLinks>
</controls:SettingsPageControl>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ScheduleModeStates">
<VisualState x:Name="OffState" />
<VisualState x:Name="SunsetToSunriseState">
<VisualState.Setters>
<Setter Target="SunLocation_Card.Visibility" Value="Visible" />
<Setter Target="SunOffset_Card.Visibility" Value="Visible" />
<Setter Target="NoScheduleCard.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="ManualState">
<VisualState.Setters>
<Setter Target="Fixed_TurnOnCard.Visibility" Value="Visible" />
<Setter Target="Fixed_TurnOffCard.Visibility" Value="Visible" />
<Setter Target="NoScheduleCard.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page> </Page>

View File

@@ -300,20 +300,20 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private void ModeSelector_SelectionChanged(object sender, SelectionChangedEventArgs e) private void ModeSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
SunriseModeChartState(); switch (ViewModel.ScheduleMode)
}
private void SunriseModeChartState()
{
if (ViewModel.Latitude == "0.0" && ViewModel.Longitude == "0.0" && ViewModel.ScheduleMode == "SunsetToSunrise")
{ {
TimelineCard.Visibility = Visibility.Collapsed; case "FixedHours":
LocationWarningBar.Visibility = Visibility.Visible; VisualStateManager.GoToState(this, "ManualState", true);
} TimelineCard.Visibility = Visibility.Visible;
else break;
{ case "SunsetToSunrise":
TimelineCard.Visibility = Visibility.Visible; VisualStateManager.GoToState(this, "SunsetToSunriseState", true);
LocationWarningBar.Visibility = Visibility.Collapsed; 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(); 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;
}
}
} }
} }

View File

@@ -5269,12 +5269,18 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="LightSwitch_ModeSettingsExpander.Description" xml:space="preserve"> <data name="LightSwitch_ModeSettingsExpander.Description" xml:space="preserve">
<value>Determine when dark mode should be turned on</value> <value>Determine when dark mode should be turned on</value>
</data> </data>
<data name="LightSwitch_ModeOff.Content" xml:space="preserve">
<value>Off</value>
</data>
<data name="LightSwitch_ModeManual.Content" xml:space="preserve"> <data name="LightSwitch_ModeManual.Content" xml:space="preserve">
<value>Manual</value> <value>Fixed hours</value>
</data> </data>
<data name="LightSwitch_ModeSunsetToSunrise.Content" xml:space="preserve"> <data name="LightSwitch_ModeSunsetToSunrise.Content" xml:space="preserve">
<value>Sunset to sunrise</value> <value>Sunset to sunrise</value>
</data> </data>
<data name="LightSwitch_ScheduleOffMessage.Title" xml:space="preserve">
<value>Scheduling is turned off.</value>
</data>
<data name="LightSwitch_TurnOnDarkMode.Header" xml:space="preserve"> <data name="LightSwitch_TurnOnDarkMode.Header" xml:space="preserve">
<value>Turn on dark mode</value> <value>Turn on dark mode</value>
</data> </data>

View File

@@ -39,6 +39,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
AvailableScheduleModes = new ObservableCollection<string> AvailableScheduleModes = new ObservableCollection<string>
{ {
"Off",
"FixedHours", "FixedHours",
"SunsetToSunrise", "SunsetToSunrise",
}; };