Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Port "non-clipping subtree" logic from WinUI #18336

Merged
merged 1 commit into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/ItemsControl/ItemsPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,37 @@ private bool WantsScrollViewerToObscureAvailableSizeBasedOnScrollBarVisibility(O
return (spPanel as FrameworkElement)?.WantsScrollViewerToObscureAvailableSizeBasedOnScrollBarVisibility(orientation) ?? true;
}

// Itemspresenter is the lynchpin in deciding whether we want to be in a non clipping subtree.
// Basically, when he decided to not want infinity, even though we were in a scrolling configuration,
// we want the measures underneath it to not constrain the desiredsize to the availablesize. That will allow
// wrapping uielements (like textblock) to size correctly, and still allow containers that want to be bigger
// to look good.
// Itemspresenter will set the value on himself (used during layout) and on the panel. Listview will set it on the
// items (so that individual measure on them will work).
internal bool EvaluateAndSetNonClippingBehavior(bool isNotBeingPassedInfinity)
{
bool result = false;
if (Panel is ItemsStackPanel panel)
{
// we only want the behavior in an isp at the moment

// first set it to ourselves
IsNonClippingSubtree = isNotBeingPassedInfinity;

// in the case of a modernpanel, lets have this itemspresenter not clip to the available size
// in overconstrained scenarios
// we're just passing through the setting to the panel here in an effort to make itemspresenter behave like a
// 'dumb' passthrough. The real logic was performed when measuring the scrollcontentpresenter

// we pass it along - the reason we want these individual elements also to have the correct value is because
// they could potentially be measured independently
panel.IsNonClippingSubtree = isNotBeingPassedInfinity;

result = isNotBeingPassedInfinity;
}

return result;
}

protected override Size ArrangeOverride(Size finalSize)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,24 +186,32 @@ protected override Size MeasureOverride(Size availableSize)
}
}

// when set to true, this means that we wanted to set to infinity but were blocked in doing it.
bool childPreventsInfiniteAvailableWidth = false;
bool childPreventsInfiniteAvailableHeight = false;

if (CanVerticallyScroll)
{
var childPreventsInfiniteAvailableHeight = !child.WantsScrollViewerToObscureAvailableSizeBasedOnScrollBarVisibility(Orientation.Vertical);
childPreventsInfiniteAvailableHeight = !child.WantsScrollViewerToObscureAvailableSizeBasedOnScrollBarVisibility(Orientation.Vertical);
if (!sizesContentToTemplatedParent && !childPreventsInfiniteAvailableHeight)
{
slotSize.Height = double.PositiveInfinity;
}
}
if (CanHorizontallyScroll)
{
var childPreventsInfiniteAvailableWidth = !child.WantsScrollViewerToObscureAvailableSizeBasedOnScrollBarVisibility(Orientation.Horizontal);
childPreventsInfiniteAvailableWidth = !child.WantsScrollViewerToObscureAvailableSizeBasedOnScrollBarVisibility(Orientation.Horizontal);
if (!sizesContentToTemplatedParent && !childPreventsInfiniteAvailableWidth)
{
slotSize.Width = double.PositiveInfinity;
}
}

if (child is ItemsPresenter itemsPresenter)
{
itemsPresenter.EvaluateAndSetNonClippingBehavior(childPreventsInfiniteAvailableWidth || childPreventsInfiniteAvailableHeight);
}

child.Measure(slotSize);

var desired = child.DesiredSize;
Expand Down
4 changes: 1 addition & 3 deletions src/Uno.UI/UI/Xaml/FrameworkElement.Layout.crossruntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,7 @@ private void InnerMeasureCore(Size availableSize)

// only clip and constrain if the tree wants that.
// currently only listviewitems do not want clipping
// UNO TODO

//if (!pLayoutManager->GetIsInNonClippingTree())
if (!IsInNonClippingTree)
{
// In overconstrained scenario, parent wins and measured size of the child,
// including any sizes set or computed, can not be larger then
Expand Down
11 changes: 11 additions & 0 deletions src/Uno.UI/UI/Xaml/UIElement.Layout.crossruntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public partial class UIElement : DependencyObject
/// </summary>
internal bool ShouldInterceptInvalidate { get; set; }

// In WinUI, this is in LayoutManager. For Uno, we make it static in FE for now.
private protected static bool IsInNonClippingTree { get; set; }

public void InvalidateMeasure()
{
if (ShouldInterceptInvalidate || IsMeasureDirty || IsLayoutFlagSet(LayoutFlag.MeasuringSelf))
Expand Down Expand Up @@ -213,6 +216,12 @@ private void DoMeasure(Size availableSize)

var remainingTries = MaxLayoutIterations;

// remember whether we need to set this value back
var wasInNonClippingTree = IsInNonClippingTree;
// once entering a tree that is non-clipping, we don't get out of it
// remember this for down-stream
IsInNonClippingTree = IsNonClippingSubtree || wasInNonClippingTree;

while (--remainingTries > 0)
{
if (isDirty)
Expand Down Expand Up @@ -306,6 +315,8 @@ private void DoMeasure(Size availableSize)

break;
}

IsInNonClippingTree = wasInNonClippingTree;
}

internal virtual void MeasureCore(Size availableSize)
Expand Down
2 changes: 2 additions & 0 deletions src/Uno.UI/UI/Xaml/UIElement.mux.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1933,5 +1933,7 @@ internal virtual void LeaveImpl(LeaveParams @params)

internal virtual bool WantsScrollViewerToObscureAvailableSizeBasedOnScrollBarVisibility(Orientation horizontal)
=> true;

internal bool IsNonClippingSubtree { get; set; }
}
}
Loading