Files
Lattice/Lattice.Layout.UI.WinUI/Controls/WinUILayoutHost.cs
2026-01-18 16:33:35 +03:00

135 lines
4.2 KiB
C#

using Lattice.Layout.Abstractions;
using Lattice.Layout.UI.Docking;
using Lattice.Layout.UI.WinUI.Docking;
using Lattice.Layout.UI.WinUI.Rendering;
using Lattice.Layout.UI.WinUI.Visuals;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
namespace Lattice.Layout.UI.WinUI.Controls;
/// <summary>
/// WinUI-контрол, отображающий дерево раскладки.
/// Оборачивает LayoutRenderer и размещает визуальное дерево внутри себя.
/// </summary>
public sealed class WinUILayoutHost : UserControl, ILayoutView
{
/// <summary>
/// Слой, в котором размещается визуальное дерево раскладки.
/// </summary>
public Grid LayoutLayer { get; }
/// <summary>
/// Слой для отображения подсветки зон докинга.
/// </summary>
public DockOverlayHost OverlayLayer { get; }
private readonly LayoutRenderer _renderer;
private readonly WinUIVisualFactory _factory;
/// <summary>
/// Функция, возвращающая UI-содержимое по ContentId.
/// Используется визуальными элементами для подстановки реального контрола.
/// </summary>
public Func<string, UIElement>? ContentResolver { get; set; }
/// <summary>
/// Корневой элемент раскладки, который необходимо отобразить.
/// </summary>
public ILayoutRoot? Root
{
get => (ILayoutRoot?)GetValue(RootProperty);
set => SetValue(RootProperty, value);
}
/// <summary>
/// Свойство зависимости для <see cref="Root"/>.
/// </summary>
public static readonly DependencyProperty RootProperty =
DependencyProperty.Register(
nameof(Root),
typeof(ILayoutRoot),
typeof(WinUILayoutHost),
new PropertyMetadata(null, OnRootChanged));
private static void OnRootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WinUILayoutHost host)
{
host.Refresh();
}
}
/// <summary>
/// Создаёт новый экземпляр <see cref="WinUILayoutHost"/>.
/// </summary>
public WinUILayoutHost()
{
LayoutLayer = new Grid();
OverlayLayer = new DockOverlayHost();
var rootGrid = new Grid();
rootGrid.Children.Add(LayoutLayer);
rootGrid.Children.Add(OverlayLayer);
Content = rootGrid;
_factory = new WinUIVisualFactory();
_renderer = new LayoutRenderer(_factory);
}
/// <summary>
/// Выполняет полную перерисовку визуального дерева.
/// </summary>
public void Refresh()
{
LayoutLayer.Children.Clear();
if (Root?.Child is null)
return;
var visual = _renderer.Build(Root.Child);
visual.Attach();
switch (visual)
{
case WinUISplitVisual splitVisual:
LayoutLayer.Children.Add(splitVisual.Control);
break;
case WinUIGroupVisual groupVisual:
LayoutLayer.Children.Add(groupVisual.Control);
break;
case WinUIItemVisual itemVisual:
LayoutLayer.Children.Add(itemVisual.Control);
break;
}
}
/// <summary>
/// Показывает подсветку зоны докинга для указанной цели.
/// </summary>
public void ShowDockOverlay(DockTarget target)
{
if (target.Visual is not IWinUIVisual winuiVisual)
return;
var control = winuiVisual.Control;
var bounds = control.TransformToVisual(LayoutLayer)
.TransformBounds(new Windows.Foundation.Rect(0, 0, control.ActualWidth, control.ActualHeight));
OverlayLayer.ShowOverlay(target, bounds);
}
/// <summary>
/// Скрывает подсветку зон докинга.
/// </summary>
public void HideDockOverlay()
{
OverlayLayer.HideOverlay();
}
}