Exemple #1
0
        protected override void OnFillChanged(Brush newValue)
        {
            base.OnFillChanged(newValue);

            if (_gradientLayer != null)
            {
                _gradientLayer.RemoveFromSuperLayer();
                _gradientLayer = null;
            }

            _rectangleLayer.FillColor = _Color.Clear.CGColor;

            var scbFill = newValue as SolidColorBrush;
            var lgbFill = newValue as LinearGradientBrush;

            if (scbFill != null)
            {
                _fillSubscription.Disposable =
                    Brush.AssignAndObserveBrush(scbFill, c => _rectangleLayer.FillColor = c);
            }
            else if (lgbFill != null)
            {
                _gradientLayer = lgbFill.GetLayer(Frame.Size);
                Layer.InsertSublayer(_gradientLayer, 0);                 // We want the _gradientLayer to be below the _rectangleLayer (which contains the stroke)
            }

            InvalidateMeasure();
        }
Exemple #2
0
        protected override void OnBackgroundChanged(DependencyPropertyChangedEventArgs e)
        {
            // Don't call base, just update the filling color.
            _brushChanged.Disposable = Brush.AssignAndObserveBrush(e.NewValue as Brush, c => UpdateBorder(), UpdateBorder);

            UpdateBorder();
        }
Exemple #3
0
        protected override void OnStrokeUpdated(Brush newValue)
        {
            base.OnStrokeUpdated(newValue);

            _strokeSubscription.Disposable =
                Brush.AssignAndObserveBrush(Stroke, c => _rectangleLayer.StrokeColor = c);

            this.SetNeedsDisplay();
        }
Exemple #4
0
        partial void OnBorderBrushChangedPartial(Brush oldValue, Brush newValue)
        {
            _borderBrushChanged.Disposable = null;
            if (newValue?.SupportsAssignAndObserveBrush ?? false)
            {
                _borderBrushChanged.Disposable = Brush.AssignAndObserveBrush(newValue, _ => UpdateBorder());
            }

            UpdateBorder();
        }
Exemple #5
0
        private void UpdateStroke()
        {
            var brush = Stroke as SolidColorBrush ?? SolidColorBrushHelper.Transparent;

            if (_pathSpriteShape != null)
            {
                _strokeSubscription.Disposable =
                    Brush.AssignAndObserveBrush(brush, c => _pathSpriteShape.StrokeBrush = Visual.Compositor.CreateColorBrush(brush.Color));
            }
        }
Exemple #6
0
        protected override void OnStrokeUpdated(Brush newValue)
        {
            base.OnStrokeUpdated(newValue);

            var brush = Stroke as SolidColorBrush ?? SolidColorBrushHelper.Transparent;

            _strokeSubscription.Disposable =
                Brush.AssignAndObserveBrush(brush, c => _rectangleLayer.StrokeColor = c);

            this.SetNeedsDisplay();
        }
Exemple #7
0
        protected virtual void OnFillChanged(Brush newValue)
        {
            _brushChanged.Disposable = Brush.AssignAndObserveBrush(newValue, _ =>
#if __WASM__
                                                                   OnFillUpdatedPartial()
#else
                                                                   RefreshShape(true)
#endif
                                                                   );

            OnFillUpdated(newValue);
        }
Exemple #8
0
        private void UpdateStroke()
        {
            if (_pathSpriteShape != null)
            {
                _strokeSubscription.Disposable = null;

                _pathSpriteShape.StrokeBrush = null;

                _strokeSubscription.Disposable =
                    Brush.AssignAndObserveBrush(Stroke, Visual.Compositor, compositionBrush => _pathSpriteShape.StrokeBrush = compositionBrush);
            }
        }
Exemple #9
0
        private void UpdateFill()
        {
            if (_pathSpriteShape != null)
            {
                _fillSubscription.Disposable = null;

                _pathSpriteShape.FillBrush = null;

                _fillSubscription.Disposable =
                    Brush.AssignAndObserveBrush(Fill, Visual.Compositor, compositionBrush => _pathSpriteShape.FillBrush = compositionBrush);
            }
        }
        private void OnForegroundChanged(Brush oldValue, Brush newValue)
        {
            _foregroundChanged.Disposable = null;
            var scb = newValue as SolidColorBrush;

            if (scb != null)
            {
                _foregroundChanged.Disposable = Brush.AssignAndObserveBrush(scb, _ => ApplyColor());
                ApplyColor();

                void ApplyColor()
                {
                    SetTextColor(scb.Color);
                    SetCursorColor(scb.Color);
                }
            }
        }
