DragAndDrop core

This commit is contained in:
FrigaT
2026-01-18 16:33:35 +03:00
parent 9ea82af329
commit 79bdd8bc62
229 changed files with 21214 additions and 2494 deletions

View File

@@ -0,0 +1,256 @@
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;
namespace Lattice.UI.DragDrop.Behaviors;
/// <summary>
/// Базовый класс поведения источника перетаскивания.
/// </summary>
/// <typeparam name="TElement">Тип UI элемента.</typeparam>
public abstract class DragSourceBehaviorBase<TElement> : IDragSource
where TElement : class
{
private IDragDropService? _dragDropService;
private Point _dragStartPosition;
private bool _isDragging;
private TElement? _associatedElement;
/// <summary>
/// Получает или задает связанный элемент.
/// </summary>
protected TElement? AssociatedElement
{
get => _associatedElement;
set
{
if (_associatedElement != value)
{
DetachFromElement();
_associatedElement = value;
AttachToElement();
}
}
}
/// <summary>
/// Получает сервис перетаскивания.
/// </summary>
protected IDragDropService DragDropService
{
get
{
if (_dragDropService == null)
{
_dragDropService = ServiceProvider.GetRequiredService<IDragDropService>();
}
return _dragDropService;
}
}
/// <summary>
/// Получает провайдер сервисов.
/// </summary>
protected IServiceProvider ServiceProvider { get; }
/// <summary>
/// Инициализирует новый экземпляр класса <see cref="DragSourceBehaviorBase{TElement}"/>.
/// </summary>
/// <param name="serviceProvider">Провайдер сервисов.</param>
protected DragSourceBehaviorBase(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
/// <summary>
/// Вызывается при прикреплении к элементу.
/// </summary>
protected virtual void AttachToElement()
{
if (_associatedElement != null)
{
SubscribeToEvents(_associatedElement);
}
}
/// <summary>
/// Вызывается при откреплении от элемента.
/// </summary>
protected virtual void DetachFromElement()
{
if (_associatedElement != null)
{
UnsubscribeFromEvents(_associatedElement);
}
}
/// <summary>
/// Подписывается на события элемента.
/// </summary>
/// <param name="element">Элемент.</param>
protected abstract void SubscribeToEvents(TElement element);
/// <summary>
/// Отписывается от событий элемента.
/// </summary>
/// <param name="element">Элемент.</param>
protected abstract void UnsubscribeFromEvents(TElement element);
/// <summary>
/// Обрабатывает начало взаимодействия (например, нажатие мыши).
/// </summary>
/// <param name="position">Позиция в координатах элемента.</param>
protected virtual void OnInteractionStarted(Point position)
{
if (_isDragging)
return;
_dragStartPosition = position;
}
/// <summary>
/// Обрабатывает перемещение во время взаимодействия.
/// </summary>
/// <param name="position">Позиция в координатах элемента.</param>
protected virtual void OnInteractionMoved(Point position)
{
if (_isDragging)
return;
var distance = CalculateDistance(_dragStartPosition, position);
if (distance > DragDropService.DragStartThreshold)
{
StartDragOperation();
}
}
/// <summary>
/// Обрабатывает завершение взаимодействия.
/// </summary>
protected virtual void OnInteractionEnded()
{
// Сброс состояния, если перетаскивание не началось
if (!_isDragging)
{
Reset();
}
}
/// <summary>
/// Обрабатывает отмену взаимодействия.
/// </summary>
protected virtual void OnInteractionCancelled()
{
if (_isDragging)
{
DragDropService.CancelDrag();
}
Reset();
}
/// <summary>
/// Начинает операцию перетаскивания.
/// </summary>
protected virtual void StartDragOperation()
{
if (_isDragging || AssociatedElement == null)
return;
// Получаем начальную позицию в экранных координатах
var screenPosition = ConvertToScreenCoordinates(_dragStartPosition);
// Начинаем перетаскивание
_isDragging = DragDropService.StartDrag(this, screenPosition);
}
/// <summary>
/// Преобразует координаты элемента в экранные координаты.
/// </summary>
/// <param name="point">Точка в координатах элемента.</param>
/// <returns>Точка в экранных координатах.</returns>
protected abstract Point ConvertToScreenCoordinates(Point point);
/// <summary>
/// Вычисляет расстояние между двумя точками.
/// </summary>
protected virtual double CalculateDistance(Point p1, Point p2)
{
var dx = p2.X - p1.X;
var dy = p2.Y - p1.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
/// <summary>
/// Сбрасывает состояние поведения.
/// </summary>
protected virtual void Reset()
{
_isDragging = false;
_dragStartPosition = default;
}
#region IDragSource Implementation
/// <inheritdoc/>
public abstract bool CanStartDrag(out DragInfo? dragInfo);
/// <inheritdoc/>
public virtual bool StartDrag(DragInfo dragInfo)
{
// Базовая реализация всегда разрешает начало перетаскивания
return true;
}
/// <inheritdoc/>
public virtual void DragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
{
_isDragging = false;
// Оповещаем о завершении перетаскивания
OnDragCompleted(dragInfo, effects);
}
/// <inheritdoc/>
public virtual void DragCancelled(DragInfo dragInfo)
{
_isDragging = false;
// Оповещаем об отмене перетаскивания
OnDragCancelled(dragInfo);
}
#endregion
#region Virtual Methods for Derived Classes
/// <summary>
/// Вызывается при успешном завершении перетаскивания.
/// </summary>
/// <param name="dragInfo">Информация о перетаскивании.</param>
/// <param name="effects">Примененные эффекты.</param>
protected virtual void OnDragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
{
}
/// <summary>
/// Вызывается при отмене перетаскивания.
/// </summary>
/// <param name="dragInfo">Информация о перетаскивании.</param>
protected virtual void OnDragCancelled(DragInfo dragInfo)
{
}
#endregion
/// <summary>
/// Освобождает ресурсы.
/// </summary>
public virtual void Detach()
{
DetachFromElement();
_associatedElement = null;
}
}