using Lattice.Core.DragDrop.Abstractions;
using Lattice.Core.DragDrop.Models;
using Lattice.Core.DragDrop.Services;
using Lattice.Core.Geometry;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Lattice.UI.DragDrop.Behaviors;
///
/// Базовый класс поведения цели сброса.
///
/// Тип UI элемента.
public abstract class DropTargetBehaviorBase : IDropTarget
where TElement : class
{
private IDragDropService? _dragDropService;
private string? _registrationId;
private TElement? _associatedElement;
private Rect _currentBounds;
///
/// Получает или задает связанный элемент.
///
protected TElement? AssociatedElement
{
get => _associatedElement;
set
{
if (_associatedElement != value)
{
UnregisterFromService();
_associatedElement = value;
RegisterToService();
}
}
}
///
/// Получает или задает приоритет цели сброса.
///
public int Priority { get; set; }
///
/// Получает или задает группу цели сброса.
///
public string? Group { get; set; }
///
/// Получает сервис перетаскивания.
///
protected IDragDropService DragDropService
{
get
{
if (_dragDropService == null)
{
_dragDropService = ServiceProvider.GetRequiredService();
}
return _dragDropService;
}
}
///
/// Получает провайдер сервисов.
///
protected IServiceProvider ServiceProvider { get; }
///
/// Получает текущие границы элемента в экранных координатах.
///
protected Rect CurrentBounds => _currentBounds;
///
/// Инициализирует новый экземпляр класса .
///
/// Провайдер сервисов.
protected DropTargetBehaviorBase(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
///
/// Вызывается при прикреплении к элементу.
///
protected virtual void AttachToElement()
{
if (_associatedElement != null)
{
SubscribeToEvents(_associatedElement);
UpdateBounds();
RegisterToService();
}
}
///
/// Вызывается при откреплении от элемента.
///
protected virtual void DetachFromElement()
{
if (_associatedElement != null)
{
UnsubscribeFromEvents(_associatedElement);
UnregisterFromService();
}
}
///
/// Подписывается на события элемента.
///
/// Элемент.
protected abstract void SubscribeToEvents(TElement element);
///
/// Отписывается от событий элемента.
///
/// Элемент.
protected abstract void UnsubscribeFromEvents(TElement element);
///
/// Обновляет границы элемента в экранных координатах.
///
protected virtual void UpdateBounds()
{
if (_associatedElement != null)
{
_currentBounds = GetScreenBounds(_associatedElement);
// Обновляем регистрацию в сервисе
if (_registrationId != null)
{
DragDropService.UpdateDropTargetBounds(_registrationId, _currentBounds);
}
}
}
///
/// Получает границы элемента в экранных координатах.
///
/// Элемент.
/// Границы в экранных координатах.
protected abstract Rect GetScreenBounds(TElement element);
///
/// Регистрирует цель в сервисе перетаскивания.
///
protected virtual void RegisterToService()
{
if (_associatedElement != null && _registrationId == null)
{
UpdateBounds();
_registrationId = DragDropService.RegisterDropTarget(this, _currentBounds, Priority, Group);
}
}
///
/// Отменяет регистрацию цели в сервисе перетаскивания.
///
protected virtual void UnregisterFromService()
{
if (_registrationId != null)
{
DragDropService.UnregisterDropTarget(_registrationId);
_registrationId = null;
}
}
///
/// Вызывается при изменении размера или позиции элемента.
///
protected virtual void OnElementLayoutChanged()
{
UpdateBounds();
}
#region IDropTarget Implementation
///
public abstract Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct = default);
///
public virtual async Task DragOverAsync(DropInfo dropInfo, CancellationToken ct = default)
{
// Базовая реализация устанавливает эффект по умолчанию
if (await CanAcceptDropAsync(dropInfo))
{
// Установить эффект по умолчанию, если он разрешен
if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Move))
{
dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Move;
}
else if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Copy))
{
dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Copy;
}
else if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Link))
{
dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Link;
}
}
else
{
dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.None;
}
}
///
public abstract Task DropAsync(DropInfo dropInfo, CancellationToken ct = default);
///
public virtual async Task DragLeaveAsync(CancellationToken ct = default)
{
// Базовая реализация не делает ничего
}
#endregion
///
/// Освобождает ресурсы.
///
public virtual void Detach()
{
DetachFromElement();
_associatedElement = null;
}
}