Files
kniip/src/Kniip/Infrastructure/ThemeManager.cs
T
2026-11-09 20:23:46 +01:00

122 lines
3.7 KiB
C#

using System;
using System.Windows;
using System.Windows.Interop;
using Kniip.Interop;
using Kniip.Settings;
using Microsoft.Win32;
namespace Kniip.Infrastructure;
/// <summary>Wisselt de kleuren van alle vensters tussen licht en donker (Themes/Light.xaml en Themes/Dark.xaml).</summary>
internal static class ThemeManager
{
private const string PersonalizeKey = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private static readonly Uri LightUri = new("pack://application:,,,/Themes/Light.xaml");
private static readonly Uri DarkUri = new("pack://application:,,,/Themes/Dark.xaml");
private static ResourceDictionary? _light;
private static ResourceDictionary? _dark;
private static ColorMode _mode;
private static bool _listening;
public static bool IsDark { get; private set; }
/// <summary>Na elke wissel tussen licht en donker.</summary>
public static event Action? Changed;
public static void Apply(ColorMode mode)
{
_mode = mode;
if (mode == ColorMode.System && !_listening)
{
_listening = true;
SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged;
}
Refresh();
}
/// <summary>Geeft het venster een titelbalk in de huidige kleurmodus, ook na een latere wissel.</summary>
public static void Attach(Window window) =>
window.SourceInitialized += (_, _) => NativeMethods.SetDarkTitleBar(new WindowInteropHelper(window).Handle, IsDark);
private static void Refresh()
{
bool dark = _mode switch
{
ColorMode.Dark => true,
ColorMode.System => IsSystemDark(),
_ => false
};
if (dark == IsDark && _light != null)
{
return;
}
IsDark = dark;
_light ??= new ResourceDictionary { Source = LightUri };
_dark ??= new ResourceDictionary { Source = DarkUri };
var merged = Application.Current.Resources.MergedDictionaries;
int index = -1;
for (int i = 0; i < merged.Count; i++)
{
if (merged[i].Source is { } source && (source.OriginalString.EndsWith("Light.xaml", StringComparison.OrdinalIgnoreCase)
|| source.OriginalString.EndsWith("Dark.xaml", StringComparison.OrdinalIgnoreCase)))
{
index = i;
break;
}
}
ResourceDictionary theme = dark ? _dark : _light;
if (index < 0)
{
merged.Insert(0, theme);
}
else if (merged[index] != theme)
{
merged[index] = theme;
}
foreach (Window window in Application.Current.Windows)
{
IntPtr hwnd = new WindowInteropHelper(window).Handle;
if (hwnd != IntPtr.Zero && window.WindowStyle != WindowStyle.None)
{
NativeMethods.SetDarkTitleBar(hwnd, dark);
}
}
Changed?.Invoke();
}
private static void OnUserPreferenceChanged(object? sender, UserPreferenceChangedEventArgs e)
{
if (e.Category == UserPreferenceCategory.General)
{
Application.Current?.Dispatcher.BeginInvoke(() =>
{
if (_mode == ColorMode.System)
{
Refresh();
}
});
}
}
private static bool IsSystemDark()
{
try
{
using RegistryKey? key = Registry.CurrentUser.OpenSubKey(PersonalizeKey);
return key?.GetValue("AppsUseLightTheme") is int light && light == 0;
}
catch (Exception)
{
return false;
}
}
}