Browse Source

Remove nesting selector validation.

Needs to be added later.
Steven Kirk 3 years ago
parent
commit
a91bad4d3b

+ 3 - 10
src/Avalonia.Base/Styling/Selector.cs

@@ -96,11 +96,7 @@ namespace Avalonia.Styling
             combinator = null;
 
             var activators = new AndActivatorBuilder();
-            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.");
+            var result = Match(control, start, parent, subscribe, ref activators, ref combinator);
 
             return result == SelectorMatchResult.Sometimes ?
                 new SelectorMatch(activators.Get()) :
@@ -113,8 +109,7 @@ namespace Avalonia.Styling
             IStyle? parent,
             bool subscribe,
             ref AndActivatorBuilder activators,
-            ref Selector? combinator,
-            ref bool foundNested)
+            ref Selector? combinator)
         {
             var previous = selector.MovePrevious();
 
@@ -123,7 +118,7 @@ namespace Avalonia.Styling
             // opportunity to exit early.
             if (previous != null && !previous.IsCombinator)
             {
-                var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator, ref foundNested);
+                var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator);
 
                 if (previousMatch < SelectorMatchResult.Sometimes)
                 {
@@ -131,8 +126,6 @@ namespace Avalonia.Styling
                 }
             }
 
-            foundNested |= selector is NestingSelector;
-
             // Match this selector.
             var match = selector.Evaluate(control, parent, subscribe);
 

+ 0 - 4
src/Avalonia.Base/Styling/Selectors.cs

@@ -111,10 +111,6 @@ namespace Avalonia.Styling
 
         public static Selector Nesting(this Selector? previous)
         {
-            if (previous is not null)
-                throw new InvalidOperationException(
-                    "Nesting selector '&' must appear at the start of the style selector.");
-
             return new NestingSelector();
         }
 

+ 11 - 1
src/Avalonia.Base/Styling/Style.cs

@@ -169,6 +169,16 @@ namespace Avalonia.Styling
             }
         }
 
-        internal void SetParent(Style? parent) => Parent = parent;
+        internal void SetParent(Style? parent)
+        {
+            if (parent?.Selector is not null)
+            {
+                if (Selector is null)
+                    throw new InvalidOperationException("Nested styles must have a selector.");
+                // TODO: Validate that selector contains & in the right place.
+            }
+
+            Parent = parent;
+        }
     }
 }

+ 74 - 31
tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Base.UnitTests.Styling
     public class SelectorTests_Nesting
     {
         [Fact]
-        public void Parent_Selector_Doesnt_Match_OfType()
+        public void Nesting_Class_Doesnt_Match_Parent_Selector()
         {
             var control = new Control2();
             Style nested;
@@ -26,43 +26,50 @@ namespace Avalonia.Base.UnitTests.Styling
         }
 
         [Fact]
-        public void Nested_Class_Selector()
+        public void Or_Nesting_Class_Doesnt_Match_Parent_Selector()
         {
-            var control = new Control1 { Classes = { "foo" } };
+            var control = new Control2();
             Style nested;
             var parent = new Style(x => x.OfType<Control1>())
             {
                 Children =
                 {
-                    (nested = new Style(x => x.Nesting().Class("foo"))),
+                    (nested = new Style(x => Selectors.Or(
+                        x.Nesting().Class("foo"),
+                        x.Nesting().Class("bar")))),
                 }
             };
 
             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);
+            Assert.Equal(SelectorMatchResult.NeverThisType, match.Result);
         }
 
         [Fact]
-        public void Nesting_With_No_Parent_Style_Fails()
+        public void Or_Nesting_Child_OfType_Does_Not_Match_Parent_Selector()
         {
             var control = new Control1();
-            var style = new Style(x => x.Nesting().OfType<Control1>());
+            var panel = new DockPanel { Children = { control } };
+            Style nested;
+            var parent = new Style(x => x.OfType<Panel>())
+            {
+                Children =
+                {
+                    (nested = new Style(x => Selectors.Or(
+                        x.Nesting().Child().OfType<Control1>(),
+                        x.Nesting().Child().OfType<Control1>()))),
+                }
+            };
 
-            Assert.Throws<InvalidOperationException>(() => style.Selector.Match(control, null));
+            var match = nested.Selector.Match(control, parent);
+            Assert.Equal(SelectorMatchResult.NeverThisInstance, match.Result);
         }
 
         [Fact]
