private static IDisposable InnerCreateLayers(BindableView view, Windows.Foundation.Rect drawArea, Brush background, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, Action onImageSet ) { var disposables = new CompositeDisposable(); var physicalBorderThickness = borderThickness.LogicalToPhysicalPixels(); if (cornerRadius != 0) { if (view is UIElement uiElement && uiElement.FrameRoundingAdjustment is { } fra) { drawArea.Height += fra.Height; drawArea.Width += fra.Width; } var adjustedArea = drawArea.DeflateBy(physicalBorderThickness); using (var backgroundPath = cornerRadius.GetOutlinePath(adjustedArea.ToRectF())) { //We only need to set a background if the drawArea is non-zero if (!drawArea.HasZeroArea()) { if (background is ImageBrush imageBrushBackground) { //Copy the path because it will be disposed when we exit the using block var pathCopy = new Path(backgroundPath); var setBackground = DispatchSetImageBrushAsBackground(view, imageBrushBackground, drawArea, onImageSet, pathCopy); disposables.Add(setBackground); } else if (background is AcrylicBrush acrylicBrush) { var apply = acrylicBrush.Subscribe(view, drawArea, backgroundPath); disposables.Add(apply); } else { var fillPaint = background?.GetFillPaint(drawArea) ?? new Paint() { Color = Android.Graphics.Color.Transparent }; ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(Brush.GetBackgroundDrawable(background, drawArea, fillPaint, backgroundPath))); } disposables.Add(() => ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(null))); } if (borderThickness != Thickness.Empty && borderBrush != null && !(borderBrush is ImageBrush)) { using (var strokePaint = new Paint(borderBrush.GetStrokePaint(drawArea))) { //Create the path for the outer and inner rectangles that will become our border shape var borderPath = cornerRadius.GetOutlinePath(drawArea.ToRectF()); borderPath.AddRoundRect(adjustedArea.ToRectF(), cornerRadius.GetRadii(), Path.Direction.Ccw); var overlay = GetOverlayDrawable( strokePaint, physicalBorderThickness, new global::System.Drawing.Size((int)drawArea.Width, (int)drawArea.Height), borderPath); if (overlay != null) { overlay.SetBounds(0, 0, view.Width, view.Height); SetOverlay(view, disposables, overlay); } } } } } else // No corner radius { //We only need to set a background if the drawArea is non-zero if (!drawArea.HasZeroArea()) { if (background is ImageBrush imageBrushBackground) { var setBackground = DispatchSetImageBrushAsBackground(view, imageBrushBackground, drawArea, onImageSet); disposables.Add(setBackground); } else if (background is AcrylicBrush acrylicBrush) { var apply = acrylicBrush.Subscribe(view, drawArea, maskingPath: null); disposables.Add(apply); } else { var fillPaint = background?.GetFillPaint(drawArea) ?? new Paint() { Color = Android.Graphics.Color.Transparent }; ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(Brush.GetBackgroundDrawable(background, drawArea, fillPaint))); } disposables.Add(() => ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(null))); } } if (borderBrush != null && !(borderBrush is ImageBrush)) { //TODO: Handle case that BorderBrush is an ImageBrush using (var strokePaint = borderBrush.GetStrokePaint(drawArea)) { var overlay = GetOverlayDrawable(strokePaint, physicalBorderThickness, new global::System.Drawing.Size(view.Width, view.Height)); if (overlay != null) { overlay.SetBounds(0, 0, view.Width, view.Height); SetOverlay(view, disposables, overlay); } } } return(disposables); }
private static IDisposable InnerCreateLayers(BindableView view, Windows.Foundation.Rect drawArea, Brush background, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, Action onImageSet ) { var disposables = new CompositeDisposable(); var physicalBorderThickness = borderThickness.LogicalToPhysicalPixels(); if (cornerRadius != 0) { var adjustedLineWidth = physicalBorderThickness.Top; // TODO: handle non-uniform BorderThickness correctly var adjustedArea = drawArea; adjustedArea.Inflate(-adjustedLineWidth / 2, -adjustedLineWidth / 2); using (var path = cornerRadius.GetOutlinePath(adjustedArea.ToRectF())) { path.SetFillType(Path.FillType.EvenOdd); //We only need to set a background if the drawArea is non-zero if (!drawArea.HasZeroArea()) { if (background is ImageBrush imageBrushBackground) { //Copy the path because it will be disposed when we exit the using block var pathCopy = new Path(path); var setBackground = DispatchSetImageBrushAsBackground(view, imageBrushBackground, drawArea, onImageSet, pathCopy); disposables.Add(setBackground); } else if (background is AcrylicBrush acrylicBrush) { var apply = acrylicBrush.Subscribe(view, drawArea, path); disposables.Add(apply); } else { var fillPaint = background?.GetFillPaint(drawArea) ?? new Paint() { Color = Android.Graphics.Color.Transparent }; ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(Brush.GetBackgroundDrawable(background, drawArea, fillPaint, path))); } disposables.Add(() => ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(null))); } if (borderThickness != Thickness.Empty && borderBrush != null && !(borderBrush is ImageBrush)) { using (var strokePaint = new Paint(borderBrush.GetStrokePaint(drawArea))) { var overlay = GetOverlayDrawable(strokePaint, physicalBorderThickness, new global::System.Drawing.Size((int)drawArea.Width, (int)drawArea.Height), path); if (overlay != null) { overlay.SetBounds(0, 0, view.Width, view.Height); SetOverlay(view, disposables, overlay); } } } } } else // No corner radius { //We only need to set a background if the drawArea is non-zero if (!drawArea.HasZeroArea()) { if (background is ImageBrush imageBrushBackground) { var setBackground = DispatchSetImageBrushAsBackground(view, imageBrushBackground, drawArea, onImageSet); disposables.Add(setBackground); } else if (background is AcrylicBrush acrylicBrush) { var apply = acrylicBrush.Subscribe(view, drawArea, maskingPath: null); disposables.Add(apply); } else { var fillPaint = background?.GetFillPaint(drawArea) ?? new Paint() { Color = Android.Graphics.Color.Transparent }; ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(Brush.GetBackgroundDrawable(background, drawArea, fillPaint))); } disposables.Add(() => ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(null))); } if (borderBrush != null && !(borderBrush is ImageBrush)) { //TODO: Handle case that BorderBrush is an ImageBrush using (var strokePaint = borderBrush.GetStrokePaint(drawArea)) { var overlay = GetOverlayDrawable(strokePaint, physicalBorderThickness, new global::System.Drawing.Size(view.Width, view.Height)); if (overlay != null) { overlay.SetBounds(0, 0, view.Width, view.Height); SetOverlay(view, disposables, overlay); } } } } return(disposables); }
private static IDisposable InnerCreateLayers(BindableView view, Windows.Foundation.Rect drawArea, Brush background, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, Action onImageSet ) { var disposables = new CompositeDisposable(); var physicalBorderThickness = borderThickness.LogicalToPhysicalPixels(); if (cornerRadius != 0) { using (Path path = new Path()) { path.SetFillType(Path.FillType.EvenOdd); var radius = new CornerRadius( topLeft: ViewHelper.LogicalToPhysicalPixels(cornerRadius.TopLeft), topRight: ViewHelper.LogicalToPhysicalPixels(cornerRadius.TopRight), bottomRight: ViewHelper.LogicalToPhysicalPixels(cornerRadius.BottomRight), bottomLeft: ViewHelper.LogicalToPhysicalPixels(cornerRadius.BottomLeft) ); var adjustedLineWidth = physicalBorderThickness.Top; var area = new Windows.Foundation.Rect(drawArea.Left, drawArea.Top, drawArea.Width, drawArea.Height); area.Inflate(-adjustedLineWidth / 2, -adjustedLineWidth / 2); // This represents the doubled radii used to draw arcs, with each one maxed at the area's size. // The width and height can vary for the same corner (elliptical arc) var topLeftDiameterHeight = Math.Min(radius.TopLeft * 2, area.Height); var topLeftDiameterWidth = Math.Min(radius.TopLeft * 2, area.Width); var topRightDiameterHeight = Math.Min(radius.TopRight * 2, area.Height); var topRightDiameterWidth = Math.Min(radius.TopRight * 2, area.Width); var bottomLeftDiameterHeight = Math.Min(radius.BottomLeft * 2, area.Height); var bottomLeftDiameterWidth = Math.Min(radius.BottomLeft * 2, area.Width); var bottomRightDiameterHeight = Math.Min(radius.BottomRight * 2, area.Height); var bottomRightDiameterWidth = Math.Min(radius.BottomRight * 2, area.Width); // Top line path.MoveTo((float)(area.X + topLeftDiameterWidth / 2), (float)(area.Y)); path.LineTo((float)(area.Right - topRightDiameterWidth / 2), (float)(area.Y)); // Top right corner path.ArcTo( new RectF( left: (float)(area.Right - topRightDiameterWidth), top: (float)(area.Y), bottom: (float)(area.Y + topRightDiameterHeight), right: (float)(area.Right) ), startAngle: 270, sweepAngle: 90 ); // Right line path.LineTo((float)area.Right, (float)(area.Bottom - bottomRightDiameterHeight / 2)); // Bottom right corner path.ArcTo( new RectF( left: (float)(area.Right - bottomRightDiameterWidth), top: (float)(area.Bottom - bottomRightDiameterHeight), bottom: (float)area.Bottom, right: (float)area.Right ), startAngle: 0, sweepAngle: 90 ); // Bottom line path.LineTo((float)(area.X + bottomLeftDiameterWidth / 2), (float)area.Bottom); // Bottom left corner path.ArcTo( new RectF( left: (float)area.X, top: (float)(area.Bottom - bottomLeftDiameterHeight), bottom: (float)area.Bottom, right: (float)(area.X + bottomLeftDiameterWidth) ), startAngle: 90, sweepAngle: 90 ); // Left line path.LineTo((float)area.X, (float)(area.Y + topLeftDiameterHeight / 2)); // Top left corner path.ArcTo( new RectF( left: (float)area.X, top: (float)area.Y, bottom: (float)(area.Y + topLeftDiameterHeight), right: (float)(area.X + topLeftDiameterWidth) ), startAngle: 180, sweepAngle: 90 ); path.Close(); //We only need to set a background if the drawArea is non-zero if (!drawArea.HasZeroArea()) { var imageBrushBackground = background as ImageBrush; if (imageBrushBackground != null) { //Copy the path because it will be disposed when we exit the using block var pathCopy = new Path(path); var setBackground = DispatchSetImageBrushAsBackground(view, imageBrushBackground, drawArea, onImageSet, pathCopy); disposables.Add(setBackground); } else { var fillPaint = background?.GetFillPaint(drawArea) ?? new Paint() { Color = Android.Graphics.Color.Transparent }; ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(GetBackgroundDrawable(background, drawArea, fillPaint, path))); } disposables.Add(() => ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(null))); } if (borderThickness != Thickness.Empty && borderBrush != null && !(borderBrush is ImageBrush)) { using (var strokePaint = new Paint(borderBrush.GetStrokePaint(drawArea))) { var overlay = GetOverlayDrawable(strokePaint, physicalBorderThickness, new Size((int)drawArea.Width, (int)drawArea.Height), path); if (overlay != null) { overlay.SetBounds(0, 0, view.Width, view.Height); SetOverlay(view, disposables, overlay); } } } } } else // No corner radius { //We only need to set a background if the drawArea is non-zero if (!drawArea.HasZeroArea()) { var imageBrushBackground = background as ImageBrush; if (imageBrushBackground != null) { var setBackground = DispatchSetImageBrushAsBackground(view, imageBrushBackground, drawArea, onImageSet); disposables.Add(setBackground); } else { var fillPaint = background?.GetFillPaint(drawArea) ?? new Paint() { Color = Android.Graphics.Color.Transparent }; ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(GetBackgroundDrawable(background, drawArea, fillPaint))); } disposables.Add(() => ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(null))); } if (borderBrush != null && !(borderBrush is ImageBrush)) { //TODO: Handle case that BorderBrush is an ImageBrush using (var strokePaint = borderBrush.GetStrokePaint(drawArea)) { var overlay = GetOverlayDrawable(strokePaint, physicalBorderThickness, new Size(view.Width, view.Height)); if (overlay != null) { overlay.SetBounds(0, 0, view.Width, view.Height); SetOverlay(view, disposables, overlay); } } } } return(disposables); }