Browse Source

Allow only well known ThemeVariant values in xaml compiler, add more documentation.

Max Katz 2 years ago
parent
commit
35662ad4cb

+ 1 - 1
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

+ 51 - 11
src/Avalonia.Base/Styling/ThemeVariant.cs

@@ -5,20 +5,60 @@ using Avalonia.Platform;
 
 namespace Avalonia.Styling;
 
+/// <summary>
+/// Specifies a UI theme variant that should be used for the 
+/// </summary>
 [TypeConverter(typeof(ThemeVariantTypeConverter))]
-public sealed record ThemeVariant(object Key)
-{ 
+public sealed record ThemeVariant
+{
+    /// <summary>
+    /// Creates a new instance of the <see cref="ThemeVariant"/>
+    /// </summary>
+    /// <param name="key">Key of the theme variant by which variants are compared.</param>
+    /// <param name="inheritVariant">Reference to a theme variant which should be used, if resource wasn't found for the requested variant.</param>
+    /// <exception cref="ArgumentException">Thrown if inheritVariant is a reference to the <see cref="ThemeVariant.Default"/> which is ambiguous value to inherit.</exception>
+    /// <exception cref="ArgumentNullException">Thrown if key is null.</exception>
     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;
+    }
+    
+    /// <summary>
+    /// Key of the theme variant by which variants are compared.
+    /// </summary>
+    public object Key { get; }
+    
+    /// <summary>
+    /// Reference to a theme variant which should be used, if resource wasn't found for the requested variant.
+    /// </summary>
+    public ThemeVariant? InheritVariant { get; }
+
+    /// <summary>
+    /// 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.
+    /// </summary>
     public static ThemeVariant Default { get; } = new(nameof(Default));
+
+    /// <summary>
+    /// Use the Light theme variant.
+    /// </summary>
     public static ThemeVariant Light { get; } = new(nameof(Light));
-    public static ThemeVariant Dark { get; } = new(nameof(Dark));
 
-    public ThemeVariant? InheritVariant { get; init; }
+    /// <summary>
+    /// Use the Dark theme variant.
+    /// </summary>
+    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;

+ 2 - 1
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.")
         };
     }
 }

+ 1 - 1
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);
         }
     }
 }

+ 1 - 1
src/Avalonia.Controls/TopLevel.cs

@@ -435,7 +435,7 @@ namespace Avalonia.Controls
             }
             else if (change.Property == ActualThemeVariantProperty)
             {
-                PlatformImpl?.SetFrameThemeVariant(change.GetNewValue<ThemeVariant>().ToPlatformThemeVariant() ?? PlatformThemeVariant.Light);
+                PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)change.GetNewValue<ThemeVariant>() ?? PlatformThemeVariant.Light);
             }
         }
         

+ 0 - 7
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<IXamlAstValueNode>()
-                    {
-                        new XamlConstantNode(node, context.Configuration.WellKnownTypes.String, variantText)
-                    });
-                return true;
             }
 
             result = null;

+ 0 - 2
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<IXamlType> { Uri, XamlIlTypes.String });
             ThemeVariant = cfg.TypeSystem.GetType("Avalonia.Styling.ThemeVariant");
-            ThemeVariantConstructor = ThemeVariant.GetConstructor(new List<IXamlType> { XamlIlTypes.String });
 
             (IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount)
             {

+ 8 - 5
tests/Avalonia.Markup.Xaml.UnitTests/ThemeDictionariesTests.cs → 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(@"
 <ThemeVariantScope xmlns='https://github.com/avaloniaui'
               xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+              xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'
               RequestedThemeVariant='Light'>
     <ThemeVariantScope.Resources>
         <ResourceDictionary>
@@ -370,7 +373,7 @@ public class ThemeDictionariesTests : XamlTestBase
                 <ResourceDictionary x:Key='Light'>
                     <SolidColorBrush x:Key='DemoBackground'>White</SolidColorBrush>
                 </ResourceDictionary>
-                <ResourceDictionary x:Key='Custom'>
+                <ResourceDictionary x:Key='{x:Static local:ThemeDictionariesTests.Custom}'>
                     <SolidColorBrush x:Key='DemoBackground'>Pink</SolidColorBrush>
                 </ResourceDictionary>
             </ResourceDictionary.ThemeDictionaries>
@@ -380,9 +383,9 @@ public class ThemeDictionariesTests : XamlTestBase
     <Border Name='border' Background='{DynamicResource DemoBackground}'/>
 </ThemeVariantScope>");
         var border = (Border)themeVariantScope.Child!;
-        
-        themeVariantScope.RequestedThemeVariant = new ThemeVariant("Custom");
 
+        themeVariantScope.RequestedThemeVariant = Custom;
+        
         Assert.Equal(Colors.Pink, ((ISolidColorBrush)border.Background)!.Color);
     }