Files
Lattice/Lattice.Core.Docking/Models/DockLeaf.cs
2026-01-27 06:07:15 +03:00

229 lines
9.1 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.Docking.Abstractions;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Lattice.Core.Docking.Models;
/// <summary>
/// Представляет конечный узел (лист) дерева компоновки, который непосредственно
/// содержит коллекцию вкладок с контентом. Этот класс является контейнером для
/// отображаемого пользователю содержимого.
/// </summary>
/// <remarks>
/// Лист является основным элементом, с которым взаимодействует пользователь
/// при работе с документами или инструментальными панелями в IDE-подобных
/// приложениях.
/// </remarks>
public class DockLeaf : IDockContainer, INotifyPropertyChanged
{
/// <summary>
/// Происходит при изменении значения свойства.
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
private readonly ObservableCollection<IDockContent> _items = new();
private IDockContent? _activeContent;
private string _id;
private TabPlacement _tabPlacement = TabPlacement.Bottom;
/// <summary>
/// Получает или задает уникальный идентификатор листа.
/// </summary>
/// <value>
/// Строковый идентификатор, уникальный в пределах дерева компоновки.
/// </value>
public string Id
{
get => _id;
internal set
{
if (_id != value)
{
_id = value;
OnPropertyChanged();
}
}
}
/// <summary>
/// Получает или задает родительский элемент в иерархии дерева компоновки.
/// </summary>
/// <value>
/// Родительский элемент или null, если этот лист является корневым.
/// </value>
public IDockElement? Parent { get; set; }
/// <summary>
/// Получает список вкладок, содержащихся в данном контейнере.
/// </summary>
/// <value>
/// Коллекция объектов, реализующих <see cref="IDockContent"/>.
/// </value>
/// <remarks>
/// Эта коллекция является наблюдаемой (ObservableCollection), что позволяет
/// автоматически обновлять пользовательский интерфейс при добавлении или
/// удалении вкладок.
/// </remarks>
public IList<IDockContent> Children => _items;
/// <summary>
/// Получает или задает активную (выбранную) вкладку в контейнере.
/// </summary>
/// <value>
/// Активная вкладка или null, если в контейнере нет вкладок.
/// </value>
/// <remarks>
/// При установке нового значения проверяется, что вкладка действительно
/// содержится в коллекции <see cref="Children"/>.
/// Изменение этого свойства вызывает событие <see cref="PropertyChanged"/>.
/// </remarks>
public IDockContent? ActiveContent
{
get => _activeContent;
set
{
if (value != null && !_items.Contains(value)) return;
if (_activeContent != value)
{
_activeContent = value;
OnPropertyChanged();
}
}
}
/// <summary>
/// Получает или задает желаемую ширину элемента.
/// </summary>
/// <value>
/// Ширина в пикселях или относительных единицах.
/// </value>
public double Width { get; set; }
/// <summary>
/// Получает или задает желаемую высоту элемента.
/// </summary>
/// <value>
/// Высота в пикселях или относительных единицах.
/// </value>
public double Height { get; set; }
/// <summary>
/// Получает или задает минимально допустимую ширину элемента.
/// </summary>
/// <value>
/// Минимальная ширина в пикселях. Значение по умолчанию: 100.
/// </value>
public double MinWidth { get; set; } = 100;
/// <summary>
/// Получает или задает минимально допустимую высоту элемента.
/// </summary>
/// <value>
/// Минимальная высота в пикселях. Значение по умолчанию: 100.
/// </value>
public double MinHeight { get; set; } = 100;
/// <summary>
/// Получает или задает положение полосы вкладок в контейнере.
/// </summary>
/// <value>
/// Значение перечисления <see cref="TabPlacement"/>, определяющее,
/// где располагаются вкладки относительно содержимого.
/// </value>
/// <remarks>
/// Поддерживаются все четыре стороны: верх, низ, лево, право.
/// </remarks>
public TabPlacement TabPlacement
{
get => _tabPlacement;
set
{
if (_tabPlacement != value)
{
_tabPlacement = value;
OnPropertyChanged();
}
}
}
/// <summary>
/// Инициализирует новый экземпляр класса <see cref="DockLeaf"/>.
/// </summary>
/// <param name="id">
/// Уникальный идентификатор листа. Если не указан, генерируется новый GUID.
/// </param>
/// <remarks>
/// Создает пустой лист с коллекцией вкладок и генерирует уникальный
/// идентификатор, если он не был предоставлен.
/// </remarks>
public DockLeaf(string? id = null)
{
_id = id ?? Guid.NewGuid().ToString();
}
/// <summary>
/// Вызывает событие <see cref="PropertyChanged"/>.
/// </summary>
/// <param name="name">
/// Имя изменившегося свойства. Если не указано, определяется автоматически.
/// </param>
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
/// <summary>
/// Добавляет контент в контейнер и делает его активным.
/// </summary>
/// <param name="content">
/// Контент для добавления.
/// </param>
/// <remarks>
/// Если контент уже содержится в коллекции, он не добавляется повторно,
/// но становится активным.
/// Этот метод обновляет свойство <see cref="ActiveContent"/> и вызывает
/// соответствующее событие изменения свойства.
/// </remarks>
public void AddContent(IDockContent content)
{
if (content == null) return;
if (!_items.Contains(content))
{
_items.Add(content);
}
ActiveContent = content;
}
/// <summary>
/// Удаляет контент из контейнера.
/// </summary>
/// <param name="content">
/// Контент для удаления.
/// </param>
/// <remarks>
/// Если удаляемый контент является активным, автоматически выбирается
/// новая активная вкладка (следующая в списке или предыдущая, если удалена
/// последняя).
/// Если после удаления контейнер становится пустым, он может быть удален
/// из дерева макета системой компоновки.
/// </remarks>
public void RemoveContent(IDockContent content)
{
if (content == null) return;
int index = _items.IndexOf(content);
if (index == -1) return;
_items.RemoveAt(index);
if (ActiveContent == content)
{
if (_items.Count > 0)
ActiveContent = _items[Math.Min(index, _items.Count - 1)];
else
ActiveContent = null;
}
}
}