Skip to content

Commit

Permalink
feat(AcrylicBrush): First Android version
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinZikmund committed Aug 5, 2020
1 parent 89fa0b2 commit 4a291e7
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 280 deletions.
60 changes: 13 additions & 47 deletions src/Uno.UI/UI/Xaml/Controls/Border/BorderLayerRenderer.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,22 @@ Action onImageSet
//We only need to set a background if the drawArea is non-zero
if (!drawArea.HasZeroArea())
{
var imageBrushBackground = background as ImageBrush;
if (imageBrushBackground != null)
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(GetBackgroundDrawable(background, drawArea, fillPaint, path)));
ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(Brush.GetBackgroundDrawable(background, drawArea, fillPaint, path)));
}
disposables.Add(() => ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(null)));
}
Expand Down Expand Up @@ -169,16 +173,15 @@ Action onImageSet
var setBackground = DispatchSetImageBrushAsBackground(view, imageBrushBackground, drawArea, onImageSet);
disposables.Add(setBackground);
}
//TODO: AcrylicBrush on Android
//else if (background is AcrylicBrush acrylicBrush)
//{
// var apply = acrylicBrush.Apply(view);
// disposables.Add(apply);
//}
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(GetBackgroundDrawable(background, drawArea, fillPaint)));
ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(Brush.GetBackgroundDrawable(background, drawArea, fillPaint)));
}
disposables.Add(() => ExecuteWithNoRelayout(view, v => v.SetBackgroundDrawable(null)));
}
Expand All @@ -202,43 +205,6 @@ Action onImageSet
return disposables;
}

private static Drawable GetBackgroundDrawable(Brush background, Windows.Foundation.Rect drawArea, Paint fillPaint, Path maskingPath = null)
{
if (background is ImageBrush)
{
throw new InvalidOperationException($"This method should not be called for ImageBrush, use {nameof(DispatchSetImageBrushAsBackground)} instead");
}

if (maskingPath == null)
{
var solidBrush = background as SolidColorBrush;

if (solidBrush != null)
{
return new ColorDrawable(solidBrush.ColorWithOpacity);
}

if (fillPaint != null)
{
var linearDrawable = new PaintDrawable();
var drawablePaint = linearDrawable.Paint;
drawablePaint.Color = fillPaint.Color;
drawablePaint.SetShader(fillPaint.Shader);

return linearDrawable;
}

return null;
}

var drawable = new PaintDrawable();
drawable.Shape = new PathShape(maskingPath, (float)drawArea.Width, (float)drawArea.Height);
var paint = drawable.Paint;
paint.Color = fillPaint.Color;
paint.SetShader(fillPaint.Shader);
return drawable;
}

private static void SetDrawableAlpha(Drawable drawable, int alpha)
{
#if __ANDROID_18__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,18 @@ public partial class AcrylicBrush
insertionIndex++, // we always use a single layer for acrylic
fillMask);

var compositeDisposable = new CompositeDisposable(5);
var compositeDisposable = new CompositeDisposable(7);

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

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

this.RegisterDisposablePropertyChangedCallback(
TintColorProperty,
(_, __) => Apply(state))
Expand All @@ -65,10 +70,12 @@ public partial class AcrylicBrush
(_, __) => Apply(state))
.DisposeWith(compositeDisposable);

#if __MACOS__
this.RegisterDisposablePropertyChangedCallback(
BackgroundSourceProperty,
(_, __) => Apply(state))
.DisposeWith(compositeDisposable);
#endif

Apply(state);

