diff --git a/src/ST.Client.Desktop.Avalonia/Application/Converters/ColorStringToBrush.cs b/src/ST.Client.Desktop.Avalonia/Application/Converters/ColorStringToBrush.cs index 14643e724..7a3b648d9 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/Converters/ColorStringToBrush.cs +++ b/src/ST.Client.Desktop.Avalonia/Application/Converters/ColorStringToBrush.cs @@ -19,8 +19,10 @@ namespace System.Application.Converters } else { - if (value is string c3 && Color2.TryParse(c3, out var cc)) + if (value is string c3 && Color.TryParse(c3, out var cc)) return cc; + if (value is string c4 && Color2.TryParse(c4, out var cc1)) + return cc1; } return value; } diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml b/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml index 435ecc396..1bf76844d 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml @@ -8,6 +8,11 @@ + @@ -22,17 +27,17 @@ - - - - + PreferSystemTheme="False"/> + + + + - + diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml.cs b/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml.cs index d49ed877f..2f613447d 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml.cs +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/App.axaml.cs @@ -30,6 +30,7 @@ using AvaloniaApplication = Avalonia.Application; using ShutdownMode = Avalonia.Controls.ShutdownMode; using Window = Avalonia.Controls.Window; using WindowState = Avalonia.Controls.WindowState; +using Avalonia.Markup.Xaml.MarkupExtensions; #if WINDOWS //using WpfApplication = System.Windows.Application; #endif @@ -123,20 +124,24 @@ namespace System.Application.UI } //var uri_0 = new Uri($"avares://Avalonia.Themes.Fluent/Fluent{themeName}.xaml"); - var uri_1 = new Uri($"avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/Theme{themeName}.xaml"); - - //Styles[0] = new FluentTheme(uri_0) - //{ - // Mode = mode, - //}; - Styles[1] = new StyleInclude(uri_1) - { - Source = uri_1, - }; + //var uri_1 = new Uri($"avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/Theme{themeName}.xaml"); var faTheme = AvaloniaLocator.Current.GetService(); if (faTheme != null) faTheme.RequestedTheme = themeName; + + var csTheme = AvaloniaLocator.Current.GetService(); + if (csTheme != null) + csTheme.Mode = themeName; + + //if (Resources.Count > 1) + //{ + // //Styles[0] = new FluentTheme(uri_0) + // //{ + // // Mode = mode, + // //}; + // Resources.MergedDictionaries[0] = (ResourceDictionary)AvaloniaXamlLoader.Load(uri_1); + //} } public static void SetThemeAccent(string? colorHex) diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/CustomTheme.cs b/src/ST.Client.Desktop.Avalonia/Application/UI/CustomTheme.cs new file mode 100644 index 000000000..98e4adf6a --- /dev/null +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/CustomTheme.cs @@ -0,0 +1,208 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml.Styling; +using Avalonia.Styling; +using FluentAvalonia.Styling; +using System; +using System.Collections.Generic; + +namespace System.Application.UI +{ + /// + /// Includes the fluent theme in an application. + /// + public class CustomTheme : AvaloniaObject, IStyle, IResourceProvider + { + private readonly Uri _baseUri; + private Styles _sharedStyles = new(); + private Styles _fluentDark = new(); + private Styles _fluentLight = new(); + private bool _isLoading; + private IStyle? _loaded; + private readonly ResourceDictionary _themeResources = new ResourceDictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The base URL for the XAML context. + public CustomTheme(Uri baseUri) + { + _baseUri = baseUri; + InitStyles(baseUri); + } + + /// + /// Initializes a new instance of the class. + /// + /// The XAML service provider. + public CustomTheme(IServiceProvider serviceProvider) + { + _baseUri = ((IUriContext)serviceProvider.GetService(typeof(IUriContext))).BaseUri; + InitStyles(_baseUri); + } + + public static readonly StyledProperty ModeProperty = + AvaloniaProperty.Register(nameof(Mode), "Dark"); + + /// + /// Gets or sets the mode of the fluent theme (light, dark). + /// + public string Mode + { + get => GetValue(ModeProperty); + set => SetValue(ModeProperty, value); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (_loaded is null) + { + // If style wasn't yet loaded, no need to change children styles, + // it will be applied later in Loaded getter. + return; + } + + if (change.Property == ModeProperty) + { + //if (Mode == FluentAvaloniaTheme.DarkModeString) + //{ + // //(Loaded as Styles)![0] = _fluentDark[0]; + //} + //else + //{ + // //(Loaded as Styles)![0] = _fluentLight[0]; + //} + + // Remove the old theme of any resources + + _themeResources.MergedDictionaries[0] = + (ResourceDictionary)AvaloniaXamlLoader.Load(new Uri($"avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/Theme{Mode}.xaml"), _baseUri); + + Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty); + } + } + + public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner; + + /// + /// Gets the loaded style. + /// + public IStyle Loaded + { + get + { + if (_loaded == null) + { + _isLoading = true; + + if (Mode == FluentAvaloniaTheme.LightModeString) + { + _loaded = new Styles() { _sharedStyles }; + } + else + { + _loaded = new Styles() { _sharedStyles }; + } + + _isLoading = false; + } + + return _loaded!; + } + } + + bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; + + IReadOnlyList IStyle.Children => _loaded?.Children ?? Array.Empty(); + + public event EventHandler? OwnerChanged + { + add + { + if (Loaded is IResourceProvider rp) + { + rp.OwnerChanged += value; + } + } + + remove + { + if (Loaded is IResourceProvider rp) + { + rp.OwnerChanged -= value; + } + } + } + + public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host); + + public bool TryGetResource(object key, out object? value) + { + // Github build failing with this not being set, even tho it passes locally + value = null; + + // We also search the app level resources so resources can be overridden. + // Do not search App level styles though as we'll have to iterate over them + // to skip the FluentAvaloniaTheme instance or we'll stack overflow + if (Avalonia.Application.Current?.Resources.TryGetResource(key, out value) == true) + return true; + + // This checks the actual ResourceDictionary where the SystemResources are stored + // and checks the merged dictionaries where base resources and theme resources are + if (_themeResources.TryGetResource(key, out value)) + return true; + + if (!_isLoading && Loaded is IResourceProvider p) + { + return p.TryGetResource(key, out value); + } + + value = null; + return false; + } + + void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); + + void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); + + private void InitStyles(Uri baseUri) + { + AvaloniaLocator.CurrentMutable.Bind().ToConstant(this); + + _sharedStyles = new Styles + { + new StyleInclude(baseUri) + { + Source = new Uri("avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/ThemeAccent.xaml") + }, + new StyleInclude(baseUri) + { + Source = new Uri("avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/Controls.xaml") + }, + }; + + //_fluentLight = new Styles + //{ + // new StyleInclude(baseUri) + // { + // Source = new Uri("avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/ThemeLight.xaml") + // } + //}; + + //_fluentDark = new Styles + //{ + // new StyleInclude(baseUri) + // { + // Source = new Uri("avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/ThemeDark.xaml") + // } + //}; + + // Theme resource colors/brushes + _themeResources.MergedDictionaries.Add( + (ResourceDictionary)AvaloniaXamlLoader.Load(new Uri($"avares://System.Application.SteamTools.Client.Avalonia/Application/UI/Styles/Theme{Mode}.xaml"), _baseUri)); + } + } +} diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Controls/DataGrid.xaml b/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Controls/DataGrid.xaml index cbc9a97af..169dd2935 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Controls/DataGrid.xaml +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/Controls/DataGrid.xaml @@ -1,10 +1,10 @@ - + - + @@ -16,9 +16,9 @@ - + --> @@ -79,7 +87,7 @@ + + diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/ThemeLight.xaml b/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/ThemeLight.xaml index 704a5a0be..b63d3c2ac 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/ThemeLight.xaml +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/Styles/ThemeLight.xaml @@ -1,135 +1,134 @@ - - - - - - - - + - - #AAFFFFFF - + #AAFFFFFF + - #EEF3F3F3 - + #EEF3F3F3 + - #FFB9B9C0 - + #FFB9B9C0 + - #FF363636 - + #FF363636 + - #FFF5F5F8 - + #FFF5F5F8 + - #FFB9B9C0 - + #FFB9B9C0 + - #FF101010 - + #FF101010 + - #FFFFFFFF - + #FFFFFFFF + - #FFB9B9C0 - + #FFB9B9C0 + - #FF000000 - + #FF000000 + - #FFEFEFF2 - + #FFEFEFF2 + - #FFD7D7DE - + #FFD7D7DE + - #FF808080 - + #FF808080 + - #FFA5A5AC - + #FFA5A5AC + - #FFA5A5AC - + #FFA5A5AC + - #FF000000 - + #FF000000 + - #FF696970 - + #FF696970 + - #FF696970 - + #FF696970 + - #FFFFFFFF - + #FFFFFFFF + - #FF4B4B52 - + #FF4B4B52 + - #FF4B4B52 - + #FF4B4B52 + - #FFFFFFFF - + #FFFFFFFF + - #FF0000C8 - + #FF0000C8 + - #FF007ACC - + #FF007ACC + - #FFC80000 - + #FFC80000 + - #33000000 - #C50500 - #FFE6E6E6 - #19000000 - #33000000 - #FFFFFFFF - #FF000000 - #66000000 - #FFF2F2F2 - #99000000 - #FFF2F2F2 + #33000000 + #C50500 + #FFE6E6E6 + #19000000 + #33000000 + #FFFFFFFF + #FF000000 + #66000000 + #FFF2F2F2 + #99000000 + #FFF2F2F2 - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Pages/Settings/Settings_UI.axaml b/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Pages/Settings/Settings_UI.axaml index aadcdaf4c..6e43680ac 100644 --- a/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Pages/Settings/Settings_UI.axaml +++ b/src/ST.Client.Desktop.Avalonia/Application/UI/Views/Pages/Settings/Settings_UI.axaml @@ -31,6 +31,13 @@