Files
Lattice/Lattice.Core/Engine/LayoutManager.cs

166 lines
5.5 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.Abstractions;
using Lattice.Core.Models;
using Lattice.Core.Models.Enums;
using Microsoft.Extensions.Logging;
namespace Lattice.Core.Engine;
/// <summary>
/// Реализация сервиса управления макетом.
/// </summary>
public class LayoutManager : ILayoutService
{
private readonly ILogger? _logger;
private LayoutNode? _root;
/// <inheritdoc/>
public LayoutNode? Root => _root;
/// <inheritdoc/>
public event EventHandler? LayoutUpdated;
public LayoutManager(ILogger<LayoutManager>? logger = null)
{
_logger = logger;
}
/// <inheritdoc/>
public void Dock(LayoutNode source, LayoutNode target, DockDirection direction)
{
if (source == target) return;
_logger?.LogDebug("Начало трансформации дерева: {Source} -> {Target} ({Direction})", source.Name, target.Name, direction);
// 1. Извлекаем источник из его текущего места в дереве
Remove(source);
// 2. Если докинг в центр — это логика объединения (например, в TabView)
// В рамках Core это может означать добавление в тот же контейнер
if (direction == DockDirection.Center)
{
HandleCenterDock(source, target);
}
else
{
// 3. Создаем разделение (Split)
HandleSideDock(source, target, direction);
}
LayoutUpdated?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Логика добавления элемента в центральную часть (вкладки).
/// </summary>
private void HandleCenterDock(LayoutNode source, LayoutNode target)
{
if (target.Parent is SplitContainerNode parent)
{
parent.AddChild(source);
}
else if (target == _root)
{
// Если таргет - корень, и мы докаем в центр, создаем контейнер по умолчанию
var container = new SplitContainerNode(SplitOrientation.Horizontal);
_root = container;
container.AddChild(target);
container.AddChild(source);
}
}
/// <summary>
/// Логика разделения существующей области на две (Side Dock).
/// </summary>
private void HandleSideDock(LayoutNode source, LayoutNode target, DockDirection direction)
{
var orientation = (direction == DockDirection.Left || direction == DockDirection.Right)
? SplitOrientation.Horizontal
: SplitOrientation.Vertical;
var parent = target.Parent as SplitContainerNode;
// Создаем новый сплиттер, который заменит target
var newContainer = new SplitContainerNode(orientation);
if (parent != null)
{
// Заменяем target на новый контейнер в списке детей родителя
int index = parent.Children.IndexOf(target);
parent.Children[index] = newContainer;
newContainer.Parent = parent;
}
else
{
// Если родителя нет, значит target был корнем
_root = newContainer;
}
// Настраиваем порядок в новом сплиттере
if (direction == DockDirection.Left || direction == DockDirection.Top)
{
newContainer.AddChild(source);
newContainer.AddChild(target);
}
else
{
newContainer.AddChild(target);
newContainer.AddChild(source);
}
// Корректируем размеры (например, делим пополам)
source.WidthValue = 0.5;
target.WidthValue = 0.5;
source.IsWidthStar = true;
target.IsWidthStar = true;
}
/// <inheritdoc/>
public void Remove(LayoutNode node)
{
if (node.Parent is SplitContainerNode parent)
{
parent.Children.Remove(node);
node.Parent = null;
// Если в контейнере остался один элемент — убираем лишнюю вложенность
if (parent.Children.Count == 1)
{
CollapseContainer(parent);
}
}
else if (node == _root)
{
_root = null;
}
LayoutUpdated?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Убирает ненужные контейнеры, если в них остался только один элемент.
/// </summary>
private void CollapseContainer(SplitContainerNode container)
{
var lastChild = container.Children[0];
var parent = container.Parent as SplitContainerNode;
if (parent != null)
{
int index = parent.Children.IndexOf(container);
parent.Children[index] = lastChild;
lastChild.Parent = parent;
}
else
{
_root = lastChild;
lastChild.Parent = null;
}
}
/// <inheritdoc/>
public string SaveLayout() { /* Реализация через JsonConverter */ return string.Empty; }
/// <inheritdoc/>
public void LoadLayout(string jsonData) { /* Реализация через JsonConverter */ }
}