mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
Refactor PowerDisplay profile management
Simplified profile management by removing the concept of "Custom profiles" and "current profile" tracking. Profiles are now treated as templates for quick application of monitor settings, rather than persistent states. Key changes include: - Replaced `ObservableCollection<string>` with `ObservableCollection<PowerDisplayProfile>` to manage profile objects directly. - Removed redundant properties and methods related to "selected" and "current" profiles. - Refactored methods for creating, updating, and deleting profiles to operate on `PowerDisplayProfile` objects. - Updated `PowerDisplayViewModel` and `ProfileManager` to streamline profile loading, saving, and application logic. - Updated the UI to replace the profile dropdown with buttons for quick application, along with context menu options for managing profiles. - Improved logging and error handling for profile operations. - Updated resource strings and removed references to "Custom profiles" and "current profile." These changes simplify the codebase, improve maintainability, and align the application with the new design philosophy of treating profiles as templates.
This commit is contained in:
@@ -475,15 +475,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// Profile-related fields
|
||||
private ObservableCollection<string> _profiles = new ObservableCollection<string>();
|
||||
private string _selectedProfile = PowerDisplayProfiles.CustomProfileName;
|
||||
private string _currentProfile = PowerDisplayProfiles.CustomProfileName;
|
||||
private ObservableCollection<PowerDisplayProfile> _profiles = new ObservableCollection<PowerDisplayProfile>();
|
||||
private string _profilesFilePath = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of available profile names (including Custom)
|
||||
/// Collection of available profiles (for button display)
|
||||
/// </summary>
|
||||
public ObservableCollection<string> Profiles
|
||||
public ObservableCollection<PowerDisplayProfile> Profiles
|
||||
{
|
||||
get => _profiles;
|
||||
set
|
||||
@@ -496,61 +494,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected profile in the ComboBox
|
||||
/// </summary>
|
||||
public string SelectedProfile
|
||||
{
|
||||
get => _selectedProfile;
|
||||
set
|
||||
{
|
||||
if (_selectedProfile != value && !string.IsNullOrEmpty(value))
|
||||
{
|
||||
_selectedProfile = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
// Apply the selected profile
|
||||
ApplyProfile(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Currently active profile (read from settings, may differ from selected during transition)
|
||||
/// </summary>
|
||||
public string CurrentProfile
|
||||
{
|
||||
get => _currentProfile;
|
||||
set
|
||||
{
|
||||
if (_currentProfile != value)
|
||||
{
|
||||
_currentProfile = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(IsCustomProfile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if current profile is Custom
|
||||
/// </summary>
|
||||
public bool IsCustomProfile => _currentProfile?.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase) ?? true;
|
||||
|
||||
/// <summary>
|
||||
/// True if a non-Custom profile is selected (enables delete/rename)
|
||||
/// </summary>
|
||||
public bool CanModifySelectedProfile => !string.IsNullOrEmpty(_selectedProfile) &&
|
||||
!_selectedProfile.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public ButtonClickCommand AddProfileCommand => new ButtonClickCommand(AddProfile);
|
||||
|
||||
public ButtonClickCommand DeleteProfileCommand => new ButtonClickCommand(DeleteProfile);
|
||||
|
||||
public ButtonClickCommand RenameProfileCommand => new ButtonClickCommand(RenameProfile);
|
||||
|
||||
public ButtonClickCommand SaveAsProfileCommand => new ButtonClickCommand(SaveAsProfile);
|
||||
|
||||
public void RefreshEnabledState()
|
||||
{
|
||||
InitializeEnabledValue();
|
||||
@@ -583,24 +526,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
var profilesData = LoadProfilesFromDisk();
|
||||
|
||||
// Build profile names list
|
||||
var profileNames = new List<string> { PowerDisplayProfiles.CustomProfileName };
|
||||
profileNames.AddRange(profilesData.Profiles.Select(p => p.Name));
|
||||
// Load profile objects (no Custom - it's not a profile anymore)
|
||||
Profiles.Clear();
|
||||
foreach (var profile in profilesData.Profiles)
|
||||
{
|
||||
Profiles.Add(profile);
|
||||
}
|
||||
|
||||
Profiles = new ObservableCollection<string>(profileNames);
|
||||
|
||||
// Set current profile from settings
|
||||
CurrentProfile = _settings.Properties.CurrentProfile ?? PowerDisplayProfiles.CustomProfileName;
|
||||
_selectedProfile = CurrentProfile;
|
||||
OnPropertyChanged(nameof(SelectedProfile));
|
||||
|
||||
Logger.LogInfo($"Loaded {profilesData.Profiles.Count} profiles, current: {CurrentProfile}");
|
||||
Logger.LogInfo($"Loaded {Profiles.Count} profiles");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to load profiles: {ex.Message}");
|
||||
Profiles = new ObservableCollection<string> { PowerDisplayProfiles.CustomProfileName };
|
||||
CurrentProfile = PowerDisplayProfiles.CustomProfileName;
|
||||
Profiles.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,40 +576,33 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a profile
|
||||
/// Apply a profile to monitors
|
||||
/// </summary>
|
||||
private void ApplyProfile(string profileName)
|
||||
public void ApplyProfile(PowerDisplayProfile profile)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogInfo($"Applying profile: {profileName}");
|
||||
|
||||
var profilesData = LoadProfilesFromDisk();
|
||||
var profile = profilesData.GetProfile(profileName);
|
||||
|
||||
if (profile == null || !profile.IsValid())
|
||||
{
|
||||
Logger.LogWarning($"Profile '{profileName}' not found or invalid");
|
||||
Logger.LogWarning("Invalid profile");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInfo($"Applying profile: {profile.Name}");
|
||||
|
||||
// Create pending operation
|
||||
var operation = new ProfileOperation(profileName, profile.MonitorSettings);
|
||||
var operation = new ProfileOperation(profile.Name, profile.MonitorSettings);
|
||||
_settings.Properties.PendingProfileOperation = operation;
|
||||
_settings.Properties.CurrentProfile = profileName;
|
||||
|
||||
// Save settings
|
||||
NotifySettingsChanged();
|
||||
|
||||
// Update current profile
|
||||
CurrentProfile = profileName;
|
||||
|
||||
// Send custom action to trigger profile application
|
||||
SendConfigMSG(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{{ \"action\": {{ \"PowerDisplay\": {{ \"action_name\": \"ApplyProfile\", \"value\": \"{0}\" }} }} }}",
|
||||
profileName));
|
||||
profile.Name));
|
||||
|
||||
// Signal PowerDisplay to apply profile
|
||||
using (var eventHandle = new System.Threading.EventWaitHandle(
|
||||
@@ -682,7 +613,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
Logger.LogInfo($"Profile '{profileName}' applied successfully");
|
||||
Logger.LogInfo($"Profile '{profile.Name}' applied successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -691,74 +622,90 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new profile
|
||||
/// Create a new profile
|
||||
/// </summary>
|
||||
private async void AddProfile()
|
||||
public void CreateProfile(PowerDisplayProfile profile)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogInfo("Adding new profile");
|
||||
|
||||
if (Monitors == null || Monitors.Count == 0)
|
||||
if (profile == null || !profile.IsValid())
|
||||
{
|
||||
Logger.LogWarning("No monitors available to create profile");
|
||||
Logger.LogWarning("Invalid profile");
|
||||
return;
|
||||
}
|
||||
|
||||
var profilesData = LoadProfilesFromDisk();
|
||||
var defaultName = profilesData.GenerateProfileName();
|
||||
|
||||
// Show profile editor dialog
|
||||
var dialog = new Views.ProfileEditorDialog(Monitors, defaultName);
|
||||
var result = await dialog.ShowAsync();
|
||||
|
||||
if (result == Microsoft.UI.Xaml.Controls.ContentDialogResult.Primary && dialog.ResultProfile != null)
|
||||
{
|
||||
var newProfile = dialog.ResultProfile;
|
||||
|
||||
// Validate profile name
|
||||
if (string.IsNullOrWhiteSpace(newProfile.Name))
|
||||
{
|
||||
newProfile = new PowerDisplayProfile(defaultName, newProfile.MonitorSettings);
|
||||
}
|
||||
|
||||
profilesData.SetProfile(newProfile);
|
||||
SaveProfilesToDisk(profilesData);
|
||||
|
||||
// Reload profile list
|
||||
LoadProfiles();
|
||||
|
||||
Logger.LogInfo($"Profile '{newProfile.Name}' created successfully");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to add profile: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete the selected profile
|
||||
/// </summary>
|
||||
private void DeleteProfile()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!CanModifySelectedProfile)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInfo($"Deleting profile: {SelectedProfile}");
|
||||
Logger.LogInfo($"Creating profile: {profile.Name}");
|
||||
|
||||
var profilesData = LoadProfilesFromDisk();
|
||||
profilesData.RemoveProfile(SelectedProfile);
|
||||
profilesData.SetProfile(profile);
|
||||
SaveProfilesToDisk(profilesData);
|
||||
|
||||
// Reload profile list
|
||||
LoadProfiles();
|
||||
|
||||
Logger.LogInfo($"Profile '{SelectedProfile}' deleted successfully");
|
||||
Logger.LogInfo($"Profile '{profile.Name}' created successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to create profile: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update an existing profile
|
||||
/// </summary>
|
||||
public void UpdateProfile(string oldName, PowerDisplayProfile newProfile)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (newProfile == null || !newProfile.IsValid())
|
||||
{
|
||||
Logger.LogWarning("Invalid profile");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInfo($"Updating profile: {oldName} -> {newProfile.Name}");
|
||||
|
||||
var profilesData = LoadProfilesFromDisk();
|
||||
|
||||
// Remove old profile and add updated one
|
||||
profilesData.RemoveProfile(oldName);
|
||||
profilesData.SetProfile(newProfile);
|
||||
SaveProfilesToDisk(profilesData);
|
||||
|
||||
// Reload profile list
|
||||
LoadProfiles();
|
||||
|
||||
Logger.LogInfo($"Profile updated to '{newProfile.Name}' successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to update profile: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a profile
|
||||
/// </summary>
|
||||
public void DeleteProfile(string profileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(profileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInfo($"Deleting profile: {profileName}");
|
||||
|
||||
var profilesData = LoadProfilesFromDisk();
|
||||
profilesData.RemoveProfile(profileName);
|
||||
SaveProfilesToDisk(profilesData);
|
||||
|
||||
// Reload profile list
|
||||
LoadProfiles();
|
||||
|
||||
Logger.LogInfo($"Profile '{profileName}' deleted successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -766,127 +713,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rename the selected profile
|
||||
/// </summary>
|
||||
private async void RenameProfile()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!CanModifySelectedProfile)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInfo($"Renaming profile: {SelectedProfile}");
|
||||
|
||||
// Load the existing profile
|
||||
var profilesData = LoadProfilesFromDisk();
|
||||
var existingProfile = profilesData.GetProfile(SelectedProfile);
|
||||
if (existingProfile == null)
|
||||
{
|
||||
Logger.LogWarning($"Profile '{SelectedProfile}' not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Show profile editor dialog with existing profile data
|
||||
var dialog = new Views.ProfileEditorDialog(Monitors, existingProfile.Name);
|
||||
|
||||
// Pre-fill monitor settings from existing profile
|
||||
foreach (var monitorSetting in existingProfile.MonitorSettings)
|
||||
{
|
||||
var monitorItem = dialog.ViewModel.Monitors.FirstOrDefault(m => m.Monitor.HardwareId == monitorSetting.HardwareId);
|
||||
if (monitorItem != null)
|
||||
{
|
||||
monitorItem.IsSelected = true;
|
||||
monitorItem.Brightness = monitorSetting.Brightness;
|
||||
monitorItem.ColorTemperature = monitorSetting.ColorTemperature;
|
||||
if (monitorSetting.Contrast.HasValue)
|
||||
{
|
||||
monitorItem.Contrast = monitorSetting.Contrast.Value;
|
||||
}
|
||||
|
||||
if (monitorSetting.Volume.HasValue)
|
||||
{
|
||||
monitorItem.Volume = monitorSetting.Volume.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
|
||||
if (result == Microsoft.UI.Xaml.Controls.ContentDialogResult.Primary && dialog.ResultProfile != null)
|
||||
{
|
||||
var updatedProfile = dialog.ResultProfile;
|
||||
|
||||
// Remove old profile and add updated one
|
||||
profilesData.RemoveProfile(SelectedProfile);
|
||||
profilesData.SetProfile(updatedProfile);
|
||||
SaveProfilesToDisk(profilesData);
|
||||
|
||||
// Reload profile list
|
||||
LoadProfiles();
|
||||
|
||||
// Select the renamed profile
|
||||
SelectedProfile = updatedProfile.Name;
|
||||
|
||||
Logger.LogInfo($"Profile renamed to '{updatedProfile.Name}' successfully");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to rename profile: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save current settings as a new profile
|
||||
/// </summary>
|
||||
private void SaveAsProfile()
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogInfo("Saving current settings as new profile");
|
||||
|
||||
var profilesData = LoadProfilesFromDisk();
|
||||
var newProfileName = profilesData.GenerateProfileName();
|
||||
|
||||
// Collect current monitor settings
|
||||
var monitorSettings = new List<ProfileMonitorSetting>();
|
||||
foreach (var monitor in Monitors)
|
||||
{
|
||||
var setting = new ProfileMonitorSetting(
|
||||
monitor.HardwareId,
|
||||
monitor.CurrentBrightness,
|
||||
monitor.ColorTemperature,
|
||||
monitor.EnableContrast ? (int?)50 : null,
|
||||
monitor.EnableVolume ? (int?)50 : null);
|
||||
|
||||
monitorSettings.Add(setting);
|
||||
}
|
||||
|
||||
if (monitorSettings.Count == 0)
|
||||
{
|
||||
Logger.LogWarning("No monitors available to save profile");
|
||||
return;
|
||||
}
|
||||
|
||||
var newProfile = new PowerDisplayProfile(newProfileName, monitorSettings);
|
||||
profilesData.SetProfile(newProfile);
|
||||
SaveProfilesToDisk(profilesData);
|
||||
|
||||
// Reload profile list and select the new profile
|
||||
LoadProfiles();
|
||||
SelectedProfile = newProfileName;
|
||||
|
||||
Logger.LogInfo($"Saved as profile '{newProfileName}' successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to save as profile: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void NotifySettingsChanged()
|
||||
{
|
||||
// Persist locally first so settings survive even if the module DLL isn't loaded yet.
|
||||
|
||||
Reference in New Issue
Block a user