Browse Source

WIP: Hackfix for #9007

fixes/9007-priority-in-control-templates
Steven Kirk 2 years ago
parent
commit
b03ae24798
  1. 30
      src/Avalonia.Base/PropertyStore/FramePriority.cs
  2. 4
      src/Avalonia.Base/PropertyStore/ValueFrame.cs
  3. 7
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  4. 2
      src/Avalonia.Base/Styling/ControlTheme.cs
  5. 2
      src/Avalonia.Base/Styling/Style.cs
  6. 4
      src/Avalonia.Base/Styling/StyleBase.cs
  7. 5
      src/Avalonia.Base/Styling/StyleInstance.cs
  8. 2
      tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs
  9. 36
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

30
src/Avalonia.Base/PropertyStore/FramePriority.cs

@ -1,4 +1,5 @@
using System.Diagnostics; using System;
using System.Diagnostics;
using Avalonia.Data; using Avalonia.Data;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
@ -8,12 +9,15 @@ namespace Avalonia.PropertyStore
Animation, Animation,
AnimationTemplatedParentTheme, AnimationTemplatedParentTheme,
AnimationTheme, AnimationTheme,
StyleTrigger, TemplateStyleTrigger,
StyleTriggerTemplatedParentTheme, TemplateStyleTriggerTemplatedParentTheme,
StyleTriggerTheme, TemplateStyleTriggerTheme,
Template, Template,
TemplateTemplatedParentTheme, TemplateTemplatedParentTheme,
TemplateTheme, TemplateTheme,
StyleTrigger,
StyleTriggerTemplatedParentTheme,
StyleTriggerTheme,
Style, Style,
StyleTemplatedParentTheme, StyleTemplatedParentTheme,
StyleTheme, StyleTheme,
@ -21,11 +25,21 @@ namespace Avalonia.PropertyStore
internal static class FramePriorityExtensions internal static class FramePriorityExtensions
{ {
public static FramePriority ToFramePriority(this BindingPriority priority, FrameType type = FrameType.Style) public static FramePriority ToFramePriority(
this BindingPriority priority,
FrameType type = FrameType.Style,
bool isTemplateSelector = false)
{ {
Debug.Assert(priority != BindingPriority.LocalValue); var p = (priority, isTemplateSelector) switch
var p = (int)(priority > 0 ? priority : priority + 1); {
return (FramePriority)(p * 3 + (int)type); (BindingPriority.Animation, _) => FramePriority.Animation,
(BindingPriority.StyleTrigger, false) => FramePriority.StyleTrigger,
(BindingPriority.StyleTrigger, true) => FramePriority.TemplateStyleTrigger,
(BindingPriority.Template, _) => FramePriority.StyleTrigger,
(BindingPriority.Style, _) => FramePriority.Style,
_ => throw new ArgumentException("Invalid priority."),
};
return (FramePriority)((int)p + (int)type);
} }
public static bool IsType(this FramePriority priority, FrameType type) public static bool IsType(this FramePriority priority, FrameType type)

4
src/Avalonia.Base/PropertyStore/ValueFrame.cs

@ -20,10 +20,10 @@ namespace Avalonia.PropertyStore
private ValueStore? _owner; private ValueStore? _owner;
private bool _isShared; private bool _isShared;
protected ValueFrame(BindingPriority priority, FrameType type) protected ValueFrame(BindingPriority priority, FrameType type, bool isTemplateSelector = false)
{ {
Priority = priority; Priority = priority;
FramePriority = priority.ToFramePriority(type); FramePriority = priority.ToFramePriority(type, isTemplateSelector);
} }
public int EntryCount => _index.Count; public int EntryCount => _index.Count;

7
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -772,7 +772,7 @@ namespace Avalonia.PropertyStore
Debug.Assert(oldValue != newValue); Debug.Assert(oldValue != newValue);
Debug.Assert(oldValue is not null || newValue is not null); Debug.Assert(oldValue is not null || newValue is not null);
// If the value is set locally, propagaton ends here. // If the value is set locally, propagation ends here.
if (_effectiveValues.ContainsKey(property) == true) if (_effectiveValues.ContainsKey(property) == true)
return; return;
@ -825,7 +825,7 @@ namespace Avalonia.PropertyStore
if (current?.Priority < priority && current?.BasePriority < priority) if (current?.Priority < priority && current?.BasePriority < priority)
break; break;
// Try to get an entry from the frame for the property we're reevaluating. // Try to get an entry from the frame for the property we're re-evaluating.
var foundEntry = frame.TryGetEntryIfActive(property, out var entry, out var activeChanged); var foundEntry = frame.TryGetEntryIfActive(property, out var entry, out var activeChanged);
// If the active state of the frame has changed since the last read, and // If the active state of the frame has changed since the last read, and
@ -855,6 +855,9 @@ namespace Avalonia.PropertyStore
AddEffectiveValue(property, current); AddEffectiveValue(property, current);
current.SetAndRaise(this, entry, priority); current.SetAndRaise(this, entry, priority);
} }
if (priority > BindingPriority.Animation)
break;
} }
if (generation != _frameGeneration) if (generation != _frameGeneration)

