mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-22 22:50:03 +01:00
<!-- 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 This pull request refactors how the `ToggleThemeHotkey` property is handled in the LightSwitch settings and view model. The changes simplify property management by directly referencing the property within `LightSwitchProperties` and ensure that the hotkey setting is consistently updated and serialized. **Settings property management:** * In `LightSwitchSettings.Clone()`, the `LightSwitchProperties` object now directly references the existing property instances instead of creating new ones, and also includes the `ToggleThemeHotkey` property. **View model property handling:** * In `LightSwitchViewModel.ToggleThemeActivationShortcut`, the getter and setter now directly access and update `ModuleSettings.Properties.ToggleThemeHotkey.Value`, removing the need for a backing field and ensuring changes are properly serialized and notified. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] Closes: #42330 - [ ] **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 - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **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 <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed --------- Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
523 lines
17 KiB
C#
523 lines
17 KiB
C#
// 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.Collections.ObjectModel;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text.Json;
|
|
using System.Windows.Input;
|
|
using ManagedCommon;
|
|
using Microsoft.PowerToys.Settings.UI.Helpers;
|
|
using Microsoft.PowerToys.Settings.UI.Library;
|
|
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
|
using Microsoft.PowerToys.Settings.UI.SerializationContext;
|
|
using Newtonsoft.Json.Linq;
|
|
using Settings.UI.Library;
|
|
using Settings.UI.Library.Helpers;
|
|
|
|
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|
{
|
|
public partial class LightSwitchViewModel : PageViewModelBase
|
|
{
|
|
protected override string ModuleName => LightSwitchSettings.ModuleName;
|
|
|
|
private Func<string, int> SendConfigMSG { get; }
|
|
|
|
public ObservableCollection<SearchLocation> SearchLocations { get; } = new();
|
|
|
|
public LightSwitchViewModel(LightSwitchSettings initialSettings = null, Func<string, int> ipcMSGCallBackFunc = null)
|
|
{
|
|
_moduleSettings = initialSettings ?? new LightSwitchSettings();
|
|
SendConfigMSG = ipcMSGCallBackFunc;
|
|
|
|
ForceLightCommand = new RelayCommand(ForceLightNow);
|
|
ForceDarkCommand = new RelayCommand(ForceDarkNow);
|
|
|
|
AvailableScheduleModes = new ObservableCollection<string>
|
|
{
|
|
"FixedHours",
|
|
"SunsetToSunrise",
|
|
};
|
|
|
|
_toggleThemeHotkey = _moduleSettings.Properties.ToggleThemeHotkey.Value;
|
|
}
|
|
|
|
public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()
|
|
{
|
|
var hotkeysDict = new Dictionary<string, HotkeySettings[]>
|
|
{
|
|
[ModuleName] = [ToggleThemeActivationShortcut],
|
|
};
|
|
|
|
return hotkeysDict;
|
|
}
|
|
|
|
private void ForceLightNow()
|
|
{
|
|
Logger.LogInfo("Sending custom action: forceLight");
|
|
SendCustomAction("forceLight");
|
|
}
|
|
|
|
private void ForceDarkNow()
|
|
{
|
|
Logger.LogInfo("Sending custom action: forceDark");
|
|
SendCustomAction("forceDark");
|
|
}
|
|
|
|
private void SendCustomAction(string actionName)
|
|
{
|
|
SendConfigMSG("{\"action\":{\"LightSwitch\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}");
|
|
}
|
|
|
|
public LightSwitchSettings ModuleSettings
|
|
{
|
|
get => _moduleSettings;
|
|
set
|
|
{
|
|
if (_moduleSettings != value)
|
|
{
|
|
_moduleSettings = value;
|
|
|
|
OnPropertyChanged(nameof(ModuleSettings));
|
|
RefreshModuleSettings();
|
|
RefreshEnabledState();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsEnabled
|
|
{
|
|
get
|
|
{
|
|
if (_enabledStateIsGPOConfigured)
|
|
{
|
|
return _enabledGPOConfiguration;
|
|
}
|
|
else
|
|
{
|
|
return _isEnabled;
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
if (_isEnabled != value)
|
|
{
|
|
if (_enabledStateIsGPOConfigured)
|
|
{
|
|
// If it's GPO configured, shouldn't be able to change this state.
|
|
return;
|
|
}
|
|
|
|
_isEnabled = value;
|
|
|
|
RefreshEnabledState();
|
|
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsEnabledGpoConfigured
|
|
{
|
|
get => _enabledStateIsGPOConfigured;
|
|
set
|
|
{
|
|
if (_enabledStateIsGPOConfigured != value)
|
|
{
|
|
_enabledStateIsGPOConfigured = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool EnabledGPOConfiguration
|
|
{
|
|
get => _enabledGPOConfiguration;
|
|
set
|
|
{
|
|
if (_enabledGPOConfiguration != value)
|
|
{
|
|
_enabledGPOConfiguration = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public string ScheduleMode
|
|
{
|
|
get => ModuleSettings.Properties.ScheduleMode.Value;
|
|
set
|
|
{
|
|
var oldMode = ModuleSettings.Properties.ScheduleMode.Value;
|
|
if (ModuleSettings.Properties.ScheduleMode.Value != value)
|
|
{
|
|
ModuleSettings.Properties.ScheduleMode.Value = value;
|
|
OnPropertyChanged(nameof(ScheduleMode));
|
|
}
|
|
|
|
if (ModuleSettings.Properties.ScheduleMode.Value == "FixedHours" && oldMode != "FixedHours")
|
|
{
|
|
LightTime = 360;
|
|
DarkTime = 1080;
|
|
SunsetTimeSpan = null;
|
|
SunriseTimeSpan = null;
|
|
|
|
OnPropertyChanged(nameof(LightTimePickerValue));
|
|
OnPropertyChanged(nameof(DarkTimePickerValue));
|
|
}
|
|
|
|
if (ModuleSettings.Properties.ScheduleMode.Value == "SunsetToSunrise")
|
|
{
|
|
if (ModuleSettings.Properties.Latitude != "0.0" && ModuleSettings.Properties.Longitude != "0.0")
|
|
{
|
|
double lat = double.Parse(ModuleSettings.Properties.Latitude.Value, CultureInfo.InvariantCulture);
|
|
double lon = double.Parse(ModuleSettings.Properties.Longitude.Value, CultureInfo.InvariantCulture);
|
|
UpdateSunTimes(lat, lon);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public ObservableCollection<string> AvailableScheduleModes { get; }
|
|
|
|
public bool ChangeSystem
|
|
{
|
|
get => ModuleSettings.Properties.ChangeSystem.Value;
|
|
set
|
|
{
|
|
if (ModuleSettings.Properties.ChangeSystem.Value != value)
|
|
{
|
|
ModuleSettings.Properties.ChangeSystem.Value = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool ChangeApps
|
|
{
|
|
get => ModuleSettings.Properties.ChangeApps.Value;
|
|
set
|
|
{
|
|
if (ModuleSettings.Properties.ChangeApps.Value != value)
|
|
{
|
|
ModuleSettings.Properties.ChangeApps.Value = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int LightTime
|
|
{
|
|
get => ModuleSettings.Properties.LightTime.Value;
|
|
set
|
|
{
|
|
if (ModuleSettings.Properties.LightTime.Value != value)
|
|
{
|
|
ModuleSettings.Properties.LightTime.Value = value;
|
|
NotifyPropertyChanged();
|
|
|
|
OnPropertyChanged(nameof(LightTimeTimeSpan));
|
|
|
|
if (ScheduleMode == "SunsetToSunrise")
|
|
{
|
|
SunriseTimeSpan = TimeSpan.FromMinutes(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public int DarkTime
|
|
{
|
|
get => ModuleSettings.Properties.DarkTime.Value;
|
|
set
|
|
{
|
|
if (ModuleSettings.Properties.DarkTime.Value != value)
|
|
{
|
|
ModuleSettings.Properties.DarkTime.Value = value;
|
|
NotifyPropertyChanged();
|
|
|
|
OnPropertyChanged(nameof(DarkTimeTimeSpan));
|
|
|
|
if (ScheduleMode == "SunsetToSunrise")
|
|
{
|
|
SunsetTimeSpan = TimeSpan.FromMinutes(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public int SunriseOffset
|
|
{
|
|
get => ModuleSettings.Properties.SunriseOffset.Value;
|
|
set
|
|
{
|
|
if (ModuleSettings.Properties.SunriseOffset.Value != value)
|
|
{
|
|
ModuleSettings.Properties.SunriseOffset.Value = value;
|
|
OnPropertyChanged(nameof(LightTimeTimeSpan));
|
|
}
|
|
}
|
|
}
|
|
|
|
public int SunsetOffset
|
|
{
|
|
get => ModuleSettings.Properties.SunsetOffset.Value;
|
|
set
|
|
{
|
|
if (ModuleSettings.Properties.SunsetOffset.Value != value)
|
|
{
|
|
ModuleSettings.Properties.SunsetOffset.Value = value;
|
|
OnPropertyChanged(nameof(DarkTimeTimeSpan));
|
|
}
|
|
}
|
|
}
|
|
|
|
// === Computed projections (OneWay bindings only) ===
|
|
public TimeSpan LightTimeTimeSpan
|
|
{
|
|
get
|
|
{
|
|
if (ScheduleMode == "SunsetToSunrise")
|
|
{
|
|
return TimeSpan.FromMinutes(LightTime + SunriseOffset);
|
|
}
|
|
else
|
|
{
|
|
return TimeSpan.FromMinutes(LightTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
public TimeSpan DarkTimeTimeSpan
|
|
{
|
|
get
|
|
{
|
|
if (ScheduleMode == "SunsetToSunrise")
|
|
{
|
|
return TimeSpan.FromMinutes(DarkTime + SunsetOffset);
|
|
}
|
|
else
|
|
{
|
|
return TimeSpan.FromMinutes(DarkTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
// === Values to pass to timeline ===
|
|
public TimeSpan? SunriseTimeSpan
|
|
{
|
|
get => _sunriseTimeSpan;
|
|
set
|
|
{
|
|
if (_sunriseTimeSpan != value)
|
|
{
|
|
_sunriseTimeSpan = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public TimeSpan? SunsetTimeSpan
|
|
{
|
|
get => _sunsetTimeSpan;
|
|
set
|
|
{
|
|
if (_sunsetTimeSpan != value)
|
|
{
|
|
_sunsetTimeSpan = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
// === Picker values (TwoWay binding targets for TimePickers) ===
|
|
public TimeSpan LightTimePickerValue
|
|
{
|
|
get => TimeSpan.FromMinutes(LightTime);
|
|
set => LightTime = (int)value.TotalMinutes;
|
|
}
|
|
|
|
public TimeSpan DarkTimePickerValue
|
|
{
|
|
get => TimeSpan.FromMinutes(DarkTime);
|
|
set => DarkTime = (int)value.TotalMinutes;
|
|
}
|
|
|
|
public string Latitude
|
|
{
|
|
get => ModuleSettings.Properties.Latitude.Value;
|
|
set
|
|
{
|
|
if (ModuleSettings.Properties.Latitude.Value != value)
|
|
{
|
|
ModuleSettings.Properties.Latitude.Value = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public string Longitude
|
|
{
|
|
get => ModuleSettings.Properties.Longitude.Value;
|
|
set
|
|
{
|
|
if (ModuleSettings.Properties.Longitude.Value != value)
|
|
{
|
|
ModuleSettings.Properties.Longitude.Value = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private SearchLocation _selectedSearchLocation;
|
|
|
|
public SearchLocation SelectedCity
|
|
{
|
|
get => _selectedSearchLocation;
|
|
set
|
|
{
|
|
if (_selectedSearchLocation != value)
|
|
{
|
|
_selectedSearchLocation = value;
|
|
NotifyPropertyChanged();
|
|
|
|
UpdateSunTimes(_selectedSearchLocation.Latitude, _selectedSearchLocation.Longitude, _selectedSearchLocation.City);
|
|
}
|
|
}
|
|
}
|
|
|
|
private string _syncButtonInformation = "Please sync your location";
|
|
|
|
public string SyncButtonInformation
|
|
{
|
|
get => _syncButtonInformation;
|
|
set
|
|
{
|
|
if (_syncButtonInformation != value)
|
|
{
|
|
_syncButtonInformation = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public HotkeySettings ToggleThemeActivationShortcut
|
|
{
|
|
get => ModuleSettings.Properties.ToggleThemeHotkey.Value;
|
|
|
|
set
|
|
{
|
|
if (value != ModuleSettings.Properties.ToggleThemeHotkey.Value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
ModuleSettings.Properties.ToggleThemeHotkey.Value = LightSwitchProperties.DefaultToggleThemeHotkey;
|
|
}
|
|
else
|
|
{
|
|
ModuleSettings.Properties.ToggleThemeHotkey.Value = value;
|
|
}
|
|
|
|
NotifyPropertyChanged();
|
|
|
|
SendConfigMSG(
|
|
string.Format(
|
|
CultureInfo.InvariantCulture,
|
|
"{{ \"powertoys\": {{ \"{0}\": {1} }} }}",
|
|
LightSwitchSettings.ModuleName,
|
|
JsonSerializer.Serialize(_moduleSettings, SourceGenerationContextContext.Default.LightSwitchSettings)));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
|
|
{
|
|
Logger.LogInfo($"Changed the property {propertyName}");
|
|
OnPropertyChanged(propertyName);
|
|
}
|
|
|
|
public void RefreshEnabledState()
|
|
{
|
|
OnPropertyChanged(nameof(IsEnabled));
|
|
}
|
|
|
|
public void RefreshModuleSettings()
|
|
{
|
|
OnPropertyChanged(nameof(ChangeSystem));
|
|
OnPropertyChanged(nameof(ChangeApps));
|
|
OnPropertyChanged(nameof(LightTime));
|
|
OnPropertyChanged(nameof(DarkTime));
|
|
OnPropertyChanged(nameof(SunriseOffset));
|
|
OnPropertyChanged(nameof(SunsetOffset));
|
|
OnPropertyChanged(nameof(Latitude));
|
|
OnPropertyChanged(nameof(Longitude));
|
|
OnPropertyChanged(nameof(ScheduleMode));
|
|
}
|
|
|
|
private void UpdateSunTimes(double latitude, double longitude, string city = "n/a")
|
|
{
|
|
SunTimes result = SunCalc.CalculateSunriseSunset(
|
|
latitude,
|
|
longitude,
|
|
DateTime.Now.Year,
|
|
DateTime.Now.Month,
|
|
DateTime.Now.Day);
|
|
|
|
LightTime = (result.SunriseHour * 60) + result.SunriseMinute;
|
|
DarkTime = (result.SunsetHour * 60) + result.SunsetMinute;
|
|
Latitude = latitude.ToString(CultureInfo.InvariantCulture);
|
|
Longitude = longitude.ToString(CultureInfo.InvariantCulture);
|
|
|
|
if (city != "n/a")
|
|
{
|
|
SyncButtonInformation = city;
|
|
}
|
|
}
|
|
|
|
public void InitializeScheduleMode()
|
|
{
|
|
if (ScheduleMode == "SunsetToSunrise" &&
|
|
double.TryParse(Latitude, NumberStyles.Float, CultureInfo.InvariantCulture, out double savedLat) &&
|
|
double.TryParse(Longitude, NumberStyles.Float, CultureInfo.InvariantCulture, out double savedLng))
|
|
{
|
|
var match = SearchLocations.FirstOrDefault(c =>
|
|
Math.Abs(c.Latitude - savedLat) < 0.0001 &&
|
|
Math.Abs(c.Longitude - savedLng) < 0.0001);
|
|
|
|
if (match != null)
|
|
{
|
|
SelectedCity = match;
|
|
}
|
|
|
|
SyncButtonInformation = SelectedCity != null
|
|
? SelectedCity.City
|
|
: $"{Latitude},{Longitude}";
|
|
|
|
double lat = double.Parse(ModuleSettings.Properties.Latitude.Value, CultureInfo.InvariantCulture);
|
|
double lon = double.Parse(ModuleSettings.Properties.Longitude.Value, CultureInfo.InvariantCulture);
|
|
UpdateSunTimes(lat, lon);
|
|
|
|
SunriseTimeSpan = TimeSpan.FromMinutes(LightTime);
|
|
SunsetTimeSpan = TimeSpan.FromMinutes(DarkTime);
|
|
}
|
|
}
|
|
|
|
private bool _enabledStateIsGPOConfigured;
|
|
private bool _enabledGPOConfiguration;
|
|
private LightSwitchSettings _moduleSettings;
|
|
private bool _isEnabled;
|
|
private HotkeySettings _toggleThemeHotkey;
|
|
private TimeSpan? _sunriseTimeSpan;
|
|
private TimeSpan? _sunsetTimeSpan;
|
|
|
|
public ICommand ForceLightCommand { get; }
|
|
|
|
public ICommand ForceDarkCommand { get; }
|
|
}
|
|
}
|