using Lattice.Core.Docking.Abstractions;
using Lattice.Core.Docking.Models;
namespace Lattice.Core.Docking.Engine;
///
/// Предоставляет статические методы для манипуляции иерархией дерева компоновки.
/// Содержит чистые алгоритмы трансформации графа без зависимости от UI.
///
public static class DockOperations
{
///
/// Извлекает элемент из дерева компоновки.
/// Если родительская группа остается с одним ребенком, она удаляется,
/// а оставшийся ребенок занимает её место в иерархии.
///
/// Элемент для удаления из дерева.
/// Текущий корневой элемент дерева.
///
/// Новый корневой элемент дерева после удаления и оптимизации структуры.
/// Возвращает null, если дерево становится пустым.
///
///
/// Выбрасывается, когда равен null.
///
public static IDockElement? Remove(IDockElement element, IDockElement root)
{
if (element == null) throw new ArgumentNullException(nameof(element));
if (root == null) throw new ArgumentNullException(nameof(root));
if (element == root) return null;
var parent = element.Parent as DockGroup;
if (parent == null) return root;
// Определяем "выжившего" соседа
var sibling = (parent.First == element) ? parent.Second : parent.First;
var grandParent = parent.Parent as DockGroup;
if (grandParent != null)
{
// Переподключаем соседа напрямую к дедушке
if (grandParent.First == parent) grandParent.First = sibling;
else grandParent.Second = sibling;
sibling.Parent = grandParent;
return root;
}
// Если дедушки нет, сосед становится новым корнем
sibling.Parent = null;
return sibling;
}
///
/// Вставляет элемент в дерево компоновки относительно целевого элемента.
/// Создает новую группу разделения или объединяет контент в зависимости от позиции.
///
/// Целевой элемент, относительно которого выполняется вставка.
/// Вставляемый элемент.
/// Позиция вставки относительно целевого элемента.
/// Текущий корневой элемент дерева.
///
/// Новый корневой элемент дерева после вставки и оптимизации структуры.
///
///
/// Выбрасывается, когда ,
/// или равны null.
///
///
/// Выбрасывается, когда имеет недопустимое значение.
///
public static IDockElement Insert(IDockElement target, IDockElement source,
DockPosition pos, IDockElement root)
{
if (target == null) throw new ArgumentNullException(nameof(target));
if (source == null) throw new ArgumentNullException(nameof(source));
if (root == null) throw new ArgumentNullException(nameof(root));
// Случай 1: Объединение вкладок в центре
if (pos == DockPosition.Center)
{
if (target is IDockContainer targetContainer && source is IDockContainer sourceContainer)
{
// Переносим все вкладки из источника в целевой контейнер
var items = new List(sourceContainer.Children);
foreach (var item in items)
{
sourceContainer.RemoveContent(item);
targetContainer.AddContent(item);
}
}
return root;
}
// Случай 2: Разделение (Split)
var direction = (pos == DockPosition.Left || pos == DockPosition.Right)
? SplitDirection.Horizontal : SplitDirection.Vertical;
bool sourceIsFirst = (pos == DockPosition.Left || pos == DockPosition.Top);
var oldParent = target.Parent;
// Создаем новую группу. Источник и цель делят пространство 50/50
var newGroup = sourceIsFirst
? new DockGroup(source, target, direction) { SplitRatio = 0.5 }
: new DockGroup(target, source, direction) { SplitRatio = 0.5 };
if (oldParent is DockGroup gp)
{
if (gp.First == target) gp.First = newGroup;
else gp.Second = newGroup;
newGroup.Parent = gp;
return root;
}
// Если target был корнем, новая группа становится новым корнем
if (target == root)
{
newGroup.Parent = null;
return newGroup;
}
// Эта точка недостижима при правильном использовании,
// но добавляем для безопасности
newGroup.Parent = null;
return newGroup;
}
}