mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +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:
@@ -54,8 +54,11 @@ namespace PowerDisplay.Helpers
|
||||
|
||||
if (profiles != null)
|
||||
{
|
||||
// Clean up any legacy Custom profiles
|
||||
profiles.Profiles.RemoveAll(p => p.Name.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
_cachedProfiles = profiles;
|
||||
Logger.LogInfo($"Loaded {profiles.Profiles.Count} profiles, current: {profiles.CurrentProfile}");
|
||||
Logger.LogInfo($"Loaded {profiles.Profiles.Count} profiles");
|
||||
return profiles;
|
||||
}
|
||||
}
|
||||
@@ -82,12 +85,15 @@ namespace PowerDisplay.Helpers
|
||||
{
|
||||
try
|
||||
{
|
||||
// Clean up any Custom profiles before saving
|
||||
profiles.Profiles.RemoveAll(p => p.Name.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
profiles.LastUpdated = DateTime.UtcNow;
|
||||
var json = JsonSerializer.Serialize(profiles, AppJsonContext.Default.PowerDisplayProfiles);
|
||||
File.WriteAllText(_profilesFilePath, json);
|
||||
_cachedProfiles = profiles;
|
||||
|
||||
Logger.LogInfo($"Saved {profiles.Profiles.Count} profiles, current: {profiles.CurrentProfile}");
|
||||
Logger.LogInfo($"Saved {profiles.Profiles.Count} profiles");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -96,62 +102,6 @@ namespace PowerDisplay.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently active profile
|
||||
/// </summary>
|
||||
public PowerDisplayProfile? GetCurrentProfile()
|
||||
{
|
||||
var profiles = LoadProfiles();
|
||||
return profiles.GetCurrentProfile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current profile by name
|
||||
/// </summary>
|
||||
public void SetCurrentProfile(string profileName)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var profiles = LoadProfiles();
|
||||
|
||||
// Validate profile exists (unless it's Custom)
|
||||
if (!profileName.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var profile = profiles.GetProfile(profileName);
|
||||
if (profile == null)
|
||||
{
|
||||
Logger.LogWarning($"Cannot set current profile: '{profileName}' not found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
profiles.CurrentProfile = profileName;
|
||||
SaveProfiles(profiles);
|
||||
|
||||
Logger.LogInfo($"Current profile set to: {profileName}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or updates the Custom profile from current monitor states
|
||||
/// </summary>
|
||||
public void CreateCustomProfileFromCurrent(List<ProfileMonitorSetting> monitorSettings)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var profiles = LoadProfiles();
|
||||
|
||||
var customProfile = new PowerDisplayProfile(
|
||||
PowerDisplayProfiles.CustomProfileName,
|
||||
monitorSettings);
|
||||
|
||||
profiles.SetProfile(customProfile);
|
||||
SaveProfiles(profiles);
|
||||
|
||||
Logger.LogInfo($"Custom profile created/updated with {monitorSettings.Count} monitors");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates a profile
|
||||
/// </summary>
|
||||
@@ -205,23 +155,5 @@ namespace PowerDisplay.Helpers
|
||||
var profiles = LoadProfiles();
|
||||
return profiles.Profiles.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current profile name
|
||||
/// </summary>
|
||||
public string GetCurrentProfileName()
|
||||
{
|
||||
var profiles = LoadProfiles();
|
||||
return profiles.CurrentProfile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if currently on a non-Custom profile
|
||||
/// </summary>
|
||||
public bool IsOnNonCustomProfile()
|
||||
{
|
||||
var currentProfileName = GetCurrentProfileName();
|
||||
return !currentProfileName.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using PowerDisplay.Commands;
|
||||
using PowerDisplay.Configuration;
|
||||
using PowerDisplay.Core;
|
||||
using PowerDisplay.Core.Interfaces;
|
||||
using PowerDisplay.Core.Models;
|
||||
@@ -555,9 +556,7 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
// Apply profile settings to monitors
|
||||
await ApplyProfileAsync(pendingOp.ProfileName, pendingOp.MonitorSettings);
|
||||
|
||||
// Update current profile in profiles.json
|
||||
_profileManager.SetCurrentProfile(pendingOp.ProfileName);
|
||||
|
||||
// Note: We no longer track "current profile" - profiles are just templates
|
||||
Logger.LogInfo($"[Profile] Successfully applied profile '{pendingOp.ProfileName}'");
|
||||
}
|
||||
else
|
||||
@@ -640,56 +639,14 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Called when user modifies monitor parameters through PowerDisplay UI
|
||||
/// Switches to Custom profile if currently on a non-Custom profile
|
||||
/// Note: Profiles are now templates, not states. Parameter changes are automatically
|
||||
/// saved to monitor_state.json without any profile state tracking.
|
||||
/// </summary>
|
||||
public void OnMonitorParameterChanged(string hardwareId, string propertyName, int value)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if we're currently on a non-Custom profile
|
||||
if (_profileManager.IsOnNonCustomProfile())
|
||||
{
|
||||
var currentProfileName = _profileManager.GetCurrentProfileName();
|
||||
Logger.LogInfo($"[Profile] Parameter changed while on profile '{currentProfileName}', switching to Custom");
|
||||
|
||||
// Create Custom profile from current state
|
||||
var customSettings = new List<ProfileMonitorSetting>();
|
||||
|
||||
foreach (var monitorVm in Monitors)
|
||||
{
|
||||
var setting = new ProfileMonitorSetting(
|
||||
monitorVm.HardwareId,
|
||||
monitorVm.Brightness,
|
||||
monitorVm.ColorTemperature,
|
||||
monitorVm.ShowContrast ? monitorVm.Contrast : null,
|
||||
monitorVm.ShowVolume ? monitorVm.Volume : null);
|
||||
|
||||
customSettings.Add(setting);
|
||||
}
|
||||
|
||||
// Save as Custom profile
|
||||
_profileManager.CreateCustomProfileFromCurrent(customSettings);
|
||||
|
||||
// Set current profile to Custom
|
||||
_profileManager.SetCurrentProfile(PowerDisplayProfiles.CustomProfileName);
|
||||
|
||||
// Update settings.json to reflect Custom profile
|
||||
var settings = _settingsUtils.GetSettingsOrDefault<PowerDisplaySettings>(PowerDisplaySettings.ModuleName);
|
||||
settings.Properties.CurrentProfile = PowerDisplayProfiles.CustomProfileName;
|
||||
_settingsUtils.SaveSettings(
|
||||
System.Text.Json.JsonSerializer.Serialize(settings, AppJsonContext.Default.PowerDisplaySettings),
|
||||
PowerDisplaySettings.ModuleName);
|
||||
|
||||
Logger.LogInfo("[Profile] Switched to Custom profile");
|
||||
|
||||
// Notify Settings UI to refresh
|
||||
NotifySettingsUIRefresh();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"[Profile] Failed to handle parameter change: {ex.Message}");
|
||||
}
|
||||
// Parameters are already saved to monitor_state.json via SaveMonitorsToSettings
|
||||
// No profile state management needed - profiles are just quick-apply templates
|
||||
Logger.LogDebug($"[Profile] Parameter '{propertyName}' changed for monitor '{hardwareId}' to {value}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -821,35 +778,8 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
// Read current settings
|
||||
var settings = _settingsUtils.GetSettingsOrDefault<PowerDisplaySettings>("PowerDisplay");
|
||||
|
||||
// Check if we should apply a profile on startup
|
||||
var currentProfileName = _profileManager.GetCurrentProfileName();
|
||||
if (!string.IsNullOrEmpty(currentProfileName) &&
|
||||
!currentProfileName.Equals(PowerDisplayProfiles.CustomProfileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Logger.LogInfo($"[Startup] Applying saved profile: {currentProfileName}");
|
||||
|
||||
var currentProfile = _profileManager.GetCurrentProfile();
|
||||
if (currentProfile != null && currentProfile.IsValid())
|
||||
{
|
||||
// Wait for color temperature initialization if needed
|
||||
if (colorTempInitTasks != null && colorTempInitTasks.Count > 0)
|
||||
{
|
||||
await Task.WhenAll(colorTempInitTasks);
|
||||
}
|
||||
|
||||
// Apply profile settings
|
||||
await ApplyProfileAsync(currentProfileName, currentProfile.MonitorSettings);
|
||||
|
||||
StatusText = $"Profile '{currentProfileName}' applied";
|
||||
IsLoading = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning($"[Startup] Profile '{currentProfileName}' not found or invalid, falling back to saved state");
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Profiles are now quick-apply templates, not startup states
|
||||
// We only restore from monitor_state.json, not from profiles
|
||||
if (settings.Properties.RestoreSettingsOnStartup)
|
||||
{
|
||||
// Restore saved settings from configuration file
|
||||
@@ -1002,11 +932,11 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
if (monitorVm != null)
|
||||
{
|
||||
// Apply default values
|
||||
monitorVm.Brightness = 30;
|
||||
monitorVm.Brightness = AppConstants.MonitorDefaults.DefaultBrightness;
|
||||
|
||||
// ColorTemperature is now read-only in flyout UI - controlled via Settings UI only
|
||||
monitorVm.Contrast = 50;
|
||||
monitorVm.Volume = 50;
|
||||
monitorVm.Contrast = AppConstants.MonitorDefaults.DefaultContrast;
|
||||
monitorVm.Volume = AppConstants.MonitorDefaults.DefaultVolume;
|
||||
|
||||
StatusText = $"Monitor {monitorVm.Name} reset to default values";
|
||||
}
|
||||
|
||||
@@ -16,21 +16,19 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
/// </summary>
|
||||
public class PowerDisplayProfiles
|
||||
{
|
||||
// NOTE: Custom profile concept has been removed. Profiles are now templates, not states.
|
||||
// This constant is kept for backward compatibility (cleaning up legacy Custom profiles).
|
||||
public const string CustomProfileName = "Custom";
|
||||
|
||||
[JsonPropertyName("profiles")]
|
||||
public List<PowerDisplayProfile> Profiles { get; set; }
|
||||
|
||||
[JsonPropertyName("currentProfile")]
|
||||
public string CurrentProfile { get; set; }
|
||||
|
||||
[JsonPropertyName("lastUpdated")]
|
||||
public DateTime LastUpdated { get; set; }
|
||||
|
||||
public PowerDisplayProfiles()
|
||||
{
|
||||
Profiles = new List<PowerDisplayProfile>();
|
||||
CurrentProfile = CustomProfileName;
|
||||
LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
@@ -42,14 +40,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
return Profiles.FirstOrDefault(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently active profile
|
||||
/// </summary>
|
||||
public PowerDisplayProfile? GetCurrentProfile()
|
||||
{
|
||||
return GetProfile(CurrentProfile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates a profile
|
||||
/// </summary>
|
||||
@@ -76,24 +66,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
/// </summary>
|
||||
public bool RemoveProfile(string name)
|
||||
{
|
||||
// Cannot remove the Custom profile
|
||||
if (name.Equals(CustomProfileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var profile = GetProfile(name);
|
||||
if (profile != null)
|
||||
{
|
||||
Profiles.Remove(profile);
|
||||
LastUpdated = DateTime.UtcNow;
|
||||
|
||||
// If the removed profile was current, switch to Custom
|
||||
if (CurrentProfile.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
CurrentProfile = CustomProfileName;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -128,12 +105,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
return false;
|
||||
}
|
||||
|
||||
// Custom is reserved
|
||||
if (name.Equals(CustomProfileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if name is already used (excluding the profile being renamed)
|
||||
var existing = GetProfile(name);
|
||||
if (existing != null && (excludeName == null || !existing.Name.Equals(excludeName, StringComparison.OrdinalIgnoreCase)))
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
BrightnessUpdateRate = "1s";
|
||||
Monitors = new List<MonitorInfo>();
|
||||
RestoreSettingsOnStartup = true;
|
||||
CurrentProfile = "Custom";
|
||||
|
||||
// Note: saved_monitor_settings has been moved to monitor_state.json
|
||||
// which is managed separately by PowerDisplay app
|
||||
@@ -37,12 +36,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("restore_settings_on_startup")]
|
||||
public bool RestoreSettingsOnStartup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current active profile name (e.g., "Custom", "Profile1", "Profile2")
|
||||
/// </summary>
|
||||
[JsonPropertyName("current_profile")]
|
||||
public string CurrentProfile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pending color temperature operation from Settings UI.
|
||||
/// This is cleared after PowerDisplay processes it.
|
||||
|
||||
@@ -56,42 +56,67 @@
|
||||
|
||||
<controls:SettingsGroup x:Uid="PowerDisplay_Profiles_GroupSettings" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="PowerDisplay_CurrentProfile"
|
||||
x:Uid="PowerDisplay_QuickProfiles"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<ComboBox
|
||||
ItemsSource="{x:Bind ViewModel.Profiles, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedProfile, Mode=TwoWay}"
|
||||
MinWidth="180" />
|
||||
<Button
|
||||
x:Uid="PowerDisplay_AddProfile_Button"
|
||||
Command="{x:Bind ViewModel.AddProfileCommand}"
|
||||
ToolTipService.ToolTip="Add new profile">
|
||||
<FontIcon Glyph="" FontSize="16" />
|
||||
</Button>
|
||||
<Button
|
||||
x:Uid="PowerDisplay_DeleteProfile_Button"
|
||||
Command="{x:Bind ViewModel.DeleteProfileCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.CanModifySelectedProfile, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="Delete selected profile">
|
||||
<FontIcon Glyph="" FontSize="16" />
|
||||
</Button>
|
||||
<Button
|
||||
x:Uid="PowerDisplay_SaveAsProfile_Button"
|
||||
Command="{x:Bind ViewModel.SaveAsProfileCommand}"
|
||||
ToolTipService.ToolTip="Save current settings as new profile">
|
||||
<FontIcon Glyph="" FontSize="16" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
<StackPanel Spacing="8">
|
||||
<!-- Profile buttons row -->
|
||||
<ItemsRepeater ItemsSource="{x:Bind ViewModel.Profiles, Mode=OneWay}">
|
||||
<ItemsRepeater.Layout>
|
||||
<StackLayout Orientation="Horizontal" Spacing="8"/>
|
||||
</ItemsRepeater.Layout>
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate x:DataType="library:PowerDisplayProfile">
|
||||
<Button
|
||||
Click="ProfileButton_Click"
|
||||
Tag="{x:Bind}"
|
||||
ToolTipService.ToolTip="Apply this profile">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<FontIcon Glyph="" FontSize="14"/>
|
||||
<TextBlock Text="{x:Bind Name}"/>
|
||||
</StackPanel>
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="PowerDisplay_ProfileStatus"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<TextBlock
|
||||
Text="{x:Bind ViewModel.CurrentProfile, Mode=OneWay}"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" />
|
||||
<Button.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem
|
||||
Text="Rename"
|
||||
Click="RenameProfile_Click"
|
||||
Tag="{x:Bind}">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutItem
|
||||
Text="Delete"
|
||||
Click="DeleteProfile_Click"
|
||||
Tag="{x:Bind}">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
</MenuFlyout>
|
||||
</Button.ContextFlyout>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
|
||||
<!-- Add profile button -->
|
||||
<Button
|
||||
Click="AddProfileButton_Click"
|
||||
ToolTipService.ToolTip="Save current settings as new profile">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<FontIcon Glyph="" FontSize="14"/>
|
||||
<TextBlock x:Uid="PowerDisplay_AddProfile_Text"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Help text -->
|
||||
<TextBlock
|
||||
x:Uid="PowerDisplay_ProfilesHelpText"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
|
||||
@@ -153,5 +153,97 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Profile button event handlers
|
||||
private void ProfileButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.Tag is PowerDisplayProfile profile)
|
||||
{
|
||||
ViewModel.ApplyProfile(profile);
|
||||
}
|
||||
}
|
||||
|
||||
private async void AddProfileButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.Monitors == null || ViewModel.Monitors.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var defaultName = GenerateDefaultProfileName();
|
||||
var dialog = new ProfileEditorDialog(ViewModel.Monitors, defaultName);
|
||||
dialog.XamlRoot = this.XamlRoot;
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
|
||||
if (result == ContentDialogResult.Primary && dialog.ResultProfile != null)
|
||||
{
|
||||
ViewModel.CreateProfile(dialog.ResultProfile);
|
||||
}
|
||||
}
|
||||
|
||||
private async void RenameProfile_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var menuItem = sender as MenuFlyoutItem;
|
||||
if (menuItem?.Tag is PowerDisplayProfile profile)
|
||||
{
|
||||
var dialog = new ProfileEditorDialog(ViewModel.Monitors, profile.Name);
|
||||
dialog.XamlRoot = this.XamlRoot;
|
||||
|
||||
// Pre-fill with existing profile settings
|
||||
dialog.PreFillProfile(profile);
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
|
||||
if (result == ContentDialogResult.Primary && dialog.ResultProfile != null)
|
||||
{
|
||||
ViewModel.UpdateProfile(profile.Name, dialog.ResultProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void DeleteProfile_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var menuItem = sender as MenuFlyoutItem;
|
||||
if (menuItem?.Tag is PowerDisplayProfile profile)
|
||||
{
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
XamlRoot = this.XamlRoot,
|
||||
Title = "Delete Profile",
|
||||
Content = $"Are you sure you want to delete '{profile.Name}'?",
|
||||
PrimaryButtonText = "Delete",
|
||||
CloseButtonText = "Cancel",
|
||||
DefaultButton = ContentDialogButton.Close,
|
||||
};
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
ViewModel.DeleteProfile(profile.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GenerateDefaultProfileName()
|
||||
{
|
||||
var existingNames = new HashSet<string>();
|
||||
foreach (var profile in ViewModel.Profiles)
|
||||
{
|
||||
existingNames.Add(profile.Name);
|
||||
}
|
||||
|
||||
int counter = 1;
|
||||
string name;
|
||||
do
|
||||
{
|
||||
name = $"Profile {counter}";
|
||||
counter++;
|
||||
}
|
||||
while (existingNames.Contains(name));
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -39,5 +40,41 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
{
|
||||
ResultProfile = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pre-fill the dialog with existing profile data
|
||||
/// </summary>
|
||||
public void PreFillProfile(PowerDisplayProfile profile)
|
||||
{
|
||||
if (profile == null || ViewModel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set profile name
|
||||
ViewModel.ProfileName = profile.Name;
|
||||
|
||||
// Pre-fill monitor settings from existing profile
|
||||
foreach (var monitorSetting in profile.MonitorSettings)
|
||||
{
|
||||
var monitorItem = 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5550,11 +5550,17 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
|
||||
<data name="PowerDisplay_Profiles_GroupSettings.Header" xml:space="preserve">
|
||||
<value>Profiles</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_CurrentProfile.Header" xml:space="preserve">
|
||||
<value>Profile</value>
|
||||
<data name="PowerDisplay_QuickProfiles.Header" xml:space="preserve">
|
||||
<value>Quick apply profiles</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_CurrentProfile.Description" xml:space="preserve">
|
||||
<value>Select a profile to apply predefined monitor settings</value>
|
||||
<data name="PowerDisplay_QuickProfiles.Description" xml:space="preserve">
|
||||
<value>Click a profile button to quickly apply saved monitor settings</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_AddProfile_Text.Text" xml:space="preserve">
|
||||
<value>Add Profile</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_ProfilesHelpText.Text" xml:space="preserve">
|
||||
<value>Right-click a profile to rename or delete it</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_AddProfile_Button.ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Add new profile</value>
|
||||
@@ -5562,15 +5568,6 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
|
||||
<data name="PowerDisplay_DeleteProfile_Button.ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Delete selected profile</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_SaveAsProfile_Button.ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Save current settings as new profile</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_ProfileStatus.Header" xml:space="preserve">
|
||||
<value>Active profile</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_ProfileStatus.Description" xml:space="preserve">
|
||||
<value>Currently active configuration profile</value>
|
||||
</data>
|
||||
<data name="PowerDisplay_Monitor_HideMonitor.Header" xml:space="preserve">
|
||||
<value>Hide monitor</value>
|
||||
</data>
|
||||
|
||||
5649
src/settings-ui/Settings.UI/Strings/en-us/Resources.resw.bak
Normal file
5649
src/settings-ui/Settings.UI/Strings/en-us/Resources.resw.bak
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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