From b8e4be76a339d061264b3de7d5b899e6645a7afc Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 17 Apr 2025 05:08:06 -0400 Subject: [PATCH] Fix height queries not matching when container name is set, remove unused query grammer and allow nesting selector in container styles (#18659) * fix height queries not matching when container name is set, remove unused query grammer * add unit test for height container queries with name --------- Co-authored-by: Julien Lebosquain --- src/Avalonia.Base/Styling/NestingSelector.cs | 8 +++ src/Avalonia.Base/Styling/ScreenQueries.cs | 8 +-- .../Markup/Parsers/ContainerQueryGrammar.cs | 44 --------------- .../Styling/ContainerTests.cs | 53 ++++++++++++++++++- 4 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index 4a6b0cfda9..4f6920c224 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -27,6 +27,14 @@ namespace Avalonia.Styling SelectorMatch.AlwaysThisType : SelectorMatch.NeverThisType; } + else if (parent is ContainerQuery query && query.Parent is ControlTheme queryTheme) + { + if (queryTheme.TargetType is null) + throw new InvalidOperationException("ControlTheme has no TargetType."); + return queryTheme.TargetType.IsAssignableFrom(StyledElement.GetStyleKey(control)) ? + SelectorMatch.AlwaysThisType : + SelectorMatch.NeverThisType; + } throw new InvalidOperationException( "Nesting selector was specified but cannot determine parent selector."); diff --git a/src/Avalonia.Base/Styling/ScreenQueries.cs b/src/Avalonia.Base/Styling/ScreenQueries.cs index 9500b634f3..2c102768dd 100644 --- a/src/Avalonia.Base/Styling/ScreenQueries.cs +++ b/src/Avalonia.Base/Styling/ScreenQueries.cs @@ -64,7 +64,7 @@ namespace Avalonia.Styling } return IsTrue(argument.@operator, argument.value) ? - new SelectorMatch(SelectorMatchResult.AlwaysThisInstance) : SelectorMatch.NeverThisInstance; + SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; } public override string ToString() => "width"; @@ -90,7 +90,7 @@ namespace Avalonia.Styling if (subscribe) { - return new SelectorMatch(new HeightActivator(visual, Argument)); + return new SelectorMatch(new HeightActivator(visual, Argument, containerName)); } if (ContainerQueryActivatorBase.GetContainer(visual, containerName) is { } container @@ -104,9 +104,9 @@ namespace Avalonia.Styling return SelectorMatch.NeverThisInstance; } - internal static SelectorMatch Evaluate(VisualQueryProvider screenSizeProvider, (StyleQueryComparisonOperator @operator, double value) argument) + internal static SelectorMatch Evaluate(VisualQueryProvider queryProvider, (StyleQueryComparisonOperator @operator, double value) argument) { - var height = screenSizeProvider.Height; + var height = queryProvider.Height; if (double.IsNaN(height)) { return SelectorMatch.NeverThisInstance; diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ContainerQueryGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ContainerQueryGrammar.cs index b12a070a33..7573646651 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ContainerQueryGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/ContainerQueryGrammar.cs @@ -169,50 +169,6 @@ namespace Avalonia.Markup.Parsers return double.Parse(number.ToString()); } - - private static StyleQueryComparisonOperator ParseOperator(ref CharacterReader r) - { - r.SkipWhitespace(); - var queryOperator = r.TakeWhile(x => !char.IsWhiteSpace(x)); - - return queryOperator.ToString() switch - { - "=" => StyleQueryComparisonOperator.Equals, - "<" => StyleQueryComparisonOperator.LessThan, - ">" => StyleQueryComparisonOperator.GreaterThan, - "<=" => StyleQueryComparisonOperator.LessThanOrEquals, - ">=" => StyleQueryComparisonOperator.GreaterThanOrEquals, - "" => StyleQueryComparisonOperator.None, - _ => throw new ExpressionParseException(r.Position, $"Expected a comparison operator after.") - }; - } - - private static T ParseEnum(ref CharacterReader r) where T: struct - { - var identifier = r.ParseIdentifier(); - - if (Enum.TryParse(identifier.ToString(), true, out T value)) - return value; - - throw new ExpressionParseException(r.Position, $"Expected a {typeof(T)} after."); - } - - private static string ParseString(ref CharacterReader r) - { - return r.ParseIdentifier().ToString(); - } - - private static void Expect(ref CharacterReader r, char c) - { - if (r.End) - { - throw new ExpressionParseException(r.Position, $"Expected '{c}', got end of selector."); - } - else if (!r.TakeIf(')')) - { - throw new ExpressionParseException(r.Position, $"Expected '{c}', got '{r.Peek}'."); - } - } public class OrSyntax : ISyntax { diff --git a/tests/Avalonia.Base.UnitTests/Styling/ContainerTests.cs b/tests/Avalonia.Base.UnitTests/Styling/ContainerTests.cs index e3949a8788..a8cb137dcc 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/ContainerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/ContainerTests.cs @@ -111,7 +111,7 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Container_Queries_Matches_Name() + public void Container_Width_Queries_Matches_Name() { using var app = UnitTestApplication.Start(); var root = new LayoutTestRoot() @@ -160,5 +160,56 @@ namespace Avalonia.Base.UnitTests.Styling root.LayoutManager.ExecuteLayoutPass(); Assert.Equal(child.Width, 300.0); } + + [Fact] + public void Container_Height_Queries_Matches_Name() + { + using var app = UnitTestApplication.Start(); + var root = new LayoutTestRoot() + { + ClientSize = new Size(600, 600) + }; + var containerQuery1 = new ContainerQuery(x => new HeightQuery(x, StyleQueryComparisonOperator.LessThanOrEquals, 500)); + containerQuery1.Children.Add(new Style(x => x.Is()) + { + Setters = { new Setter(Control.HeightProperty, 200.0) } + }); + var containerQuery2 = new ContainerQuery(x => new HeightQuery(x, StyleQueryComparisonOperator.LessThanOrEquals, 500), "TEST"); + containerQuery2.Children.Add(new Style(x => x.Is()) + { + Setters = { new Setter(Control.HeightProperty, 300.0) } + }); + root.Styles.Add(containerQuery2); + root.Styles.Add(containerQuery1); + var child = new Border() + { + Name = "Child", + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch + }; + var controlInner = new ContentControl() + { + Width = 400, + Height = 400, + Content = child, + Name = "Inner" + }; + Container.SetSizing(controlInner, Avalonia.Styling.ContainerSizing.Height); + Container.SetName(controlInner, "TEST"); + var border = new Border() + { + HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch, + Child = controlInner, + Name = "Parent" + }; + Container.SetSizing(border, Avalonia.Styling.ContainerSizing.Height); + + root.Child = border; + + root.LayoutManager.ExecuteInitialLayoutPass(); + + root.LayoutManager.ExecuteLayoutPass(); + Assert.Equal(child.Height, 300.0); + } } }