Browse Source

Fix parent selectors with /template/ at end.

Previously the combinator state was lost when traversing nested selectors. To fix it, instead of running the parent selector in `NestingSelector.Evaluate`, return the parent selector with `MovePrevious`. This required `MovePrevious` to be aware of parent styles and because I had to change its signature, I also made it internal as it doesn't need to be a public API.
pull/8263/head
Steven Kirk 4 years ago
parent
commit
e50b416d5b
  1. 2
      src/Avalonia.Base/Styling/ChildSelector.cs
  2. 2
      src/Avalonia.Base/Styling/DescendentSelector.cs
  3. 8
      src/Avalonia.Base/Styling/NestingSelector.cs
  4. 2
      src/Avalonia.Base/Styling/NotSelector.cs
  5. 2
      src/Avalonia.Base/Styling/NthChildSelector.cs
  6. 2
      src/Avalonia.Base/Styling/OrSelector.cs
  7. 2
      src/Avalonia.Base/Styling/PropertyEqualsSelector.cs
  8. 12
      src/Avalonia.Base/Styling/Selector.cs
  9. 2
      src/Avalonia.Base/Styling/TemplateSelector.cs
  10. 2
      src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
  11. 80
      tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs

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

@ -64,7 +64,7 @@ namespace Avalonia.Styling
}
}
protected override Selector? MovePrevious() => null;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? nestingParent) => (null, null);
protected override Selector? MovePreviousOrParent() => _parent;
}
}

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

@ -69,7 +69,7 @@ namespace Avalonia.Styling
}
}
protected override Selector? MovePrevious() => null;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? nestingParent) => (null, null);
protected override Selector? MovePreviousOrParent() => _parent;
}
}

8
src/Avalonia.Base/Styling/NestingSelector.cs

@ -17,7 +17,7 @@ namespace Avalonia.Styling
{
if (parent is Style s && s.Selector is not null)
{
return s.Selector.Match(control, s.Parent, subscribe);
return SelectorMatch.AlwaysThisType;
}
else if (parent is ControlTheme theme)
{
@ -32,7 +32,11 @@ namespace Avalonia.Styling
"Nesting selector was specified but cannot determine parent selector.");
}
protected override Selector? MovePrevious() => null;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? parent)
{
return parent is Style parentStyle ? (parentStyle.Selector, parentStyle.Parent) : (null, null);
}
protected override Selector? MovePreviousOrParent() => null;
}
}

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

@ -66,7 +66,7 @@ namespace Avalonia.Styling
}
}
protected override Selector? MovePrevious() => _previous;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? nestingParent) => (_previous, nestingParent);
protected override Selector? MovePreviousOrParent() => _previous;
}
}

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

@ -104,7 +104,7 @@ namespace Avalonia.Styling
return match ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
}
protected override Selector? MovePrevious() => _previous;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? nestingParent) => (_previous, nestingParent);
protected override Selector? MovePreviousOrParent() => _previous;
public override string ToString()

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

@ -102,7 +102,7 @@ namespace Avalonia.Styling
}
}
protected override Selector? MovePrevious() => null;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? nestingParent) => (null, null);
protected override Selector? MovePreviousOrParent() => null;
internal override void ValidateNestingSelector(bool inControlTheme)

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

@ -89,7 +89,7 @@ namespace Avalonia.Styling
}
protected override Selector? MovePrevious() => _previous;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? nestingParent) => (_previous, nestingParent);
protected override Selector? MovePreviousOrParent() => _previous;
internal static bool Compare(Type propertyType, object? propertyValue, object? value)

12
src/Avalonia.Base/Styling/Selector.cs

