private static void SetOverlay(BindableView view, CompositeDisposable disposables, Drawable overlay)
        {
#if __ANDROID_18__
            if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat)
            {
                ExecuteWithNoRelayout(view, v => v.Overlay.Add(overlay));
                disposables.Add(() => ExecuteWithNoRelayout(view, v => v.Overlay.Remove(overlay)));
            }
            else
#endif
            {
                // Set overlay is not supported by this platform, set the background instead
                // and merge with the existing background.
                // It'll break some scenarios, like having borders on top of the content.

                var list = new List <Drawable>();

                var currentBackground = view.Background;
                if (currentBackground != null)
                {
                    list.Add(currentBackground);
                }

                list.Add(overlay);

                view.SetBackgroundDrawable(new LayerDrawable(list.ToArray()));
                disposables.Add(() => view.SetBackgroundDrawable(null));
            }
        }
Example #2
0
 private void ExecuteWithNoRelayout(BindableView view, Action <BindableView> action)
 {
     using (view.PreventRequestLayout())
     {
         action(view);
     }
 }
        private static void ExecuteWithNoRelayout(BindableView target, Action <BindableView> action)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            using (target.PreventRequestLayout())
            {
                action(target);
            }
        }
        private static IDisposable DispatchSetImageBrushAsBackground(BindableView view, ImageBrush background, Windows.Foundation.Rect drawArea, Action onImageSet, Path maskingPath = null)
        {
            var disposable = new CompositeDisposable();

            Dispatch(
                view?.Dispatcher,
                async ct =>
            {
                var bitmapDisposable = await SetImageBrushAsBackground(ct, view, background, drawArea, maskingPath, onImageSet);
                disposable.Add(bitmapDisposable);
            }
                )
            .DisposeWith(disposable);

            return(disposable);
        }
Example #5
0
        public override void OnInspectorGUI()
        {
            serializedObject.Update();
            BindableView actualObject = serializedObject.targetObject as System.Object as BindableView;

            actualObject.Initialize();
            SerializedProperty tps = serializedObject.FindProperty("serializedInfos");

            EditorGUI.BeginChangeCheck();
            for (int i = 0; i < tps.arraySize; i++)
            {
                EditorGUILayout.PropertyField(tps.GetArrayElementAtIndex(i), new GUIContent(actualObject.BindableValueRestraints[i].Label), true);
            }
            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
            }
        }
Example #6
0
        internal IDisposable Subscribe(BindableView owner, Rect drawArea, Path maskingPath)
        {
            var state = new AcrylicState(owner, drawArea, maskingPath);

            var compositeDisposable = new CompositeDisposable(6);

            this.RegisterDisposablePropertyChangedCallback(
                AlwaysUseFallbackProperty,
                (_, __) => Apply(state))
            .DisposeWith(compositeDisposable);

            this.RegisterDisposablePropertyChangedCallback(
                FallbackColorProperty,
                (_, __) => Apply(state))
            .DisposeWith(compositeDisposable);

            this.RegisterDisposablePropertyChangedCallback(
                TintColorProperty,
                (_, __) => Apply(state))
            .DisposeWith(compositeDisposable);

            this.RegisterDisposablePropertyChangedCallback(
                TintOpacityProperty,
                (_, __) => Apply(state))
            .DisposeWith(compositeDisposable);

            this.RegisterDisposablePropertyChangedCallback(
                OpacityProperty,
                (_, __) => Apply(state))
            .DisposeWith(compositeDisposable);

            Apply(state);

            Disposable.Create(() =>
            {
                state.FallbackDisposable.Disposable = null;
                state.BlurDisposable.Disposable     = null;
            }).DisposeWith(compositeDisposable);

            return(compositeDisposable);
        }
        //Load bitmap from ImageBrush and set it as a bitmapDrawable background on target view
        private static async Task <IDisposable> SetImageBrushAsBackground(CancellationToken ct, BindableView view, ImageBrush background, Windows.Foundation.Rect drawArea, Path maskingPath, Action onImageSet)
        {
            var bitmap = await background.GetBitmap(ct, drawArea, maskingPath);

            onImageSet();

            if (ct.IsCancellationRequested || bitmap == null)
            {
                bitmap?.Recycle();
                bitmap?.Dispose();
                return(Disposable.Empty);
            }

            var bitmapDrawable = new BitmapDrawable(bitmap);

            SetDrawableAlpha(bitmapDrawable, (int)(background.Opacity * __opaqueAlpha));
            ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(bitmapDrawable));

            return(Disposable.Create(() =>
            {
                bitmapDrawable?.Bitmap?.Recycle();
                bitmapDrawable?.Dispose();
            }));
        }
        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);
        }
Example #9
0
        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);
        }
Example #10
0
        private void SetBackground(BindableView view, Drawable drawable)
        {
#pragma warning disable 618 // Using older method for compatibility with API 15
            view.SetBackgroundDrawable(drawable);
#pragma warning restore 618
        }
Example #11
0
 public UIElementCollection(BindableView owner)
 {
     _owner = owner;
 }
Example #12
0
        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);
        }
Example #13
0
 public AcrylicState(BindableView owner, Rect drawArea, Path maskingPath)
 {
     Owner       = owner;
     DrawArea    = drawArea;
     MaskingPath = maskingPath;
 }