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; } }