using Lattice.Core.DragDrop.Abstractions;
using Lattice.Core.DragDrop.Enums;
using Lattice.Core.DragDrop.Models;
namespace Lattice.Core.DragDrop.Utilities;
///
/// Предоставляет утилитарные методы и фабричные методы для работы с системой перетаскивания с поддержкой async.
///
public static class AsyncDragDropUtilities
{
///
/// Создает асинхронную реализацию источника перетаскивания.
///
public static IAsyncDragSource CreateAsyncDragSource(
Func> dataProviderAsync,
Func>? canDragAsync = null,
Func? onCompletedAsync = null,
Func? onCancelledAsync = null)
{
return new AsyncDragSourceWrapper(dataProviderAsync, canDragAsync, onCompletedAsync, onCancelledAsync);
}
///
/// Создает асинхронную реализацию цели сброса.
///
public static IAsyncDropTarget CreateAsyncDropTarget(
Func>? canAcceptAsync = null,
Func? onDragOverAsync = null,
Func? onDropAsync = null,
Func? onDragLeaveAsync = null)
{
return new AsyncDropTargetWrapper(canAcceptAsync, onDragOverAsync, onDropAsync, onDragLeaveAsync);
}
///
/// Создает адаптер для преобразования синхронного источника в асинхронный.
///
public static IAsyncDragSource CreateAsyncAdapter(IDragSource syncSource)
{
return new SyncToAsyncDragSourceAdapter(syncSource);
}
///
/// Создает адаптер для преобразования синхронной цели в асинхронную.
///
public static IAsyncDropTarget CreateAsyncAdapter(IDropTarget syncTarget)
{
return new SyncToAsyncDropTargetAdapter(syncTarget);
}
#region Обертки-реализации
///
/// Обертка для создания асинхронного источника перетаскивания.
///
private sealed class AsyncDragSourceWrapper : IAsyncDragSource
{
private readonly Func> _dataProviderAsync;
private readonly Func>? _canDragAsync;
private readonly Func? _onCompletedAsync;
private readonly Func? _onCancelledAsync;
public AsyncDragSourceWrapper(
Func> dataProviderAsync,
Func>? canDragAsync = null,
Func? onCompletedAsync = null,
Func? onCancelledAsync = null)
{
_dataProviderAsync = dataProviderAsync ?? throw new ArgumentNullException(nameof(dataProviderAsync));
_canDragAsync = canDragAsync;
_onCompletedAsync = onCompletedAsync;
_onCancelledAsync = onCancelledAsync;
}
public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
{
try
{
// Проверяем, может ли начаться перетаскивание
if (_canDragAsync != null)
{
var canDrag = await _canDragAsync().ConfigureAwait(false);
if (!canDrag)
return (false, null);
}
// Получаем данные
var data = await _dataProviderAsync().ConfigureAwait(false);
if (data == null)
return (false, null);
// Создаем информацию о перетаскивании
var dragInfo = DragDropUtilities.CreateDragInfo(
data,
Geometry.Point.Zero,
DragDropEffects.Copy | DragDropEffects.Move,
this);
return (true, dragInfo);
}
catch
{
return (false, null);
}
}
public Task StartDragAsync(DragInfo dragInfo)
{
// Базовая реализация всегда разрешает начало
return Task.FromResult(true);
}
public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects)
{
if (_onCompletedAsync != null)
{
await _onCompletedAsync(dragInfo, effects).ConfigureAwait(false);
}
}
public async Task DragCancelledAsync(DragInfo dragInfo)
{
if (_onCancelledAsync != null)
{
await _onCancelledAsync(dragInfo).ConfigureAwait(false);
}
}
#region Синхронная реализация (для IDragSource)
public bool CanStartDrag(out DragInfo? dragInfo)
{
// Для синхронного вызова используем Task.Result
var result = Task.Run(() => CanStartDragAsync()).GetAwaiter().GetResult();
dragInfo = result.DragInfo;
return result.CanStart;
}
public bool StartDrag(DragInfo dragInfo)
{
return Task.Run(() => StartDragAsync(dragInfo)).GetAwaiter().GetResult();
}
public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
{
Task.Run(() => DragCompletedAsync(dragInfo, effects)).Wait();
}
public void DragCancelled(DragInfo dragInfo)
{
Task.Run(() => DragCancelledAsync(dragInfo)).Wait();
}
#endregion
}
///
/// Обертка для создания асинхронной цели сброса.
///
private sealed class AsyncDropTargetWrapper : IAsyncDropTarget
{
private readonly Func>? _canAcceptAsync;
private readonly Func? _onDragOverAsync;
private readonly Func? _onDropAsync;
private readonly Func? _onDragLeaveAsync;
public AsyncDropTargetWrapper(
Func>? canAcceptAsync = null,
Func? onDragOverAsync = null,
Func? onDropAsync = null,
Func? onDragLeaveAsync = null)
{
_canAcceptAsync = canAcceptAsync;
_onDragOverAsync = onDragOverAsync;
_onDropAsync = onDropAsync;
_onDragLeaveAsync = onDragLeaveAsync;
}
public async Task CanAcceptDropAsync(DropInfo dropInfo)
{
try
{
if (_canAcceptAsync != null)
{
return await _canAcceptAsync(dropInfo).ConfigureAwait(false);
}
return true; // По умолчанию принимаем все
}
catch
{
return false; // При ошибке не принимаем
}
}
public async Task DragOverAsync(DropInfo dropInfo)
{
try
{
if (_onDragOverAsync != null)
{
await _onDragOverAsync(dropInfo).ConfigureAwait(false);
}
}
catch
{
// Игнорируем ошибки в обработчике
}
}
public async Task DropAsync(DropInfo dropInfo)
{
try
{
if (_onDropAsync != null)
{
await _onDropAsync(dropInfo).ConfigureAwait(false);
}
}
catch
{
// Игнорируем ошибки в обработчике
}
}
public async Task DragLeaveAsync()
{
try
{
if (_onDragLeaveAsync != null)
{
await _onDragLeaveAsync().ConfigureAwait(false);
}
}
catch
{
// Игнорируем ошибки в обработчике
}
}
#region Синхронная реализация (для IDropTarget)
public bool CanAcceptDrop(DropInfo dropInfo)
{
return Task.Run(() => CanAcceptDropAsync(dropInfo)).GetAwaiter().GetResult();
}
public void DragOver(DropInfo dropInfo)
{
Task.Run(() => DragOverAsync(dropInfo)).Wait();
}
public void Drop(DropInfo dropInfo)
{
Task.Run(() => DropAsync(dropInfo)).Wait();
}
public void DragLeave()
{
Task.Run(DragLeaveAsync).Wait();
}
#endregion
}
///
/// Адаптер для преобразования синхронного источника в асинхронный.
///
private sealed class SyncToAsyncDragSourceAdapter : IAsyncDragSource
{
private readonly IDragSource _syncSource;
public SyncToAsyncDragSourceAdapter(IDragSource syncSource)
{
_syncSource = syncSource ?? throw new ArgumentNullException(nameof(syncSource));
}
public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
{
return await Task.Run(() =>
{
var canStart = _syncSource.CanStartDrag(out var dragInfo);
return (canStart, dragInfo);
}).ConfigureAwait(false);
}
public async Task StartDragAsync(DragInfo dragInfo)
{
return await Task.Run(() => _syncSource.StartDrag(dragInfo)).ConfigureAwait(false);
}
public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects)
{
await Task.Run(() => _syncSource.DragCompleted(dragInfo, effects)).ConfigureAwait(false);
}
public async Task DragCancelledAsync(DragInfo dragInfo)
{
await Task.Run(() => _syncSource.DragCancelled(dragInfo)).ConfigureAwait(false);
}
#region Синхронная реализация (делегируем синхронному источнику)
public bool CanStartDrag(out DragInfo? dragInfo)
{
return _syncSource.CanStartDrag(out dragInfo);
}
public bool StartDrag(DragInfo dragInfo)
{
return _syncSource.StartDrag(dragInfo);
}
public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
{
_syncSource.DragCompleted(dragInfo, effects);
}
public void DragCancelled(DragInfo dragInfo)
{
_syncSource.DragCancelled(dragInfo);
}
#endregion
}
///
/// Адаптер для преобразования синхронной цели в асинхронную.
///
private sealed class SyncToAsyncDropTargetAdapter : IAsyncDropTarget
{
private readonly IDropTarget _syncTarget;
public SyncToAsyncDropTargetAdapter(IDropTarget syncTarget)
{
_syncTarget = syncTarget ?? throw new ArgumentNullException(nameof(syncTarget));
}
public async Task CanAcceptDropAsync(DropInfo dropInfo)
{
return await Task.Run(() => _syncTarget.CanAcceptDrop(dropInfo)).ConfigureAwait(false);
}
public async Task DragOverAsync(DropInfo dropInfo)
{
await Task.Run(() => _syncTarget.DragOver(dropInfo)).ConfigureAwait(false);
}
public async Task DropAsync(DropInfo dropInfo)
{
await Task.Run(() => _syncTarget.Drop(dropInfo)).ConfigureAwait(false);
}
public async Task DragLeaveAsync()
{
await Task.Run(() => _syncTarget.DragLeave()).ConfigureAwait(false);
}
#region Синхронная реализация (делегируем синхронной цели)
public bool CanAcceptDrop(DropInfo dropInfo)
{
return _syncTarget.CanAcceptDrop(dropInfo);
}
public void DragOver(DropInfo dropInfo)
{
_syncTarget.DragOver(dropInfo);
}
public void Drop(DropInfo dropInfo)
{
_syncTarget.Drop(dropInfo);
}
public void DragLeave()
{
_syncTarget.DragLeave();
}
#endregion
}
#endregion
#region Утилитарные методы
///
/// Выполняет асинхронную операцию с таймаутом.
///
public static async Task ExecuteWithTimeoutAsync(
Task task,
TimeSpan timeout,
T defaultValue = default!)
{
if (timeout <= TimeSpan.Zero)
return await task.ConfigureAwait(false);
var delayTask = Task.Delay(timeout);
var completedTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false);
if (completedTask == delayTask)
{
return defaultValue;
}
return await task.ConfigureAwait(false);
}
///
/// Выполняет асинхронную операцию с таймаутом.
///
public static async Task ExecuteWithTimeoutAsync(
Task task,
TimeSpan timeout)
{
if (timeout <= TimeSpan.Zero)
{
await task.ConfigureAwait(false);
return true;
}
var delayTask = Task.Delay(timeout);
var completedTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false);
return completedTask == task;
}
///
/// Выполняет асинхронную операцию с таймаутом и обработкой ошибок.
///
public static async Task ExecuteSafeWithTimeoutAsync(
Task task,
TimeSpan timeout,
Func errorHandler = null) where T : class
{
try
{
if (timeout <= TimeSpan.Zero)
return await task.ConfigureAwait(false);
var delayTask = Task.Delay(timeout);
var completedTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false);
if (completedTask == delayTask)
{
return default;
}
return await task.ConfigureAwait(false);
}
catch (Exception ex)
{
return errorHandler?.Invoke(ex) ?? default;
}
}
///
/// Создает комбинированный источник из синхронного и асинхронного.
///
public static IAsyncDragSource Combine(
IDragSource syncSource,
IAsyncDragSource asyncSource,
bool preferAsync = true)
{
return new CombinedDragSource(syncSource, asyncSource, preferAsync);
}
///
/// Создает комбинированную цель из синхронной и асинхронной.
///
public static IAsyncDropTarget Combine(
IDropTarget syncTarget,
IAsyncDropTarget asyncTarget,
bool preferAsync = true)
{
return new CombinedDropTarget(syncTarget, asyncTarget, preferAsync);
}
#endregion
#region Комбинированные реализации
///
/// Комбинированный источник, поддерживающий как синхронный, так и асинхронный API.
///
private sealed class CombinedDragSource : IAsyncDragSource
{
private readonly IDragSource _syncSource;
private readonly IAsyncDragSource _asyncSource;
private readonly bool _preferAsync;
public CombinedDragSource(IDragSource syncSource, IAsyncDragSource asyncSource, bool preferAsync)
{
_syncSource = syncSource ?? throw new ArgumentNullException(nameof(syncSource));
_asyncSource = asyncSource ?? throw new ArgumentNullException(nameof(asyncSource));
_preferAsync = preferAsync;
}
public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
{
if (_preferAsync)
{
try
{
return await _asyncSource.CanStartDragAsync().ConfigureAwait(false);
}
catch
{
// В случае ошибки пробуем синхронную версию
}
}
// Используем синхронную версию в отдельной задаче
return await Task.Run(() =>
{
var canStart = _syncSource.CanStartDrag(out var dragInfo);
return (canStart, dragInfo);
}).ConfigureAwait(false);
}
public async Task StartDragAsync(DragInfo dragInfo)
{
if (_preferAsync)
{
try
{
return await _asyncSource.StartDragAsync(dragInfo).ConfigureAwait(false);
}
catch
{
// В случае ошибки пробуем синхронную версию
}
}
return await Task.Run(() => _syncSource.StartDrag(dragInfo)).ConfigureAwait(false);
}
public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects)
{
if (_preferAsync)
{
try
{
await _asyncSource.DragCompletedAsync(dragInfo, effects).ConfigureAwait(false);
return;
}
catch
{
// В случае ошибки пробуем синхронную версию
}
}
await Task.Run(() => _syncSource.DragCompleted(dragInfo, effects)).ConfigureAwait(false);
}
public async Task DragCancelledAsync(DragInfo dragInfo)
{
if (_preferAsync)
{
try
{
await _asyncSource.DragCancelledAsync(dragInfo).ConfigureAwait(false);
return;
}
catch
{
// В случае ошибки пробуем синхронную версию
}
}
await Task.Run(() => _syncSource.DragCancelled(dragInfo)).ConfigureAwait(false);
}
#region Синхронная реализация
public bool CanStartDrag(out DragInfo? dragInfo)
{
return _syncSource.CanStartDrag(out dragInfo);
}
public bool StartDrag(DragInfo dragInfo)
{
return _syncSource.StartDrag(dragInfo);
}
public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
{
_syncSource.DragCompleted(dragInfo, effects);
}
public void DragCancelled(DragInfo dragInfo)
{
_syncSource.DragCancelled(dragInfo);
}
#endregion
}
///
/// Комбинированная цель, поддерживающая как синхронный, так и асинхронный API.
///
private sealed class CombinedDropTarget : IAsyncDropTarget
{
private readonly IDropTarget _syncTarget;
private readonly IAsyncDropTarget _asyncTarget;
private readonly bool _preferAsync;
public CombinedDropTarget(IDropTarget syncTarget, IAsyncDropTarget asyncTarget, bool preferAsync)
{
_syncTarget = syncTarget ?? throw new ArgumentNullException(nameof(syncTarget));
_asyncTarget = asyncTarget ?? throw new ArgumentNullException(nameof(asyncTarget));
_preferAsync = preferAsync;
}
public async Task CanAcceptDropAsync(DropInfo dropInfo)
{
if (_preferAsync)
{
try
{
return await _asyncTarget.CanAcceptDropAsync(dropInfo).ConfigureAwait(false);
}
catch
{
// В случае ошибки пробуем синхронную версию
}
}
return await Task.Run(() => _syncTarget.CanAcceptDrop(dropInfo)).ConfigureAwait(false);
}
public async Task DragOverAsync(DropInfo dropInfo)
{
if (_preferAsync)
{
try
{
await _asyncTarget.DragOverAsync(dropInfo).ConfigureAwait(false);
return;
}
catch
{
// В случае ошибки пробуем синхронную версию
}
}
await Task.Run(() => _syncTarget.DragOver(dropInfo)).ConfigureAwait(false);
}
public async Task DropAsync(DropInfo dropInfo)
{
if (_preferAsync)
{
try
{
await _asyncTarget.DropAsync(dropInfo).ConfigureAwait(false);
return;
}
catch
{
// В случае ошибки пробуем синхронную версию
}
}
await Task.Run(() => _syncTarget.Drop(dropInfo)).ConfigureAwait(false);
}
public async Task DragLeaveAsync()
{
if (_preferAsync)
{
try
{
await _asyncTarget.DragLeaveAsync().ConfigureAwait(false);
return;
}
catch
{
// В случае ошибки пробуем синхронную версию
}
}
await Task.Run(() => _syncTarget.DragLeave()).ConfigureAwait(false);
}
#region Синхронная реализация
public bool CanAcceptDrop(DropInfo dropInfo)
{
return _syncTarget.CanAcceptDrop(dropInfo);
}
public void DragOver(DropInfo dropInfo)
{
_syncTarget.DragOver(dropInfo);
}
public void Drop(DropInfo dropInfo)
{
_syncTarget.Drop(dropInfo);
}
public void DragLeave()
{
_syncTarget.DragLeave();
}
#endregion
}
#endregion
}