Pixiview/Gallery/Utils/Extensions.cs

280 lines
8.7 KiB
C#
Executable File

using System;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Gallery.Utils
{
public static class Extensions
{
public static T Binding<T>(this T view, BindableProperty property, string name,
BindingMode mode = BindingMode.Default, IValueConverter converter = null) where T : BindableObject
{
if (name == null)
{
view.SetValue(property, property.DefaultValue);
}
else
{
view.SetBinding(property, name, mode, converter);
}
return view;
}
public static T DynamicResource<T>(this T view, BindableProperty property, string key) where T : Element
{
view.SetDynamicResource(property, key);
return view;
}
public static T GridRow<T>(this T view, int row) where T : BindableObject
{
Grid.SetRow(view, row);
return view;
}
public static T GridColumn<T>(this T view, int column) where T : BindableObject
{
Grid.SetColumn(view, column);
return view;
}
public static T GridColumnSpan<T>(this T view, int columnSpan) where T : BindableObject
{
Grid.SetColumnSpan(view, columnSpan);
return view;
}
public static int IndexOf<T>(this T[] array, Predicate<T> predicate)
{
for (var i = 0; i < array.Length; i++)
{
if (predicate(array[i]))
{
return i;
}
}
return -1;
}
public static int LastIndexOf<T>(this T[] array, Predicate<T> predicate)
{
for (var i = array.Length - 1; i >= 0; i--)
{
if (predicate(array[i]))
{
return i;
}
}
return -1;
}
public static bool All<T>(this T[] array, Predicate<T> predicate)
{
for (var i = 0; i < array.Length; i++)
{
if (!predicate(array[i]))
{
return false;
}
}
return true;
}
public static bool AnyFor<T>(this T[] array, int from, int to, Predicate<T> predicate)
{
for (var i = from; i <= to; i++)
{
if (predicate(array[i]))
{
return true;
}
}
return false;
}
}
public class ParallelTask : IDisposable
{
public static ParallelTask Start(string tag, int from, int toExclusive, int maxCount, Predicate<int> action, int tagIndex = -1, Action complete = null)
{
if (toExclusive <= from)
{
if (complete != null)
{
Task.Run(complete);
}
return null;
}
var task = new ParallelTask(tag, from, toExclusive, maxCount, action, tagIndex, complete);
task.Start();
return task;
}
private readonly object sync = new object();
private int count;
private bool disposed;
public int TagIndex { get; private set; }
private readonly string tag;
private readonly int max;
private readonly int from;
private readonly int to;
private readonly Predicate<int> action;
private readonly Action complete;
private ParallelTask(string tag, int from, int to, int maxCount, Predicate<int> action, int tagIndex, Action complete)
{
if (maxCount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxCount));
}
max = maxCount;
if (from >= to)
{
throw new ArgumentOutOfRangeException(nameof(from));
}
TagIndex = tagIndex;
this.tag = tag;
this.from = from;
this.to = to;
this.action = action;
this.complete = complete;
}
public void Start()
{
_ = ThreadPool.QueueUserWorkItem(DoStart);
}
private void DoStart(object state)
{
#if LOG
var sw = new System.Diagnostics.Stopwatch();
long lastElapsed = 0;
sw.Start();
#endif
for (int i = from; i < to; i++)
{
var index = i;
while (true)
{
if (count < max)
{
break;
}
#if LOG
var elapsed = sw.ElapsedMilliseconds;
if (elapsed - lastElapsed > 60000)
{
lastElapsed = elapsed;
App.DebugPrint($"WARNING: parallel task ({tag}), {count} tasks in queue, cost too much time ({elapsed:n0}ms)");
}
#endif
if (disposed)
{
#if LOG
sw.Stop();
App.DebugPrint($"parallel task determinate, disposed ({tag}), cost time ({elapsed:n0}ms)");
#endif
return;
}
Thread.Sleep(16);
}
lock (sync)
{
count++;
}
ThreadPool.QueueUserWorkItem(o =>
//Task.Run(() =>
{
try
{
if (!action(index))
{
disposed = true;
}
}
catch (Exception ex)
{
App.DebugError($"parallel.start ({tag})", $"failed to run action, index: {index}, error: {ex}");
}
finally
{
lock (sync)
{
count--;
}
}
});
}
while (count > 0)
{
#if LOG
var elapsed = sw.ElapsedMilliseconds;
if (elapsed - lastElapsed > 60000)
{
lastElapsed = elapsed;
App.DebugPrint($"WARNING: parallel task ({tag}), {count} tasks are waiting for end, cost too much time ({elapsed:n0}ms)");
}
#endif
if (disposed)
{
#if LOG
sw.Stop();
App.DebugPrint($"parallel task determinate, disposed ({tag}), cost time ({elapsed:n0}ms)");
#endif
return;
}
Thread.Sleep(16);
}
#if LOG
sw.Stop();
App.DebugPrint($"parallel task done ({tag}), cost time ({sw.ElapsedMilliseconds:n0}ms)");
#endif
complete?.Invoke();
}
public void Dispose()
{
disposed = true;
}
}
public static class Screen
{
private const string StatusBarStyle = nameof(StatusBarStyle);
private const string HomeIndicatorAutoHidden = nameof(HomeIndicatorAutoHidden);
public static readonly BindableProperty StatusBarStyleProperty = BindableProperty.CreateAttached(
StatusBarStyle,
typeof(StatusBarStyles),
typeof(Page),
StatusBarStyles.WhiteText);
public static StatusBarStyles GetStatusBarStyle(VisualElement page) => (StatusBarStyles)page.GetValue(StatusBarStyleProperty);
public static void SetStatusBarStyle(VisualElement page, StatusBarStyles value) => page.SetValue(StatusBarStyleProperty, value);
public static readonly BindableProperty HomeIndicatorAutoHiddenProperty = BindableProperty.CreateAttached(
HomeIndicatorAutoHidden,
typeof(bool),
typeof(Shell),
false);
public static bool GetHomeIndicatorAutoHidden(VisualElement page) => (bool)page.GetValue(HomeIndicatorAutoHiddenProperty);
public static void SetHomeIndicatorAutoHidden(VisualElement page, bool value) => page.SetValue(HomeIndicatorAutoHiddenProperty, value);
}
public enum StatusBarStyles
{
Default,
// Will behave as normal.
// White text on black NavigationBar/in iOS Dark mode and
// Black text on white NavigationBar/in iOS Light mode
DarkText,
// Will switch the color of content of StatusBar to black.
WhiteText,
// Will switch the color of content of StatusBar to white.
Hidden
// Will hide the StatusBar
}
}