Browse Source

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

pull/8166/head
Max Katz 4 years ago
parent
commit
35662ad4cb
  1. 2
      samples/ControlCatalog/Pages/ThemePage.axaml.cs
  2. 62
      src/Avalonia.Base/Styling/ThemeVariant.cs
  3. 3
      src/Avalonia.Base/Styling/ThemeVariantTypeConverter.cs
  4. 2
      src/Avalonia.Controls/Application.cs
  5. 2
      src/Avalonia.Controls/TopLevel.cs
  6. 7
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
  7. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  8. 13
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ThemeDictionariesTests.cs

2
samples/ControlCatalog/Pages/ThemePage.axaml.cs

@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
selector.Items = new[] selector.Items = new[]
{ {
new ThemeVariant("Default"), ThemeVariant.Default,
ThemeVariant.Dark, ThemeVariant.Dark,
ThemeVariant.Light, ThemeVariant.Light,
Pink Pink

62
src/Avalonia.Base/Styling/ThemeVariant.cs

@ -5,20 +5,60 @@ using Avalonia.Platform;
namespace Avalonia.Styling; namespace Avalonia.Styling;
/// <summary>
/// Specifies a UI theme variant that should be used for the
/// </summary>
[TypeConverter(typeof(ThemeVariantTypeConverter))] [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) public ThemeVariant(object key, ThemeVariant? inheritVariant)
: this(key)
{ {
Key = key ?? throw new ArgumentNullException(nameof(key));
InheritVariant = inheritVariant; 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)); 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 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() public override string ToString()
{ {
@ -35,7 +75,7 @@ public sealed record ThemeVariant(object Key)
return Key == other?.Key; return Key == other?.Key;
} }
public static ThemeVariant FromPlatformThemeVariant(PlatformThemeVariant themeVariant) public static explicit operator ThemeVariant(PlatformThemeVariant themeVariant)
{ {
return themeVariant switch 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; return PlatformThemeVariant.Light;
} }
else if (this == Dark) else if (themeVariant == Dark)
{ {
return PlatformThemeVariant.Dark; return PlatformThemeVariant.Dark;
} }
else if (InheritVariant is { } inheritVariant) else if (themeVariant.InheritVariant is { } inheritVariant)
{ {
return inheritVariant.ToPlatformThemeVariant(); return (PlatformThemeVariant?)inheritVariant;
} }
return null; return null;

3
src/Avalonia.Base/Styling/ThemeVariantTypeConverter.cs

@ -15,9 +15,10 @@ public class ThemeVariantTypeConverter : TypeConverter
{ {
return value switch return value switch
{ {
nameof(ThemeVariant.Default) => ThemeVariant.Default,
nameof(ThemeVariant.Light) => ThemeVariant.Light, nameof(ThemeVariant.Light) => ThemeVariant.Light,
nameof(ThemeVariant.Dark) => ThemeVariant.Dark, 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.")
}; };
} }
} }

2
src/Avalonia.Controls/Application.cs

@ -340,7 +340,7 @@ namespace Avalonia
private void OnColorValuesChanged(object? sender, PlatformColorValues e) private void OnColorValuesChanged(object? sender, PlatformColorValues e)
{ {
SetValue(ActualThemeVariantProperty, ThemeVariant.FromPlatformThemeVariant(e.ThemeVariant), BindingPriority.Template); SetValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant, BindingPriority.Template);
} }
} }
} }

