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)); } }
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); }
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(); } }
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); }
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 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 }
public UIElementCollection(BindableView owner) { _owner = owner; }
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); }
public AcrylicState(BindableView owner, Rect drawArea, Path maskingPath) { Owner = owner; DrawArea = drawArea; MaskingPath = maskingPath; }