146 lines
5.2 KiB
C#
146 lines
5.2 KiB
C#
using Lattice.Core.Models;
|
||
using Lattice.Core.Models.Enums;
|
||
using Lattice.UI.Controls;
|
||
using Lattice.UI.Services;
|
||
using Microsoft.UI.Xaml;
|
||
using Microsoft.UI.Xaml.Media;
|
||
using Windows.Foundation;
|
||
|
||
namespace Lattice.UI.DragDrop;
|
||
|
||
/// <summary>
|
||
/// Обработчик перетаскивания панелей и вкладок для системы Lattice.
|
||
/// </summary>
|
||
public class DockTabHandler
|
||
{
|
||
private bool _isDragging;
|
||
private readonly LatticeDockHost _host;
|
||
|
||
// Состояние текущей операции перетаскивания
|
||
private LayoutNode? _sourceNode;
|
||
private LayoutNode? _targetNode;
|
||
private DockDirection _currentSide;
|
||
|
||
public DockTabHandler(LatticeDockHost host)
|
||
{
|
||
_host = host;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Привязывает логику перетаскивания к визуальному элементу (заголовку панели).
|
||
/// </summary>
|
||
/// <param name="header">Элемент, за который пользователь "хватает" панель.</param>
|
||
/// <param name="node">Узел макета, связанный с этой панелью.</param>
|
||
public void Attach(FrameworkElement header, LayoutNode node)
|
||
{
|
||
header.PointerPressed += (s, e) =>
|
||
{
|
||
_isDragging = true;
|
||
_sourceNode = node;
|
||
header.CapturePointer(e.Pointer);
|
||
|
||
if (_host.AnchorOverlay != null)
|
||
_host.AnchorOverlay.Visibility = Visibility.Visible;
|
||
};
|
||
|
||
header.PointerMoved += (s, e) =>
|
||
{
|
||
if (!_isDragging) return;
|
||
|
||
// Получаем позицию курсора относительно всего хоста
|
||
Point pointerPos = e.GetCurrentPoint(_host).Position;
|
||
UpdateOverlayPosition(pointerPos);
|
||
};
|
||
|
||
header.PointerReleased += (s, e) =>
|
||
{
|
||
if (!_isDragging) return;
|
||
|
||
_isDragging = false;
|
||
header.ReleasePointerCapture(e.Pointer);
|
||
CompleteDocking();
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обновляет положение визуальных подсказок и рассчитывает зоны сброса.
|
||
/// </summary>
|
||
private void UpdateOverlayPosition(Point pointerPosition)
|
||
{
|
||
var overlay = _host.AnchorOverlay;
|
||
if (overlay == null) return;
|
||
|
||
// 1. Позиционируем "ромб" с кнопками докинга
|
||
overlay.PositionAnchors(pointerPosition);
|
||
|
||
// 2. Хит-тестинг: ищем LatticePane под курсором (исключая саму перетаскиваемую панель)
|
||
var elements = VisualTreeHelper.FindElementsInHostCoordinates(pointerPosition, _host);
|
||
var targetPane = elements.OfType<LatticePane>()
|
||
.FirstOrDefault(p => (p.DataContext as LayoutNode)?.Id != _sourceNode?.Id);
|
||
|
||
if (targetPane != null && targetPane.DataContext is LayoutNode targetNode)
|
||
{
|
||
_targetNode = targetNode;
|
||
|
||
// 3. Расчет локальной позиции для определения стороны
|
||
var transform = targetPane.TransformToVisual(_host);
|
||
Point localPoint = transform.Inverse.TransformPoint(pointerPosition);
|
||
|
||
// 4. Определяем сторону через сервис
|
||
_currentSide = VisualTreeService.GetHitZone(targetPane, localPoint);
|
||
|
||
// 5. Показываем синее превью зоны сброса
|
||
Rect previewRect = CalculatePreviewRect(targetPane, _currentSide);
|
||
Rect globalPreviewRect = transform.TransformBounds(previewRect);
|
||
|
||
overlay.ShowPreview(globalPreviewRect);
|
||
}
|
||
else
|
||
{
|
||
_targetNode = null;
|
||
overlay.HidePreview();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Рассчитывает прямоугольник предпросмотра на основе выбранной стороны.
|
||
/// </summary>
|
||
private Rect CalculatePreviewRect(FrameworkElement pane, DockDirection side)
|
||
{
|
||
double w = pane.ActualWidth;
|
||
double h = pane.ActualHeight;
|
||
|
||
return side switch
|
||
{
|
||
DockDirection.Left => new Rect(0, 0, w / 2, h),
|
||
DockDirection.Right => new Rect(w / 2, 0, w / 2, h),
|
||
DockDirection.Top => new Rect(0, 0, w, h / 2),
|
||
DockDirection.Bottom => new Rect(0, h / 2, w, h / 2),
|
||
_ => new Rect(0, 0, w, h) // Center
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// Завершает операцию докинга, передавая данные в Core Engine.
|
||
/// </summary>
|
||
private void CompleteDocking()
|
||
{
|
||
if (_sourceNode != null && _targetNode != null && _host.Manager != null)
|
||
{
|
||
// Вызываем логику перестроения дерева в Lattice.Core
|
||
_host.Manager.Dock(_sourceNode, _targetNode, _currentSide);
|
||
}
|
||
|
||
// Очистка UI
|
||
var overlay = _host.AnchorOverlay;
|
||
if (overlay != null)
|
||
{
|
||
overlay.Visibility = Visibility.Collapsed;
|
||
overlay.HidePreview();
|
||
}
|
||
|
||
_sourceNode = null;
|
||
_targetNode = null;
|
||
}
|
||
}
|