Skip to content

Commit

Permalink
feat: Port LayoutRound code from WinUI
Browse files Browse the repository at this point in the history
  • Loading branch information
Youssef1313 committed Nov 2, 2023
1 parent 7228ad4 commit 95fba8f
Show file tree
Hide file tree
Showing 7 changed files with 620 additions and 183 deletions.
15 changes: 15 additions & 0 deletions src/Uno.UI/UI/LayoutHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ internal static (Size min, Size max) GetMinMax(this IFrameworkElement e)
.AtMost(maxSize)
.AtLeast(minSize); // UWP is applying "min" after "max", so if "min" > "max", "min" wins

if (e is UIElement uiElement && uiElement.GetUseLayoutRounding())
{
// It is possible for max vars to be INF so be don't want to round those.

minSize.Width = uiElement.LayoutRound(minSize.Width);

if (double.IsFinite(maxSize.Width))
maxSize.Width = uiElement.LayoutRound(maxSize.Width);

minSize.Height = uiElement.LayoutRound(minSize.Height);

if (double.IsFinite(maxSize.Height))
maxSize.Height = uiElement.LayoutRound(maxSize.Height);
}

return (minSize, maxSize);
}

Expand Down
32 changes: 6 additions & 26 deletions src/Uno.UI/UI/Xaml/Controls/Border/Border.Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ internal static Size HelperGetCombinedThickness(FrameworkElement element)
{
var thickness = element.GetBorderThickness();

if (Border.UseLayoutRoundingForBorderThickness(element))
if (element.GetUseLayoutRounding())
{
thickness = GetLayoutRoundedThickness(element);
}

// Compute the chrome size added by the border
var border = HelperCollapseThickness(thickness);

// Compute the chrome size added by the padding.
// No need to adjust for layout rounding here since padding is not "drawn" by the border.
var padding = HelperCollapseThickness(element.GetPadding());

// Combine both.
var combined = new Size(
width: border.Width + padding.Width,
height: border.Height + padding.Height);
Expand All @@ -33,29 +37,6 @@ private static Size HelperCollapseThickness(Thickness thickness)
return new Size(thickness.Left + thickness.Right, thickness.Top + thickness.Bottom);
}

internal static bool UseLayoutRoundingForBorderThickness(FrameworkElement element)
{
// Only snap BorderThickness if:
// 1) LayoutRounding is enabled - prerequisite for any pixel snapping.
// 2) There is no CornerRadius - we cannot guarantee snapping when there is a CornerRadius.
// 3) Plateau != 1.0 - this snapping is to ensure that integral values of BorderThickness don't cause subpixel rendering at high plateau.
// TODO: Remove check for plateau, BorderThickness should be consistently rounded at all plateaus.
//return (RootScale.GetRasterizationScaleForElement(element) != 1.0f) &&
// element.GetUseLayoutRounding() &&
// (!HasNonZeroCornerRadius(element.GetCornerRadius()));
return (RootScale.GetRasterizationScaleForElement(element) != 1.0f) &&
element.GetUseLayoutRounding() &&
(!HasNonZeroCornerRadius(element.GetCornerRadius()));
}

internal static bool HasNonZeroCornerRadius(CornerRadius cornerRadius)
{
return (cornerRadius.TopLeft > 0.0f) ||
(cornerRadius.TopRight > 0.0f) ||
(cornerRadius.BottomRight > 0.0f) ||
(cornerRadius.BottomLeft > 0.0f);
}

