Skip to content

Commit

Permalink
fix(Border): Allow null BorderBrush to be set
Browse files Browse the repository at this point in the history
  • Loading branch information
kazo0 committed Oct 12, 2021
1 parent 43e8c35 commit decb376
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 57 deletions.
Expand Up @@ -57,7 +57,7 @@ public void FrameworkElement_NativeLayout()

[Test]
[AutoRetry]
[ActivePlatforms(Platform.Browser, Platform.Android)] // Not supported on iOS & Skia yet.
[ActivePlatforms(Platform.Browser, Platform.Android, Platform.iOS)] // Not supported on Skia yet.
public void FrameworkElement_BackgroundSizing()
{
Run("UITests.Windows_UI_Xaml.FrameworkElementTests.DynamicBackgroundSizing");
Expand Down
118 changes: 62 additions & 56 deletions src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.iOSmacOS.cs
Expand Up @@ -89,6 +89,7 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay
var borderThickness = state.BorderThickness;
var borderBrush = state.BorderBrush;
var cornerRadius = state.CornerRadius;
var backgroundSizing = state.BackgroundSizing;

var disposables = new CompositeDisposable();
var sublayers = new List<CALayer>();
Expand Down Expand Up @@ -123,8 +124,8 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay
Math.Min(cornerRadius.BottomLeft, maxInnerRadius));

var outerLayer = new CAShapeLayer();
var innerLayer = new CAShapeLayer();
innerLayer.FillColor = null;
var backgroundLayer = new CAShapeLayer();
backgroundLayer.FillColor = null;
outerLayer.FillRule = CAShapeLayer.FillRuleEvenOdd;
outerLayer.LineWidth = 0;

Expand All @@ -133,23 +134,27 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay
var innerPath = GetRoundedPath(cornerRadius, adjustedArea);
var outerPath = GetRoundedPath(cornerRadius, area);

var isInnerBorderEdge = backgroundSizing == BackgroundSizing.InnerBorderEdge;
var backgroundPath = isInnerBorderEdge ? innerPath : outerPath;
var backgroundArea = isInnerBorderEdge ? adjustedArea : area;

var insertionIndex = 0;

if (background is GradientBrush gradientBackground)
{
var fillMask = new CAShapeLayer()
{
Path = innerPath,
Path = backgroundPath,
Frame = area,
// We only use the fill color to create the mask area
FillColor = _Color.White.CGColor,
};

CreateGradientBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask);
CreateGradientBrushLayers(area, backgroundArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask);
}
else if (background is SolidColorBrush scbBackground)
{
Brush.AssignAndObserveBrush(scbBackground, color => innerLayer.FillColor = color)
Brush.AssignAndObserveBrush(scbBackground, color => backgroundLayer.FillColor = color)
.DisposeWith(disposables);
}
else if (background is ImageBrush imgBackground)
Expand All @@ -159,47 +164,39 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay
{
var fillMask = new CAShapeLayer()
{
Path = innerPath,
Path = backgroundPath,
Frame = area,
// We only use the fill color to create the mask area
FillColor = _Color.White.CGColor,
};

CreateImageBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, imgBackground, fillMask);
CreateImageBrushLayers(area, backgroundArea, parent, sublayers, ref insertionIndex, imgBackground, fillMask);
}
}
else if (background is AcrylicBrush acrylicBrush)
{
var fillMask = new CAShapeLayer()
{
Path = innerPath,
Path = backgroundPath,
Frame = area,
// We only use the fill color to create the mask area
FillColor = _Color.White.CGColor,
};

acrylicBrush.Subscribe(owner, area, adjustedArea, parent, sublayers, ref insertionIndex, fillMask)
acrylicBrush.Subscribe(owner, area, backgroundArea, parent, sublayers, ref insertionIndex, fillMask)
.DisposeWith(disposables);
}
else if (background is XamlCompositionBrushBase unsupportedCompositionBrush)
{
Brush.AssignAndObserveBrush(unsupportedCompositionBrush, color => innerLayer.FillColor = color)
Brush.AssignAndObserveBrush(unsupportedCompositionBrush, color => backgroundLayer.FillColor = color)
.DisposeWith(disposables);
}
else
{
innerLayer.FillColor = Colors.Transparent;
backgroundLayer.FillColor = Colors.Transparent;
}

outerLayer.Path = path;
innerLayer.Path = innerPath;

sublayers.Add(outerLayer);
sublayers.Add(innerLayer);
parent.AddSublayer(outerLayer);
parent.InsertSublayer(innerLayer, insertionIndex);

