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: Fix corner radius handling on Wasm #13682

Merged
merged 6 commits into from
Sep 24, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
Maximum="100"
x:Name="LeftBorderThicknessSlider"
Value="{x:Bind LeftBorderThickness, Mode=TwoWay}" />
<Slider Header="Left:"
<Slider Header="Top:"
Minimum="0"
Maximum="100"
x:Name="TopBorderThicknessSlider"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,96 @@ public async Task Border_CornerRadius_GradientBrush()
ImageAssert.HasColorAt(result, textBoxRect.CenterX, textBoxRect.CenterY, "#FF00FF00", tolerance: 10);
}

[TestMethod]
public async Task When_CornerRadius()
{
var case1A = new Border()
{
Width = 200,
Height = 100,
CornerRadius = new(200),
Background = new SolidColorBrush(Colors.Red),
};

var case1B = new Border()
{
Width = 200,
Height = 100,
CornerRadius = new(100),
Background = new SolidColorBrush(Colors.Red),
};

var case1Expected = new Ellipse()
{
Width = 200,
Height = 100,
Fill = new SolidColorBrush(Colors.Red),
};

var case2 = new Border()
{
Width = 200,
Height = 100,
CornerRadius = new(200, 0, 0, 0),
Background = new SolidColorBrush(Colors.Blue),
};

var case2Expected = new Grid()
{
Width = 200,
Height = 100,
Children =
{
new Ellipse()
{
Width = 400,
Height = 200,
Fill = new SolidColorBrush(Colors.Blue),
}
}
};

var stackPanel = new StackPanel()
{
Children =
{
case1A,
case1B,
case1Expected,
case2,
case2Expected,
}
};

WindowHelper.WindowContent = stackPanel;
await WindowHelper.WaitForLoaded(stackPanel);

var renderer1A = new RenderTargetBitmap();
await renderer1A.RenderAsync(case1A);
var bitmap1A = await RawBitmap.From(renderer1A, case1A);

var renderer1B = new RenderTargetBitmap();
await renderer1B.RenderAsync(case1B);
var bitmap1B = await RawBitmap.From(renderer1B, case1B);

var renderer1Expected = new RenderTargetBitmap();
await renderer1Expected.RenderAsync(case1Expected);
var bitmap1Expected = await RawBitmap.From(renderer1Expected, case1Expected);

await ImageAssert.AreSimilarAsync(bitmap1A, bitmap1Expected, imperceptibilityThreshold: 0.7);
await ImageAssert.AreSimilarAsync(bitmap1B, bitmap1Expected, imperceptibilityThreshold: 0.7);

var renderer2 = new RenderTargetBitmap();
await renderer2.RenderAsync(case2);
var bitmap2 = await RawBitmap.From(renderer2, case2);

var renderer2Expected = new RenderTargetBitmap();
await renderer2Expected.RenderAsync(case2Expected);
var bitmap2Expected = await RawBitmap.From(renderer2Expected, case2Expected);

await ImageAssert.AreSimilarAsync(bitmap2, bitmap2Expected, imperceptibilityThreshold: 0.7);
}

