Avalonia icon indicating copy to clipboard operation
Avalonia copied to clipboard

Fix RelativePanel crash inside StackPanel

Open byme8 opened this issue 1 year ago • 3 comments

What does the pull request do?

Fix for https://github.com/AvaloniaUI/Avalonia/issues/8624

What is the current behavior?

When you put RelativePanel inside the StackPanel, the RelativePanel will try to take all available space. In the case of the vertical StackPanel, it would try to take infinite height and fail because of it.

What is the updated/expected behavior with this PR?

The Grid, WrapPanel, and DockPanel completely ignore the infinite size, They will limit their height based on the child component's height. The same behavior is applied to the RelativePanel.

Fixed issues

Fixes #8624

byme8 avatar Aug 11 '22 18:08 byme8

CLA assistant check
All CLA requirements met.

dnfadmin avatar Aug 11 '22 18:08 dnfadmin

When I was working on the issue, I found peculiar logic. In the RelativePanel.MeasureOverride. it measures and resets measurements multiple times. Does someone have idea why it happens?

// RelativePanel.cs
protected override Size MeasureOverride(Size availableSize)
{
    _childGraph.Clear();
    foreach (Layoutable child in Children)
    {
        if (child == null)
        {
            continue;
        }

        var node = _childGraph.AddNode(child);

        node.AlignLeftWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignLeftWithProperty, child));
        node.AlignTopWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignTopWithProperty, child));
        node.AlignRightWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignRightWithProperty, child));
        node.AlignBottomWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignBottomWithProperty, child));

        node.LeftOfNode = _childGraph.AddLink(node, GetDependencyElement(LeftOfProperty, child));
        node.AboveNode = _childGraph.AddLink(node, GetDependencyElement(AboveProperty, child));
        node.RightOfNode = _childGraph.AddLink(node, GetDependencyElement(RightOfProperty, child));
        node.BelowNode = _childGraph.AddLink(node, GetDependencyElement(BelowProperty, child));

        node.AlignHorizontalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignHorizontalCenterWithProperty, child));
        node.AlignVerticalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignVerticalCenterWithProperty, child));
    }

    _childGraph.Measure(availableSize); // <== measure first time

    _childGraph.Reset(false);  // <== reset
    var calcWidth = Width.IsNaN() && (HorizontalAlignment != HorizontalAlignment.Stretch);
    var calcHeight = Height.IsNaN() && (VerticalAlignment != VerticalAlignment.Stretch);

    var boundingSize = _childGraph.GetBoundingSize(calcWidth, calcHeight);
    _childGraph.Reset();  // <== reset
    _childGraph.Measure(boundingSize);  // <== measure second time
    
    return boundingSize;
}

byme8 avatar Aug 11 '22 18:08 byme8

You can test this PR using the following package version. 0.10.999-cibuild0023117-beta. (feed url: https://nuget.avaloniaui.net/repository/avalonia-all/index.json) [PRBUILDID]

avaloniaui-team avatar Aug 11 '22 18:08 avaloniaui-team

When I was working on the issue, I found peculiar logic. In the RelativePanel.MeasureOverride. it measures and resets measurements multiple times. Does someone have idea why it happens?

// RelativePanel.cs
protected override Size MeasureOverride(Size availableSize)
{
    _childGraph.Clear();
    foreach (Layoutable child in Children)
    {
        if (child == null)
        {
            continue;
        }

        var node = _childGraph.AddNode(child);

        node.AlignLeftWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignLeftWithProperty, child));
        node.AlignTopWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignTopWithProperty, child));
        node.AlignRightWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignRightWithProperty, child));
        node.AlignBottomWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignBottomWithProperty, child));

        node.LeftOfNode = _childGraph.AddLink(node, GetDependencyElement(LeftOfProperty, child));
        node.AboveNode = _childGraph.AddLink(node, GetDependencyElement(AboveProperty, child));
        node.RightOfNode = _childGraph.AddLink(node, GetDependencyElement(RightOfProperty, child));
        node.BelowNode = _childGraph.AddLink(node, GetDependencyElement(BelowProperty, child));

        node.AlignHorizontalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignHorizontalCenterWithProperty, child));
        node.AlignVerticalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignVerticalCenterWithProperty, child));
    }

    _childGraph.Measure(availableSize); // <== measure first time

    _childGraph.Reset(false);  // <== reset
    var calcWidth = Width.IsNaN() && (HorizontalAlignment != HorizontalAlignment.Stretch);
    var calcHeight = Height.IsNaN() && (VerticalAlignment != VerticalAlignment.Stretch);

    var boundingSize = _childGraph.GetBoundingSize(calcWidth, calcHeight);
    _childGraph.Reset();  // <== reset
    _childGraph.Measure(boundingSize);  // <== measure second time
    
    return boundingSize;
}

Ported from: https://github.com/HandyOrg/HandyControl/blob/master/src/Shared/HandyControl_Shared/Controls/Panel/RelativePanel.cs#L220-L228

The reset call has an argument and has different behavior.

danwalmsley avatar Aug 15 '22 20:08 danwalmsley

@byme8 any chance to add a unit test here?

https://github.com/AvaloniaUI/Avalonia/blob/master/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs

danwalmsley avatar Aug 15 '22 21:08 danwalmsley

You can test this PR using the following package version. 0.10.999-cibuild0023221-beta. (feed url: https://nuget.avaloniaui.net/repository/avalonia-all/index.json) [PRBUILDID]

avaloniaui-team avatar Aug 15 '22 21:08 avaloniaui-team

@byme8 any chance to add a unit test here?

https://github.com/AvaloniaUI/Avalonia/blob/master/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs

I will add tests

byme8 avatar Aug 16 '22 05:08 byme8

@danwalmsley I added tests for a stretched relative panel.

byme8 avatar Aug 17 '22 16:08 byme8

You can test this PR using the following package version. 0.10.999-cibuild0023266-beta. (feed url: https://nuget.avaloniaui.net/repository/avalonia-all/index.json) [PRBUILDID]

avaloniaui-team avatar Aug 18 '22 11:08 avaloniaui-team