14 changed files with 253 additions and 25 deletions
@ -0,0 +1,29 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Styling |
|||
{ |
|||
/// <summary>
|
|||
/// The `&` nesting style selector.
|
|||
/// </summary>
|
|||
internal class NestingSelector : Selector |
|||
{ |
|||
public override bool InTemplate => false; |
|||
public override bool IsCombinator => false; |
|||
public override Type? TargetType => null; |
|||
|
|||
public override string ToString() => "&"; |
|||
|
|||
protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) |
|||
{ |
|||
if (parent is Style s && s.Selector is Selector selector) |
|||
{ |
|||
return selector.Match(control, null, subscribe); |
|||
} |
|||
|
|||
throw new InvalidOperationException( |
|||
"Nesting selector was specified but cannot determine parent selector."); |
|||
} |
|||
|
|||
protected override Selector? MovePrevious() => null; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System.Collections.ObjectModel; |
|||
|
|||
namespace Avalonia.Styling |
|||
{ |
|||
internal class StyleChildren : Collection<IStyle> |
|||
{ |
|||
private readonly Style _owner; |
|||
|
|||
public StyleChildren(Style owner) => _owner = owner; |
|||
|
|||
protected override void InsertItem(int index, IStyle item) |
|||
{ |
|||
base.InsertItem(index, item); |
|||
(item as Style)?.SetParent(_owner); |
|||
} |
|||
|
|||
protected override void RemoveItem(int index) |
|||
{ |
|||
(Items[index] as Style)?.SetParent(null); |
|||
base.RemoveItem(index); |
|||
} |
|||
|
|||
protected override void SetItem(int index, IStyle item) |
|||
{ |
|||
base.SetItem(index, item); |
|||
(item as Style)?.SetParent(_owner); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Styling; |
|||
using Avalonia.Styling.Activators; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Base.UnitTests.Styling |
|||
{ |
|||
public class SelectorTests_Nesting |
|||
{ |
|||
[Fact] |
|||
public void Parent_Selector_Doesnt_Match_OfType() |
|||
{ |
|||
var control = new Control2(); |
|||
Style nested; |
|||
var parent = new Style(x => x.OfType<Control1>()) |
|||
{ |
|||
Children = |
|||
{ |
|||
(nested = new Style(x => x.Nesting().Class("foo"))), |
|||
} |
|||
}; |
|||
|
|||
var match = nested.Selector.Match(control, parent); |
|||
Assert.Equal(SelectorMatchResult.NeverThisType, match.Result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Nested_Class_Selector() |
|||
{ |
|||
var control = new Control1 { Classes = { "foo" } }; |
|||
Style nested; |
|||
var parent = new Style(x => x.OfType<Control1>()) |
|||
{ |
|||
Children = |
|||
{ |
|||
(nested = new Style(x => x.Nesting().Class("foo"))), |
|||
} |
|||
}; |
|||
|
|||
var match = nested.Selector.Match(control, parent); |
|||
Assert.Equal(SelectorMatchResult.Sometimes, match.Result); |
|||
|
|||
var sink = new ActivatorSink(match.Activator); |
|||
|
|||
Assert.True(sink.Active); |
|||
control.Classes.Clear(); |
|||
Assert.False(sink.Active); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Nesting_With_No_Parent_Style_Fails() |
|||
{ |
|||
var control = new Control1(); |
|||
var style = new Style(x => x.Nesting().OfType<Control1>()); |
|||
|
|||
Assert.Throws<InvalidOperationException>(() => style.Selector.Match(control, null)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Nesting_With_No_Parent_Selector_Fails() |
|||
{ |
|||
var control = new Control1(); |
|||
Style nested; |
|||
var parent = new Style |
|||
{ |
|||
Children = |
|||
{ |
|||
(nested = new Style(x => x.Nesting().Class("foo"))), |
|||
} |
|||
}; |
|||
|
|||
Assert.Throws<InvalidOperationException>(() => nested.Selector.Match(control, parent)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Nesting_Must_Appear_At_Start_Of_Selector() |
|||
{ |
|||
var control = new Control1(); |
|||
Assert.Throws<InvalidOperationException>(() => new Style(x => x.OfType<Control1>().Nesting())); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Nesting_Must_Appear() |
|||
{ |
|||
var control = new Control1(); |
|||
Style nested; |
|||
var parent = new Style |
|||
{ |
|||
Children = |
|||
{ |
|||
(nested = new Style(x => x.OfType<Control1>().Class("foo"))), |
|||
} |
|||
}; |
|||
|
|||
Assert.Throws<InvalidOperationException>(() => nested.Selector.Match(control, parent)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Nesting_Must_Appear_In_All_Or_Arguments() |
|||
{ |
|||
var control = new Control1(); |
|||
Style nested; |
|||
var parent = new Style(x => x.OfType<Control1>()) |
|||
{ |
|||
Children = |
|||
{ |
|||
(nested = new Style(x => Selectors.Or( |
|||
x.Nesting().Class("foo"), |
|||
x.Class("bar")))) |
|||
} |
|||
}; |
|||
|
|||
Assert.Throws<InvalidOperationException>(() => nested.Selector.Match(control, parent)); |
|||
} |
|||
|
|||
public class Control1 : Control |
|||
{ |
|||
} |
|||
|
|||
public class Control2 : Control |
|||
{ |
|||
} |
|||
|
|||
private class ActivatorSink : IStyleActivatorSink |
|||
{ |
|||
public ActivatorSink(IStyleActivator source) => source.Subscribe(this); |
|||
public bool Active { get; private set; } |
|||
public void OnNext(bool value, int tag) => Active = value; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue