using Lattice.Core.DragDrop.Abstractions;
using Lattice.Core.DragDrop.Services;
using Lattice.UI.Docking.Abstractions;
using Lattice.UI.Docking.Models;
namespace Lattice.UI.Docking.Services;
///
/// Реализация сервиса перетаскивания для UI-слоя док-системы.
/// Координирует взаимодействие между базовым менеджером перетаскивания
/// и UI-контролами, обеспечивая визуальную обратную связь.
///
public class DockDragDropService : IDockDragDropService
{
private readonly DragDropManagerEx _dragDropManager;
private readonly Dictionary _registeredDragSources = new();
private readonly Dictionary _registeredDropTargets = new();
private UiDragInfo? _currentUiDragInfo;
private UiDropInfo? _currentUiDropInfo;
private IDropTarget? _lastDropTarget;
///
/// Инициализирует новый экземпляр .
///
public DockDragDropService()
{
_dragDropManager = new DragDropManagerEx();
HookEvents();
}
///
/// Инициализирует новый экземпляр с указанным менеджером перетаскивания.
///
public DockDragDropService(DragDropManagerEx dragDropManager)
{
_dragDropManager = dragDropManager;
HookEvents();
}
private void HookEvents()
{
_dragDropManager.DragStarted += OnDragStarted;
_dragDropManager.DragUpdated += OnDragUpdated;
_dragDropManager.DragCompleted += OnDragCompleted;
_dragDropManager.DragCancelled += OnDragCancelled;
_dragDropManager.DropTargetChanged += OnDropTargetChanged;
}
///
public void RegisterDragSource(IDockControl element, IDragSource dragSource)
{
if (element == null) throw new ArgumentNullException(nameof(element));
if (dragSource == null) throw new ArgumentNullException(nameof(dragSource));
_registeredDragSources[element] = dragSource;
// Регистрируем границы элемента в менеджере
var bounds = CalculateBounds(element);
_dragDropManager.RegisterDropTarget(dragSource as IDropTarget ?? new AdapterDropTarget(dragSource),
bounds, 0, element.GetType().Name);
}
///
public void RegisterDropTarget(IDockControl element, IDropTarget dropTarget)
{
if (element == null) throw new ArgumentNullException(nameof(element));
if (dropTarget == null) throw new ArgumentNullException(nameof(dropTarget));
_registeredDropTargets[element] = dropTarget;
var bounds = CalculateBounds(element);
_dragDropManager.RegisterDropTarget(dropTarget, bounds, 0, element.GetType().Name);
}
///
public void UnregisterDragSource(IDockControl element)
{
if (element == null) throw new ArgumentNullException(nameof(element));
_registeredDragSources.Remove(element);
// TODO: Реализовать отмену регистрации в менеджере
}
///
public void UnregisterDropTarget(IDockControl element)
{
if (element == null) throw new ArgumentNullException(nameof(element));
_registeredDropTargets.Remove(element);
// TODO: Реализовать отмену регистрации в менеджере
}
///
public void StartDrag(IDockControl element, Core.DragDrop.Models.DragInfo dragInfo)
{
if (element == null) throw new ArgumentNullException(nameof(element));
if (dragInfo == null) throw new ArgumentNullException(nameof(dragInfo));
if (_registeredDragSources.TryGetValue(element, out var dragSource))
{
_currentUiDragInfo = new UiDragInfo(dragInfo, element);
_dragDropManager.StartDrag(dragSource, dragInfo.StartPosition);
}
}
///
public void UpdateDragVisual(double x, double y)
{
var position = new Core.DragDrop.Geometry.Point(x, y);
_dragDropManager.UpdateDrag(position);
if (_currentUiDragInfo != null)
{
// Обновляем позицию визуального представления
OnDragVisualUpdated?.Invoke(this, new DragVisualUpdatedEventArgs(
_currentUiDragInfo, position));
}
}
///
public void EndDrag(double x, double y)
{
var position = new Core.DragDrop.Geometry.Point(x, y);
_dragDropManager.EndDrag(position);
}
///
public void CancelDrag()
{
_dragDropManager.CancelDrag();
}
///
public void ShowDropHint(IDockControl element, DropPosition position)
{
if (_currentUiDropInfo != null)
{
_currentUiDropInfo.DropPosition = position;
_currentUiDropInfo.IsOverValidTarget = true;
_currentUiDropInfo.HighlightIntensity = 0.8;
OnDropHintChanged?.Invoke(this, new DropHintEventArgs(
element, position, true, _currentUiDropInfo.HighlightIntensity));
}
}
///
public void HideDropHint()
{
if (_currentUiDropInfo != null)
{
_currentUiDropInfo.IsOverValidTarget = false;
_currentUiDropInfo.HighlightIntensity = 0.0;
OnDropHintChanged?.Invoke(this, new DropHintEventArgs(
_currentUiDropInfo.TargetControl,
_currentUiDropInfo.DropPosition,
false,
0.0));
}
}
///
public event EventHandler? DragStarted;
///
public event EventHandler? DragUpdated;
///
public event EventHandler? DragCompleted;
///
public event EventHandler? DragCancelled;
///
/// Событие, возникающее при обновлении визуального представления перетаскивания.
///
public event EventHandler? OnDragVisualUpdated;
///
/// Событие, возникающее при изменении визуальной подсказки сброса.
///
public event EventHandler? OnDropHintChanged;
private void OnDragStarted(object? sender, DragStartedEventArgs e)
{
// Обновляем UI-информацию
if (_currentUiDragInfo != null)
{
_currentUiDragInfo.BaseDragInfo.StartPosition = e.StartPosition;
// Создаем визуальное представление
CreateDragVisual(_currentUiDragInfo);
DragStarted?.Invoke(this, new DragStartedEventArgs(
_currentUiDragInfo.SourceControl,
_currentUiDragInfo.BaseDragInfo));
}
}
private void OnDragUpdated(object? sender, DragUpdatedEventArgs e)
{
if (_currentUiDragInfo != null)
{
// Обновляем позицию визуального представления
UpdateDragVisualPosition(e.Position);
DragUpdated?.Invoke(this, new DragUpdatedEventArgs(
_currentUiDragInfo.SourceControl,
e.Position.X,
e.Position.Y,
_currentUiDragInfo.BaseDragInfo));
}
}
private void OnDragCompleted(object? sender, DragCompletedEventArgs e)
{
var targetControl = _currentUiDropInfo?.TargetControl;
var dropPosition = _currentUiDropInfo?.DropPosition ?? DropPosition.Center;
DragCompleted?.Invoke(this, new DragCompletedEventArgs(
_currentUiDragInfo?.SourceControl,
targetControl,
dropPosition,
_currentUiDragInfo?.BaseDragInfo,
e.Effects != Core.DragDrop.Enums.DragDropEffects.None));
// Очищаем визуальные представления
CleanupDragVisual();
CleanupDropHint();
_currentUiDragInfo = null;
_currentUiDropInfo = null;
_lastDropTarget = null;
}
private void OnDragCancelled(object? sender, DragCancelledEventArgs e)
{
DragCancelled?.Invoke(this, EventArgs.Empty);
CleanupDragVisual();
CleanupDropHint();
_currentUiDragInfo = null;
_currentUiDropInfo = null;
_lastDropTarget = null;
}
private void OnDropTargetChanged(object? sender, DropTargetChangedEventArgs e)
{
var dropTarget = e.Target;
// Обновляем UI-информацию о сбросе
if (dropTarget != null)
{
// Находим соответствующий UI-контрол
var targetControl = _registeredDropTargets
.FirstOrDefault(kv => kv.Value == dropTarget)
.Key;
_currentUiDropInfo = new UiDropInfo(
new Core.DragDrop.Models.DropInfo(
_dragDropManager.CurrentDragInfo?.Data,
e.TargetBounds.Center,
_dragDropManager.CurrentDragInfo?.AllowedEffects ?? Core.DragDrop.Enums.DragDropEffects.None,
dropTarget),
targetControl);
_currentUiDropInfo.DropPosition = CalculateDropPosition(
_currentUiDragInfo?.BaseDragInfo.StartPosition ?? Core.DragDrop.Geometry.Point.Zero,
e.TargetBounds);
}
else
{
_currentUiDropInfo = null;
}
// Уведомляем об изменении цели сброса
if (_lastDropTarget != dropTarget)
{
if (_lastDropTarget != null)
{
HideDropHint();
}
if (dropTarget != null && _currentUiDropInfo != null)
{
ShowDropHint(_currentUiDropInfo.TargetControl, _currentUiDropInfo.DropPosition);
}
_lastDropTarget = dropTarget;
}
}
private Core.DragDrop.Geometry.Rect CalculateBounds(IDockControl element)
{
// В UI-реализациях этот метод должен быть переопределен
// для вычисления реальных границ элемента на экране
return new Core.DragDrop.Geometry.Rect(0, 0, 100, 100);
}
private DropPosition CalculateDropPosition(Core.DragDrop.Geometry.Point cursorPos, Core.DragDrop.Geometry.Rect targetBounds)
{
// Простая логика определения позиции сброса
var center = targetBounds.Center;
var relativeX = (cursorPos.X - targetBounds.X) / targetBounds.Width;
var relativeY = (cursorPos.Y - targetBounds.Y) / targetBounds.Height;
if (relativeX < 0.25) return DropPosition.Left;
if (relativeX > 0.75) return DropPosition.Right;
if (relativeY < 0.25) return DropPosition.Top;
if (relativeY > 0.75) return DropPosition.Bottom;
return DropPosition.Center;
}
private void CreateDragVisual(UiDragInfo dragInfo)
{
// В UI-реализациях этот метод должен создавать визуальное представление
OnDragVisualUpdated?.Invoke(this, new DragVisualUpdatedEventArgs(
dragInfo, dragInfo.BaseDragInfo.StartPosition));
}
private void UpdateDragVisualPosition(Core.DragDrop.Geometry.Point position)
{
if (_currentUiDragInfo != null)
{
OnDragVisualUpdated?.Invoke(this, new DragVisualUpdatedEventArgs(
_currentUiDragInfo, position));
}
}
private void CleanupDragVisual()
{
// В UI-реализациях этот метод должен очищать визуальное представление
OnDragVisualUpdated?.Invoke(this, new DragVisualUpdatedEventArgs(null, Core.DragDrop.Geometry.Point.Zero));
}
private void CleanupDropHint()
{
HideDropHint();
}
}
///
/// Адаптер для преобразования IDragSource в IDropTarget.
///
internal class AdapterDropTarget : IDropTarget
{
private readonly IDragSource _dragSource;
public AdapterDropTarget(IDragSource dragSource)
{
_dragSource = dragSource;
}
public bool CanAcceptDrop(Core.DragDrop.Models.DropInfo dropInfo) => false;
public void DragOver(Core.DragDrop.Models.DropInfo dropInfo) { }
public void Drop(Core.DragDrop.Models.DropInfo dropInfo) { }
public void DragLeave() { }
}
///
/// Аргументы события обновления визуального представления перетаскивания.
///
public class DragVisualUpdatedEventArgs : EventArgs
{
///
/// Информация о перетаскивании.
///
public UiDragInfo? DragInfo { get; }
///
/// Текущая позиция.
///
public Core.DragDrop.Geometry.Point Position { get; }
public DragVisualUpdatedEventArgs(UiDragInfo? dragInfo, Core.DragDrop.Geometry.Point position)
{
DragInfo = dragInfo;
Position = position;
}
}
///
/// Аргументы события изменения визуальной подсказки сброса.
///
public class DropHintEventArgs : EventArgs
{
///
/// Целевой элемент.
///
public IDockControl? Target { get; }
///
/// Позиция сброса.
///
public DropPosition Position { get; }
///
/// Показывает, видима ли подсказка.
///
public bool IsVisible { get; }
///
/// Интенсивность подсветки.
///
public double Intensity { get; }
public DropHintEventArgs(IDockControl? target, DropPosition position, bool isVisible, double intensity)
{
Target = target;
Position = position;
IsVisible = isVisible;
Intensity = intensity;
}
}