Browse Source

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 <julien@lebosquain.net>
pull/18679/head
Emmanuel Hansen 10 months ago
committed by GitHub
parent
commit
b8e4be76a3
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 8
      src/Avalonia.Base/Styling/NestingSelector.cs
  2. 8
      src/Avalonia.Base/Styling/ScreenQueries.cs
  3. 44
      src/Markup/Avalonia.Markup/Markup/Parsers/ContainerQueryGrammar.cs
  4. 53
      tests/Avalonia.Base.UnitTests/Styling/ContainerTests.cs

8
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.");

8
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;

44
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<T>(ref CharacterReader r) where T: struct
{
var identifier = r.ParseIdentifier();
if (Enum.TryParse<T>(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
{

53
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<Border>())
{
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<Border>())
{
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);
}
}
}

Loading…
Cancel
Save