if (borderBrush is SolidColorBrush scbBorder)
if (borderBrush is SolidColorBrush scbBorder || borderBrush == null)
{
Brush.AssignAndObserveBrush(borderBrush, color =>
{
Expand All @@ -222,6 +219,16 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay
CreateGradientBrushLayers(area, area, parent, sublayers, ref borderLayerIndex, gradientBorder, fillMask);
}


outerLayer.Path = path;
backgroundLayer.Path = backgroundPath;

sublayers.Add(outerLayer);
sublayers.Add(backgroundLayer);
parent.AddSublayer(outerLayer);
parent.InsertSublayer(backgroundLayer, insertionIndex);


parent.Mask = new CAShapeLayer()
{
Path = outerPath,
Expand All @@ -239,22 +246,25 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay
}
else
{
if (background is GradientBrush gradientBackground)
{
var fullArea = new CGRect(
area.X + borderThickness.Left,
area.Y + borderThickness.Top,
area.Width - borderThickness.Left - borderThickness.Right,
area.Height - borderThickness.Top - borderThickness.Bottom);
var backgroundLayer = new CAShapeLayer();
backgroundLayer.FillColor = null;

var insideArea = new CGRect(CGPoint.Empty, fullArea.Size);
var insertionIndex = 0;
var innerPath = GetRoundedPath(CornerRadius.None, adjustedArea);
var outerPath = GetRoundedPath(CornerRadius.None, area);

CreateGradientBrushLayers(fullArea, insideArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask: null);
var isInnerBorderEdge = backgroundSizing == BackgroundSizing.InnerBorderEdge;
var backgroundPath = isInnerBorderEdge ? innerPath : outerPath;
var backgroundArea = isInnerBorderEdge ? adjustedArea : area;

var insertionIndex = 0;

if (background is GradientBrush gradientBackground)
{
CreateGradientBrushLayers(area, backgroundArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask: null);
}
else if (background is SolidColorBrush scbBackground)
{
Brush.AssignAndObserveBrush(scbBackground, c => parent.BackgroundColor = c)
Brush.AssignAndObserveBrush(scbBackground, c => backgroundLayer.FillColor = c)
.DisposeWith(disposables);

// This is required because changing the CornerRadius changes the background drawing
Expand All @@ -267,44 +277,26 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay
var bgSrc = imgBackground.ImageSource;
if (bgSrc != null && bgSrc.TryOpenSync(out var uiImage) && uiImage.Size != CGSize.Empty)
{
var fullArea = new CGRect(
area.X + borderThickness.Left,
area.Y + borderThickness.Top,
area.Width - borderThickness.Left - borderThickness.Right,
area.Height - borderThickness.Top - borderThickness.Bottom);

var insideArea = new CGRect(CGPoint.Empty, fullArea.Size);
var insertionIndex = 0;

CreateImageBrushLayers(fullArea, insideArea, parent, sublayers, ref insertionIndex, imgBackground, fillMask: null);
CreateImageBrushLayers(area, backgroundArea, parent, sublayers, ref insertionIndex, imgBackground, fillMask: null);
}
}
else if (background is AcrylicBrush acrylicBrush)
{
var fullArea = new CGRect(
area.X + borderThickness.Left,
area.Y + borderThickness.Top,
area.Width - borderThickness.Left - borderThickness.Right,
area.Height - borderThickness.Top - borderThickness.Bottom);

var insideArea = new CGRect(CGPoint.Empty, fullArea.Size);
var insertionIndex = 0;

acrylicBrush.Subscribe(owner, fullArea, insideArea, parent, sublayers, ref insertionIndex, fillMask: null);
acrylicBrush.Subscribe(owner, area, backgroundArea, parent, sublayers, ref insertionIndex, fillMask: null);
}
else if (background is XamlCompositionBrushBase unsupportedCompositionBrush)
{
Brush.AssignAndObserveBrush(unsupportedCompositionBrush, color => parent.BackgroundColor = color)
Brush.AssignAndObserveBrush(unsupportedCompositionBrush, color => backgroundLayer.FillColor = color)
.DisposeWith(disposables);

// This is required because changing the CornerRadius changes the background drawing
// implementation and we don't want a rectangular background behind a rounded background.
Disposable.Create(() => parent.BackgroundColor = null)
Disposable.Create(() => backgroundLayer.FillColor = null)
.DisposeWith(disposables);
}
else
{
parent.BackgroundColor = Colors.Transparent;
backgroundLayer.FillColor = Colors.Transparent;
}

if (borderThickness != Thickness.Empty)
Expand All @@ -319,7 +311,7 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay
// Must be inserted below the other subviews, which may happen when
// the current view has subviews.
sublayers.Add(layer);
parent.InsertSublayer(layer, 0);
parent.AddSublayer(layer);

if (borderBrush is SolidColorBrush scbBorder)
{
Expand All @@ -343,7 +335,21 @@ private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, Lay

}

state.BoundsPath = CGPath.FromRect(parent.Bounds);
backgroundLayer.Path = backgroundPath;

sublayers.Add(backgroundLayer);
parent.InsertSublayer(backgroundLayer, insertionIndex);

parent.Mask = new CAShapeLayer()
{
Path = outerPath,
Frame = area,
// We only use the fill color to create the mask area
FillColor = _Color.White.CGColor,
};


state.BoundsPath = outerPath;
}

disposables.Add(() =>
Expand Down

0 comments on commit decb376

Please sign in to comment.