[TestMethod]
public async Task Border_AntiAlias()
{
Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UI.Toolkit/ElevatedView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private void UpdateElevation()
{
#if __WASM__
this.SetElevationInternal(Elevation, ShadowColor);
this.SetCornerRadius(CornerRadius);
this.SetBorder(BorderThickness, BorderBrush, CornerRadius);
#elif __IOS__ || __MACOS__
this.SetElevationInternal(Elevation, ShadowColor, _border.BoundsPath);
#elif __ANDROID__
Expand Down
17 changes: 4 additions & 13 deletions src/Uno.UI/UI/Xaml/Controls/Border/Border.wasm.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
using Uno.Extensions;
using System.Linq;
using System.Drawing;
using Uno.Disposables;
using Windows.UI.Xaml.Media;
using Uno.UI;

namespace Windows.UI.Xaml.Controls
namespace Windows.UI.Xaml.Controls
{
public partial class Border
{
public Border()
{
this.SizeChanged += (_, _) => UpdateBorder();
}

partial void OnChildChangedPartial(UIElement previousValue, UIElement newValue)
Expand All @@ -28,7 +19,7 @@ partial void OnChildChangedPartial(UIElement previousValue, UIElement newValue)

private void UpdateBorder()
{
SetBorder(BorderThickness, BorderBrush);
SetBorder(BorderThickness, BorderBrush, CornerRadius);
}

private protected override void OnLoaded()
Expand All @@ -54,7 +45,7 @@ partial void OnPaddingChangedPartial(Thickness oldValue, Thickness newValue)

partial void OnCornerRadiusUpdatedPartial(CornerRadius oldValue, CornerRadius newValue)
{
SetCornerRadius(newValue);
UpdateBorder();
}

protected override void OnBackgroundChanged(DependencyPropertyChangedEventArgs e)
Expand Down
47 changes: 18 additions & 29 deletions src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
using RadialGradientBrush = Microsoft.UI.Xaml.Media.RadialGradientBrush;
using Uno;
using Uno.UI.Helpers;
using Uno.UI.Extensions;

namespace Windows.UI.Xaml.Shapes
{
partial class BorderLayerRenderer
{
private Brush _background;
private (Brush, Thickness) _border;
private CornerRadius _cornerRadius;
private (Brush, Thickness, CornerRadius) _border;

private Action _backgroundChanged;

Expand All @@ -38,35 +38,14 @@ public void UpdateLayer(
SetAndObserveBackgroundBrush(fwElt, oldValue, background, ref _backgroundChanged);
}

if (_border != (borderBrush, borderThickness))
if (_border != (borderBrush, borderThickness, cornerRadius))
{
_border = (borderBrush, borderThickness);
SetBorder(element, borderThickness, borderBrush);
}

if (_cornerRadius != cornerRadius)
{
_cornerRadius = cornerRadius;
SetCornerRadius(element, cornerRadius);
}
}

public static void SetCornerRadius(UIElement element, CornerRadius cornerRadius)
{
if (cornerRadius == CornerRadius.None)
{
element.ResetStyle("border-radius", "overflow");
}
else
{
var borderRadiusCssString = $"min(50%,{cornerRadius.TopLeft.ToStringInvariant()}px) min(50%,{cornerRadius.TopRight.ToStringInvariant()}px) min(50%,{cornerRadius.BottomRight.ToStringInvariant()}px) min(50%,{cornerRadius.BottomLeft.ToStringInvariant()}px)";
element.SetStyle(
("border-radius", borderRadiusCssString),
("overflow", "hidden")); // overflow: hidden is required here because the clipping can't do its job when it's non-rectangular.
_border = (borderBrush, borderThickness, cornerRadius);
SetBorder(element, borderThickness, borderBrush, cornerRadius);
}
}

public static void SetBorder(UIElement element, Thickness thickness, Brush brush)
public static void SetBorder(UIElement element, Thickness thickness, Brush brush, CornerRadius cornerRadius)
{
if (thickness == Thickness.Empty)
{
Expand All @@ -89,7 +68,7 @@ public static void SetBorder(UIElement element, Thickness thickness, Brush brush
("border-width", borderWidth));
break;
case GradientBrush gradientBrush:
var border = gradientBrush.ToCssString(element.RenderSize); // TODO: Reevaluate when size is changing
var border = gradientBrush.ToCssString(element.RenderSize);
element.SetStyle(
("border-style", "solid"),
("border-color", ""),
Expand All @@ -98,7 +77,7 @@ public static void SetBorder(UIElement element, Thickness thickness, Brush brush
("border-image-slice", "1"));
break;
case RadialGradientBrush radialGradientBrush:
var radialBorder = radialGradientBrush.ToCssString(element.RenderSize); // TODO: Reevaluate when size is changing
var radialBorder = radialGradientBrush.ToCssString(element.RenderSize);
element.SetStyle(
("border-style", "solid"),
("border-color", ""),
Expand All @@ -119,6 +98,16 @@ public static void SetBorder(UIElement element, Thickness thickness, Brush brush
break;
}
}

if (cornerRadius == CornerRadius.None)
{
element.ResetStyle("border-radius", "overflow");
}
else
{
var outer = cornerRadius.GetRadii(element.RenderSize, thickness).Outer;
WindowManagerInterop.SetCornerRadius(element.HtmlId, outer.TopLeft.X, outer.TopLeft.Y, outer.TopRight.X, outer.TopRight.Y, outer.BottomRight.X, outer.BottomRight.Y, outer.BottomLeft.X, outer.BottomLeft.Y);
}
}

public static void SetAndObserveBackgroundBrush(FrameworkElement element, Brush oldValue, Brush newValue, ref Action brushChanged)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public partial class ContentPresenter : FrameworkElement
public ContentPresenter()
{
InitializeContentPresenter();
this.SizeChanged += (_, _) => UpdateBorder();
}

private void SetUpdateTemplate()
Expand All @@ -39,12 +40,12 @@ partial void UnregisterContentTemplateRoot()

private void UpdateCornerRadius(CornerRadius radius)
{
SetCornerRadius(radius);
UpdateBorder();
}

private void UpdateBorder()
{
SetBorder(BorderThickness, BorderBrush);
SetBorder(BorderThickness, BorderBrush, CornerRadius);
}

private void ClearBorder()
Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UI/UI/Xaml/Controls/Page/Page.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ private void InitializeBorder()

private void UpdateBorder()
{
SetBorder(Thickness.Empty, null);
SetBorder(Thickness.Empty, null, default);
}
}
}
5 changes: 3 additions & 2 deletions src/Uno.UI/UI/Xaml/Controls/Panel/Panel.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ public partial class Panel : IEnumerable
public Panel()
{
Initialize();
this.SizeChanged += (_, _) => UpdateBorder();
}

partial void Initialize();

partial void UpdateBorder()
{
SetBorder(BorderThicknessInternal, BorderBrushInternal);
SetBorder(BorderThicknessInternal, BorderBrushInternal, CornerRadiusInternal);
}

protected virtual void OnChildrenChanged()
Expand All @@ -58,7 +59,7 @@ partial void OnBorderThicknessChangedPartial(Thickness oldValue, Thickness newVa

partial void OnCornerRadiusChangedPartial(CornerRadius oldValue, CornerRadius newValue)
{
SetCornerRadius(newValue);
UpdateBorder();
}

/// <summary>
Expand Down
7 changes: 2 additions & 5 deletions src/Uno.UI/UI/Xaml/FrameworkElement.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,8 @@ public event RoutedEventHandler Unloaded

public IEnumerator GetEnumerator() => _children.GetEnumerator();

protected void SetCornerRadius(CornerRadius cornerRadius)
=> BorderLayerRenderer.SetCornerRadius(this, cornerRadius);

protected void SetBorder(Thickness thickness, Brush brush)
=> BorderLayerRenderer.SetBorder(this, thickness, brush);
protected void SetBorder(Thickness thickness, Brush brush, CornerRadius cornerRadius)
=> BorderLayerRenderer.SetBorder(this, thickness, brush, cornerRadius);

partial void OnBackgroundSizingChangedPartial(DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
Expand Down
7 changes: 7 additions & 0 deletions src/Uno.UI/UI/Xaml/WindowManagerInterop.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,10 @@

internal static void SetImageAsMonochrome(IntPtr htmlId, string url, string color)
=> NativeMethods.SetImageAsMonochrome(htmlId, url, color);

internal static void SetCornerRadius(IntPtr htmlId, float topLeftX, float topLeftY, float topRightX, float topRightY, float bottomRightX, float bottomRightY, float bottomLeftX, float bottomLeftY)
=> NativeMethods.SetCornerRadius(htmlId, topLeftX, topLeftY, topRightX, topRightY, bottomRightX, bottomRightY, bottomLeftX, bottomLeftY);

internal static void SetRootElement(IntPtr htmlId)
{
NativeMethods.SetRootElement(htmlId);
Expand Down Expand Up @@ -1144,6 +1148,9 @@
[JSImport("globalThis.Uno.UI.WindowManager.current.setImageAsMonochrome")]
internal static partial void SetImageAsMonochrome(IntPtr htmlId, string url, string color);

[JSImport("globalThis.Uno.UI.WindowManager.current.setCornerRadius")]
internal static partial void SetCornerRadius(IntPtr htmlId, float topLeftX, float topLeftY, float topRightX, float topRightY, float bottomRightX, float bottomRightY, float bottomLeftX, float bottomLeftY);

Check failure on line 1152 in src/Uno.UI/UI/Xaml/WindowManagerInterop.wasm.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/UI/Xaml/WindowManagerInterop.wasm.cs#L1152

Rename this method to not shadow the outer class' member with the same name.

[JSImport("globalThis.Uno.UI.WindowManager.current.setPointerCapture")]
internal static partial void SetPointerCapture(IntPtr htmlId, double pointerId);

Expand Down
6 changes: 6 additions & 0 deletions src/Uno.UI/ts/WindowManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,12 @@
}
}

public setCornerRadius(viewId: number, topLeftX: number, topLeftY: number, topRightX: number, topRightY: number, bottomRightX: number, bottomRightY: number, bottomLeftX: number, bottomLeftY: number) {

Check warning on line 1501 in src/Uno.UI/ts/WindowManager.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/ts/WindowManager.ts#L1501

Exceeds maximum line length of 120
const element = this.getView(viewId);
element.style.borderRadius = `${topLeftX}px ${topRightX}px ${bottomRightX}px ${bottomLeftX}px / ${topLeftY}px ${topRightY}px ${bottomRightY}px ${bottomLeftY}px`;
element.style.overflow = "hidden"; // overflow: hidden is required here because the clipping can't do its job when it's non-rectangular.

Check warning on line 1504 in src/Uno.UI/ts/WindowManager.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/ts/WindowManager.ts#L1504

Exceeds maximum line length of 120
}

public setPointerCapture(viewId: number, pointerId: number): void {
this.getView(viewId).setPointerCapture(pointerId);
}
Expand Down
Loading