Browse Source

Call ApplyStyling unconditionally in MeasureCore (#21572)

* Add unit test for styling affecting measurements in WindowDrawnDecorationsContent

* Refactor layout measurement to apply styling before visibility checks

* Add core measure test

* Add StackPanel test

* Call ApplyStyling unconditionally in MeasureCore

---------

Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
pull/22024/head
羊撅撅 1 month ago
committed by GitHub
parent
commit
055e123b5c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 3
      src/Avalonia.Base/Layout/Layoutable.cs
  2. 5
      src/Avalonia.Controls/StackPanel.cs
  3. 29
      tests/Avalonia.Base.UnitTests/Layout/MeasureTests.cs
  4. 34
      tests/Avalonia.Controls.UnitTests/StackPanelTests.cs
  5. 77
      tests/Avalonia.Controls.UnitTests/WindowTests.cs

3
src/Avalonia.Base/Layout/Layoutable.cs

@ -543,6 +543,8 @@ namespace Avalonia.Layout
/// </remarks>
protected virtual Size MeasureCore(Size availableSize)
{
ApplyStyling();
if (IsVisible)
{
var margin = Margin;
@ -555,7 +557,6 @@ namespace Avalonia.Layout
margin = LayoutHelper.RoundLayoutThickness(margin, scale);
}
ApplyStyling();
ApplyTemplate();
var minMax = new MinMax(this);

5
src/Avalonia.Controls/StackPanel.cs

@ -261,6 +261,9 @@ namespace Avalonia.Controls
// Get next child.
var child = children[i];
// Measure the child.
child.Measure(layoutSlotSize);
bool isVisible = child.IsVisible;
if (isVisible && !hasVisibleChild)
@ -268,8 +271,6 @@ namespace Avalonia.Controls
hasVisibleChild = true;
}
// Measure the child.
child.Measure(layoutSlotSize);
Size childDesiredSize = child.DesiredSize;
// Accumulate child size.

29
tests/Avalonia.Base.UnitTests/Layout/MeasureTests.cs

@ -1,10 +1,39 @@
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Base.UnitTests.Layout
{
public class MeasureTests
{
[Fact]
public void Style_Hiding_Control_Should_Be_Applied_Before_Measuring()
{
var child = new Border
{
Width = 100,
Height = 100,
Classes = { "hidden" }
};
var target = new Decorator
{
Child = child
};
var root = new TestRoot(target);
root.Styles.Add(new Style(x => x.OfType<Border>().Class("hidden"))
{
Setters = { new Setter(Visual.IsVisibleProperty, false) }
});
target.Measure(Size.Infinity);
Assert.False(child.IsVisible);
Assert.Equal(new Size(0,0), child.DesiredSize);
Assert.Equal(new Size(0,0), target.DesiredSize);
}
[Fact]
public void Margin_Should_Be_Included_In_DesiredSize()
{

34
tests/Avalonia.Controls.UnitTests/StackPanelTests.cs

@ -1,5 +1,6 @@
using System.Linq;
using Avalonia.Layout;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
@ -330,6 +331,39 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(sizeWithTwoChildren, sizeWithThreeChildren);
}
[Theory]
[InlineData(Orientation.Horizontal)]
[InlineData(Orientation.Vertical)]
public void Spacing_Not_Added_For_Children_Hidden_By_Style_Applied_During_Measure(Orientation orientation)
{
var target = new StackPanel
{
Spacing = 40,
Orientation = orientation,
Children =
{
new StackPanel { Width = 10, Height = 10, Classes = { "hidden" } },
new StackPanel { Width = 10, Height = 10 },
new StackPanel { Width = 10, Height = 10 },
}
};
var root = new TestRoot(target);
root.Styles.Add(new Style(x => x.OfType<StackPanel>().Class("hidden"))
{
Setters = { new Setter(Visual.IsVisibleProperty, false) }
});
target.Measure(Size.Infinity);
var expected = orientation == Orientation.Horizontal ?
new Size(60, 10) :
new Size(10, 60);
Assert.Equal(expected, target.DesiredSize);
}
[Theory]
[InlineData(Orientation.Horizontal)]
[InlineData(Orientation.Vertical)]

77
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -1642,6 +1642,83 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void IsVisible_Setter_Should_Affect_Measurements_Inside_WindowDrawnDecorationsContent()
{
using var app = UnitTestApplication.Start(TestServices.StyledWindow);
var windowImpl = MockWindowingPlatform.CreateWindowMock();
windowImpl.Setup(x => x.NeedsManagedDecorations).Returns(true);
windowImpl.Setup(x => x.RequestedDrawnDecorations).Returns(PlatformRequestedDrawnDecoration.TitleBar);
var window = new Window(windowImpl.Object);
var stackPanel = new StackPanel
{
Width = 32,
Spacing = 2,
Children =
{
new Control { Height = 32 },
new Control
{
Height = 32,
Classes = { "hidden-by-style" }
},
}
};
var contentControl = new ContentControl
{
Content = new Control
{
Height = 32,
Width = 32,
Classes = { "hidden-by-style" }
}
};
var content = new WindowDrawnDecorationsContent
{
Overlay = new ContentControl
{
Content = new Panel
{
Children = { stackPanel, contentControl }
}
}
};
var template = new WindowDrawnDecorationsTemplate
{
Content = (IServiceProvider? _) => new TemplateResult<WindowDrawnDecorationsContent>(content, new NameScope())
};
var theme = new ControlTheme(typeof(WindowDrawnDecorations))
{
Setters =
{
new Setter(WindowDrawnDecorations.TemplateProperty, template)
}
};
var style = new Style(x => x.Is<WindowDrawnDecorations>().Template().OfType<Control>().Class("hidden-by-style"))
{
Setters =
{
new Setter(Visual.IsVisibleProperty, false)
}
};
window.WindowDecorationsTheme = theme;
window.Styles.Add(style);
window.Show();
window.Measure(Size.Infinity);
Assert.Equal(new Size(), contentControl.DesiredSize);
Assert.Equal(new Size(32, 32), stackPanel.DesiredSize);
}
public class TitleBarDecorationsTests : ScopedTestBase
{
private static Window CreateWindowWithDrawnDecorations(PlatformAllowedWindowActions allowedActions = PlatformAllowedWindowActions.All)

Loading…
Cancel
Save