-        public void Nesting_With_No_Parent_Selector_Fails()
+        public void Nesting_Class_Matches()
         {
-            var control = new Control1();
+            var control = new Control1 { Classes = { "foo" } };
             Style nested;
-            var parent = new Style
+            var parent = new Style(x => x.OfType<Control1>())
             {
                 Children =
                 {
@@ -70,44 +77,80 @@ namespace Avalonia.Base.UnitTests.Styling
                 }
             };
 
-            Assert.Throws<InvalidOperationException>(() => nested.Selector.Match(control, parent));
+            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_Must_Appear_At_Start_Of_Selector()
+        public void Or_Nesting_Class_Matches()
         {
-            var control = new Control1();
-            Assert.Throws<InvalidOperationException>(() => new Style(x => x.OfType<Control1>().Nesting()));
+            var control = new Control1 { Classes = { "foo" } };
+            Style nested;
+            var parent = new Style(x => x.OfType<Control1>())
+            {
+                Children =
+                {
+                    (nested = new Style(x => Selectors.Or(
+                        x.Nesting().Class("foo"),
+                        x.Nesting().Class("bar")))),
+                }
+            };
+
+            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_Must_Appear()
+        public void Or_Nesting_Child_OfType_Matches()
         {
-            var control = new Control1();
+            var control = new Control1 { Classes = { "foo" } };
+            var panel = new Panel { Children = { control } };
             Style nested;
-            var parent = new Style
+            var parent = new Style(x => x.OfType<Panel>())
             {
                 Children =
                 {
-                    (nested = new Style(x => x.OfType<Control1>().Class("foo"))),
+                    (nested = new Style(x => Selectors.Or(
+                        x.Nesting().Child().OfType<Control1>(),
+                        x.Nesting().Child().OfType<Control1>()))),
                 }
             };
 
-            Assert.Throws<InvalidOperationException>(() => nested.Selector.Match(control, parent));
+            var match = nested.Selector.Match(control, parent);
+            Assert.Equal(SelectorMatchResult.AlwaysThisInstance, match.Result);
         }
 
         [Fact]
-        public void Nesting_Must_Appear_In_All_Or_Arguments()
+        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(x => x.OfType<Control1>())
+            var parent = new Style
             {
                 Children =
                 {
-                    (nested = new Style(x => Selectors.Or(
-                        x.Nesting().Class("foo"),
-                        x.Class("bar"))))
+                    (nested = new Style(x => x.Nesting().Class("foo"))),
                 }
             };
 

+ 42 - 0
tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs

@@ -722,6 +722,48 @@ namespace Avalonia.Base.UnitTests.Styling
             resources.Verify(x => x.AddOwner(host.Object), Times.Once);
         }
 
+        [Fact]
+        public void Nested_Style_Can_Be_Added()
+        {
+            var parent = new Style(x => x.OfType<Class1>());
+            var nested = new Style(x => x.Nesting().Class("foo"));
+
+            parent.Children.Add(nested);
+
+            Assert.Same(parent, nested.Parent);
+        }
+
+        [Fact]
+        public void Nested_Or_Style_Can_Be_Added()
+        {
+            var parent = new Style(x => x.OfType<Class1>());
+            var nested = new Style(x => Selectors.Or(
+                x.Nesting().Class("foo"),
+                x.Nesting().Class("bar")));
+
+            parent.Children.Add(nested);
+
+            Assert.Same(parent, nested.Parent);
+        }
+
+        [Fact]
+        public void Nested_Style_Without_Selector_Throws()
+        {
+            var parent = new Style(x => x.OfType<Class1>());
+            var nested = new Style();
+
+            Assert.Throws<InvalidOperationException>(() => parent.Children.Add(nested));
+        }
+
+        [Fact(Skip = "TODO")]
+        public void Nested_Style_Without_Nesting_Operator_Throws()
+        {
+            var parent = new Style(x => x.OfType<Class1>());
+            var nested = new Style(x => x.Class("foo"));
+
+            Assert.Throws<InvalidOperationException>(() => parent.Children.Add(nested));
+        }
+
         private class Class1 : Control
         {
             public static readonly StyledProperty<string> FooProperty =