Files
Lattice/Lattice.UI/DragDrop/DockTabHandler.cs

146 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.Service != null)
{
// Вызываем логику перестроения дерева в Lattice.Core
_host.Service.Dock(_sourceNode, _targetNode, _currentSide);
}
// Очистка UI
var overlay = _host.AnchorOverlay;
if (overlay != null)
{
overlay.Visibility = Visibility.Collapsed;
overlay.HidePreview();
}
_sourceNode = null;
_targetNode = null;
}
}