160 lines
5.7 KiB
C#
160 lines
5.7 KiB
C#
using Lattice.Core.Geometry;
|
||
using Lattice.UI.DragDrop.Abstractions;
|
||
using Lattice.UI.DragDrop.WinUI.Controls;
|
||
using Microsoft.UI.Xaml;
|
||
using Microsoft.UI.Xaml.Controls;
|
||
using System;
|
||
|
||
namespace Lattice.UI.DragDrop.WinUI.Services;
|
||
|
||
/// <summary>
|
||
/// Хост для управления визуальными элементами перетаскивания в окне WinUI.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// Этот класс отвечает за отображение и управление визуальными элементами
|
||
/// во время операций перетаскивания, включая:
|
||
/// - Drag-визуализацию (элемент, следующий за курсором)
|
||
/// - Drop-превью (подсветка областей сброса)
|
||
/// </para>
|
||
/// <para>
|
||
/// Хост создает оверлейный слой поверх всего содержимого окна для корректного
|
||
/// отображения визуальных элементов поверх других UI-компонентов.
|
||
/// </para>
|
||
/// </remarks>
|
||
public sealed class WinUIDragDropHost : IDragDropHost, IDisposable
|
||
{
|
||
private DragDropOverlay? _overlay;
|
||
private Window? _window;
|
||
private bool _disposed;
|
||
|
||
/// <summary>
|
||
/// Инициализирует хост для работы с указанным окном.
|
||
/// </summary>
|
||
/// <param name="window">Окно, в котором будет работать перетаскивание.</param>
|
||
/// <exception cref="ArgumentNullException">
|
||
/// Выбрасывается, если <paramref name="window"/> равен null.
|
||
/// </exception>
|
||
/// <exception cref="ObjectDisposedException">
|
||
/// Выбрасывается, если хост уже был удален.
|
||
/// </exception>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// Этот метод создает оверлейный слой и добавляет его в визуальное дерево окна.
|
||
/// Если содержимое окна не является <see cref="Panel"/>, создается контейнер <see cref="Grid"/>.
|
||
/// </para>
|
||
/// <para>
|
||
/// Метод должен быть вызван один раз перед использованием других методов хоста.
|
||
/// </para>
|
||
/// </remarks>
|
||
public void Initialize(Window window)
|
||
{
|
||
if (_disposed) throw new ObjectDisposedException(nameof(WinUIDragDropHost));
|
||
|
||
_window = window ?? throw new ArgumentNullException(nameof(window));
|
||
|
||
// Создаем оверлей
|
||
_overlay = new DragDropOverlay();
|
||
|
||
// Добавляем оверлей в окно
|
||
if (_window.Content is Panel panel)
|
||
{
|
||
panel.Children.Add(_overlay);
|
||
}
|
||
else
|
||
{
|
||
// Если контент не Panel, создаем Grid
|
||
var grid = new Grid();
|
||
grid.Children.Add(_window.Content as UIElement ?? new Grid());
|
||
grid.Children.Add(_overlay);
|
||
_window.Content = grid;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Отображает визуальное представление перетаскиваемого элемента.
|
||
/// </summary>
|
||
/// <param name="dragVisual">Визуальный элемент для отображения.</param>
|
||
/// <param name="position">Позиция отображения в координатах экрана.</param>
|
||
/// <remarks>
|
||
/// Визуальный элемент будет отображен на оверлейном слое в указанной позиции
|
||
/// и будет следовать за курсором при обновлении через <see cref="UpdateDragVisualPosition"/>.
|
||
/// </remarks>
|
||
public void ShowDragVisual(object dragVisual, Point position)
|
||
{
|
||
if (_overlay == null || _disposed) return;
|
||
|
||
if (dragVisual is UIElement element)
|
||
{
|
||
_overlay.ShowDragVisual(element, position.X, position.Y);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public void UpdateDragVisualPosition(object dragVisual, Point position)
|
||
{
|
||
if (_overlay == null || _disposed) return;
|
||
|
||
if (dragVisual is UIElement element)
|
||
{
|
||
_overlay.UpdateDragVisualPosition(element, position.X, position.Y);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public void HideDragVisual(object dragVisual)
|
||
{
|
||
if (_overlay == null || _disposed) return;
|
||
|
||
if (dragVisual is UIElement element)
|
||
{
|
||
_overlay.HideDragVisual(element);
|
||
}
|
||
else
|
||
{
|
||
// Скрываем все визуальные элементы
|
||
var current = _overlay.GetCurrentDragVisual();
|
||
if (current != null)
|
||
{
|
||
_overlay.HideDragVisual(current);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public void ShowDropAdorner(IDropVisualAdorner adorner)
|
||
{
|
||
if (_overlay == null || _disposed) return;
|
||
|
||
if (adorner is DropPreviewAdorner dropAdorner)
|
||
{
|
||
// Для WinUI пока просто игнорируем
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public void HideDropAdorner(IDropVisualAdorner adorner)
|
||
{
|
||
if (_overlay == null || _disposed) return;
|
||
|
||
_overlay.HideAllDropPreviews();
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public void Dispose()
|
||
{
|
||
if (_disposed) return;
|
||
|
||
if (_overlay != null && _window?.Content is Panel panel)
|
||
{
|
||
panel.Children.Remove(_overlay);
|
||
_overlay.ClearAllVisuals();
|
||
}
|
||
|
||
_overlay = null;
|
||
_window = null;
|
||
_disposed = true;
|
||
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
} |