refactored thememanager

This commit is contained in:
Orbmu2k
2026-03-23 19:56:18 +01:00
parent 9b6749a915
commit 1e52b9933a
7 changed files with 112 additions and 108 deletions

View File

@@ -30,6 +30,8 @@ namespace nvidiaProfileInspector.Common.Helper
public List<uint> FavoriteSettingIds { get; set; } = new List<uint>();
public string Theme { get; set; } = "DarkTheme.xaml";
private static string GetSettingsFilename()
{
var fiPortalbleSettings = new FileInfo("settings.xml");

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
@@ -6,143 +7,49 @@ using nvidiaProfileInspector.Common.Helper;
namespace nvidiaProfileInspector.Services
{
/// <summary>
/// Manages application themes including loading, saving, and switching themes.
/// </summary>
public class ThemeManager
{
private const string DarkTheme = "DarkTheme.xaml";
private const string LightTheme = "LightTheme.xaml";
private const string SlateLightTheme = "SlateLightTheme.xaml";
private static readonly string[] ValidThemes = { DarkTheme, LightTheme, SlateLightTheme };
private static readonly string[] ValidThemes = {
DarkTheme,
SlateLightTheme
};
/// <summary>
/// Gets the current theme name.
/// </summary>
public string CurrentTheme { get; private set; }
/// <summary>
/// Initializes a new instance of the ThemeManager class.
/// </summary>
public ThemeManager()
{
LoadSavedTheme();
}
/// <summary>
/// Loads the saved theme from user settings or defaults to DarkTheme.
/// </summary>
public void LoadSavedTheme()
{
try
{
var settings = UserSettings.LoadSettings();
var themeName = settings.Theme ?? DarkTheme;
// Ensure themeName has .xaml extension
if (!themeName.EndsWith(".xaml"))
themeName = DarkTheme;
// Validate theme
if (!ValidThemes.Contains(themeName))
themeName = DarkTheme;
CurrentTheme = themeName;
ApplyTheme(themeName);
ApplyAndPersistTheme(NormalizeThemeName(settings.Theme), savePreference: false);
}
catch
{
// Default to dark theme on error
CurrentTheme = DarkTheme;
ApplyTheme(DarkTheme);
ApplyAndPersistTheme(DarkTheme, savePreference: false);
}
}
/// <summary>
/// Toggles to the next theme in the sequence: Dark -> Light -> SlateLight -> Dark.
/// </summary>
public void ToggleTheme()
{
try
{
var app = Application.Current;
if (app == null) return;
var mergedDicts = app.Resources.MergedDictionaries;
var existingTheme = mergedDicts.FirstOrDefault(d =>
d.Source != null && (d.Source.OriginalString.Contains(DarkTheme) ||
d.Source.OriginalString.Contains(LightTheme) ||
d.Source.OriginalString.Contains(SlateLightTheme)));
string newSource;
if (existingTheme != null)
{
if (existingTheme.Source.OriginalString.Contains(DarkTheme))
newSource = LightTheme;
else if (existingTheme.Source.OriginalString.Contains(LightTheme))
newSource = SlateLightTheme;
else if (existingTheme.Source.OriginalString.Contains(SlateLightTheme))
newSource = DarkTheme;
else
newSource = DarkTheme; // fallback
// Update the theme dictionary
mergedDicts.Remove(existingTheme);
mergedDicts.Add(new ResourceDictionary { Source = new Uri($"/UI/Themes/{newSource}", UriKind.Relative) });
}
else
{
// No theme found, apply dark theme
newSource = DarkTheme;
ApplyTheme(newSource);
}
// Save the new theme preference
CurrentTheme = newSource;
SaveThemePreference(newSource);
var currentTheme = GetCurrentAppliedThemeName();
ApplyAndPersistTheme(GetNextThemeName(currentTheme), savePreference: true);
}
catch (Exception ex)
{
// Log error but don't crash
System.Diagnostics.Debug.WriteLine($"Error toggling theme: {ex.Message}");
}
}
/// <summary>
/// Applies the specified theme to the application resources.
/// </summary>
/// <param name="themeName">The name of the theme to apply.</param>
private void ApplyTheme(string themeName)
{
try
{
var app = Application.Current;
if (app == null) return;
var mergedDicts = app.Resources.MergedDictionaries;
var themeDict = mergedDicts.FirstOrDefault(d =>
d.Source != null && (d.Source.OriginalString.Contains("DarkTheme.xaml") ||
d.Source.OriginalString.Contains("LightTheme.xaml") ||
d.Source.OriginalString.Contains("SlateLightTheme.xaml")));
if (themeDict != null)
{
string newSource = $"/UI/Themes/{themeName}";
mergedDicts.Remove(themeDict);
mergedDicts.Add(new ResourceDictionary { Source = new Uri(newSource, UriKind.Relative) });
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error applying theme: {ex.Message}");
}
}
/// <summary>
/// Saves the theme preference to user settings.
/// </summary>
/// <param name="themeName">The name of the theme to save.</param>
private void SaveThemePreference(string themeName)
{
try
@@ -156,5 +63,97 @@ namespace nvidiaProfileInspector.Services
System.Diagnostics.Debug.WriteLine($"Error saving theme preference: {ex.Message}");
}
}
private static ResourceDictionary GetThemeDictionary(IList<ResourceDictionary> mergedDicts)
{
return mergedDicts.FirstOrDefault(d => GetThemeName(d.Source) != null);
}
private static string GetCurrentAppliedThemeName()
{
var app = Application.Current;
if (app == null)
return null;
return GetThemeName(GetThemeDictionary(app.Resources.MergedDictionaries)?.Source);
}
private static string GetThemeName(Uri source)
{
if (source == null)
return null;
var themeName = Path.GetFileName(source.OriginalString);
return ValidThemes.FirstOrDefault(validTheme =>
string.Equals(validTheme, themeName, StringComparison.OrdinalIgnoreCase));
}
private static string NormalizeThemeName(string themeName)
{
if (string.IsNullOrWhiteSpace(themeName))
return DarkTheme;
if (!themeName.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase))
return DarkTheme;
return ValidThemes.FirstOrDefault(validTheme =>
string.Equals(validTheme, themeName, StringComparison.OrdinalIgnoreCase))
?? DarkTheme;
}
private static string GetNextThemeName(string currentTheme)
{
if (ValidThemes.Length == 0)
return DarkTheme;
if (string.IsNullOrWhiteSpace(currentTheme))
return ValidThemes[0];
var currentThemeIndex = Array.FindIndex(
ValidThemes,
theme => string.Equals(theme, currentTheme, StringComparison.OrdinalIgnoreCase));
if (currentThemeIndex < 0)
return ValidThemes[0];
var nextThemeIndex = (currentThemeIndex + 1) % ValidThemes.Length;
return ValidThemes[nextThemeIndex];
}
private void ApplyAndPersistTheme(string themeName, bool savePreference)
{
var app = Application.Current;
if (app == null)
return;
var normalizedThemeName = NormalizeThemeName(themeName);
var mergedDicts = app.Resources.MergedDictionaries;
var themeDict = GetThemeDictionary(mergedDicts);
ReplaceThemeDictionary(mergedDicts, themeDict, normalizedThemeName);
CurrentTheme = normalizedThemeName;
if (savePreference)
SaveThemePreference(normalizedThemeName);
}
private static void ReplaceThemeDictionary(IList<ResourceDictionary> mergedDicts, ResourceDictionary existingTheme, string themeName)
{
var newThemeDictionary = new ResourceDictionary
{
Source = new Uri($"/UI/Themes/{themeName}", UriKind.Relative)
};
if (existingTheme == null)
{
mergedDicts.Add(newThemeDictionary);
return;
}
var index = mergedDicts.IndexOf(existingTheme);
mergedDicts.RemoveAt(index);
mergedDicts.Insert(index, newThemeDictionary);
}
}
}
}