Exemple #11
0
        private void UpdateFill()
        {
            if (_pathSpriteShape != null)
            {
                _fillSubscription.Disposable = null;

                _pathSpriteShape.FillBrush = null;

                var scbFill = Fill as SolidColorBrush;
                var lgbFill = Fill as LinearGradientBrush;
                if (scbFill != null)
                {
                    _fillSubscription.Disposable =
                        Brush.AssignAndObserveBrush(scbFill, c => _pathSpriteShape.FillBrush = Visual.Compositor.CreateColorBrush(scbFill.Color));
                }
                else if (lgbFill != null)
                {
                }
            }
        }
        public void OnForegroundChanged(Brush oldValue, Brush newValue)
        {
            _foregroundChanged.Disposable = null;
            var textBox = _textBox.GetTarget();

            if (textBox != null)
            {
                var scb = newValue as SolidColorBrush;

                if (scb != null)
                {
                    _foregroundChanged.Disposable = Brush.AssignAndObserveBrush(scb, _ => ApplyColor());
                    ApplyColor();

                    void ApplyColor()
                    {
                        TextColor = scb.Color;
                        TintColor = scb.Color;
                    }
                }
            }
        }
Exemple #13
0
        private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, LayoutState state)
        {
            var area            = state.Area;
            var background      = state.Background;
            var borderThickness = state.BorderThickness;
            var borderBrush     = state.BorderBrush;
            var cornerRadius    = state.CornerRadius;

            var disposables = new CompositeDisposable();
            var sublayers   = new List <CALayer>();

            var adjustedLineWidth       = borderThickness.Top;
            var adjustedLineWidthOffset = adjustedLineWidth / 2;

            var adjustedArea = area;

            adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

            if (cornerRadius != CornerRadius.None)
            {
                var maxRadius = Math.Max(0, Math.Min((float)area.Width / 2 - adjustedLineWidthOffset, (float)area.Height / 2 - adjustedLineWidthOffset));
                cornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxRadius),
                    Math.Min(cornerRadius.TopRight, maxRadius),
                    Math.Min(cornerRadius.BottomRight, maxRadius),
                    Math.Min(cornerRadius.BottomLeft, maxRadius));

                CAShapeLayer layer = new CAShapeLayer();
                layer.LineWidth = (nfloat)adjustedLineWidth;
                layer.FillColor = null;


                Brush.AssignAndObserveBrush(borderBrush, color => layer.StrokeColor = color)
                .DisposeWith(disposables);
                var path = GetRoundedPath(cornerRadius, adjustedArea);

                var outerPath = GetRoundedPath(cornerRadius, area);

                var insertionIndex = 0;

                if (background is GradientBrush gradientBackground)
                {
                    var fillMask = new CAShapeLayer()
                    {
                        Path  = path,
                        Frame = area,
                        // We only use the fill color to create the mask area
                        FillColor = _Color.White.CGColor,
                    };
                    // We reduce the adjustedArea again so that the gradient is inside the border (like in Windows)
                    adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

                    CreateGradientBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask);
                }
                else if (background is SolidColorBrush scbBackground)
                {
                    Brush.AssignAndObserveBrush(scbBackground, color => layer.FillColor = color)
                    .DisposeWith(disposables);
                }
                else if (background is ImageBrush imgBackground)
                {
                    var uiImage = imgBackground.ImageSource?.ImageData;
                    if (uiImage != null && uiImage.Size != CGSize.Empty)
                    {
                        var fillMask = new CAShapeLayer()
                        {
                            Path  = path,
                            Frame = area,
                            // We only use the fill color to create the mask area
                            FillColor = _Color.White.CGColor,
                        };
                        // We reduce the adjustedArea again so that the image is inside the border (like in Windows)
                        adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

                        CreateImageBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, imgBackground, fillMask);
                    }
                }
                else
                {
                    layer.FillColor = Colors.Transparent;
                }

                layer.Path = path;

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

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

                if (owner != null)
                {
                    owner.ClippingIsSetByCornerRadius = true;
                }

                state.BoundsPath = path;
            }
            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 insideArea     = new CGRect(CGPoint.Empty, fullArea.Size);
                    var insertionIndex = 0;

                    CreateGradientBrushLayers(fullArea, insideArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask: null);
                }
                else if (background is SolidColorBrush scbBackground)
                {
                    Brush.AssignAndObserveBrush(scbBackground, c => parent.BackgroundColor = c)
                    .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)
                    .DisposeWith(disposables);
                }
                else if (background is ImageBrush imgBackground)
                {
                    var uiImage = imgBackground.ImageSource?.ImageData;
                    if (uiImage != null && 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);
                    }
                }
                else
                {
                    parent.BackgroundColor = Colors.Transparent;
                }

                if (borderThickness != Thickness.Empty)
                {
                    Action <Action <CAShapeLayer, CGPath> > createLayer = builder =>
                    {
                        CAShapeLayer layer = new CAShapeLayer();
                        var          path  = new CGPath();

                        Brush.AssignAndObserveBrush(borderBrush, c => layer.StrokeColor = c)
                        .DisposeWith(disposables);

                        builder(layer, path);
                        layer.Path = path;

                        // Must be inserted below the other subviews, which may happen when
                        // the current view has subviews.
                        sublayers.Add(layer);
                        parent.InsertSublayer(layer, 0);
                    };

                    if (borderThickness.Top != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.LineWidth         = (nfloat)borderThickness.Top;
                            var lineWidthAdjust = (nfloat)(borderThickness.Top / 2);
                            path.MoveToPoint(area.X + (nfloat)borderThickness.Left, area.Y + lineWidthAdjust);
                            path.AddLineToPoint(area.X + area.Width - (nfloat)borderThickness.Right, area.Y + lineWidthAdjust);
                            path.CloseSubpath();
                        });
                    }

                    if (borderThickness.Bottom != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.LineWidth         = (nfloat)borderThickness.Bottom;
                            var lineWidthAdjust = borderThickness.Bottom / 2;
                            path.MoveToPoint(area.X + (nfloat)borderThickness.Left, (nfloat)(area.Y + area.Height - lineWidthAdjust));
                            path.AddLineToPoint(area.X + area.Width - (nfloat)borderThickness.Right, (nfloat)(area.Y + area.Height - lineWidthAdjust));
                            path.CloseSubpath();
                        });
                    }

                    if (borderThickness.Left != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.LineWidth         = (nfloat)borderThickness.Left;
                            var lineWidthAdjust = borderThickness.Left / 2;
                            path.MoveToPoint((nfloat)(area.X + lineWidthAdjust), area.Y);
                            path.AddLineToPoint((nfloat)(area.X + lineWidthAdjust), area.Y + area.Height);
                            path.CloseSubpath();
                        });
                    }

                    if (borderThickness.Right != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.LineWidth         = (nfloat)borderThickness.Right;
                            var lineWidthAdjust = borderThickness.Right / 2;
                            path.MoveToPoint((nfloat)(area.X + area.Width - lineWidthAdjust), area.Y);
                            path.AddLineToPoint((nfloat)(area.X + area.Width - lineWidthAdjust), area.Y + area.Height);
                            path.CloseSubpath();
                        });
                    }
                }

                state.BoundsPath = CGPath.FromRect(parent.Bounds);
            }

            disposables.Add(() =>
            {
                foreach (var sl in sublayers)
                {
                    sl.RemoveFromSuperLayer();
                    sl.Dispose();
                }

                if (owner != null)
                {
                    owner.ClippingIsSetByCornerRadius = false;
                }
            }
                            );
            return(disposables);
        }
        private static IDisposable InnerCreateLayer(UIElement owner, LayoutState state)
        {
            var parent          = owner.Visual;
            var compositor      = parent.Compositor;
            var area            = owner.LayoutRound(state.Area);
            var background      = state.Background;
            var borderThickness = owner.LayoutRound(state.BorderThickness);
            var borderBrush     = state.BorderBrush;
            var cornerRadius    = state.CornerRadius;

            var disposables = new CompositeDisposable();
            var sublayers   = new List <Visual>();

            var heightOffset = ((float)borderThickness.Top / 2) + ((float)borderThickness.Bottom / 2);
            var widthOffset  = ((float)borderThickness.Left / 2) + ((float)borderThickness.Right / 2);
            var halfWidth    = (float)area.Width / 2;
            var halfHeight   = (float)area.Height / 2;
            var adjustedArea = area;

            adjustedArea = adjustedArea.DeflateBy(borderThickness);

            if (cornerRadius != CornerRadius.None)
            {
                var maxOuterRadius = Math.Max(0, Math.Min(halfWidth - widthOffset, halfHeight - heightOffset));
                var maxInnerRadius = Math.Max(0, Math.Min(halfWidth, halfHeight));

                cornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxOuterRadius),
                    Math.Min(cornerRadius.TopRight, maxOuterRadius),
                    Math.Min(cornerRadius.BottomRight, maxOuterRadius),
                    Math.Min(cornerRadius.BottomLeft, maxOuterRadius));

                var innerCornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxInnerRadius),
                    Math.Min(cornerRadius.TopRight, maxInnerRadius),
                    Math.Min(cornerRadius.BottomRight, maxInnerRadius),
                    Math.Min(cornerRadius.BottomLeft, maxInnerRadius));

                var borderShape     = compositor.CreateSpriteShape();
                var backgroundShape = compositor.CreateSpriteShape();
                var outerShape      = compositor.CreateSpriteShape();

                // Border brush
                Brush.AssignAndObserveBrush(borderBrush, compositor, brush => borderShape.FillBrush = brush)
                .DisposeWith(disposables);

                // Background brush
                if (background is ImageBrush imgBackground)
                {
                    adjustedArea = CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, adjustedArea, imgBackground);
                }
                else
                {
                    Brush.AssignAndObserveBrush(background, compositor, brush => backgroundShape.FillBrush = brush)
                    .DisposeWith(disposables);
                }

                var borderPath     = GetRoundedRect(cornerRadius, innerCornerRadius, area, adjustedArea);
                var backgroundPath = GetRoundedPath(cornerRadius, adjustedArea);
                var outerPath      = GetRoundedPath(cornerRadius, area);

                backgroundShape.Geometry = compositor.CreatePathGeometry(backgroundPath);
                borderShape.Geometry     = compositor.CreatePathGeometry(borderPath);
                outerShape.Geometry      = compositor.CreatePathGeometry(outerPath);

                var borderVisual     = compositor.CreateShapeVisual();
                var backgroundVisual = compositor.CreateShapeVisual();
                backgroundVisual.Shapes.Add(backgroundShape);
                borderVisual.Shapes.Add(borderShape);

                sublayers.Add(backgroundVisual);
                sublayers.Add(borderVisual);
                parent.Children.InsertAtBottom(backgroundVisual);
                parent.Children.InsertAtTop(borderVisual);

                owner.ClippingIsSetByCornerRadius = cornerRadius != CornerRadius.None;
                if (owner.ClippingIsSetByCornerRadius)
                {
                    parent.Clip = compositor.CreateGeometricClip(outerShape.Geometry);
                }
            }
            else
            {
                var shapeVisual = compositor.CreateShapeVisual();

                var backgroundShape = compositor.CreateSpriteShape();

                var backgroundArea = area;

                // Background brush
                if (background is ImageBrush imgBackground)
                {
                    backgroundArea = CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, backgroundArea, imgBackground);
                }
                else
                {
                    Brush.AssignAndObserveBrush(background, compositor, brush => backgroundShape.FillBrush = brush)
                    .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(() => backgroundShape.FillBrush = null)
                    .DisposeWith(disposables);
                }

                var geometrySource = new SkiaGeometrySource2D();
                var geometry       = geometrySource.Geometry;

                geometry.AddRect(backgroundArea.ToSKRect());

                backgroundShape.Geometry = compositor.CreatePathGeometry(new CompositionPath(geometrySource));

                shapeVisual.Shapes.Add(backgroundShape);

                if (borderThickness != Thickness.Empty)
                {
                    Action <Action <CompositionSpriteShape, SKPath> > createLayer = builder =>
                    {
                        var spriteShape = compositor.CreateSpriteShape();
                        var geometry    = new SkiaGeometrySource2D();

                        // Border brush
                        Brush.AssignAndObserveBrush(borderBrush, compositor, brush => spriteShape.StrokeBrush = brush)
                        .DisposeWith(disposables);

                        builder(spriteShape, geometry.Geometry);
                        spriteShape.Geometry = compositor.CreatePathGeometry(new CompositionPath(geometry));

                        shapeVisual.Shapes.Add(spriteShape);
                    };

                    if (borderThickness.Top != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.StrokeThickness         = (float)borderThickness.Top;
                            var StrokeThicknessAdjust = (float)(borderThickness.Top / 2);
                            path.MoveTo((float)(area.X + borderThickness.Left), (float)(area.Y + StrokeThicknessAdjust));
                            path.LineTo((float)(area.X + area.Width - borderThickness.Right), (float)(area.Y + StrokeThicknessAdjust));
                            path.Close();
                        });
                    }

                    if (borderThickness.Bottom != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.StrokeThickness         = (float)borderThickness.Bottom;
                            var StrokeThicknessAdjust = borderThickness.Bottom / 2;
                            path.MoveTo((float)(area.X + (float)borderThickness.Left), (float)(area.Y + area.Height - StrokeThicknessAdjust));
                            path.LineTo((float)(area.X + area.Width - (float)borderThickness.Right), (float)(area.Y + area.Height - StrokeThicknessAdjust));
                            path.Close();
                        });
                    }

                    if (borderThickness.Left != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.StrokeThickness         = (float)borderThickness.Left;
                            var StrokeThicknessAdjust = borderThickness.Left / 2;
                            path.MoveTo((float)(area.X + StrokeThicknessAdjust), (float)area.Y);
                            path.LineTo((float)(area.X + StrokeThicknessAdjust), (float)(area.Y + area.Height));
                            path.Close();
                        });
                    }

                    if (borderThickness.Right != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.StrokeThickness         = (float)borderThickness.Right;
                            var StrokeThicknessAdjust = borderThickness.Right / 2;
                            path.MoveTo((float)(area.X + area.Width - StrokeThicknessAdjust), (float)area.Y);
                            path.LineTo((float)(area.X + area.Width - StrokeThicknessAdjust), (float)(area.Y + area.Height));
                            path.Close();
                        });
                    }
                }

                sublayers.Add(shapeVisual);

                // Must be inserted below the other subviews, which may happen when
                // the current view has subviews.
                parent.Children.InsertAtBottom(shapeVisual);
            }

            disposables.Add(() =>
            {
                owner.ClippingIsSetByCornerRadius = false;

                foreach (var sv in sublayers)
                {
                    parent.Children.Remove(sv);
                    sv.Dispose();
                }
            }
                            );

            compositor.InvalidateRender();

            return(disposables);
        }
