DragAndDrop core

This commit is contained in:
FrigaT
2026-01-18 16:33:35 +03:00
parent 9ea82af329
commit 79bdd8bc62
229 changed files with 21214 additions and 2494 deletions

View File

@@ -0,0 +1,83 @@
using Lattice.Layout.Abstractions;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.Foundation;
namespace Lattice.Layout.UI.WinUI.Docking;
/// <summary>
/// Полупрозрачная подсветка зоны докинга.
/// </summary>
public sealed class DockOverlay : Control
{
public static readonly DependencyProperty ZoneProperty =
DependencyProperty.Register(
nameof(Zone),
typeof(DockZone),
typeof(DockOverlay),
new PropertyMetadata(DockZone.Center, OnVisualPropertyChanged));
public static readonly DependencyProperty BoundsProperty =
DependencyProperty.Register(
nameof(Bounds),
typeof(Rect),
typeof(DockOverlay),
new PropertyMetadata(Rect.Empty, OnVisualPropertyChanged));
/// <summary>
/// Зона докинга, которую нужно подсветить.
/// </summary>
public DockZone Zone
{
get => (DockZone)GetValue(ZoneProperty);
set => SetValue(ZoneProperty, value);
}
/// <summary>
/// Прямоугольник зоны в координатах родительского контейнера.
/// </summary>
public Rect Bounds
{
get => (Rect)GetValue(BoundsProperty);
set => SetValue(BoundsProperty, value);
}
private Border? _border;
public DockOverlay()
{
IsHitTestVisible = false;
DefaultStyleKey = typeof(DockOverlay);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_border = GetTemplateChild("PART_Border") as Border;
UpdateVisual();
}
private static void OnVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DockOverlay overlay)
{
overlay.UpdateVisual();
}
}
private void UpdateVisual()
{
if (_border is null)
return;
Canvas.SetLeft(this, Bounds.X);
Canvas.SetTop(this, Bounds.Y);
Width = Bounds.Width;
Height = Bounds.Height;
_border.BorderBrush = new SolidColorBrush(Microsoft.UI.Colors.DeepSkyBlue);
_border.BorderThickness = new Thickness(2);
_border.Background = new SolidColorBrush(Microsoft.UI.Colors.LightSkyBlue) { Opacity = 0.25 };
}
}

View File

@@ -0,0 +1,47 @@
using Lattice.Layout.UI.Docking;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Foundation;
namespace Lattice.Layout.UI.WinUI.Docking;
/// <summary>
/// Контейнер для отображения подсветки зон докинга.
/// Обычно используется как Overlay-слой внутри WinUILayoutHost.
/// </summary>
public sealed class DockOverlayHost : Canvas
{
private DockOverlay? _currentOverlay;
public DockOverlayHost()
{
IsHitTestVisible = false;
}
/// <summary>
/// Отображает подсветку для указанной цели докинга.
/// </summary>
public void ShowOverlay(DockTarget target, Rect bounds)
{
if (_currentOverlay is null)
{
_currentOverlay = new DockOverlay();
Children.Add(_currentOverlay);
}
_currentOverlay.Zone = target.Zone;
_currentOverlay.Bounds = bounds;
_currentOverlay.Visibility = Visibility.Visible;
}
/// <summary>
/// Скрывает подсветку зоны докинга.
/// </summary>
public void HideOverlay()
{
if (_currentOverlay is not null)
{
_currentOverlay.Visibility = Visibility.Collapsed;
}
}
}

View File

@@ -0,0 +1,78 @@
using Lattice.Layout.UI.Docking;
using Lattice.Layout.UI.WinUI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using System;
using System.Linq;
using Windows.Foundation;
namespace Lattice.Layout.UI.WinUI.Docking;
/// <summary>
/// Выполняет hit-test для определения зоны докинга в WinUI.
/// </summary>
public static class DockZoneHitTester
{
/// <summary>
/// Выполняет hit-test по экранной точке и возвращает цель докинга.
/// </summary>
/// <param name="host">WinUI-хост раскладки.</param>
/// <param name="screenPoint">Точка в координатах окна.</param>
public static DockTarget? HitTest(WinUILayoutHost host, Point screenPoint)
{
if (host is null)
throw new ArgumentNullException(nameof(host));
// Предполагаем, что LayoutLayer — основной слой, в котором живёт визуальное дерево.
var layoutLayer = host.LayoutLayer;
if (layoutLayer is null)
return null;
// Переводим координаты в систему координат layoutLayer.
var elements = VisualTreeHelper.FindElementsInHostCoordinates(screenPoint, host.LayoutLayer);
var firstElement = elements.FirstOrDefault();
if (firstElement is null)
return null;
// Ищем ближайший IWinUIVisual.
var visual = FindVisual(firstElement);
if (visual is null)
return null;
if (visual is not ILayoutVisual layoutVisual)
return null;
// Вычисляем зону докинга для найденного контрола.
var control = visual.Control;
var bounds = control.TransformToVisual(layoutLayer)
.TransformBounds(new Rect(0, 0, control.ActualWidth, control.ActualHeight));
var localX = screenPoint.X - bounds.X;
var localY = screenPoint.Y - bounds.Y;
var zone = DockingUtils.GetZone(localX, localY, bounds.Width, bounds.Height);
return new DockTarget(layoutVisual, zone);
}
private static IWinUIVisual? FindVisual(UIElement element)
{
DependencyObject? current = element;
while (current is not null)
{
if (current is FrameworkElement fe && fe.DataContext is IWinUIVisual ctxVisual)
return ctxVisual;
if (current is IWinUIVisual winuiVisual)
return winuiVisual;
current = VisualTreeHelper.GetParent(current);
}
return null;
}
}

View File

@@ -0,0 +1,15 @@
using Microsoft.UI.Xaml;
namespace Lattice.Layout.UI.WinUI.Docking;
/// <summary>
/// Интерфейс для визуальных элементов WinUI, соответствующих элементам раскладки.
/// Нужен для hit-test и расчёта зон докинга.
/// </summary>
public interface IWinUIVisual
{
/// <summary>
/// Реальный WinUI-элемент, отображающий данный визуальный элемент раскладки.
/// </summary>
FrameworkElement Control { get; }
}