internal static Thickness GetLayoutRoundedThickness(FrameworkElement element)
{
// Layout rounding will correctly round element sizes and offsets at the current plateau,
Expand All @@ -65,7 +46,6 @@ internal static Thickness GetLayoutRoundedThickness(FrameworkElement element)
// inner edges and other rendering artifacts. This method rounds the BorderThickness at the current plateau
// using plateau-aware LayoutRound utility.
var roundedThickness = new Thickness();

var thickness = element.GetBorderThickness();
roundedThickness.Left = element.LayoutRound(thickness.Left);
roundedThickness.Right = element.LayoutRound(thickness.Right);
Expand All @@ -81,7 +61,7 @@ internal static Rect HelperGetInnerRect(FrameworkElement element, Size outerSize
// Set up the bound rectangle
var outerRect = new Rect(0, 0, outerSize.Width, outerSize.Height);

if (UseLayoutRoundingForBorderThickness(element))
if (element.GetUseLayoutRounding())
{
outerRect.Width = element.LayoutRound(outerRect.Width);
outerRect.Height = element.LayoutRound(outerRect.Height);
Expand Down
52 changes: 25 additions & 27 deletions src/Uno.UI/UI/Xaml/Controls/Border/Border.Layout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,46 +32,44 @@ public partial class Border
{
protected override Size MeasureOverride(Size availableSize)
{
var padding = Padding;
var borderThickness = BorderThickness;
Size childAvailableSize = default;

Size measuredSize;
Size combined = HelperGetCombinedThickness(this);

// Get the child to measure it - if any.
//If we have a child
if (Child is { } child)
{
var childSize = new Size(
availableSize.Width - padding.Left - padding.Right - borderThickness.Left - borderThickness.Right,
availableSize.Height - padding.Top - padding.Bottom - borderThickness.Top - borderThickness.Bottom
);
measuredSize = MeasureElement(child, childSize);
// Remove combined size from child's reference size.
childAvailableSize.Width = Math.Max(0.0f, availableSize.Width - combined.Width);
childAvailableSize.Height = Math.Max(0.0f, availableSize.Height - combined.Height);

var desiredSize = MeasureElement(child, childAvailableSize);

//IFC(pChild->EnsureLayoutStorage());

// Desired size would be my child's desired size plus the border
desiredSize.Width = desiredSize.Width + combined.Width;
desiredSize.Height = desiredSize.Height + combined.Height;
return desiredSize;
}
else
{
measuredSize = default;
return combined;
}

return new Size(
measuredSize.Width + padding.Left + padding.Right + borderThickness.Left + borderThickness.Right,
measuredSize.Height + padding.Top + padding.Bottom + borderThickness.Top + borderThickness.Bottom
);
}

protected override Size ArrangeOverride(Size finalSize)
{
var child = this.FindFirstChild();

if (child != null)
// Get the child to arrange it - if any.
//If we have a child
if (Child is { } child)
{
var padding = Padding;
var borderThickness = BorderThickness;

var finalRect = new Rect(
padding.Left + borderThickness.Left,
padding.Top + borderThickness.Top,
finalSize.Width - padding.Left - padding.Right - borderThickness.Left - borderThickness.Right,
finalSize.Height - padding.Top - padding.Bottom - borderThickness.Top - borderThickness.Bottom
);
Rect childRect = HelperGetInnerRect(this, finalSize);

base.ArrangeElement(child, finalRect);
// Give the child the inner rectangle as the available size
// and ask it to arrange itself within this rectangle.
child.Arrange(childRect);
}

return finalSize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using DirectUI;
using CCalendarViewBaseItemChrome = Windows.UI.Xaml.Controls.CalendarViewBaseItem;
using DateTime = System.DateTimeOffset;
using Uno.UI.Xaml.Core;

namespace Windows.UI.Xaml.Controls
{
Expand Down Expand Up @@ -241,6 +242,25 @@ protected override Size MeasureOverride(
// Uno workaround
Uno_MeasureChrome(availableSize);

// TODO UNO
//if (IsRoundedCalendarViewBaseItemChromeEnabled())
//{
// if (m_outerBorder)
// {
// IFC_RETURN(m_outerBorder->Measure(availableSize));
// }

// if (m_innerBorder)
// {
// IFC_RETURN(m_innerBorder->Measure(availableSize));
// }

// if (m_strikethroughLine)
// {
// IFC_RETURN(m_strikethroughLine->Measure(availableSize));
// }
//}

if (m_pMainTextBlock is { })
{
m_pMainTextBlock.Measure(availableSize);
Expand Down Expand Up @@ -275,8 +295,30 @@ protected override Size MeasureOverride(
protected override Size ArrangeOverride(
Size finalSize)
{
Size newFinalSize = default;
Rect finalBounds = new Rect(0.0f, 0.0f, finalSize.Width, finalSize.Height);

// TODO UNO
//if (m_outerBorder)
//{
// ASSERT(IsRoundedCalendarViewBaseItemChromeEnabled());

// IFC_RETURN(m_outerBorder->Arrange(finalBounds));
//}

//if (m_innerBorder)
//{
// ASSERT(IsRoundedCalendarViewBaseItemChromeEnabled());

// IFC_RETURN(m_innerBorder->Arrange(finalBounds));
//}

//if (m_strikethroughLine)
//{
// ASSERT(IsRoundedCalendarViewBaseItemChromeEnabled());

// IFC_RETURN(m_strikethroughLine->Arrange(finalBounds));
//}

Thickness borderThickness = GetItemBorderThickness();
Thickness padding = Padding;

Expand Down Expand Up @@ -316,9 +358,7 @@ protected override Size ArrangeOverride(
pChildNoRef.Arrange(finalBounds);
}

newFinalSize = finalSize;

return newFinalSize;
return finalSize;
}

private void CreateTextBlock(
Expand Down Expand Up @@ -451,12 +491,9 @@ IContentRenderer pContentRenderer

private bool ShouldUseLayoutRounding()
{
// TODO UNO
//// Similar to what Borders do, but we don't care about corner radius (ours is always 0).
//var scale = RootScale.GetRasterizationScaleForElement(this);
//return (scale != 1.0f) && GetUseLayoutRounding();

return false;
// Similar to what Borders do, but we don't care about corner radius (ours is always 0).
var scale = RootScale.GetRasterizationScaleForElement(this);
return (scale != 1.0f) && GetUseLayoutRounding();
}

#if false
Expand Down
23 changes: 21 additions & 2 deletions src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Windows.UI.Xaml.Media;
using Uno.UI;
using Windows.UI.Xaml.Documents.TextFormatting;
using Uno.UI.Xaml.Core;

#nullable enable

Expand Down Expand Up @@ -42,11 +43,29 @@ private protected override void OnLoaded()
protected override Size MeasureOverride(Size availableSize)
{
var padding = Padding;
var availableSizeWithoutPadding = availableSize.Subtract(padding);
var availableSizeWithoutPadding = availableSize.Subtract(padding).AtLeastZero();
var defaultLineHeight = GetComputedLineHeight();
var desiredSize = Inlines.Measure(availableSizeWithoutPadding, defaultLineHeight);

return desiredSize.Add(padding);
desiredSize = desiredSize.Add(padding);

if (GetUseLayoutRounding())
{
// In order to prevent text clipping as a result of layout rounding at
// scales other than 1.0x, the ceiling of the rescaled size is used.
var plateauScale = RootScale.GetRasterizationScaleForElement(this);
Size pageNodeSize = desiredSize;
desiredSize.Width = ((int)Math.Ceiling(pageNodeSize.Width * plateauScale)) / plateauScale;
desiredSize.Height = ((int)Math.Ceiling(pageNodeSize.Height * plateauScale)) / plateauScale;

// LsTextLine is not aware of layoutround and uses baseline height to place the rendered text.
// However, because the height of the *block is potentionally layoutround-ed up, we should adjust the
// placement of text by the difference. Horizontal adjustment is not of concern since
// LsTextLine uses arranged size which is already layoutround-ed.
//_layoutRoundingHeightAdjustment = desiredSize.Height - pageNodeSize.Height;
}

return desiredSize;
}

private void ApplyFlowDirection(float width)
Expand Down
Loading

0 comments on commit 95fba8f

Please sign in to comment.