Expand Down
7 changes: 6 additions & 1 deletion src/Uno.UI/UI/Xaml/Media/AcrylicBrush/AcrylicBrush.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@ public partial class AcrylicBrush
/// <returns>Disposable.</returns>
internal IDisposable Subscribe(UIElement uiElement)
{
var compositeDisposable = new CompositeDisposable(5);
var compositeDisposable = new CompositeDisposable(6);

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

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

this.RegisterDisposablePropertyChangedCallback(
TintColorProperty,
(_, __) => Apply(uiElement))
Expand Down
134 changes: 116 additions & 18 deletions src/Uno.UI/UI/Xaml/Media/AcrylicBrush/Android/AcrylicBrush.Android.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,138 @@
using System;
#pragma warning disable CS0618
using System;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;
using Java.Lang;
using Uno.Disposables;
using Uno.UI.Controls;
using Uno.UI.Xaml.Media;
using Windows.UI.Xaml.Controls;

namespace Windows.UI.Xaml.Media
{
public partial class AcrylicBrush
{
protected override Paint GetPaintInner(Foundation.Rect destinationRect)
{
/// <summary>
/// Returns the fallback solid color brush.
/// </summary>
/// <param name="destinationRect">Destination rect.</param>
/// <returns></returns>
protected override Paint GetPaintInner(Foundation.Rect destinationRect) =>
new Paint()
{
Color = FallbackColorWithOpacity,
AntiAlias = true
};

internal IDisposable Subscribe(BindableView owner, Foundation.Rect drawArea, Path maskingPath)
{
return new Paint() { Color = FallbackColorWithOpacity, AntiAlias = true };
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;
}

bool ready = false;
internal IDisposable Apply(BindableView owner)
private void Apply(AcrylicState state)
{
if(!(owner is Border))
if (AlwaysUseFallback || !SupportsBlur())
{
throw new InvalidOperationException(
"AcrylicBrush can currently be applied " +
"to empty border only on Android");
state.BlurDisposable.Disposable = null;

// Fall back to solid color
var fillPaint = GetFillPaint(Foundation.Rect.Empty);
ExecuteWithNoRelayout(state.Owner, v => v.SetBackgroundDrawable(Brush.GetBackgroundDrawable(this, state.DrawArea, fillPaint, state.MaskingPath)));

if (state.FallbackDisposable.Disposable == null)
{
state.FallbackDisposable.Disposable = Disposable.Create(
() => ExecuteWithNoRelayout(state.Owner, v => v.SetBackgroundDrawable(null)));
}
}
View v = owner;

if (!ready)
else
{
Cleanup(owner);
SetAcrylicBlur(owner);
UpdateProperties();
ready = true;
state.FallbackDisposable.Disposable = null;

ApplyAcrylicBlur(state);

if (state.BlurDisposable.Disposable == null)
{
state.BlurDisposable.Disposable = Disposable.Create(
() => RemoveAcrylicBlur(state));
}
}
return new CompositeDisposable();
}

private void ExecuteWithNoRelayout(BindableView view, Action<BindableView> action)
{
using (view.PreventRequestLayout())
{
action(view);
}
}

/// <summary>
/// Wraps the acrylic brush metadata for a single view.
/// </summary>
private class AcrylicState
{
public AcrylicState(BindableView owner, Foundation.Rect drawArea, Path maskingPath)
{
Owner = owner;
DrawArea = drawArea;
MaskingPath = maskingPath;
}

public SerialDisposable FallbackDisposable { get; } = new SerialDisposable();

public SerialDisposable BlurDisposable { get; } = new SerialDisposable();

public GradientDrawable BackgroundDrawable { get; set; }

public View BlurViewWrapper { get; set; }

public RealtimeBlurView BlurView { get; set; }

public BindableView Owner { get; }

public Foundation.Rect DrawArea { get; }

public Path MaskingPath { get; }
}
}
}
#pragma warning restore CS6018
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,10 @@ namespace Windows.UI.Xaml.Media
/// </summary>
public partial class AcrylicBrush
{
private GradientDrawable _mainDrawable;
private GradientDrawable _acrylicLayer;

protected void UpdateProperties()
private void UpdateProperties(AcrylicState state)
{
UpdateCornerRadius();
UpdateAcrylicGlowColor();
UpdateAndroidBlurOverlayColor();
UpdateAndroidBlurRadius();
//UpdateAndroidBlurRootElement();
UpdateMaterialBlurStyle();
}

protected void Cleanup(BindableView view)
{
//TODO:
//MaterialFrame?.Unsubscribe();
_mainDrawable = null;

DestroyBlur(view);

_acrylicLayer?.Dispose();
_acrylicLayer = null;

_mainDrawable = null;

_acrylicLayer?.Dispose();
_acrylicLayer = null;
UpdateTint(state);
}

private void UpdateCornerRadius()
Expand All @@ -63,33 +39,37 @@ private void UpdateCornerRadius()
//_realtimeBlurView?.SetCornerRadius(ContextHelper.Current.ToPixels(MaterialFrame.CornerRadius));
}

private void UpdateElevation(BindableView view)
private void ApplyAcrylicBlur(AcrylicState state)
{
ViewCompat.SetElevation(view, 0);
}
ApplyBackground(state);

private void UpdateAcrylicGlowColor()
{
Android.Graphics.Color androidColor = TintColor;
_acrylicLayer?.SetColor(androidColor);
EnableBlur(state);

UpdateProperties(state);
}

private void SetAcrylicBlur(BindableView view)
private void ApplyBackground(AcrylicState state)
{
Border b = (Border)(view);
_mainDrawable = new GradientDrawable();
_mainDrawable.SetShape(ShapeType.Rectangle);
if (state.BackgroundDrawable == null)
{
state.BackgroundDrawable = new GradientDrawable();
state.BackgroundDrawable.SetShape(ShapeType.Rectangle);
Android.Graphics.Color androidColor = Colors.Transparent;

Android.Graphics.Color androidColor = Colors.Transparent;
_mainDrawable.SetColor(androidColor);
state.BackgroundDrawable.SetColor(androidColor);

SetBackground(view, _mainDrawable);
SetBackground(state.Owner, state.BackgroundDrawable);
}
}

//view.LayoutChange += (s,e)=> LayoutBlurView(view);
UpdateElevation(view);
//LayoutBlurView(view);
private void RemoveAcrylicBlur(AcrylicState state)
{
SetBackground(state.Owner, null);
state.BackgroundDrawable?.Dispose();
state.BackgroundDrawable = null;

EnableBlur(view);
DisableBlur(state);
DestroyBlur(state);
}

private void SetBackground(BindableView view, Drawable drawable)
Expand Down
Loading

0 comments on commit 4a291e7

Please sign in to comment.