using Lattice.Core.Docking.Abstractions;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Lattice.Core.Docking.Models;
///
/// Представляет узел дерева компоновки, который разделяет доступную область
/// между двумя дочерними элементами. Этот класс является основным структурным
/// элементом для создания сложных макетов с разделителями.
///
///
/// Каждая группа содержит два дочерних элемента ( и ),
/// которые могут быть либо другими группами (для создания вложенной структуры),
/// либо листами () с контентом.
/// Направление разделения определяется свойством .
///
public class DockGroup : IDockElement, INotifyPropertyChanged
{
///
/// Происходит при изменении значения свойства.
///
public event PropertyChangedEventHandler? PropertyChanged;
private double _splitRatio = 0.5;
private string _id;
private IDockElement _first;
private IDockElement _second;
///
/// Получает или задает уникальный идентификатор группы.
///
///
/// Строковый идентификатор, уникальный в пределах дерева компоновки.
///
///
/// Идентификатор используется для сериализации/десериализации макета,
/// поиска элементов и отслеживания изменений в дереве.
///
public string Id
{
get => _id;
internal set
{
if (_id != value)
{
_id = value;
OnPropertyChanged();
}
}
}
///
/// Получает или задает родительский элемент в иерархии дерева компоновки.
///
///
/// Родительский элемент или null, если эта группа является корневой.
///
///
/// Это свойство управляется системой компоновки при добавлении или
/// удалении элементов из дерева.
///
public IDockElement? Parent { get; set; }
///
/// Получает или задает первый дочерний элемент (левую или верхнюю область).
///
///
/// Элемент, занимающий первую часть разделенной области.
///
///
/// Выбрасывается при попытке установить значение null.
///
///
/// При установке нового значения автоматически обновляется свойство
/// у дочернего элемента.
///
public IDockElement First
{
get => _first;
set
{
if (_first != value)
{
_first = value ?? throw new ArgumentNullException(nameof(value));
_first.Parent = this;
OnPropertyChanged();
}
}
}
///
/// Получает или задает второй дочерний элемент (правую или нижнюю область).
///
///
/// Элемент, занимающий вторую часть разделенной области.
///
///
/// Выбрасывается при попытке установить значение null.
///
///
/// При установке нового значения автоматически обновляется свойство
/// у дочернего элемента.
///
public IDockElement Second
{
get => _second;
set
{
if (_second != value)
{
_second = value ?? throw new ArgumentNullException(nameof(value));
_second.Parent = this;
OnPropertyChanged();
}
}
}
///
/// Получает или задает направление разделения данной группы.
///
///
/// Значение перечисления , указывающее,
/// как разделена область: горизонтально или вертикально.
///
///
///
/// - создает левую и правую области
/// - создает верхнюю и нижнюю области
///
///
public SplitDirection Orientation { get; set; }
///
/// Получает или задает соотношение разделения между первым и вторым элементами.
///
///
/// Значение от 0.0 до 1.0, где:
///
/// - 0.0 - вся область принадлежит второму элементу
/// - 0.5 - область разделена поровну
/// - 1.0 - вся область принадлежит первому элементу
///
///
///
/// Изменение этого свойства вызывает событие
/// и может привести к перерисовке пользовательского интерфейса.
///
public double SplitRatio
{
get => _splitRatio;
set
{
if (Math.Abs(_splitRatio - value) > double.Epsilon)
{
_splitRatio = value;
OnPropertyChanged();
}
}
}
///
/// Получает или задает желаемую ширину элемента.
///
///
/// Ширина в пикселях или относительных единицах.
///
public double Width { get; set; }
///
/// Получает или задает желаемую высоту элемента.
///
///
/// Высота в пикселях или относительных единицах.
///
public double Height { get; set; }
///
/// Получает минимально допустимую ширину элемента.
///
///
/// Минимальная ширина в пикселях, при которой элемент сохраняет функциональность.
///
///
/// Для группы минимальная ширина вычисляется как сумма минимальных ширин
/// дочерних элементов при горизонтальной ориентации или максимум минимальных
/// ширин при вертикальной ориентации.
///
public double MinWidth => Orientation == SplitDirection.Horizontal
? First.MinWidth + Second.MinWidth
: Math.Max(First.MinWidth, Second.MinWidth);
///
/// Получает минимально допустимую высоту элемента.
///
///
/// Минимальная высота в пикселях, при которой элемент сохраняет функциональность.
///
///
/// Для группы минимальная высота вычисляется как сумма минимальных высот
/// дочерних элементов при вертикальной ориентации или максимум минимальных
/// высот при горизонтальной ориентации.
///
public double MinHeight => Orientation == SplitDirection.Vertical
? First.MinHeight + Second.MinHeight
: Math.Max(First.MinHeight, Second.MinHeight);
///
/// Инициализирует новый экземпляр класса .
///
///
/// Первый дочерний элемент (левая или верхняя область).
///
///
/// Второй дочерний элемент (правая или нижняя область).
///
///
/// Направление разделения между дочерними элементами.
///
///
/// Уникальный идентификатор группы. Если не указан, генерируется новый GUID.
///
///
/// Выбрасывается, когда или
/// равны null.
///
///
/// Конструктор автоматически устанавливает свойство
/// у дочерних элементов на текущую группу и генерирует уникальный идентификатор,
/// если он не был предоставлен.
///
public DockGroup(IDockElement first, IDockElement second,
SplitDirection orientation, string? id = null)
{
First = first ?? throw new ArgumentNullException(nameof(first));
Second = second ?? throw new ArgumentNullException(nameof(second));
Orientation = orientation;
Id = id ?? Guid.NewGuid().ToString();
First.Parent = this;
Second.Parent = this;
}
///
/// Вызывает событие .
///
///
/// Имя изменившегося свойства. Если не указано, определяется автоматически.
///
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}