2
src/Avalonia.Base/Styling/ControlTheme.cs

@ -48,7 +48,7 @@ namespace Avalonia.Styling
if (HasSettersOrAnimations && TargetType.IsAssignableFrom(StyledElement.GetStyleKey(target))) if (HasSettersOrAnimations && TargetType.IsAssignableFrom(StyledElement.GetStyleKey(target)))
{ {
Attach(target, null, type); Attach(target, null, type, false);
return SelectorMatchResult.AlwaysThisType; return SelectorMatchResult.AlwaysThisType;
} }

2
src/Avalonia.Base/Styling/Style.cs

@ -74,7 +74,7 @@ namespace Avalonia.Styling
if (match.IsMatch) if (match.IsMatch)
{ {
Attach(target, match.Activator, type); Attach(target, match.Activator, type, Selector?.InTemplate ?? false);
} }
result = match.Result; result = match.Result;

4
src/Avalonia.Base/Styling/StyleBase.cs

@ -92,7 +92,7 @@ namespace Avalonia.Styling
return false; return false;
} }
internal ValueFrame Attach(StyledElement target, IStyleActivator? activator, FrameType type) internal ValueFrame Attach(StyledElement target, IStyleActivator? activator, FrameType type, bool isTemplateSelector)
{ {
if (target is not AvaloniaObject ao) if (target is not AvaloniaObject ao)
throw new InvalidOperationException("Styles can only be applied to AvaloniaObjects."); throw new InvalidOperationException("Styles can only be applied to AvaloniaObjects.");
@ -107,7 +107,7 @@ namespace Avalonia.Styling
{ {
var canShareInstance = activator is null; var canShareInstance = activator is null;
instance = new StyleInstance(this, activator, type); instance = new StyleInstance(this, activator, type, isTemplateSelector);
if (_setters is not null) if (_setters is not null)
{ {

5
src/Avalonia.Base/Styling/StyleInstance.cs

@ -29,8 +29,9 @@ namespace Avalonia.Styling
public StyleInstance( public StyleInstance(
IStyle style, IStyle style,
IStyleActivator? activator, IStyleActivator? activator,
FrameType type) FrameType type,
: base(GetPriority(activator), type) bool isTemplateSelector)
: base(GetPriority(activator), type, isTemplateSelector)
{ {
_activator = activator; _activator = activator;
Source = style; Source = style;

2
tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs

@ -117,7 +117,7 @@ namespace Avalonia.Base.UnitTests.PropertyStore
private static StyleInstance InstanceStyle(Style style, StyledElement target) private static StyleInstance InstanceStyle(Style style, StyledElement target)
{ {
var result = new StyleInstance(style, null, FrameType.Style); var result = new StyleInstance(style, null, FrameType.Style, false);
foreach (var setter in style.Setters) foreach (var setter in style.Setters)
result.Add(setter.Instance(result, target)); result.Add(setter.Instance(result, target));

36
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

@ -5,6 +5,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Diagnostics; using Avalonia.Diagnostics;
using Avalonia.Layout;
using Avalonia.Markup.Xaml.Templates; using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.UnitTests; using Avalonia.UnitTests;
@ -246,6 +247,41 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
} }
} }
[Fact]
public void Foo()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<ControlTheme x:Key='{x:Type Button}' TargetType='Button'>
<Style Selector='^:disabled'>
<Setter Property='Background' Value='LightGray'/>
</Style>
</ControlTheme>
</Window.Resources>
<TemplatedControl Background='LightBlue' IsEnabled='False'>
<TemplatedControl.Template>
<ControlTemplate TargetType='TemplatedControl'>
<Button Background='{TemplateBinding Background}'/>
</ControlTemplate>
</TemplatedControl.Template>
</TemplatedControl>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
window.Show();
var control = (TemplatedControl)window.Content;
var button = Assert.IsType<Button>(control.GetVisualChildren().Single());
var brush = Assert.IsAssignableFrom<ISolidColorBrush>(button.Background);
Assert.Equal(Colors.LightBlue, brush.Color);
}
}
[Fact] [Fact]
public void ControlTemplate_With_Nested_Child_Is_Operational() public void ControlTemplate_With_Nested_Child_Is_Operational()
{ {

Loading…
Cancel
Save