using Lattice.Core.Abstractions;
using Lattice.Core.Models;
using Lattice.UI.Primitives;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Lattice.UI.Controls;
///
/// Корневой контрол Lattice, отвечающий за отображение и управление макетом докинга.
///
public class LatticeDockHost : Control
{
public DockAnchorOverlay? AnchorOverlay => GetTemplateChild("AnchorOverlay") as DockAnchorOverlay;
///
/// Определяет свойство зависимости для LayoutService.
///
public static readonly DependencyProperty ServiceProperty =
DependencyProperty.Register(nameof(Service), typeof(ILayoutService), typeof(LatticeDockHost), new PropertyMetadata(null, OnServiceChanged));
///
/// Сервис управления макетом, привязанный к данному хосту.
///
public ILayoutService? Service
{
get => (ILayoutService?)GetValue(ServiceProperty);
set => SetValue(ServiceProperty, value);
}
///
/// Указывает конкретный узел, который должен стать корнем для этого хоста.
/// Если null — используется Service.Root.
///
public static readonly DependencyProperty RootNodeProperty =
DependencyProperty.Register(nameof(RootNode), typeof(LayoutNode), typeof(LatticeDockHost), new PropertyMetadata(null, OnServiceChanged));
public LayoutNode? RootNode
{
get => (LayoutNode?)GetValue(RootNodeProperty);
set => SetValue(RootNodeProperty, value);
}
public LatticeDockHost()
{
this.DefaultStyleKey = typeof(LatticeDockHost);
}
private static void OnServiceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is LatticeDockHost host)
{
// Отписываемся от событий старого менеджера (если он был)
if (e.OldValue is ILayoutService oldService)
{
oldService.LayoutUpdated -= host.OnLayoutUpdated;
}
// Подписываемся на новый менеджер
if (e.NewValue is ILayoutService newService)
{
newService.LayoutUpdated += host.OnLayoutUpdated;
host.RebuildUI();
}
}
}
///
/// Именованный метод для обработки обновления макета.
/// Позволяет корректно отписываться от событий и избегать утечек памяти.
///
private void OnLayoutUpdated(object? sender, EventArgs e)
{
// WinUI 3 требует обновления UI только из основного потока
this.DispatcherQueue.TryEnqueue(() =>
{
this.RebuildUI();
});
}
///
/// Полностью перестраивает визуальное дерево на основе текущего состояния Core-движка.
///
private void RebuildUI()
{
if (this.GetTemplateChild("LayoutPresenter") is ContentPresenter presenter)
{
// Приоритет: сначала проверяем локальный RootNode, затем глобальный Service.Root
var effectiveRoot = RootNode ?? Service?.Root;
if (effectiveRoot != null)
{
var rootPanel = new LayoutPanel(this);
rootPanel.Build(effectiveRoot);
presenter.Content = rootPanel;
}
else
{
presenter.Content = null;
}
}
}
}