View File

@@ -50,9 +50,9 @@
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="{DynamicResource ApplicationRemoveBrush}" />
<Setter TargetName="border" Property="BorderBrush" Value="{DynamicResource ErrorBrush}" />
<Setter TargetName="Text" Property="Foreground" Value="White" />
<Setter TargetName="Text" Property="Foreground" Value="{DynamicResource ApplicationRemoveForegroundBrush}" />
<Setter TargetName="Icon" Property="Data" Value="{StaticResource IconDelete}" />
<Setter TargetName="Icon" Property="Fill" Value="White" />
<Setter TargetName="Icon" Property="Fill" Value="{DynamicResource ApplicationRemoveForegroundBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Opacity" Value="0.7" />

View File

@@ -66,6 +66,7 @@
<SolidColorBrush x:Key="ScrollBarThumbPressedBrush" Color="#80FFFFFF" />
<SolidColorBrush x:Key="ApplicationRemoveBrush" Color="#3D2B2B" />
<SolidColorBrush x:Key="ApplicationRemoveForegroundBrush" Color="#FFFFFF" />
<SolidColorBrush x:Key="ApplicationAddBrush" Color="#154CC331" />
<SolidColorBrush x:Key="ApplicationAddHoverBrush" Color="#254CC331" />
<SolidColorBrush x:Key="ListViewAlternatingRowBrush" Color="#252525" />

