diff --git a/src/OrthoStreamXP/Gui/App.xaml.cs b/src/OrthoStreamXP/Gui/App.xaml.cs index c4e43ba..858f194 100644 --- a/src/OrthoStreamXP/Gui/App.xaml.cs +++ b/src/OrthoStreamXP/Gui/App.xaml.cs @@ -25,6 +25,7 @@ protected override void OnStartup(StartupEventArgs e) Log.Configure(LogOutput.None, null); DispatcherUnhandledException += OnUnhandledException; _main = new MainViewModel(); + _main.Preferences.Theme.Apply(); var window = new MainWindow(_main); MainWindow = window; window.Show(); diff --git a/src/OrthoStreamXP/Gui/AppTheme.cs b/src/OrthoStreamXP/Gui/AppTheme.cs new file mode 100644 index 0000000..42feb26 --- /dev/null +++ b/src/OrthoStreamXP/Gui/AppTheme.cs @@ -0,0 +1,71 @@ +using System.Windows; +using Microsoft.Win32; + +namespace OrthoStreamXP.Gui; + +/// The window's colours: the Windows theme (follows Windows), or always light or dark. +internal enum AppTheme +{ + System, + Light, + Dark, +} + +/// The sidebar's theme button: what it shows for a theme, and the theme a click switches to. +internal sealed record ThemeChoice(string Label, string Glyph, AppTheme Theme) +{ + public static ThemeChoice Windows { get; } = new("Windows theme", "", AppTheme.System); + public static ThemeChoice Light { get; } = new("Light theme", "", AppTheme.Light); + public static ThemeChoice Dark { get; } = new("Dark theme", "", AppTheme.Dark); + + public static ThemeChoice Of(AppTheme theme) => theme switch + { + AppTheme.Light => Light, + AppTheme.Dark => Dark, + _ => Windows, + }; + + /// + /// The Windows theme (follows Windows), then the other look, then Windows' look kept fixed, then the Windows theme again: + /// the first click always changes the colours. + /// + public ThemeChoice Next(Func windowsUsesLight) + { + ThemeChoice same = windowsUsesLight() ? Light : Dark; + ThemeChoice other = same == Light ? Dark : Light; + if (this == Windows) return other; + return this == other ? same : Windows; + } +} + +internal static class AppThemeExtensions +{ +#pragma warning disable WPF0001 // WPF's Fluent theme is marked experimental. + /// The WPF Fluent theme mode for the choice. + public static ThemeMode ToThemeMode(this AppTheme theme) => theme switch + { + AppTheme.Light => ThemeMode.Light, + AppTheme.Dark => ThemeMode.Dark, + _ => ThemeMode.System, + }; + + /// Switches the running application to the theme; the window's DynamicResource brushes follow. + public static void Apply(this AppTheme theme) + { + if (Application.Current is { } app) app.ThemeMode = theme.ToThemeMode(); + } +#pragma warning restore WPF0001 +} + +/// Whether Windows shows apps light or dark (Settings > Personalisation > Colours). +internal static class WindowsTheme +{ + private const string Personalize = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"; + + /// True for light apps, also when Windows does not say (older versions default to light). + public static bool AppsUseLight() + { + using RegistryKey? key = Registry.CurrentUser.OpenSubKey(Personalize); + return key?.GetValue("AppsUseLightTheme") is not int value || value != 0; + } +} diff --git a/src/OrthoStreamXP/Gui/MainWindow.xaml b/src/OrthoStreamXP/Gui/MainWindow.xaml index 419800c..d29c9fc 100644 --- a/src/OrthoStreamXP/Gui/MainWindow.xaml +++ b/src/OrthoStreamXP/Gui/MainWindow.xaml @@ -66,6 +66,16 @@ + + + diff --git a/src/OrthoStreamXP/Gui/Theme.xaml b/src/OrthoStreamXP/Gui/Theme.xaml index 876aee5..ae7db94 100644 --- a/src/OrthoStreamXP/Gui/Theme.xaml +++ b/src/OrthoStreamXP/Gui/Theme.xaml @@ -8,6 +8,11 @@ + +