namespace Lattice.Core.DragDrop.Utilities; /// /// Утилиты для работы с системой перетаскивания. /// public static class DragDropUtilities { #region Effect Utilities /// /// Проверяет, совместимы ли эффекты источника и цели. /// public static bool AreEffectsCompatible(Enums.DragDropEffects sourceEffects, Enums.DragDropEffects targetEffects) { if (sourceEffects == Enums.DragDropEffects.None || targetEffects == Enums.DragDropEffects.None) return false; return (sourceEffects & targetEffects) != Enums.DragDropEffects.None; } /// /// Получает наиболее подходящий эффект на основе доступных. /// public static Enums.DragDropEffects GetBestEffect(Enums.DragDropEffects available, Enums.DragDropEffects preferred) { if ((available & preferred) != Enums.DragDropEffects.None) return available & preferred; if ((available & Enums.DragDropEffects.Move) != Enums.DragDropEffects.None) return Enums.DragDropEffects.Move; if ((available & Enums.DragDropEffects.Copy) != Enums.DragDropEffects.None) return Enums.DragDropEffects.Copy; if ((available & Enums.DragDropEffects.Link) != Enums.DragDropEffects.None) return Enums.DragDropEffects.Link; return Enums.DragDropEffects.None; } #endregion #region Geometry Utilities /// /// Вычисляет расстояние между двумя точками. /// public static double CalculateDistance(Geometry.Point p1, Geometry.Point p2) { var dx = p2.X - p1.X; var dy = p2.Y - p1.Y; return Math.Sqrt(dx * dx + dy * dy); } /// /// Проверяет, превысило ли перемещение пороговое значение. /// public static bool HasExceededDragThreshold(Geometry.Point startPoint, Geometry.Point currentPoint, double threshold) { var distance = CalculateDistance(startPoint, currentPoint); return distance >= threshold; } /// /// Определяет позицию сброса относительно прямоугольника. /// public static Enums.DropPosition GetDropPosition(Geometry.Point point, Geometry.Rect bounds, double edgeThreshold = 20.0) { if (!bounds.Contains(new Geometry.Point(point.X, point.Y))) return Enums.DropPosition.Inside; var relativeX = (point.X - bounds.X) / bounds.Width; var relativeY = (point.Y - bounds.Y) / bounds.Height; if (relativeX < edgeThreshold / bounds.Width) return Enums.DropPosition.Left; if (relativeX > 1 - edgeThreshold / bounds.Width) return Enums.DropPosition.Right; if (relativeY < edgeThreshold / bounds.Height) return Enums.DropPosition.Top; if (relativeY > 1 - edgeThreshold / bounds.Height) return Enums.DropPosition.Bottom; return Enums.DropPosition.Center; } #endregion #region Factory Methods /// /// Создает информацию о перетаскивании. /// public static Models.DragInfo CreateDragInfo( object data, Geometry.Point startPosition, Enums.DragDropEffects allowedEffects = Enums.DragDropEffects.Copy | Enums.DragDropEffects.Move, object? source = null, Dictionary? 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; } /// /// Создает простую реализацию источника перетаскивания. /// public static Abstractions.IDragSource CreateSimpleDragSource( Func dataProvider, Func? canDrag = null, Action? onCompleted = null, Action? onCancelled = null) { return new SimpleDragSource(dataProvider, canDrag, onCompleted, onCancelled); } /// /// Создает простую реализацию цели сброса. /// public static Abstractions.IDropTarget CreateSimpleDropTarget( Func? canAccept = null, Action? onDragOver = null, Action? onDrop = null, Action? onDragLeave = null) { return new SimpleDropTarget(canAccept, onDragOver, onDrop, onDragLeave); } #endregion #region Data Extraction /// /// Извлекает данные из элемента с поддержкой различных паттернов. /// public static object? ExtractData(object? element) { if (element == null) return null; // Проверяем, реализует ли элемент специальный интерфейс if (element is Abstractions.IDragSource dragSource) { if (dragSource.CanStartDrag(out var dragInfo) && dragInfo != null) return dragInfo.Data; } // В реальной реализации здесь будет рефлексия для проверки свойств // DataContext, Content и т.д. // Возвращаем сам элемент как данные return element; } /// /// Проверяет, совместимы ли данные с указанными типами. /// public static bool IsDataCompatible(object? data, IEnumerable? acceptedTypes) { if (data == null || acceptedTypes == null) return false; var dataType = data.GetType(); foreach (var acceptedType in acceptedTypes) { if (acceptedType.IsAssignableFrom(dataType)) return true; } return false; } #endregion #region Helper Classes private sealed class SimpleDragSource : Abstractions.IDragSource { private readonly Func _dataProvider; private readonly Func? _canDrag; private readonly Action? _onCompleted; private readonly Action? _onCancelled; public SimpleDragSource( Func dataProvider, Func? canDrag = null, Action? onCompleted = null, Action? onCancelled = null) { _dataProvider = dataProvider ?? throw new ArgumentNullException(nameof(dataProvider)); _canDrag = canDrag; _onCompleted = onCompleted; _onCancelled = onCancelled; } public bool CanStartDrag(out Models.DragInfo? dragInfo) { dragInfo = null; if (_canDrag?.Invoke() == false) return false; var data = _dataProvider(); if (data == null) return false; dragInfo = CreateDragInfo(data, Geometry.Point.Zero, Enums.DragDropEffects.Copy | Enums.DragDropEffects.Move, this); return true; } public bool StartDrag(Models.DragInfo dragInfo) => true; public void DragCompleted(Models.DragInfo dragInfo, Enums.DragDropEffects effects) { _onCompleted?.Invoke(dragInfo, effects); } public void DragCancelled(Models.DragInfo dragInfo) { _onCancelled?.Invoke(dragInfo); } } private sealed class SimpleDropTarget : Abstractions.IDropTarget { private readonly Func? _canAccept; private readonly Action? _onDragOver; private readonly Action? _onDrop; private readonly Action? _onDragLeave; public SimpleDropTarget( Func? canAccept = null, Action? onDragOver = null, Action? onDrop = null, Action? onDragLeave = null) { _canAccept = canAccept; _onDragOver = onDragOver; _onDrop = onDrop; _onDragLeave = onDragLeave; } public bool CanAcceptDrop(Models.DropInfo dropInfo) { return _canAccept?.Invoke(dropInfo) ?? true; } public void DragOver(Models.DropInfo dropInfo) { _onDragOver?.Invoke(dropInfo); } public void Drop(Models.DropInfo dropInfo) { _onDrop?.Invoke(dropInfo); } public void DragLeave() { _onDragLeave?.Invoke(); } } #endregion }