View File

@@ -56,7 +56,7 @@
<SolidColorBrush x:Key="QuestionBrush" Color="{StaticResource QuestionColor}" />
<SolidColorBrush x:Key="LabBrush" Color="{StaticResource LabColor}" />
<!-- Overlay / Transparency Brushes -->
<!-- Overlay / Transparency Brushes -->
<SolidColorBrush x:Key="HighlightOverlayBrush" Color="#08000000" />
<SolidColorBrush x:Key="HoverOverlayBrush" Color="#0D000000" />
<SolidColorBrush x:Key="PressedOverlayBrush" Color="#1A000000" />
@@ -66,7 +66,7 @@
<SolidColorBrush x:Key="ScrollBarThumbPressedBrush" Color="#50000000" />
<SolidColorBrush x:Key="ApplicationRemoveBrush" Color="#FFEBEB" />
<SolidColorBrush x:Key="ApplicationAddBrush" Color="#E8F8E8" />
<SolidColorBrush x:Key="ApplicationRemoveForegroundBrush" Color="#1A1A1A" />
<SolidColorBrush x:Key="ApplicationAddHoverBrush" Color="#D1F1D1" />
<SolidColorBrush x:Key="ListViewAlternatingRowBrush" Color="#08000000" />
<SolidColorBrush x:Key="ModifiedSettingBackgroundBrush" Color="#15000000" />

View File

@@ -10,7 +10,7 @@
<Color x:Key="Layer3BackgroundColor">#E8EAED</Color>
<!-- NVIDIA Green Brand Colors -->
<Color x:Key="NvidiaGreenColor">#76B900</Color>
<Color x:Key="NvidiaGreenColor">#66A900</Color>
<Color x:Key="NvidiaGreenHoverColor">#8AD400</Color>
<Color x:Key="NvidiaGreenPressedColor">#659F00</Color>
@@ -76,6 +76,7 @@
<!-- Special Purpose Brushes -->
<SolidColorBrush x:Key="ApplicationRemoveBrush" Color="#FEF2F2" />
<SolidColorBrush x:Key="ApplicationRemoveForegroundBrush" Color="#1A1D21" />
<SolidColorBrush x:Key="ApplicationAddBrush" Color="#ECFDF5" />
<SolidColorBrush x:Key="ApplicationAddHoverBrush" Color="#D1FAE5" />
<SolidColorBrush x:Key="ListViewAlternatingRowBrush" Color="#F9FAFB" />

View File

@@ -956,6 +956,7 @@ using System.Windows.Input;
{
var themeManager = App.Bootstrapper.Resolve<ThemeManager>();
themeManager.ToggleTheme();
RefreshCurrentProfile();
}
}