diff --git a/Perspex.Styling/Selectors.cs b/Perspex.Styling/Selectors.cs index ec0f1e51ec..e4ecf639c3 100644 --- a/Perspex.Styling/Selectors.cs +++ b/Perspex.Styling/Selectors.cs @@ -59,6 +59,13 @@ namespace Perspex.Styling return previous.OfType(typeof(T)); } + public static Selector Parent(this Selector previous) + { + Contract.Requires(previous != null); + + return new Selector(previous, x => MatchParent(x, previous), " < ", stopTraversal: true); + } + public static Selector Template(this Selector previous) { Contract.Requires(previous != null); @@ -128,6 +135,12 @@ namespace Perspex.Styling return new SelectorMatch(controlType == type); } + private static SelectorMatch MatchParent(IStyleable control, Selector previous) + { + var parent = ((ILogical)control).LogicalParent; + return previous.Match((IStyleable)parent); + } + private static SelectorMatch MatchTemplate(IStyleable control, Selector previous) { IStyleable templatedParent = control.TemplatedParent as IStyleable; diff --git a/Tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj b/Tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj index e9b57a6931..155d1ea704 100644 --- a/Tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj +++ b/Tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj @@ -83,6 +83,7 @@ + diff --git a/Tests/Perspex.Styling.UnitTests/SelectorTests_Parent.cs b/Tests/Perspex.Styling.UnitTests/SelectorTests_Parent.cs new file mode 100644 index 0000000000..e7b23d14cc --- /dev/null +++ b/Tests/Perspex.Styling.UnitTests/SelectorTests_Parent.cs @@ -0,0 +1,102 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Styling.UnitTests +{ + using System; + using System.Linq; + using System.Reactive.Linq; + using System.Threading.Tasks; + using Perspex.Collections; + using Perspex.Styling; + using Xunit; + + public class SelectorTests_Parent + { + [Fact] + public void Parent_Matches_Control_When_It_Is_Child_OfType() + { + var parent = new TestLogical1(); + var child = new TestLogical2(); + + child.LogicalParent = parent; + + var selector = new Selector().OfType().Parent().OfType(); + + Assert.True(selector.Match(child).ImmediateResult); + } + + [Fact] + public void Parent_Doesnt_Match_Control_When_It_Is_Grandchild_OfType() + { + var grandparent = new TestLogical1(); + var parent = new TestLogical2(); + var child = new TestLogical3(); + + parent.LogicalParent = grandparent; + child.LogicalParent = parent; + + var selector = new Selector().OfType().Parent().OfType(); + + Assert.False(selector.Match(child).ImmediateResult); + } + + [Fact] + public async Task Parent_Matches_Control_When_It_Is_Child_OfType_And_Class() + { + var parent = new TestLogical1(); + var child = new TestLogical2(); + + child.LogicalParent = parent; + + var selector = new Selector().OfType().Class("foo").Parent().OfType(); + var activator = selector.Match(child).ObservableResult; + + Assert.False(await activator.Take(1)); + parent.Classes.Add("foo"); + Assert.True(await activator.Take(1)); + parent.Classes.Remove("foo"); + Assert.False(await activator.Take(1)); + } + + public abstract class TestLogical : ILogical, IStyleable + { + public TestLogical() + { + this.Classes = new Classes(); + } + + public Classes Classes { get; } + public string Name { get; set; } + public IPerspexReadOnlyList LogicalChildren { get; set; } + public ILogical LogicalParent { get; set; } + public Type StyleKey { get; } + public ITemplatedControl TemplatedParent { get; } + + public IDisposable Bind(PerspexProperty property, IObservable source, BindingPriority priority) + { + throw new NotImplementedException(); + } + + public void SetValue(PerspexProperty property, object value, BindingPriority priority) + { + throw new NotImplementedException(); + } + } + + public class TestLogical1 : TestLogical + { + } + + public class TestLogical2 : TestLogical + { + } + + public class TestLogical3 : TestLogical + { + } + } +}