225 lines
8.0 KiB
C#
225 lines
8.0 KiB
C#
using Lattice.Core.DragDrop.Abstractions;
|
||
using Lattice.Core.DragDrop.Enums;
|
||
using Lattice.Core.DragDrop.Models;
|
||
|
||
namespace Lattice.Core.DragDrop.Utilities;
|
||
|
||
/// <summary>
|
||
/// Предоставляет утилитарные методы и фабричные методы для работы с системой перетаскивания с поддержкой async.
|
||
/// </summary>
|
||
public static class AsyncDragDropUtilities
|
||
{
|
||
/// <summary>
|
||
/// Создает асинхронную реализацию источника перетаскивания.
|
||
/// </summary>
|
||
public static IDragSource CreateAsyncDragSource(
|
||
Func<Task<object>> dataProviderAsync,
|
||
Func<Task<bool>>? canDragAsync = null,
|
||
Func<DragInfo, DragDropEffects, Task>? onCompletedAsync = null,
|
||
Func<DragInfo, Task>? onCancelledAsync = null)
|
||
{
|
||
return new AsyncDragSourceWrapper(dataProviderAsync, canDragAsync, onCompletedAsync, onCancelledAsync);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Создает асинхронную реализацию цели сброса.
|
||
/// </summary>
|
||
public static IDropTarget CreateAsyncDropTarget(
|
||
Func<DropInfo, Task<bool>>? canAcceptAsync = null,
|
||
Func<DropInfo, Task>? onDragOverAsync = null,
|
||
Func<DropInfo, Task>? onDropAsync = null,
|
||
Func<Task>? onDragLeaveAsync = null)
|
||
{
|
||
return new AsyncDropTargetWrapper(canAcceptAsync, onDragOverAsync, onDropAsync, onDragLeaveAsync);
|
||
}
|
||
|
||
|
||
#region Factory Methods
|
||
|
||
/// <summary>
|
||
/// Создает информацию о перетаскивании.
|
||
/// </summary>
|
||
public static Models.DragInfo CreateDragInfo(
|
||
object data,
|
||
Geometry.Point startPosition,
|
||
Enums.DragDropEffects allowedEffects = Enums.DragDropEffects.Copy | Enums.DragDropEffects.Move,
|
||
object? source = null,
|
||
Dictionary<string, object>? parameters = null)
|
||
{
|
||
var dragInfo = new Models.DragInfo(data, allowedEffects, startPosition, source);
|
||
|
||
if (parameters != null)
|
||
{
|
||
foreach (var param in parameters)
|
||
{
|
||
dragInfo.SetParameter(param.Key, param.Value);
|
||
}
|
||
}
|
||
|
||
return dragInfo;
|
||
}
|
||
#endregion
|
||
|
||
#region Обертки-реализации
|
||
|
||
/// <summary>
|
||
/// Обертка для создания асинхронного источника перетаскивания.
|
||
/// </summary>
|
||
private sealed class AsyncDragSourceWrapper : IDragSource
|
||
{
|
||
private readonly Func<Task<object>> _dataProviderAsync;
|
||
private readonly Func<Task<bool>>? _canDragAsync;
|
||
private readonly Func<DragInfo, DragDropEffects, Task>? _onCompletedAsync;
|
||
private readonly Func<DragInfo, Task>? _onCancelledAsync;
|
||
|
||
public AsyncDragSourceWrapper(
|
||
Func<Task<object>> dataProviderAsync,
|
||
Func<Task<bool>>? canDragAsync = null,
|
||
Func<DragInfo, DragDropEffects, Task>? onCompletedAsync = null,
|
||
Func<DragInfo, Task>? onCancelledAsync = null)
|
||
{
|
||
_dataProviderAsync = dataProviderAsync ?? throw new ArgumentNullException(nameof(dataProviderAsync));
|
||
_canDragAsync = canDragAsync;
|
||
_onCompletedAsync = onCompletedAsync;
|
||
_onCancelledAsync = onCancelledAsync;
|
||
}
|
||
|
||
public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync(CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
// Проверяем, может ли начаться перетаскивание
|
||
var canDrag = _canDragAsync != null ? await _canDragAsync().ConfigureAwait(false) : true;
|
||
if (!canDrag)
|
||
return (false, null);
|
||
|
||
// Получаем данные
|
||
var data = await _dataProviderAsync().ConfigureAwait(false);
|
||
if (data == null)
|
||
return (false, null);
|
||
|
||
// Создаем информацию о перетаскивании
|
||
var dragInfo = CreateDragInfo(
|
||
data,
|
||
Geometry.Point.Zero,
|
||
DragDropEffects.Copy | DragDropEffects.Move,
|
||
this);
|
||
|
||
return (true, dragInfo);
|
||
}
|
||
catch
|
||
{
|
||
return (false, null);
|
||
}
|
||
}
|
||
|
||
public Task<bool> StartDragAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
|
||
{
|
||
// Базовая реализация всегда разрешает начало
|
||
return Task.FromResult(true);
|
||
}
|
||
|
||
public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects, CancellationToken cancellationToken = default)
|
||
{
|
||
if (_onCompletedAsync != null)
|
||
{
|
||
await _onCompletedAsync(dragInfo, effects).ConfigureAwait(false);
|
||
}
|
||
}
|
||
|
||
public async Task DragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
|
||
{
|
||
if (_onCancelledAsync != null)
|
||
{
|
||
await _onCancelledAsync(dragInfo).ConfigureAwait(false);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обертка для создания асинхронной цели сброса.
|
||
/// </summary>
|
||
private sealed class AsyncDropTargetWrapper : IDropTarget
|
||
{
|
||
private readonly Func<DropInfo, Task<bool>>? _canAcceptAsync;
|
||
private readonly Func<DropInfo, Task>? _onDragOverAsync;
|
||
private readonly Func<DropInfo, Task>? _onDropAsync;
|
||
private readonly Func<Task>? _onDragLeaveAsync;
|
||
|
||
public AsyncDropTargetWrapper(
|
||
Func<DropInfo, Task<bool>>? canAcceptAsync = null,
|
||
Func<DropInfo, Task>? onDragOverAsync = null,
|
||
Func<DropInfo, Task>? onDropAsync = null,
|
||
Func<Task>? onDragLeaveAsync = null)
|
||
{
|
||
_canAcceptAsync = canAcceptAsync;
|
||
_onDragOverAsync = onDragOverAsync;
|
||
_onDropAsync = onDropAsync;
|
||
_onDragLeaveAsync = onDragLeaveAsync;
|
||
}
|
||
|
||
public async Task<bool> CanAcceptDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
if (_canAcceptAsync != null)
|
||
{
|
||
return await _canAcceptAsync(dropInfo).ConfigureAwait(false);
|
||
}
|
||
return true; // По умолчанию принимаем все
|
||
}
|
||
catch
|
||
{
|
||
return false; // При ошибке не принимаем
|
||
}
|
||
}
|
||
|
||
public async Task DragOverAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
if (_onDragOverAsync != null)
|
||
{
|
||
await _onDragOverAsync(dropInfo).ConfigureAwait(false);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Игнорируем ошибки в обработчике
|
||
}
|
||
}
|
||
|
||
public async Task DropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
if (_onDropAsync != null)
|
||
{
|
||
await _onDropAsync(dropInfo).ConfigureAwait(false);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Игнорируем ошибки в обработчике
|
||
}
|
||
}
|
||
|
||
public async Task DragLeaveAsync(CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
if (_onDragLeaveAsync != null)
|
||
{
|
||
await _onDragLeaveAsync().ConfigureAwait(false);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// Игнорируем ошибки в обработчике
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
} |