2
src/Avalonia.Controls/TopLevel.cs

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

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); result = new XamlStaticExtensionNode(new XamlAstObjectNode(node, node.Type), themeVariantTypeRef, foundConstProperty.Name);
return true; return true;
} }
result = new XamlAstNewClrObjectNode(node, themeVariantTypeRef, types.ThemeVariantConstructor,
new List<IXamlAstValueNode>()
{
new XamlConstantNode(node, context.Configuration.WellKnownTypes.String, variantText)
});
return true;
} }
result = null; result = null;

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 IXamlType Thickness { get; }
public IXamlConstructor ThicknessFullConstructor { get; } public IXamlConstructor ThicknessFullConstructor { get; }
public IXamlType ThemeVariant { get; } public IXamlType ThemeVariant { get; }
public IXamlConstructor ThemeVariantConstructor { get; }
public IXamlType Point { get; } public IXamlType Point { get; }
public IXamlConstructor PointFullConstructor { get; } public IXamlConstructor PointFullConstructor { get; }
public IXamlType Vector { get; } public IXamlType Vector { get; }
@ -193,7 +192,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily"); FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily");
FontFamilyConstructorUriName = FontFamily.GetConstructor(new List<IXamlType> { Uri, XamlIlTypes.String }); FontFamilyConstructorUriName = FontFamily.GetConstructor(new List<IXamlType> { Uri, XamlIlTypes.String });
ThemeVariant = cfg.TypeSystem.GetType("Avalonia.Styling.ThemeVariant"); ThemeVariant = cfg.TypeSystem.GetType("Avalonia.Styling.ThemeVariant");
ThemeVariantConstructor = ThemeVariant.GetConstructor(new List<IXamlType> { XamlIlTypes.String });
(IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount) (IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount)
{ {

13
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 Moq;
using Xunit; using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests; namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
public class ThemeDictionariesTests : XamlTestBase public class ThemeDictionariesTests : XamlTestBase
{ {
public static ThemeVariant Custom { get; } = new(nameof(Custom), ThemeVariant.Light);
[Fact] [Fact]
public void DynamicResource_Updated_When_Control_Theme_Changed() public void DynamicResource_Updated_When_Control_Theme_Changed()
{ {
@ -353,13 +355,14 @@ public class ThemeDictionariesTests : XamlTestBase
Assert.Equal(Colors.Red, ((ISolidColorBrush)border.Background)!.Color); Assert.Equal(Colors.Red, ((ISolidColorBrush)border.Background)!.Color);
} }
[Fact] [Fact]
public void Custom_Theme_Can_Be_Defined_In_ThemeDictionaries() public void Custom_Theme_Can_Be_Defined_In_ThemeDictionaries()
{ {
var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@" var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
<ThemeVariantScope xmlns='https://github.com/avaloniaui' <ThemeVariantScope xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' 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'> RequestedThemeVariant='Light'>
<ThemeVariantScope.Resources> <ThemeVariantScope.Resources>
<ResourceDictionary> <ResourceDictionary>
@ -370,7 +373,7 @@ public class ThemeDictionariesTests : XamlTestBase
<ResourceDictionary x:Key='Light'> <ResourceDictionary x:Key='Light'>
<SolidColorBrush x:Key='DemoBackground'>White</SolidColorBrush> <SolidColorBrush x:Key='DemoBackground'>White</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Key='Custom'> <ResourceDictionary x:Key='{x:Static local:ThemeDictionariesTests.Custom}'>
<SolidColorBrush x:Key='DemoBackground'>Pink</SolidColorBrush> <SolidColorBrush x:Key='DemoBackground'>Pink</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
@ -380,9 +383,9 @@ public class ThemeDictionariesTests : XamlTestBase
<Border Name='border' Background='{DynamicResource DemoBackground}'/> <Border Name='border' Background='{DynamicResource DemoBackground}'/>
</ThemeVariantScope>"); </ThemeVariantScope>");
var border = (Border)themeVariantScope.Child!; var border = (Border)themeVariantScope.Child!;
themeVariantScope.RequestedThemeVariant = new ThemeVariant("Custom");
themeVariantScope.RequestedThemeVariant = Custom;
Assert.Equal(Colors.Pink, ((ISolidColorBrush)border.Background)!.Color); Assert.Equal(Colors.Pink, ((ISolidColorBrush)border.Background)!.Color);
} }
Loading…
Cancel
Save