diff --git a/Perspex.UnitTests/Styling/SelectorTests_Descendent.cs b/Perspex.UnitTests/Styling/SelectorTests_Descendent.cs index 70cf926e31..6c5571ad19 100644 --- a/Perspex.UnitTests/Styling/SelectorTests_Descendent.cs +++ b/Perspex.UnitTests/Styling/SelectorTests_Descendent.cs @@ -23,10 +23,7 @@ namespace Perspex.UnitTests.Styling var parent = new Mock(); var child = new Mock(); - parent.Setup(x => x.LogicalChildren).Returns(new[] - { - child.Object, - }); + child.Setup(x => x.LogicalParent).Returns(parent.Object); var selector = new Selector().OfType().Descendent().OfType(); @@ -36,7 +33,16 @@ namespace Perspex.UnitTests.Styling [TestMethod] public void Descendent_Matches_Control_When_It_Is_Descendent_OfType() { - Assert.Fail(); + var grandparent = new Mock(); + var parent = new Mock(); + var child = new Mock(); + + parent.Setup(x => x.LogicalParent).Returns(grandparent.Object); + child.Setup(x => x.LogicalParent).Returns(parent.Object); + + var selector = new Selector().OfType().Descendent().OfType(); + + Assert.AreEqual(true, ActivatorValue(selector, child.Object)); } private static bool ActivatorValue(Match selector, IStyleable control) @@ -51,5 +57,9 @@ namespace Perspex.UnitTests.Styling public abstract class TestLogical2 : TestLogical { } + + public abstract class TestLogical3 : TestLogical + { + } } } diff --git a/Perspex/Styling/Selector.cs b/Perspex/Styling/Selector.cs index 0ab8095d4a..4e3d8f2416 100644 --- a/Perspex/Styling/Selector.cs +++ b/Perspex/Styling/Selector.cs @@ -15,15 +15,18 @@ namespace Perspex.Styling public class Selector { + private bool stopTraversal; + private Func> observable; public Selector() { } - public Selector(Selector previous) + public Selector(Selector previous, bool stopTraversal = false) { this.Previous = previous; + this.stopTraversal = stopTraversal; } public Func> Observable @@ -42,7 +45,7 @@ namespace Perspex.Styling public Selector Previous { get; - set; + private set; } public string SelectorString @@ -51,6 +54,11 @@ namespace Perspex.Styling set; } + public Selector MovePrevious() + { + return this.stopTraversal ? null : this.Previous; + } + public Activator GetActivator(IStyleable control) { List> inputs = new List>(); @@ -63,7 +71,7 @@ namespace Perspex.Styling inputs.Add(selector.Observable(control)); } - selector = selector.Previous; + selector = selector.MovePrevious(); } return new Activator(inputs); @@ -71,16 +79,14 @@ namespace Perspex.Styling public override string ToString() { - Selector match = this; - StringBuilder b = new StringBuilder(); + string result = string.Empty; - while (match != null) + if (this.Previous != null) { - b.Append(match.SelectorString); - match = match.Previous; + result = this.Previous.ToString(); } - return b.ToString(); + return result + this.SelectorString; } } } diff --git a/Perspex/Styling/Selectors.cs b/Perspex/Styling/Selectors.cs index 7841e48c62..8191dec65b 100644 --- a/Perspex/Styling/Selectors.cs +++ b/Perspex/Styling/Selectors.cs @@ -7,17 +7,18 @@ namespace Perspex.Styling { using System; + using System.Collections.Generic; using System.Reactive.Linq; using Perspex.Controls; public static class Selectors { - public static Selector Class(this Selector match, string name) + public static Selector Class(this Selector previous, string name) { - Contract.Requires(match != null); + Contract.Requires(previous != null); Contract.Requires(name != null); - return new Selector(match) + return new Selector(previous) { Observable = control => Observable .Return(control.Classes.Contains(name)) @@ -26,39 +27,60 @@ namespace Perspex.Styling }; } - public static Selector Descendent(this Selector match) + public static Selector Descendent(this Selector previous) { - throw new NotImplementedException(); + return new Selector(previous, stopTraversal: true) + { + SelectorString = " ", + Observable = control => + { + ILogical c = (ILogical)control; + List> descendentMatches = new List>(); + + while (c != null) + { + c = c.LogicalParent; + + if (c is IStyleable) + { + descendentMatches.Add(previous.Observable((IStyleable)c)); + } + } + + return new Activator(descendentMatches, ActivatorMode.Or); + }, + }; } - public static Selector Id(this Selector match, string id) + public static Selector Id(this Selector previous, string id) { - Contract.Requires(match != null); + Contract.Requires(previous != null); - return new Selector(match) + return new Selector(previous) { Observable = control => Observable.Return(control.Id == id), SelectorString = '#' + id, }; } - public static Selector OfType(this Selector match) where T : IStyleable + public static Selector OfType(this Selector previous) where T : IStyleable { - Contract.Requires(match != null); + Contract.Requires(previous != null); - return new Selector(match) + return new Selector(previous) { Observable = control => Observable.Return(control is T), SelectorString = typeof(T).Name, }; } - public static Selector Template(this Selector match) + public static Selector Template(this Selector previous) { - Contract.Requires(match != null); + Contract.Requires(previous != null); - return new Selector(match) + return new Selector(previous) { + Observable = control => Observable.Return(control.TemplatedParent != null), SelectorString = " $ ", }; }