Exemple #15
0
        protected virtual void OnFillChanged(Brush newValue)
        {
            _brushChanged.Disposable = Brush.AssignAndObserveBrush(newValue, _ => RefreshShape(true));

            OnFillUpdated(newValue);
        }
Exemple #16
0
        private static IDisposable InnerCreateLayer(CALayer parent, CGRect area, Brush background, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius)
        {
            var disposables = new CompositeDisposable();
            var sublayers   = new List <CALayer>();

            var adjustedLineWidth       = borderThickness.Top;
            var adjustedLineWidthOffset = adjustedLineWidth / 2;

            var adjustedArea = area;

            adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

            if (cornerRadius != CornerRadius.None)
            {
                var maxRadius = Math.Max(0, Math.Min((float)area.Width / 2 - adjustedLineWidthOffset, (float)area.Height / 2 - adjustedLineWidthOffset));
                cornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxRadius),
                    Math.Min(cornerRadius.TopRight, maxRadius),
                    Math.Min(cornerRadius.BottomRight, maxRadius),
                    Math.Min(cornerRadius.BottomLeft, maxRadius));

                CAShapeLayer layer = new CAShapeLayer();
                layer.LineWidth = (nfloat)adjustedLineWidth;
                layer.FillColor = null;

                var path = new CGPath();

                Brush.AssignAndObserveBrush(borderBrush, color => layer.StrokeColor = color)
                .DisposeWith(disposables);

                // How AddArcToPoint works:
                // http://www.twistedape.me.uk/blog/2013/09/23/what-arctopointdoes/

                path.MoveToPoint(adjustedArea.GetMidX(), adjustedArea.Y);
                path.AddArcToPoint(adjustedArea.Right, adjustedArea.Top, adjustedArea.Right, adjustedArea.GetMidY(), (float)cornerRadius.TopRight);
                path.AddArcToPoint(adjustedArea.Right, adjustedArea.Bottom, adjustedArea.GetMidX(), adjustedArea.Bottom, (float)cornerRadius.BottomRight);
                path.AddArcToPoint(adjustedArea.Left, adjustedArea.Bottom, adjustedArea.Left, adjustedArea.GetMidY(), (float)cornerRadius.BottomLeft);
                path.AddArcToPoint(adjustedArea.Left, adjustedArea.Top, adjustedArea.GetMidX(), adjustedArea.Top, (float)cornerRadius.TopLeft);

                path.CloseSubpath();

                var lgbBackground  = background as LinearGradientBrush;
                var scbBackground  = background as SolidColorBrush;
                var imgBackground  = background as ImageBrush;
                var insertionIndex = 0;

                if (lgbBackground != null)
                {
                    var fillMask = new CAShapeLayer()
                    {
                        Path  = path,
                        Frame = area,
                        // We only use the fill color to create the mask area
                        FillColor = UIColor.White.CGColor,
                    };

                    // We reduce the adjustedArea again so that the gradient is inside the border (like in Windows)
                    adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

                    CreateLinearGradientBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, lgbBackground, fillMask);
                }
                else if (scbBackground != null)
                {
                    Brush.AssignAndObserveBrush(scbBackground, color => layer.FillColor = color)
                    .DisposeWith(disposables);
                }
                else if (imgBackground != null)
                {
                    var uiImage = imgBackground.ImageSource?.ImageData;
                    if (uiImage != null && uiImage.Size != CGSize.Empty)
                    {
                        var fillMask = new CAShapeLayer()
                        {
                            Path  = path,
                            Frame = area,
                            // We only use the fill color to create the mask area
                            FillColor = UIColor.White.CGColor,
                        };

                        // We reduce the adjustedArea again so that the image is inside the border (like in Windows)
                        adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

                        CreateImageBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, imgBackground, fillMask);
                    }
                }
                else
                {
                    layer.FillColor = Colors.Transparent;
                }

                layer.Path = path;

                sublayers.Add(layer);
                parent.InsertSublayer(layer, insertionIndex);
            }
            else
            {
                var lgbBackground = background as LinearGradientBrush;
                var scbBackground = background as SolidColorBrush;
                var imgBackground = background as ImageBrush;

                if (lgbBackground != null)
                {
                    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;

                    CreateLinearGradientBrushLayers(fullArea, insideArea, parent, sublayers, ref insertionIndex, lgbBackground, fillMask: null);
                }
                else if (scbBackground != null)
                {
                    Brush.AssignAndObserveBrush(scbBackground, c => parent.BackgroundColor = c)
                    .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)
                    .DisposeWith(disposables);
                }
                else if (imgBackground != null)
                {
                    var uiImage = imgBackground.ImageSource?.ImageData;
                    if (uiImage != null && 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);
                    }
                }
                else
                {
                    parent.BackgroundColor = Colors.Transparent;
                }

                if (borderThickness != Thickness.Empty)
                {
                    var strokeColor = borderBrush ?? SolidColorBrushHelper.Transparent;

                    Action <Action <CAShapeLayer, CGPath> > createLayer = builder =>
                    {
                        CAShapeLayer layer = new CAShapeLayer();
                        var          path  = new CGPath();

                        Brush.AssignAndObserveBrush(borderBrush, c => layer.StrokeColor = c)
                        .DisposeWith(disposables);

                        builder(layer, path);
                        layer.Path = path;

                        // Must be inserted below the other subviews, which may happen when
                        // the current view has subviews.
                        sublayers.Add(layer);
                        parent.InsertSublayer(layer, 0);
                    };

                    if (borderThickness.Top != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.LineWidth         = (nfloat)borderThickness.Top;
                            var lineWidthAdjust = (nfloat)(borderThickness.Top / 2);
                            path.MoveToPoint(area.X, area.Y + lineWidthAdjust);
                            path.AddLineToPoint(area.X + area.Width, area.Y + lineWidthAdjust);
                            path.CloseSubpath();
                        });
                    }

                    if (borderThickness.Bottom != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.LineWidth         = (nfloat)borderThickness.Bottom;
                            var lineWidthAdjust = borderThickness.Bottom / 2;
                            path.MoveToPoint(area.X, (nfloat)(area.Y + area.Height - lineWidthAdjust));
                            path.AddLineToPoint(area.X + area.Width, (nfloat)(area.Y + area.Height - lineWidthAdjust));
                            path.CloseSubpath();
                        });
                    }

                    if (borderThickness.Left != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.LineWidth         = (nfloat)borderThickness.Left;
                            var lineWidthAdjust = borderThickness.Left / 2;
                            path.MoveToPoint((nfloat)(area.X + lineWidthAdjust), area.Y);
                            path.AddLineToPoint((nfloat)(area.X + lineWidthAdjust), area.Y + area.Height);
                            path.CloseSubpath();
                        });
                    }

                    if (borderThickness.Right != 0)
                    {
                        createLayer((l, path) =>
                        {
                            l.LineWidth         = (nfloat)borderThickness.Right;
                            var lineWidthAdjust = borderThickness.Right / 2;
                            path.MoveToPoint((nfloat)(area.X + area.Width - lineWidthAdjust), area.Y);
                            path.AddLineToPoint((nfloat)(area.X + area.Width - lineWidthAdjust), area.Y + area.Height);
                            path.CloseSubpath();
                        });
                    }
                }
            }

            disposables.Add(() =>
            {
                foreach (var sl in sublayers)
                {
                    sl.RemoveFromSuperLayer();
                    sl.Dispose();
                }
            }
                            );
            return(disposables);
        }
        private static IDisposable InnerCreateLayer(UIElement owner, CALayer parent, LayoutState state)
        {
            var area             = state.Area;
            var background       = state.Background;
            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>();

            var heightOffset = ((float)borderThickness.Top / 2) + ((float)borderThickness.Bottom / 2);
            var widthOffset  = ((float)borderThickness.Left / 2) + ((float)borderThickness.Right / 2);
            var halfWidth    = (float)area.Width / 2;
            var halfHeight   = (float)area.Height / 2;
            var adjustedArea = area;

            adjustedArea = adjustedArea.Shrink(
                (nfloat)borderThickness.Left,
                (nfloat)borderThickness.Top,
                (nfloat)borderThickness.Right,
                (nfloat)borderThickness.Bottom
                );

            if (cornerRadius != CornerRadius.None)
            {
                var maxOuterRadius = Math.Max(0, Math.Min(halfWidth - widthOffset, halfHeight - heightOffset));
                var maxInnerRadius = Math.Max(0, Math.Min(halfWidth, halfHeight));

                cornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxOuterRadius),
                    Math.Min(cornerRadius.TopRight, maxOuterRadius),
                    Math.Min(cornerRadius.BottomRight, maxOuterRadius),
                    Math.Min(cornerRadius.BottomLeft, maxOuterRadius));

                var innerCornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxInnerRadius),
                    Math.Min(cornerRadius.TopRight, maxInnerRadius),
                    Math.Min(cornerRadius.BottomRight, maxInnerRadius),
                    Math.Min(cornerRadius.BottomLeft, maxInnerRadius));

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


                var path      = GetRoundedRect(cornerRadius, innerCornerRadius, area, adjustedArea);
                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  = backgroundPath,
                        Frame = area,
                        // We only use the fill color to create the mask area
                        FillColor = _Color.White.CGColor,
                    };

                    CreateGradientBrushLayers(area, backgroundArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask);
                }
                else if (background is SolidColorBrush scbBackground)
                {
                    Brush.AssignAndObserveBrush(scbBackground, color => backgroundLayer.FillColor = color)
                    .DisposeWith(disposables);
                }
                else if (background is ImageBrush imgBackground)
                {
                    var imgSrc = imgBackground.ImageSource;
                    if (imgSrc != null && imgSrc.TryOpenSync(out var uiImage) && uiImage.Size != CGSize.Empty)
                    {
                        var fillMask = new CAShapeLayer()
                        {
                            Path  = backgroundPath,
                            Frame = area,
                            // We only use the fill color to create the mask area
                            FillColor = _Color.White.CGColor,
                        };

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

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

                if (borderBrush is SolidColorBrush scbBorder || borderBrush == null)
                {
                    Brush.AssignAndObserveBrush(borderBrush, color =>
                    {
                        outerLayer.StrokeColor = color;
                        outerLayer.FillColor   = color;
                    })
                    .DisposeWith(disposables);
                }
                else if (borderBrush is GradientBrush gradientBorder)
                {
                    var fillMask = new CAShapeLayer()
                    {
                        Path  = path,
                        Frame = area,
                        // We only use the fill color to create the mask area
                        FillColor = _Color.White.CGColor,
                    };

                    var borderLayerIndex = parent.Sublayers.Length;
                    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,
                    Frame = area,
                    // We only use the fill color to create the mask area
                    FillColor = _Color.White.CGColor,
                };

                if (owner != null)
                {
                    owner.ClippingIsSetByCornerRadius = true;
                }

                state.BoundsPath = outerPath;
            }
Exemple #18
0
 partial void OnBorderBrushChangedPartial(Brush oldValue, Brush newValue)
 {
     _borderBrushChanged.Disposable = Brush.AssignAndObserveBrush(newValue, _ => UpdateBorder(), UpdateBorder);
     UpdateBorder();
 }
Exemple #19
0
        private static IDisposable InnerCreateLayer(UIElement owner, LayoutState state)
        {
            var parent          = owner.Visual;
            var compositor      = parent.Compositor;
            var area            = owner.LayoutRound(state.Area);
            var background      = state.Background;
            var borderThickness = owner.LayoutRound(state.BorderThickness);
            var borderBrush     = state.BorderBrush;
            var cornerRadius    = state.CornerRadius;

            var disposables = new CompositeDisposable();
            var sublayers   = new List <Visual>();

            var heightOffset = ((float)borderThickness.Top / 2) + ((float)borderThickness.Bottom / 2);
            var widthOffset  = ((float)borderThickness.Left / 2) + ((float)borderThickness.Right / 2);
            var adjustedArea = area;

            adjustedArea = adjustedArea.DeflateBy(borderThickness);

            if (cornerRadius != CornerRadius.None)
            {
                var maxRadius = Math.Max(0, Math.Min((float)area.Width / 2 - heightOffset, (float)area.Height / 2 - widthOffset));
                cornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxRadius),
                    Math.Min(cornerRadius.TopRight, maxRadius),
                    Math.Min(cornerRadius.BottomRight, maxRadius),
                    Math.Min(cornerRadius.BottomLeft, maxRadius));

                var borderShape     = compositor.CreateSpriteShape();
                var backgroundShape = compositor.CreateSpriteShape();
                var outerShape      = compositor.CreateSpriteShape();

                Brush.AssignAndObserveBrush(borderBrush, color => borderShape.FillBrush = compositor.CreateColorBrush(color))
                .DisposeWith(disposables);

                if (background is GradientBrush gradientBackground)
                {
                    //var fillMask = new CAShapeLayer()
                    //{
                    //	Path = path,
                    //	Frame = area,
                    //	// We only use the fill color to create the mask area
                    //	FillColor = _Color.White.CGColor,
                    //};
                    //// We reduce the adjustedArea again so that the gradient is inside the border (like in Windows)
                    //adjustedArea = adjustedArea.Shrink((float)adjustedStrokeThicknessOffset);

                    //CreateGradientBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, gradientBackground, fillMask);
                }
                else if (background is SolidColorBrush scbBackground)
                {
                    Brush.AssignAndObserveBrush(scbBackground, color => backgroundShape.FillBrush = compositor.CreateColorBrush(color))
                    .DisposeWith(disposables);
                }
                else if (background is ImageBrush imgBackground)
                {
                    adjustedArea = CreateImageLayer(compositor, disposables, borderThickness, adjustedArea, backgroundShape, adjustedArea, imgBackground);
                }
                else if (background is AcrylicBrush acrylicBrush)
                {
                    Brush.AssignAndObserveBrush(acrylicBrush, color => backgroundShape.FillBrush = compositor.CreateColorBrush(color))
                    .DisposeWith(disposables);
                }
                else
                {
                    backgroundShape.FillBrush = null;
                }

                var borderPath     = GetRoundedRect(cornerRadius, area, adjustedArea);
                var backgroundPath = GetRoundedPath(cornerRadius, adjustedArea);
                var outerPath      = GetRoundedPath(cornerRadius, area);

                backgroundShape.Geometry = compositor.CreatePathGeometry(backgroundPath);
                borderShape.Geometry     = compositor.CreatePathGeometry(borderPath);
                outerShape.Geometry      = compositor.CreatePathGeometry(outerPath);

                var borderVisual     = compositor.CreateShapeVisual();
                var backgroundVisual = compositor.CreateShapeVisual();
                backgroundVisual.Shapes.Add(backgroundShape);
                borderVisual.Shapes.Add(borderShape);

                sublayers.Add(backgroundVisual);
                sublayers.Add(borderVisual);
                parent.Children.InsertAtBottom(backgroundVisual);
                parent.Children.InsertAtTop(borderVisual);

                owner.ClippingIsSetByCornerRadius = cornerRadius != CornerRadius.None;
                if (owner.ClippingIsSetByCornerRadius)
                {
                    parent.Clip = compositor.CreateGeometricClip(outerShape.Geometry);
                }
            }
            else
            {
                var shapeVisual = compositor.CreateShapeVisual();

                var backgroundShape = compositor.CreateSpriteShape();

                var backgroundArea = area;

                if (background is GradientBrush gradientBackground)
                {
                    var fullArea = new Rect(
                        area.X + borderThickness.Left,
                        area.Y + borderThickness.Top,
                        area.Width - borderThickness.Left - borderThickness.Right,
                        area.Height - borderThickness.Top - borderThickness.Bottom);

                    var insideArea = new Rect(default, fullArea.Size);
Exemple #20
0
        private static IDisposable InnerCreateLayer(
            ContainerVisual parent,
            Rect area,
            Brush background,
            Thickness borderThickness,
            Brush borderBrush,
            CornerRadius cornerRadius)
        {
            var disposables = new CompositeDisposable();
            var subVisuals  = new List <Visual>();

            var adjustedLineWidth       = borderThickness.Top;
            var adjustedLineWidthOffset = adjustedLineWidth / 2;

            var adjustedArea = area;

            adjustedArea.Inflate(-adjustedLineWidthOffset, -adjustedLineWidthOffset);

            var compositor = parent.Compositor;

            var shapeVisual = compositor.CreateShapeVisual();

            parent.Children.InsertAtBottom(shapeVisual);

            var spriteShape = compositor.CreateSpriteShape();

            SkiaGeometrySource2D BuildGeometry()
            {
                var maxRadius = Math.Max(0, Math.Min((float)area.Width / 2 - adjustedLineWidthOffset, (float)area.Height / 2 - adjustedLineWidthOffset));

                cornerRadius = new CornerRadius(
                    Math.Min(cornerRadius.TopLeft, maxRadius),
                    Math.Min(cornerRadius.TopRight, maxRadius),
                    Math.Min(cornerRadius.BottomRight, maxRadius),
                    Math.Min(cornerRadius.BottomLeft, maxRadius));

                var geometry = new SkiaGeometrySource2D();

                Brush.AssignAndObserveBrush(borderBrush, color => spriteShape.StrokeBrush = compositor.CreateColorBrush(color))
                .DisposeWith(disposables);

                geometry.Geometry.MoveTo((float)adjustedArea.GetMidX(), (float)adjustedArea.Y);
                geometry.Geometry.ArcTo((float)adjustedArea.Right, (float)adjustedArea.Top, (float)adjustedArea.Right, (float)adjustedArea.GetMidY(), (float)cornerRadius.TopRight);
                geometry.Geometry.ArcTo((float)adjustedArea.Right, (float)adjustedArea.Bottom, (float)adjustedArea.GetMidX(), (float)adjustedArea.Bottom, (float)cornerRadius.BottomRight);
                geometry.Geometry.ArcTo((float)adjustedArea.Left, (float)adjustedArea.Bottom, (float)adjustedArea.Left, (float)adjustedArea.GetMidY(), (float)cornerRadius.BottomLeft);
                geometry.Geometry.ArcTo((float)adjustedArea.Left, (float)adjustedArea.Top, (float)adjustedArea.GetMidX(), (float)adjustedArea.Top, (float)cornerRadius.TopLeft);

                geometry.Geometry.Close();

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

                    //// We reduce the adjustedArea again so that the gradient is inside the border (like in Windows)
                    //adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

                    //CreateLinearGradientBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, lgbBackground, fillMask);
                }
                else if (background is SolidColorBrush scbBackground)
                {
                    Brush.AssignAndObserveBrush(scbBackground, color => spriteShape.FillBrush = compositor.CreateColorBrush(color))
                    .DisposeWith(disposables);
                }
                else if (background is ImageBrush imgBackground)
                {
                    //var uiImage = imgBackground.ImageSource?.ImageData;
                    //if (uiImage != null && uiImage.Size != CGSize.Empty)
                    //{
                    //	var fillMask = new CAShapeLayer()
                    //	{
                    //		Path = path,
                    //		Frame = area,
                    //		// We only use the fill color to create the mask area
                    //		FillColor = _Color.White.CGColor,
                    //	};

                    //	// We reduce the adjustedArea again so that the image is inside the border (like in Windows)
                    //	adjustedArea = adjustedArea.Shrink((nfloat)adjustedLineWidthOffset);

                    //	CreateImageBrushLayers(area, adjustedArea, parent, sublayers, ref insertionIndex, imgBackground, fillMask);
                    //}
                }
                else
                {
                    spriteShape.FillBrush = null;
                }

                return(geometry);
            }

            spriteShape.Geometry = compositor.CreatePathGeometry(new CompositionPath(BuildGeometry()));

            shapeVisual.Size   = new Vector2((float)area.Width, (float)area.Height);
            shapeVisual.Offset = new Vector3(0, 0, 0);

            shapeVisual.Shapes.Add(spriteShape);

            subVisuals.Add(shapeVisual);

            disposables.Add(() =>
            {
                foreach (var sv in subVisuals)
                {
                    parent.Children.Remove(sv);
                    sv.Dispose();
                }
            });

            Window.Current.QueueInvalidateRender();

            return(disposables);
        }