diff --git a/samples/ControlCatalog/Pages/ThemePage.axaml.cs b/samples/ControlCatalog/Pages/ThemePage.axaml.cs index af7b2fe37d..f0ae1a722d 100644 --- a/samples/ControlCatalog/Pages/ThemePage.axaml.cs +++ b/samples/ControlCatalog/Pages/ThemePage.axaml.cs @@ -18,7 +18,7 @@ namespace ControlCatalog.Pages selector.Items = new[] { - new ThemeVariant("Default"), + ThemeVariant.Default, ThemeVariant.Dark, ThemeVariant.Light, Pink diff --git a/src/Avalonia.Base/Styling/ThemeVariant.cs b/src/Avalonia.Base/Styling/ThemeVariant.cs index d9cb123925..8218533f4f 100644 --- a/src/Avalonia.Base/Styling/ThemeVariant.cs +++ b/src/Avalonia.Base/Styling/ThemeVariant.cs @@ -5,20 +5,60 @@ using Avalonia.Platform; namespace Avalonia.Styling; +/// +/// Specifies a UI theme variant that should be used for the +/// [TypeConverter(typeof(ThemeVariantTypeConverter))] -public sealed record ThemeVariant(object Key) -{ +public sealed record ThemeVariant +{ + /// + /// Creates a new instance of the + /// + /// Key of the theme variant by which variants are compared. + /// Reference to a theme variant which should be used, if resource wasn't found for the requested variant. + /// Thrown if inheritVariant is a reference to the which is ambiguous value to inherit. + /// Thrown if key is null. public ThemeVariant(object key, ThemeVariant? inheritVariant) - : this(key) { + Key = key ?? throw new ArgumentNullException(nameof(key)); InheritVariant = inheritVariant; + + if (inheritVariant == Default) + { + throw new ArgumentException("Inheriting default theme variant is not supported.", nameof(inheritVariant)); + } } + private ThemeVariant(object key) + { + Key = key; + } + + /// + /// Key of the theme variant by which variants are compared. + /// + public object Key { get; } + + /// + /// Reference to a theme variant which should be used, if resource wasn't found for the requested variant. + /// + public ThemeVariant? InheritVariant { get; } + + /// + /// Inherit theme variant from the parent. If set on Application, system theme is inherited. + /// Using Default as the ResourceDictionary.Key marks this dictionary as a fallback in case the theme variant or resource key is not found in other theme dictionaries. + /// public static ThemeVariant Default { get; } = new(nameof(Default)); + + /// + /// Use the Light theme variant. + /// public static ThemeVariant Light { get; } = new(nameof(Light)); - public static ThemeVariant Dark { get; } = new(nameof(Dark)); - public ThemeVariant? InheritVariant { get; init; } + /// + /// Use the Dark theme variant. + /// + public static ThemeVariant Dark { get; } = new(nameof(Dark)); public override string ToString() { @@ -35,7 +75,7 @@ public sealed record ThemeVariant(object Key) return Key == other?.Key; } - public static ThemeVariant FromPlatformThemeVariant(PlatformThemeVariant themeVariant) + public static explicit operator ThemeVariant(PlatformThemeVariant themeVariant) { return themeVariant switch { @@ -45,19 +85,19 @@ public sealed record ThemeVariant(object Key) }; } - public PlatformThemeVariant? ToPlatformThemeVariant() + public static explicit operator PlatformThemeVariant?(ThemeVariant themeVariant) { - if (this == Light) + if (themeVariant == Light) { return PlatformThemeVariant.Light; } - else if (this == Dark) + else if (themeVariant == Dark) { return PlatformThemeVariant.Dark; } - else if (InheritVariant is { } inheritVariant) + else if (themeVariant.InheritVariant is { } inheritVariant) { - return inheritVariant.ToPlatformThemeVariant(); + return (PlatformThemeVariant?)inheritVariant; } return null; diff --git a/src/Avalonia.Base/Styling/ThemeVariantTypeConverter.cs b/src/Avalonia.Base/Styling/ThemeVariantTypeConverter.cs index 4da1b495f5..acb2d7651b 100644 --- a/src/Avalonia.Base/Styling/ThemeVariantTypeConverter.cs +++ b/src/Avalonia.Base/Styling/ThemeVariantTypeConverter.cs @@ -15,9 +15,10 @@ public class ThemeVariantTypeConverter : TypeConverter { return value switch { + nameof(ThemeVariant.Default) => ThemeVariant.Default, nameof(ThemeVariant.Light) => ThemeVariant.Light, nameof(ThemeVariant.Dark) => ThemeVariant.Dark, - _ => new ThemeVariant(value) + _ => throw new NotSupportedException("ThemeVariant type converter supports only build in variants. For custom variants please use x:Static markup extension.") }; } } diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 3dcba4ded9..6d3ba3cf8a 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -340,7 +340,7 @@ namespace Avalonia private void OnColorValuesChanged(object? sender, PlatformColorValues e) { - SetValue(ActualThemeVariantProperty, ThemeVariant.FromPlatformThemeVariant(e.ThemeVariant), BindingPriority.Template); + SetValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant, BindingPriority.Template); } } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 7fe82a452e..676fa1519a 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -435,7 +435,7 @@ namespace Avalonia.Controls } else if (change.Property == ActualThemeVariantProperty) { - PlatformImpl?.SetFrameThemeVariant(change.GetNewValue().ToPlatformThemeVariant() ?? PlatformThemeVariant.Light); + PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)change.GetNewValue() ?? PlatformThemeVariant.Light); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs index 365a07a7f6..4068caac21 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -302,13 +302,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions result = new XamlStaticExtensionNode(new XamlAstObjectNode(node, node.Type), themeVariantTypeRef, foundConstProperty.Name); return true; } - - result = new XamlAstNewClrObjectNode(node, themeVariantTypeRef, types.ThemeVariantConstructor, - new List() - { - new XamlConstantNode(node, context.Configuration.WellKnownTypes.String, variantText) - }); - return true; } result = null; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index a4a3bcce94..16f6a32ae1 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -69,7 +69,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType Thickness { get; } public IXamlConstructor ThicknessFullConstructor { get; } public IXamlType ThemeVariant { get; } - public IXamlConstructor ThemeVariantConstructor { get; } public IXamlType Point { get; } public IXamlConstructor PointFullConstructor { get; } public IXamlType Vector { get; } @@ -193,7 +192,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily"); FontFamilyConstructorUriName = FontFamily.GetConstructor(new List { Uri, XamlIlTypes.String }); ThemeVariant = cfg.TypeSystem.GetType("Avalonia.Styling.ThemeVariant"); - ThemeVariantConstructor = ThemeVariant.GetConstructor(new List { XamlIlTypes.String }); (IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount) { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/ThemeDictionariesTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ThemeDictionariesTests.cs similarity index 97% rename from tests/Avalonia.Markup.Xaml.UnitTests/ThemeDictionariesTests.cs rename to tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ThemeDictionariesTests.cs index 56040c2186..c5b62cdff2 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/ThemeDictionariesTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ThemeDictionariesTests.cs @@ -6,10 +6,12 @@ using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Markup.Xaml.UnitTests; +namespace Avalonia.Markup.Xaml.UnitTests.Xaml; public class ThemeDictionariesTests : XamlTestBase { + public static ThemeVariant Custom { get; } = new(nameof(Custom), ThemeVariant.Light); + [Fact] public void DynamicResource_Updated_When_Control_Theme_Changed() { @@ -353,13 +355,14 @@ public class ThemeDictionariesTests : XamlTestBase Assert.Equal(Colors.Red, ((ISolidColorBrush)border.Background)!.Color); } - + [Fact] public void Custom_Theme_Can_Be_Defined_In_ThemeDictionaries() { var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@" @@ -370,7 +373,7 @@ public class ThemeDictionariesTests : XamlTestBase White - + Pink @@ -380,9 +383,9 @@ public class ThemeDictionariesTests : XamlTestBase "); var border = (Border)themeVariantScope.Child!; - - themeVariantScope.RequestedThemeVariant = new ThemeVariant("Custom"); + themeVariantScope.RequestedThemeVariant = Custom; + Assert.Equal(Colors.Pink, ((ISolidColorBrush)border.Background)!.Color); }