@ -84,7 +84,13 @@ namespace Avalonia.Styling
/// <summary>
/// Moves to the previous selector.
/// </summary>
protected abstract Selector? MovePrevious();
/// <param name="nestingParent">
/// The parent style, if the selector is on a nested style.
/// </param>
/// <remarks>
/// The previous selector, and its nesting parent.
/// </remarks>
private protected abstract (Selector?, IStyle?) MovePrevious(IStyle? nestingParent);
/// <summary>
/// Moves to the previous selector or the parent selector.
@ -142,14 +148,14 @@ namespace Avalonia.Styling
ref AndActivatorBuilder activators,
ref Selector? combinator)
{
var previous = selector.MovePrevious();
var (previous, previousParent) = selector.MovePrevious(parent);
// Selectors are stored from right-to-left, so we recurse into the selector in order to
// reverse this order, because the type selector will be on the left and is our best
// opportunity to exit early.
if (previous != null && !previous.IsCombinator)
{
var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator);
var previousMatch = Match(control, previous, previousParent, subscribe, ref activators, ref combinator);
if (previousMatch < SelectorMatchResult.Sometimes)
{

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

@ -48,7 +48,7 @@ namespace Avalonia.Styling
return _parent.Match(templatedParent, parent, subscribe);
}
protected override Selector? MovePrevious() => null;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? nestingParent) => (null, null);
protected override Selector? MovePreviousOrParent() => _parent;
}
}

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

@ -139,7 +139,7 @@ namespace Avalonia.Styling
return Name == null ? SelectorMatch.AlwaysThisType : SelectorMatch.AlwaysThisInstance;
}
protected override Selector? MovePrevious() => _previous;
private protected override (Selector?, IStyle?) MovePrevious(IStyle? nestingParent) => (_previous, nestingParent);
protected override Selector? MovePreviousOrParent() => _previous;
private string BuildSelectorString()

80
tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs

@ -1,5 +1,6 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Styling;
using Avalonia.Styling.Activators;
using Xunit;
@ -148,6 +149,85 @@ namespace Avalonia.Base.UnitTests.Styling
control.Classes.Remove("foo");
Assert.False(sink.Active);
}
[Fact]
public void Template_Nesting_OfType_Matches()
{
var control = new Control1 { Classes = { "foo" } };
var button = new Button
{
Template = new FuncControlTemplate((x, _) => control),
};
button.ApplyTemplate();
Style nested;
var parent = new Style(x => x.OfType<Button>().Template())
{
Children =
{
(nested = new Style(x => x.Nesting().OfType<Control1>())),
}
};
// REMOOOOVEEEE
var foo = new Style(x => x.OfType<Button>().Template().OfType<Control1>());
var match2 = foo.Selector.Match(control, parent);
Assert.Equal(SelectorMatchResult.AlwaysThisInstance, match2.Result);
var match = nested.Selector.Match(control, parent);
Assert.Equal(SelectorMatchResult.AlwaysThisInstance, match.Result);
}
[Fact]
public void Template_Nesting_OfType_Class_Matches()
{
var control = new Control1 { Classes = { "foo" } };
var button = new Button
{
Template = new FuncControlTemplate((x, _) => control),
};
button.ApplyTemplate();
Style nested;
var parent = new Style(x => x.OfType<Button>().Template())
{
Children =
{
(nested = new Style(x => x.Nesting().OfType<Control1>().Class("foo"))),
}
};
var match = nested.Selector.Match(control, parent);
Assert.Equal(SelectorMatchResult.Sometimes, match.Result);
}
[Fact]
public void Class_Template_Nesting_OfType_Matches()
{
var control = new Control1 { Classes = { "foo" } };
var button = new Button
{
Template = new FuncControlTemplate((x, _) => control),
};
button.ApplyTemplate();
Style nested;
var parent = new Style(x => x.OfType<Button>().Class("bar").Template())
{
Children =
{
(nested = new Style(x => x.Nesting().OfType<Control1>())),
}
};
var match = nested.Selector.Match(control, parent);
Assert.Equal(SelectorMatchResult.Sometimes, match.Result);
}
[Fact]
public void Or_Nesting_Class_Matches()

Loading…
Cancel
Save