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 }