-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Changes from 10 commits
6bedb16
f50c839
8118532
6da7b45
df9b336
23debba
610d1b7
48dfb29
72d47ed
fe643c5
51e637e
5d64983
0ed4d5f
72bb7e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
} | ||
} | ||
} |
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 | ||
{ | ||
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; | ||
} | ||
} | ||
} | ||
} |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
} | ||
} | ||
} | ||
} |
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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want this to be public ? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
{ | ||||||
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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
{ | ||||||
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; | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
} |
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; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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?