|
|
@@ -33,22 +33,25 @@ namespace Avalonia.Styling
|
|
|
/// Tries to match the selector with a control.
|
|
|
/// </summary>
|
|
|
/// <param name="control">The control.</param>
|
|
|
+ /// <param name="parent">
|
|
|
+ /// The parent style, if the style containing the selector is a nested style.
|
|
|
+ /// </param>
|
|
|
/// <param name="subscribe">
|
|
|
/// Whether the match should subscribe to changes in order to track the match over time,
|
|
|
/// or simply return an immediate result.
|
|
|
/// </param>
|
|
|
/// <returns>A <see cref="SelectorMatch"/>.</returns>
|
|
|
- public SelectorMatch Match(IStyleable control, bool subscribe = true)
|
|
|
+ public SelectorMatch Match(IStyleable control, IStyle? parent = null, bool subscribe = true)
|
|
|
{
|
|
|
// First match the selector until a combinator is found. Selectors are stored from
|
|
|
// right-to-left, so MatchUntilCombinator reverses this order because the type selector
|
|
|
// will be on the left.
|
|
|
- var match = MatchUntilCombinator(control, this, subscribe, out var combinator);
|
|
|
+ var match = MatchUntilCombinator(control, this, parent, subscribe, out var combinator);
|
|
|
|
|
|
// If the pre-combinator selector matches, we can now match the combinator, if any.
|
|
|
if (match.IsMatch && combinator is object)
|
|
|
{
|
|
|
- match = match.And(combinator.Match(control, subscribe));
|
|
|
+ match = match.And(combinator.Match(control, parent, subscribe));
|
|
|
|
|
|
// If we have a combinator then we can never say that we always match a control of
|
|
|
// this type, because by definition the combinator matches on things outside of the
|
|
|
@@ -68,12 +71,15 @@ namespace Avalonia.Styling
|
|
|
/// Evaluates the selector for a match.
|
|
|
/// </summary>
|
|
|
/// <param name="control">The control.</param>
|
|
|
+ /// <param name="parent">
|
|
|
+ /// The parent style, if the style containing the selector is a nested style.
|
|
|
+ /// </param>
|
|
|
/// <param name="subscribe">
|
|
|
/// Whether the match should subscribe to changes in order to track the match over time,
|
|
|
/// or simply return an immediate result.
|
|
|
/// </param>
|
|
|
/// <returns>A <see cref="SelectorMatch"/>.</returns>
|
|
|
- protected abstract SelectorMatch Evaluate(IStyleable control, bool subscribe);
|
|
|
+ protected abstract SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe);
|
|
|
|
|
|
/// <summary>
|
|
|
/// Moves to the previous selector.
|
|
|
@@ -83,13 +89,18 @@ namespace Avalonia.Styling
|
|
|
private static SelectorMatch MatchUntilCombinator(
|
|
|
IStyleable control,
|
|
|
Selector start,
|
|
|
+ IStyle? parent,
|
|
|
bool subscribe,
|
|
|
out Selector? combinator)
|
|
|
{
|
|
|
combinator = null;
|
|
|
|
|
|
var activators = new AndActivatorBuilder();
|
|
|
- var result = Match(control, start, subscribe, ref activators, ref combinator);
|
|
|
+ var foundNested = false;
|
|
|
+ var result = Match(control, start, parent, subscribe, ref activators, ref combinator, ref foundNested);
|
|
|
+
|
|
|
+ if (parent is not null && !foundNested)
|
|
|
+ throw new InvalidOperationException("Nesting selector '&' must appear in child selector.");
|
|
|
|
|
|
return result == SelectorMatchResult.Sometimes ?
|
|
|
new SelectorMatch(activators.Get()) :
|
|
|
@@ -99,9 +110,11 @@ namespace Avalonia.Styling
|
|
|
private static SelectorMatchResult Match(
|
|
|
IStyleable control,
|
|
|
Selector selector,
|
|
|
+ IStyle? parent,
|
|
|
bool subscribe,
|
|
|
ref AndActivatorBuilder activators,
|
|
|
- ref Selector? combinator)
|
|
|
+ ref Selector? combinator,
|
|
|
+ ref bool foundNested)
|
|
|
{
|
|
|
var previous = selector.MovePrevious();
|
|
|
|
|
|
@@ -110,7 +123,7 @@ namespace Avalonia.Styling
|
|
|
// opportunity to exit early.
|
|
|
if (previous != null && !previous.IsCombinator)
|
|
|
{
|
|
|
- var previousMatch = Match(control, previous, subscribe, ref activators, ref combinator);
|
|
|
+ var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator, ref foundNested);
|
|
|
|
|
|
if (previousMatch < SelectorMatchResult.Sometimes)
|
|
|
{
|
|
|
@@ -118,8 +131,10 @@ namespace Avalonia.Styling
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ foundNested |= selector is NestingSelector;
|
|
|
+
|
|
|
// Match this selector.
|
|
|
- var match = selector.Evaluate(control, subscribe);
|
|
|
+ var match = selector.Evaluate(control, parent, subscribe);
|
|
|
|
|
|
if (!match.IsMatch)
|
|
|
{
|