Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ActivityIndicatorHandler in WinUI #761

Merged
merged 14 commits into from
May 18, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ void OnControlLoaded(object sender, RoutedEventArgs routedEventArgs)
UpdateColor();
}

[PortHandler]
void UpdateColor()
{
Color color = Element.Color;
Expand All @@ -59,6 +60,7 @@ void UpdateColor()
}
}

[PortHandler]
void UpdateIsRunning()
{
Control.ElementOpacity = Element.IsRunning ? Element.Opacity : 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
{
[PortHandler]
internal static class FrameworkElementExtensions
{
static readonly Lazy<ConcurrentDictionary<Type, DependencyProperty>> ForegroundProperties =
Expand Down
1 change: 1 addition & 0 deletions src/Controls/src/Core/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace Microsoft.Maui.Controls.Internals
{
[PortHandler]
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ReflectionExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
using System;
using Microsoft.UI.Xaml.Controls;

#nullable enable
namespace Microsoft.Maui.Handlers
{
public partial class ActivityIndicatorHandler : ViewHandler<IActivityIndicator, ProgressBar>
public partial class ActivityIndicatorHandler : ViewHandler<IActivityIndicator, MauiActivityIndicator>
{
protected override ProgressBar CreateNativeView() => new ProgressBar();
object? _foregroundDefault;

protected override MauiActivityIndicator CreateNativeView() => new MauiActivityIndicator
{
IsIndeterminate = true,
Style = UI.Xaml.Application.Current.Resources["MauiActivityIndicatorStyle"] as UI.Xaml.Style
};

protected override void SetupDefaults(MauiActivityIndicator nativeView)
{
_foregroundDefault = nativeView.GetForegroundCache();

base.SetupDefaults(nativeView);
}

[MissingMapper]
public static void MapIsRunning(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator) { }
public static void MapIsRunning(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
{
handler.NativeView?.UpdateIsRunning(activityIndicator);
}

[MissingMapper]
public static void MapColor(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator) { }
public static void MapColor(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
{
handler.NativeView?.UpdateColor(activityIndicator, handler._foregroundDefault);
}
}
}
95 changes: 95 additions & 0 deletions src/Core/src/Platform/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Microsoft.Maui
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want this public ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was internal in Xamarin.Forms, should we changed it?, any reason to not be public?

{
public static class ReflectionExtensions
{
public static FieldInfo? GetField(this Type type, Func<FieldInfo, bool> predicate)
{
return GetFields(type).FirstOrDefault(predicate);
}

public static FieldInfo? GetField(this Type type, string name)
{
return type.GetField(fi => fi.Name == name);
}

public static IEnumerable<FieldInfo> GetFields(this Type type)
{
return GetParts(type, i => i.DeclaredFields);
}

public static IEnumerable<PropertyInfo> GetProperties(this Type type)
{
return GetParts(type, ti => ti.DeclaredProperties);
}

public static PropertyInfo? GetProperty(this Type type, string name)
{
Type? t = type;
while (t != null)
{
TypeInfo ti = t.GetTypeInfo();
PropertyInfo? property = ti.GetDeclaredProperty(name);

if (property != null)
return property;

t = ti.BaseType;
}

return null;
}

internal static object[]? GetCustomAttributesSafe(this Assembly assembly, Type attrType)
{
try
{
#if !NETSTANDARD1_0
return assembly.GetCustomAttributes(attrType, true);
#else
return assembly.GetCustomAttributes(attrType).ToArray();
#endif
}
catch (FileNotFoundException)
{
// Sometimes the previewer doesn't actually have everything required for these loads to work
// TODO: Register the exception in the Log when we have the Logger ported
}

return null;
}

public static Type[] GetExportedTypes(this Assembly assembly)
{
return assembly.ExportedTypes.ToArray();
}

public static bool IsAssignableFrom(this Type self, Type c)
{
return self.GetTypeInfo().IsAssignableFrom(c.GetTypeInfo());
}

public static bool IsInstanceOfType(this Type self, object o)
{
return self.GetTypeInfo().IsAssignableFrom(o.GetType().GetTypeInfo());
}

static IEnumerable<T> GetParts<T>(Type type, Func<TypeInfo, IEnumerable<T>> selector)
{
Type? t = type;
while (t != null)
{
TypeInfo ti = t.GetTypeInfo();
foreach (T f in selector(ti))
yield return f;
t = ti.BaseType;
}
}
}
}
33 changes: 33 additions & 0 deletions src/Core/src/Platform/Windows/ActivityIndicatorExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#nullable enable
using Microsoft.Maui.Graphics;

namespace Microsoft.Maui
{
public static class ActivityIndicatorExtensions
{
public static void UpdateIsRunning(this MauiActivityIndicator mauiActivityIndicator, IActivityIndicator activityIndicator)
{
// TODO: Use IView Opacity if the ActivityIndicator is running.
mauiActivityIndicator.ElementOpacity = activityIndicator.IsRunning ? 1 : 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No other way to do this? @PureWeen any ideas?

}
public static void UpdateColor(this MauiActivityIndicator mauiActivityIndicator, IActivityIndicator activityIndicator)
{
mauiActivityIndicator.UpdateColor(activityIndicator, null);
}

public static void UpdateColor(this MauiActivityIndicator mauiActivityIndicator, IActivityIndicator activityIndicator, object? foregroundDefault)
rmarinho marked this conversation as resolved.
Show resolved Hide resolved
{
Color color = activityIndicator.Color;

if (color.IsDefault())
{
if (foregroundDefault != null)
mauiActivityIndicator.RestoreForegroundCache(foregroundDefault);
}
else
{
mauiActivityIndicator.Foreground = color.ToNative();
}
}
}
}
144 changes: 144 additions & 0 deletions src/Core/src/Platform/Windows/FrameworkElementExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#nullable enable
using System;
using System.Linq;
using Microsoft.UI.Xaml;
using System.Collections.Concurrent;
using Microsoft.UI.Xaml.Media;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.UI.Xaml.Controls;
using WBinding = Microsoft.UI.Xaml.Data.Binding;
using WBrush = Microsoft.UI.Xaml.Media.Brush;
using WBindingExpression = Microsoft.UI.Xaml.Data.BindingExpression;

namespace Microsoft.Maui
{
public static class FrameworkElementExtensions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want this to be public ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was internal in Xamarin.Forms, should we changed it?, any reason to not be public?

{
static readonly Lazy<ConcurrentDictionary<Type, DependencyProperty>> ForegroundProperties =
new Lazy<ConcurrentDictionary<Type, DependencyProperty>>(() => new ConcurrentDictionary<Type, DependencyProperty>());

public static WBrush GetForeground(this FrameworkElement element)
{
if (element == null)
throw new ArgumentNullException(nameof(element));

return (WBrush)element.GetValue(GetForegroundProperty(element));
}

public static WBinding? GetForegroundBinding(this FrameworkElement element)
{
WBindingExpression expr = element.GetBindingExpression(GetForegroundProperty(element));

if (expr == null)
return null;

return expr.ParentBinding;
}

public static object GetForegroundCache(this FrameworkElement element)
{
WBinding? binding = GetForegroundBinding(element);

if (binding != null)
return binding;

return GetForeground(element);
}

public static void RestoreForegroundCache(this FrameworkElement element, object cache)
{
var binding = cache as WBinding;
if (binding != null)
SetForeground(element, binding);
else
SetForeground(element, (WBrush)cache);
}

public static void SetForeground(this FrameworkElement element, WBrush foregroundBrush)
{
if (element == null)
throw new ArgumentNullException(nameof(element));

element.SetValue(GetForegroundProperty(element), foregroundBrush);
}

public static void SetForeground(this FrameworkElement element, WBinding binding)
{
if (element == null)
throw new ArgumentNullException(nameof(element));

element.SetBinding(GetForegroundProperty(element), binding);
}

internal static IEnumerable<T?> GetDescendantsByName<T>(this DependencyObject parent, string elementName) where T : DependencyObject
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
internal static IEnumerable<T?> GetDescendantsByName<T>(this DependencyObject parent, string elementName) where T : DependencyObject
internal static IEnumerable<T> GetDescendantsByName<T>(this DependencyObject parent, string elementName) where T : DependencyObject

{
int myChildrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < myChildrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var controlName = child.GetValue(FrameworkElement.NameProperty) as string;
if (controlName == elementName && child is T t)
yield return t;
else
{
foreach (var subChild in child.GetDescendantsByName<T>(elementName))
yield return subChild;
}
}
}

internal static T? GetFirstDescendant<T>(this DependencyObject element) where T : FrameworkElement
{
int count = VisualTreeHelper.GetChildrenCount(element);
for (var i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(element, i);

if ((child as T ?? GetFirstDescendant<T>(child)) is T target)
return target;
}

return null;
}

static DependencyProperty? GetForegroundProperty(FrameworkElement element)
{
if (element is Control)
return Control.ForegroundProperty;
if (element is TextBlock)
return TextBlock.ForegroundProperty;

Type type = element.GetType();

if (!ForegroundProperties.Value.TryGetValue(type, out var foregroundProperty))
{
if (ReflectionExtensions.GetFields(type).FirstOrDefault(f => f.Name == "ForegroundProperty") is not FieldInfo field)
throw new ArgumentException("type is not a Foregroundable type");

if (field.GetValue(null) is DependencyProperty property)
ForegroundProperties.Value.TryAdd(type, property);

return null;
}

return foregroundProperty;
}

internal static IEnumerable<T?> GetChildren<T>(this DependencyObject parent) where T : DependencyObject
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
internal static IEnumerable<T?> GetChildren<T>(this DependencyObject parent) where T : DependencyObject
internal static IEnumerable<T> GetChildren<T>(this DependencyObject parent) where T : DependencyObject

{
int myChildrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < myChildrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T t)
yield return t;
else
{
foreach (var subChild in child.GetChildren<T>())
yield return subChild;
}
}
}
}
}
27 changes: 27 additions & 0 deletions src/Core/src/Platform/Windows/MauiActivityIndicator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace Microsoft.Maui
{
public class MauiActivityIndicator : ProgressBar
{
public static readonly DependencyProperty ElementOpacityProperty = DependencyProperty.Register(
nameof(ElementOpacity), typeof(double), typeof(MauiActivityIndicator), new PropertyMetadata(default(double)));

public double ElementOpacity
{
get => (double)GetValue(ElementOpacityProperty);
set => SetValue(ElementOpacityProperty, value);
}

protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
{
var result = base.MeasureOverride(availableSize);

if (!double.IsInfinity(availableSize.Width))
result.Width = availableSize.Width;

return result;
}
}
}
Loading