120 lines
5.8 KiB
C#
120 lines
5.8 KiB
C#
using Lattice.Core.Docking.Abstractions;
|
||
using Lattice.Core.Docking.Models;
|
||
|
||
namespace Lattice.Core.Docking.Engine;
|
||
|
||
/// <summary>
|
||
/// Предоставляет статические методы для манипуляции иерархией дерева компоновки.
|
||
/// Содержит чистые алгоритмы трансформации графа без зависимости от UI.
|
||
/// </summary>
|
||
public static class DockOperations
|
||
{
|
||
/// <summary>
|
||
/// Извлекает элемент из дерева компоновки.
|
||
/// Если родительская группа остается с одним ребенком, она удаляется,
|
||
/// а оставшийся ребенок занимает её место в иерархии.
|
||
/// </summary>
|
||
/// <param name="element">Элемент для удаления из дерева.</param>
|
||
/// <param name="root">Текущий корневой элемент дерева.</param>
|
||
/// <returns>
|
||
/// Новый корневой элемент дерева после удаления и оптимизации структуры.
|
||
/// Возвращает null, если дерево становится пустым.
|
||
/// </returns>
|
||
/// <exception cref="ArgumentNullException">
|
||
/// Выбрасывается, когда <paramref name="root"/> равен null.
|
||
/// </exception>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Вставляет элемент в дерево компоновки относительно целевого элемента.
|
||
/// Создает новую группу разделения или объединяет контент в зависимости от позиции.
|
||
/// </summary>
|
||
/// <param name="target">Целевой элемент, относительно которого выполняется вставка.</param>
|
||
/// <param name="source">Вставляемый элемент.</param>
|
||
/// <param name="pos">Позиция вставки относительно целевого элемента.</param>
|
||
/// <param name="root">Текущий корневой элемент дерева.</param>
|
||
/// <returns>
|
||
/// Новый корневой элемент дерева после вставки и оптимизации структуры.
|
||
/// </returns>
|
||
/// <exception cref="ArgumentNullException">
|
||
/// Выбрасывается, когда <paramref name="target"/>, <paramref name="source"/>
|
||
/// или <paramref name="root"/> равны null.
|
||
/// </exception>
|
||
/// <exception cref="ArgumentException">
|
||
/// Выбрасывается, когда <paramref name="pos"/> имеет недопустимое значение.
|
||
/// </exception>
|
||
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<IDockContent>(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;
|
||
}
|
||
|
||
newGroup.Parent = null;
|
||
return newGroup; // Новая группа стала корнем
|
||
}
|
||
} |