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; } newGroup.Parent = null; return newGroup; // Новая группа стала корнем } }