Доработан Docking
This commit is contained in:
@@ -1,8 +1,23 @@
|
||||
namespace Lattice.Core.Docking.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Определяет контракт для команды в системе докинга.
|
||||
/// Команды представляют действия, которые могут быть выполнены над элементами док-системы.
|
||||
/// </summary>
|
||||
public interface IDockCommand : System.Windows.Input.ICommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает отображаемое имя команды.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает идентификатор ресурса для иконки команды.
|
||||
/// </summary>
|
||||
string Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает текстовое представление жеста (горячей клавиши) для команды.
|
||||
/// </summary>
|
||||
string GestureText { get; }
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,36 @@
|
||||
namespace Lattice.Core.Docking.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс для элементов (листьев дерева), которые физически содержат внутри себя коллекцию вкладок.
|
||||
/// Определяет контракт для контейнеров, содержащих коллекцию вкладок.
|
||||
/// Контейнеры являются листьями дерева компоновки и непосредственно отображают содержимое.
|
||||
/// </summary>
|
||||
public interface IDockContainer : IDockElement
|
||||
{
|
||||
/// <summary> Список вкладок, находящихся в данном контейнере. </summary>
|
||||
/// <summary>
|
||||
/// Получает список вкладок, находящихся в данном контейнере.
|
||||
/// </summary>
|
||||
IList<IDockContent> Children { get; }
|
||||
|
||||
/// <summary> Ссылка на текущую выбранную и отображаемую вкладку. </summary>
|
||||
/// <summary>
|
||||
/// Получает или задает текущую активную (выбранную) вкладку.
|
||||
/// </summary>
|
||||
IDockContent? ActiveContent { get; set; }
|
||||
|
||||
/// <summary> Добавляет контент в контейнер и делает его активным. </summary>
|
||||
/// <summary>
|
||||
/// Добавляет контент в контейнер и делает его активным.
|
||||
/// </summary>
|
||||
/// <param name="content">Контент для добавления.</param>
|
||||
void AddContent(IDockContent content);
|
||||
|
||||
/// <summary> Удаляет контент. Если Children становится пустым, контейнер может быть удален из дерева макета. </summary>
|
||||
/// <summary>
|
||||
/// Удаляет контент из контейнера. Если коллекция становится пустой,
|
||||
/// контейнер может быть удален из дерева макета.
|
||||
/// </summary>
|
||||
/// <param name="content">Контент для удаления.</param>
|
||||
void RemoveContent(IDockContent content);
|
||||
|
||||
/// <summary> Положение вкладок в интерфейсе. </summary>
|
||||
/// <summary>
|
||||
/// Получает или задает положение панели вкладок в интерфейсе.
|
||||
/// </summary>
|
||||
TabPlacement TabPlacement { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,37 @@
|
||||
namespace Lattice.Core.Docking.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Описывает объект содержимого (вкладку), который может быть размещен внутри IDockContainer.
|
||||
/// Определяет контракт для содержимого (вкладки), которое может быть размещено внутри контейнера.
|
||||
/// </summary>
|
||||
public interface IDockContent
|
||||
{
|
||||
/// <summary> Уникальный идентификатор контента (например, путь к файлу или ID инструмента). </summary>
|
||||
/// <summary>
|
||||
/// Получает уникальный идентификатор контента.
|
||||
/// Используется для идентификации вкладки в системе.
|
||||
/// </summary>
|
||||
string Id { get; }
|
||||
|
||||
/// <summary> Заголовок, отображаемый пользователю в интерфейсе (на вкладке). </summary>
|
||||
/// <summary>
|
||||
/// Получает заголовок, отображаемый пользователю на вкладке.
|
||||
/// </summary>
|
||||
string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Сам визуальный элемент (например, Microsoft.UI.Xaml.UIElement).
|
||||
/// Lattice просто отображает этот объект в теле вкладки.
|
||||
/// <summary>
|
||||
/// Получает или задает визуальный элемент для отображения в теле вкладки.
|
||||
/// </summary>
|
||||
object View { get; set; }
|
||||
|
||||
/// <summary> Флаг, определяющий доступность кнопки закрытия для пользователя. </summary>
|
||||
/// <summary>
|
||||
/// Получает значение, указывающее, можно ли закрыть вкладку.
|
||||
/// </summary>
|
||||
bool CanClose { get; }
|
||||
|
||||
/// <summary> Вызывается системой при попытке закрытия контента. Возвращает true, если закрытие разрешено. </summary>
|
||||
/// <summary>
|
||||
/// Вызывается системой при попытке закрытия контента.
|
||||
/// Позволяет выполнить дополнительные проверки или сохранить состояние.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true, если закрытие разрешено; в противном случае false.
|
||||
/// </returns>
|
||||
bool OnClosing();
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,91 @@
|
||||
namespace Lattice.Core.Docking.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый интерфейс для любого элемента, который может быть частью дерева компоновки Lattice.
|
||||
/// Базовый интерфейс для любого элемента, являющегося частью дерева компоновки.
|
||||
/// Определяет общие свойства и методы для всех элементов док-системы.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Элементы док-системы образуют древовидную структуру, где каждый элемент может иметь
|
||||
/// родителя и дочерние элементы. Эта иерархия используется для организации пространства
|
||||
/// главного окна и плавающих окон в IDE-подобных приложениях.
|
||||
/// </remarks>
|
||||
public interface IDockElement
|
||||
{
|
||||
/// <summary> Уникальный идентификатор элемента. </summary>
|
||||
/// <summary>
|
||||
/// Получает уникальный идентификатор элемента.
|
||||
/// Используется для поиска элементов, сериализации состояния и отслеживания изменений.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Строковый идентификатор, гарантированно уникальный в пределах дерева компоновки.
|
||||
/// Обычно представляет собой GUID в строковом формате.
|
||||
/// </value>
|
||||
string Id { get; }
|
||||
|
||||
/// <summary> Родительский элемент в иерархии. Если null — элемент является корневым. </summary>
|
||||
/// <summary>
|
||||
/// Получает или задает родительский элемент в иерархии дерева компоновки.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Родительский элемент или null, если элемент является корневым.
|
||||
/// Это свойство управляется системой компоновки при добавлении или удалении элементов.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Изменение этого свойства вручную может привести к нарушению целостности дерева.
|
||||
/// Для манипуляции структурой дерева следует использовать методы <see cref="DockOperations"/>.
|
||||
/// </remarks>
|
||||
IDockElement? Parent { get; set; }
|
||||
|
||||
/// <summary> Желаемая ширина элемента в относительных или абсолютных единицах. </summary>
|
||||
/// <summary>
|
||||
/// Получает или задает желаемую ширину элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Ширина элемента в пикселях или относительных единицах.
|
||||
/// Может быть выражена как абсолютное значение (в пикселях) или как пропорция
|
||||
/// (например, 0.5 для 50% доступного пространства).
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Фактическая ширина элемента определяется родительским контейнером с учетом
|
||||
/// минимальных размеров и соотношений разделения.
|
||||
/// </remarks>
|
||||
double Width { get; set; }
|
||||
|
||||
/// <summary> Желаемая высота элемента в относительных или абсолютных единицах. </summary>
|
||||
/// <summary>
|
||||
/// Получает или задает желаемую высоту элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Высота элемента в пикселях или относительных единицах.
|
||||
/// Может быть выражена как абсолютное значение (в пикселях) или как пропорция.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Фактическая высота элемента определяется родительским контейнером с учетом
|
||||
/// минимальных размеров и соотношений разделения.
|
||||
/// </remarks>
|
||||
double Height { get; set; }
|
||||
|
||||
/// <summary> Минимально допустимая ширина, при которой элемент сохраняет функциональность. </summary>
|
||||
/// <summary>
|
||||
/// Получает минимально допустимую ширину элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Минимальная ширина элемента в пикселях, при которой элемент сохраняет
|
||||
/// базовую функциональность и читаемость содержимого.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Система компоновки не позволит уменьшить элемент ниже этого значения.
|
||||
/// Для групп разделения минимальная ширина вычисляется рекурсивно на основе
|
||||
/// минимальных размеров дочерних элементов.
|
||||
/// </remarks>
|
||||
double MinWidth { get; }
|
||||
|
||||
/// <summary> Минимально допустимая высота, при которой элемент сохраняет функциональность. </summary>
|
||||
/// <summary>
|
||||
/// Получает минимально допустимую высоту элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Минимальная высота элемента в пикселях, при которой элемент сохраняет
|
||||
/// базовую функциональность и читаемость содержимого.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Система компоновки не позволит уменьшить элемент ниже этого значения.
|
||||
/// Для групп разделения минимальная высота вычисляется рекурсивно на основе
|
||||
/// минимальных размеров дочерних элементов.
|
||||
/// </remarks>
|
||||
double MinHeight { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using Lattice.Core.DragDrop.Abstractions;
|
||||
|
||||
namespace Lattice.Core.Docking.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Расширяет интерфейс элемента док-системы для поддержки операций перетаскивания.
|
||||
/// </summary>
|
||||
public interface IDockElementDragSource : IDockElement, IDragSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает или устанавливает признак того, что элемент можно перетаскивать.
|
||||
/// </summary>
|
||||
bool CanDrag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает тип данных для перетаскивания этого элемента.
|
||||
/// </summary>
|
||||
string DragDataType { get; }
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using Lattice.Core.DragDrop.Abstractions;
|
||||
|
||||
namespace Lattice.Core.Docking.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Расширяет интерфейс элемента док-системы для возможности быть целью сброса.
|
||||
/// </summary>
|
||||
public interface IDockElementDropTarget : IDockElement, IDropTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает или устанавливает признак того, что элемент может принимать сброс.
|
||||
/// </summary>
|
||||
bool CanDrop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает типы данных, которые может принимать элемент.
|
||||
/// </summary>
|
||||
IEnumerable<string> AcceptableDropTypes { get; }
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using Lattice.Core.Docking.Models;
|
||||
using Lattice.Core.Geometry;
|
||||
|
||||
namespace Lattice.Core.Docking.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Предоставляет абстракцию для операции перетаскивания в док-системе.
|
||||
/// Эта абстракция позволяет отделить логику перетаскивания от конкретной UI-платформы.
|
||||
/// </summary>
|
||||
public interface IDragService
|
||||
{
|
||||
/// <summary>
|
||||
/// Начинает операцию перетаскивания указанного элемента.
|
||||
/// </summary>
|
||||
/// <param name="element">Элемент для перетаскивания.</param>
|
||||
/// <param name="visualFeedback">Визуальная обратная связь (зависит от платформы).</param>
|
||||
void StartDrag(IDockElement element, object? visualFeedback = null);
|
||||
|
||||
/// <summary>
|
||||
/// Обновляет позицию перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="x">Координата X.</param>
|
||||
/// <param name="y">Координата Y.</param>
|
||||
void UpdateDrag(double x, double y);
|
||||
|
||||
/// <summary>
|
||||
/// Завершает операцию перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="x">Координата X завершения.</param>
|
||||
/// <param name="y">Координата Y завершения.</param>
|
||||
void EndDrag(double x, double y);
|
||||
|
||||
/// <summary>
|
||||
/// Отменяет операцию перетаскивания.
|
||||
/// </summary>
|
||||
void CancelDrag();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Представляет область для сброса при операции перетаскивания.
|
||||
/// </summary>
|
||||
public class DropArea
|
||||
{
|
||||
/// <summary>
|
||||
/// Целевой элемент для сброса.
|
||||
/// </summary>
|
||||
public IDockElement Target { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Позиция сброса относительно цели.
|
||||
/// </summary>
|
||||
public DockPosition Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Границы области в экранных координатах.
|
||||
/// </summary>
|
||||
public Rect Bounds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Видимость области (для анимации).
|
||||
/// </summary>
|
||||
public double Visibility { get; set; } = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр области сброса.
|
||||
/// </summary>
|
||||
/// <param name="target">Целевой элемент.</param>
|
||||
/// <param name="position">Позиция сброса.</param>
|
||||
/// <param name="bounds">Границы области.</param>
|
||||
public DropArea(IDockElement target, DockPosition position, Rect bounds)
|
||||
{
|
||||
Target = target;
|
||||
Position = position;
|
||||
Bounds = bounds;
|
||||
}
|
||||
}
|
||||
@@ -4,20 +4,30 @@ 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>Новый корень дерева после оптимизации.</returns>
|
||||
/// <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;
|
||||
@@ -43,15 +53,36 @@ public static class DockOperations
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вставляет элемент в дерево, создавая новую группу разделения или объединяя контент.
|
||||
/// Вставляет элемент в дерево компоновки относительно целевого элемента.
|
||||
/// Создает новую группу разделения или объединяет контент в зависимости от позиции.
|
||||
/// </summary>
|
||||
public static IDockElement Insert(IDockElement target, IDockElement source, DockPosition pos, IDockElement root)
|
||||
/// <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)
|
||||
{
|
||||
|
||||
@@ -8,54 +8,87 @@ using System.Runtime.CompilerServices;
|
||||
namespace Lattice.Core.Docking.Engine;
|
||||
|
||||
/// <summary>
|
||||
/// Расширенный менеджер макета, поддерживающий автоскрываемые панели, группы документов
|
||||
/// и расширенные операции управления макетом.
|
||||
/// Центральный менеджер макета, управляющий всей структурой док-системы.
|
||||
/// Координирует дерево компоновки, плавающие окна, автоскрываемые панели
|
||||
/// и предоставляет API для манипуляции макетом.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Этот класс является центральным координатором всей док-системы, управляя деревом компоновки,
|
||||
/// плавающими окнами, автоскрываемыми панелями и предоставляя API для манипуляции макетом.
|
||||
/// Этот класс является основным координатором док-системы. Он управляет:
|
||||
/// <list type="bullet">
|
||||
/// <item>Деревом компоновки главного окна</item>
|
||||
/// <item>Коллекцией плавающих окон</item>
|
||||
/// <item>Коллекцией автоскрываемых панелей</item>
|
||||
/// <item>Реестром типов контента</item>
|
||||
/// </list>
|
||||
/// Все изменения в структуре макета должны выполняться через методы этого класса.
|
||||
/// </remarks>
|
||||
public class LayoutManager
|
||||
{
|
||||
private readonly ObservableCollection<AutoHidePanel> _autoHidePanels = new();
|
||||
private IDockElement? _root;
|
||||
|
||||
/// <summary>
|
||||
/// Корневой элемент главного окна IDE.
|
||||
/// Получает или задает корневой элемент дерева компоновки главного окна.
|
||||
/// </summary>
|
||||
public IDockElement? Root { get; internal set; }
|
||||
/// <value>
|
||||
/// Корневой элемент или null, если макет пуст.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// При изменении этого свойства генерируется событие <see cref="LayoutUpdated"/>.
|
||||
/// </remarks>
|
||||
public IDockElement? Root
|
||||
{
|
||||
get => _root;
|
||||
internal set
|
||||
{
|
||||
if (_root != value)
|
||||
{
|
||||
_root = value;
|
||||
LayoutUpdated?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Список активных плавающих окон.
|
||||
/// Получает список активных плавающих окон.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Коллекция объектов <see cref="DockWindow"/>, представляющих плавающие окна.
|
||||
/// </value>
|
||||
public List<DockWindow> FloatingWindows { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Коллекция автоскрываемых панелей.
|
||||
/// Получает коллекцию автоскрываемых панелей.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Доступная только для чтения коллекция объектов <see cref="AutoHidePanel"/>.
|
||||
/// </value>
|
||||
public ReadOnlyObservableCollection<AutoHidePanel> AutoHidePanels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Реестр типов контента (опционально).
|
||||
/// Получает или задает реестр типов контента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Реестр типов контента или null, если реестр не установлен.
|
||||
/// </value>
|
||||
public Services.ContentRegistry? ContentRegistry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Уведомляет UI, что структура дерева изменилась.
|
||||
/// Происходит при изменении структуры дерева компоновки.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Событие генерируется при любых изменениях в дереве компоновки,
|
||||
/// включая добавление, удаление или перемещение элементов.
|
||||
/// </remarks>
|
||||
public event Action? LayoutUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Уведомляет об изменении в коллекции автоскрываемых панелей.
|
||||
/// Происходит при изменении коллекции автоскрываемых панелей.
|
||||
/// </summary>
|
||||
public event EventHandler? AutoHidePanelsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Событие, возникающее при операции перетаскивания элемента.
|
||||
/// </summary>
|
||||
public event EventHandler<DragDropEventArgs>? DragDropOperation;
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр менеджера макета.
|
||||
/// Инициализирует новый экземпляр класса <see cref="LayoutManager"/>.
|
||||
/// </summary>
|
||||
public LayoutManager()
|
||||
{
|
||||
@@ -63,13 +96,20 @@ public class LayoutManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавляет автоскрываемую панель.
|
||||
/// Добавляет автоскрываемую панель с указанным содержимым к заданной стороне окна.
|
||||
/// </summary>
|
||||
/// <param name="content">Содержимое панели.</param>
|
||||
/// <param name="side">Сторона для прикрепления.</param>
|
||||
/// <returns>Созданная автоскрываемая панель.</returns>
|
||||
/// <param name="side">Сторона окна для прикрепления панели.</param>
|
||||
/// <returns>
|
||||
/// Созданная автоскрываемая панель.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="content"/> равен null.
|
||||
/// </exception>
|
||||
public AutoHidePanel AddAutoHidePanel(IDockContent content, DockSide side)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
|
||||
var panel = new AutoHidePanel(content, side);
|
||||
_autoHidePanels.Add(panel);
|
||||
AutoHidePanelsChanged?.Invoke(this, EventArgs.Empty);
|
||||
@@ -77,23 +117,36 @@ public class LayoutManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет автоскрываемую панель.
|
||||
/// Удаляет автоскрываемую панель из коллекции.
|
||||
/// </summary>
|
||||
/// <param name="panel">Панель для удаления.</param>
|
||||
public void RemoveAutoHidePanel(AutoHidePanel panel)
|
||||
/// <returns>
|
||||
/// true, если панель была успешно удалена; в противном случае false.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="panel"/> равен null.
|
||||
/// </exception>
|
||||
public bool RemoveAutoHidePanel(AutoHidePanel panel)
|
||||
{
|
||||
if (panel == null) throw new ArgumentNullException(nameof(panel));
|
||||
|
||||
if (_autoHidePanels.Remove(panel))
|
||||
{
|
||||
AutoHidePanelsChanged?.Invoke(this, EventArgs.Empty);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает документ из зарегистрированного типа контента.
|
||||
/// Создает документ указанного типа контента с заданным идентификатором.
|
||||
/// </summary>
|
||||
/// <param name="contentTypeId">Идентификатор типа контента.</param>
|
||||
/// <param name="id">Уникальный идентификатор документа.</param>
|
||||
/// <returns>Созданный контент или null, если ContentRegistry не установлен.</returns>
|
||||
/// <returns>
|
||||
/// Созданный контент или null, если ContentRegistry не установлен
|
||||
/// или тип контента не зарегистрирован.
|
||||
/// </returns>
|
||||
public IDockContent? CreateDocument(string contentTypeId, string id)
|
||||
{
|
||||
if (ContentRegistry == null || !ContentRegistry.IsRegistered(contentTypeId))
|
||||
@@ -103,16 +156,31 @@ public class LayoutManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Основной метод перемещения элементов в макете.
|
||||
/// Выполняет перемещение элемента в макете относительно целевого элемента.
|
||||
/// </summary>
|
||||
/// <param name="source">Что перетаскиваем.</param>
|
||||
/// <param name="target">Куда приземляем.</param>
|
||||
/// <param name="position">Позиция относительно цели.</param>
|
||||
/// <param name="source">Перемещаемый элемент.</param>
|
||||
/// <param name="target">Целевой элемент, относительно которого выполняется перемещение.</param>
|
||||
/// <param name="position">Позиция перемещения относительно цели.</param>
|
||||
/// <param name="asDocument">
|
||||
/// Если true, контент будет добавлен как документ в центральную область.
|
||||
/// В текущей реализации этот параметр не используется.
|
||||
/// </param>
|
||||
public void Move(IDockElement source, IDockElement? target, DockPosition position, bool asDocument = false)
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="source"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Метод выполняет следующие шаги:
|
||||
/// <list type="number">
|
||||
/// <item>Удаляет источник из текущего местоположения</item>
|
||||
/// <item>Вставляет источник в новое местоположение относительно цели</item>
|
||||
/// <item>Обновляет структуру дерева компоновки</item>
|
||||
/// </list>
|
||||
/// Если <paramref name="target"/> равен null, элемент помещается в новое плавающее окно.
|
||||
/// </remarks>
|
||||
public void Move(IDockElement source, IDockElement? target,
|
||||
DockPosition position, bool asDocument = false)
|
||||
{
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
if (source == target) return;
|
||||
|
||||
// 1. Удаляем источник из текущего местоположения
|
||||
@@ -145,13 +213,14 @@ public class LayoutManager
|
||||
// 2. Вставляем в цель
|
||||
if (target == null)
|
||||
{
|
||||
FloatingWindows.Add(new DockWindow { Root = source as IDockElement });
|
||||
// Создаем новое плавающее окно
|
||||
FloatingWindows.Add(new DockWindow { Root = source });
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDescendantOf(target, Root))
|
||||
if (Root != null && IsDescendantOf(target, Root))
|
||||
{
|
||||
Root = DockOperations.Insert(target, source, position, Root!);
|
||||
Root = DockOperations.Insert(target, source, position, Root);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -162,6 +231,13 @@ public class LayoutManager
|
||||
LayoutUpdated?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет элемент из всех плавающих окон.
|
||||
/// </summary>
|
||||
/// <param name="element">Элемент для удаления.</param>
|
||||
/// <returns>
|
||||
/// true, если элемент был найден и удален; в противном случае false.
|
||||
/// </returns>
|
||||
private bool RemoveFromFloatingWindows(IDockElement element)
|
||||
{
|
||||
foreach (var win in FloatingWindows.ToArray())
|
||||
@@ -177,7 +253,14 @@ public class LayoutManager
|
||||
return false;
|
||||
}
|
||||
|
||||
private void InsertIntoFloatingWindow(IDockElement target, IDockElement source, DockPosition position)
|
||||
/// <summary>
|
||||
/// Вставляет элемент в плавающее окно, содержащее целевой элемент.
|
||||
/// </summary>
|
||||
/// <param name="target">Целевой элемент в плавающем окне.</param>
|
||||
/// <param name="source">Вставляемый элемент.</param>
|
||||
/// <param name="position">Позиция вставки.</param>
|
||||
private void InsertIntoFloatingWindow(IDockElement target, IDockElement source,
|
||||
DockPosition position)
|
||||
{
|
||||
foreach (var win in FloatingWindows)
|
||||
{
|
||||
@@ -189,6 +272,14 @@ public class LayoutManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Определяет, является ли элемент потомком указанного предка.
|
||||
/// </summary>
|
||||
/// <param name="element">Проверяемый элемент.</param>
|
||||
/// <param name="ancestor">Предполагаемый предок.</param>
|
||||
/// <returns>
|
||||
/// true, если элемент является потомком предка; в противном случае false.
|
||||
/// </returns>
|
||||
private bool IsDescendantOf(IDockElement element, IDockElement ancestor)
|
||||
{
|
||||
if (element == ancestor) return true;
|
||||
@@ -197,9 +288,17 @@ public class LayoutManager
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary> Поиск элемента по ID во всех окнах. </summary>
|
||||
/// <summary>
|
||||
/// Находит элемент по его идентификатору во всех окнах (главном и плавающих).
|
||||
/// </summary>
|
||||
/// <param name="id">Идентификатор элемента для поиска.</param>
|
||||
/// <returns>
|
||||
/// Найденный элемент или null, если элемент с таким идентификатором не найден.
|
||||
/// </returns>
|
||||
public IDockElement? FindById(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return null;
|
||||
|
||||
var found = FindRecursive(Root, id);
|
||||
if (found != null) return found;
|
||||
|
||||
@@ -211,16 +310,37 @@ public class LayoutManager
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Рекурсивно ищет элемент по идентификатору в поддереве.
|
||||
/// </summary>
|
||||
/// <param name="node">Корневой узел поддерева для поиска.</param>
|
||||
/// <param name="id">Идентификатор элемента для поиска.</param>
|
||||
/// <returns>
|
||||
/// Найденный элемент или null, если элемент не найден.
|
||||
/// </returns>
|
||||
private IDockElement? FindRecursive(IDockElement? node, string id)
|
||||
{
|
||||
if (node == null || node.Id == id) return node;
|
||||
if (node is DockGroup g) return FindRecursive(g.First, id) ?? FindRecursive(g.Second, id);
|
||||
if (node == null) return null;
|
||||
if (node.Id == id) return node;
|
||||
|
||||
if (node is DockGroup g)
|
||||
return FindRecursive(g.First, id) ?? FindRecursive(g.Second, id);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сбрасывает макет к состоянию по умолчанию.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Метод выполняет следующие действия:
|
||||
/// <list type="bullet">
|
||||
/// <item>Очищает корневой элемент</item>
|
||||
/// <item>Закрывает все плавающие окна</item>
|
||||
/// <item>Удаляет все автоскрываемые панели</item>
|
||||
/// <item>Генерирует соответствующие события</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public void Reset()
|
||||
{
|
||||
Root = null;
|
||||
@@ -231,86 +351,12 @@ public class LayoutManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает операцию перетаскивания между элементами.
|
||||
/// </summary>
|
||||
/// <param name="source">Источник перетаскивания.</param>
|
||||
/// <param name="target">Цель сброса.</param>
|
||||
/// <param name="position">Позиция сброса относительно цели.</param>
|
||||
/// <param name="data">Данные перетаскивания.</param>
|
||||
/// <returns>true, если операция успешно выполнена; иначе false.</returns>
|
||||
public bool HandleDragDrop(IDockElement source, IDockElement target,
|
||||
DockPosition position, object data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (source == target)
|
||||
return false;
|
||||
|
||||
// Определяем тип операции на основе данных
|
||||
if (data is ContentDragData contentData)
|
||||
{
|
||||
return HandleContentDragDrop(contentData, target, position);
|
||||
}
|
||||
else if (data is DockElementDragData elementData)
|
||||
{
|
||||
return HandleElementDragDrop(elementData, target, position);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DragDropOperation?.Invoke(this, new DragDropEventArgs(
|
||||
source, target, position, false, ex.Message));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandleContentDragDrop(ContentDragData data, IDockElement target, DockPosition position)
|
||||
{
|
||||
// Находим исходный контейнер с контентом
|
||||
var sourceContainer = FindElementById(data.ElementId) as IDockContainer;
|
||||
if (sourceContainer == null)
|
||||
return false;
|
||||
|
||||
// Находим контент
|
||||
var content = sourceContainer.Children.FirstOrDefault(c => c.Id == data.ContentId);
|
||||
if (content == null)
|
||||
return false;
|
||||
|
||||
if (target is IDockContainer targetContainer && position == DockPosition.Center)
|
||||
{
|
||||
// Объединение вкладок
|
||||
sourceContainer.RemoveContent(content);
|
||||
targetContainer.AddContent(content);
|
||||
|
||||
DragDropOperation?.Invoke(this, new DragDropEventArgs(
|
||||
sourceContainer as IDockElement ?? sourceContainer as IDockElement,
|
||||
target, position, true, "Content merged"));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool HandleElementDragDrop(DockElementDragData data, IDockElement target, DockPosition position)
|
||||
{
|
||||
// Находим перетаскиваемый элемент
|
||||
var sourceElement = FindElementById(data.ElementId);
|
||||
if (sourceElement == null)
|
||||
return false;
|
||||
|
||||
// Выполняем перемещение
|
||||
Move(sourceElement, target, position);
|
||||
|
||||
DragDropOperation?.Invoke(this, new DragDropEventArgs(
|
||||
sourceElement, target, position, true, "Element moved"));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Находит элемент по идентификатору.
|
||||
/// Находит элемент по идентификатору в дереве компоновки.
|
||||
/// </summary>
|
||||
/// <param name="id">Идентификатор элемента для поиска.</param>
|
||||
/// <returns>
|
||||
/// Найденный элемент или null, если элемент с таким идентификатором не найден.
|
||||
/// </returns>
|
||||
public IDockElement? FindElementById(string id)
|
||||
{
|
||||
return FindElementByIdRecursive(Root, id) ??
|
||||
@@ -318,6 +364,14 @@ public class LayoutManager
|
||||
.FirstOrDefault(result => result != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Рекурсивно ищет элемент по идентификатору в поддереве.
|
||||
/// </summary>
|
||||
/// <param name="element">Корневой элемент поддерева для поиска.</param>
|
||||
/// <param name="id">Идентификатор элемента для поиска.</param>
|
||||
/// <returns>
|
||||
/// Найденный элемент или null, если элемент не найден.
|
||||
/// </returns>
|
||||
private IDockElement? FindElementByIdRecursive(IDockElement? element, string id)
|
||||
{
|
||||
if (element == null) return null;
|
||||
@@ -331,39 +385,4 @@ public class LayoutManager
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Аргументы события операции перетаскивания.
|
||||
/// </summary>
|
||||
public class DragDropEventArgs : EventArgs
|
||||
{
|
||||
/// <summary> Источник перетаскивания. </summary>
|
||||
public IDockElement Source { get; }
|
||||
|
||||
/// <summary> Цель сброса. </summary>
|
||||
public IDockElement Target { get; }
|
||||
|
||||
/// <summary> Позиция сброса. </summary>
|
||||
public DockPosition Position { get; }
|
||||
|
||||
/// <summary> Показывает, была ли операция успешной. </summary>
|
||||
public bool Success { get; }
|
||||
|
||||
/// <summary> Сообщение о результате операции. </summary>
|
||||
public string Message { get; }
|
||||
|
||||
/// <summary> Время выполнения операции. </summary>
|
||||
public DateTime Timestamp { get; }
|
||||
|
||||
public DragDropEventArgs(IDockElement source, IDockElement target,
|
||||
DockPosition position, bool success, string message)
|
||||
{
|
||||
Source = source;
|
||||
Target = target;
|
||||
Position = position;
|
||||
Success = success;
|
||||
Message = message;
|
||||
Timestamp = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,5 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Lattice.Core.Geometry\Lattice.Core.Geometry.csproj" />
|
||||
<ProjectReference Include="..\Lattice.Core.DragDrop\Lattice.Core.DragDrop.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -5,44 +5,81 @@ namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Представляет автоскрываемую панель, которая может быть прикреплена к одной из сторон окна.
|
||||
/// Автоскрываемые панели скрываются, оставляя только заголовок, и появляются при наведении курсора.
|
||||
/// Автоскрываемые панели скрываются, оставляя видимой только полоску-заголовок,
|
||||
/// и разворачиваются при наведении курсора или клике.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Автоскрываемые панели являются ключевым элементом интерфейса современных IDE,
|
||||
/// Автоскрываемые панели являются важным элементом современных IDE-подобных приложений,
|
||||
/// позволяя экономить пространство экрана при сохранении быстрого доступа к инструментам.
|
||||
/// </remarks>
|
||||
public class AutoHidePanel : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Происходит при изменении значения свойства.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
protected void OnPropertyChanged([CallerMemberName] string? name = null) =>
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
|
||||
private bool _isVisible = false;
|
||||
private double _slideOffset = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Уникальный идентификатор автоскрываемой панели.
|
||||
/// Получает уникальный идентификатор автоскрываемой панели.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Строковый идентификатор, сгенерированный с помощью GUID.
|
||||
/// </value>
|
||||
public string Id { get; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Содержимое панели.
|
||||
/// Получает или задает содержимое панели.
|
||||
/// </summary>
|
||||
public Abstractions.IDockContent Content { get; set; }
|
||||
/// <value>
|
||||
/// Объект, реализующий <see cref="Abstractions.IDockContent"/>.
|
||||
/// </value>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается при попытке установить значение null.
|
||||
/// </exception>
|
||||
public Abstractions.IDockContent Content
|
||||
{
|
||||
get => _content;
|
||||
set
|
||||
{
|
||||
if (_content != value)
|
||||
{
|
||||
_content = value ?? throw new ArgumentNullException(nameof(value));
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
}
|
||||
private Abstractions.IDockContent _content;
|
||||
|
||||
/// <summary>
|
||||
/// Сторона окна, к которой прикреплена панель.
|
||||
/// Получает или задает сторону окна, к которой прикреплена панель.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Значение перечисления <see cref="DockSide"/>, указывающее сторону прикрепления.
|
||||
/// </value>
|
||||
public DockSide Side { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ширина панели (для левой/правой сторон) или высота (для верхней/нижней сторон).
|
||||
/// Получает или задает ширину панели (для левой/правой сторон)
|
||||
/// или высоту (для верхней/нижней сторон).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Размер панели в пикселях. Значение по умолчанию: 300.
|
||||
/// </value>
|
||||
public double Size { get; set; } = 300;
|
||||
|
||||
/// <summary>
|
||||
/// Признак видимости панели.
|
||||
/// Получает или задает признак видимости панели.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если панель развернута и видима; в противном случае false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// При изменении этого свойства генерируется событие <see cref="PropertyChanged"/>.
|
||||
/// </remarks>
|
||||
public bool IsVisible
|
||||
{
|
||||
get => _isVisible;
|
||||
@@ -57,8 +94,14 @@ public class AutoHidePanel : INotifyPropertyChanged
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Смещение для анимации выезда/заезда панели (0-1).
|
||||
/// Получает или задает смещение для анимации выезда/заезда панели.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Значение от 0.0 до 1.0, где 0.0 - полностью скрыта, 1.0 - полностью развернута.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Используется для плавной анимации отображения/скрытия панели.
|
||||
/// </remarks>
|
||||
public double SlideOffset
|
||||
{
|
||||
get => _slideOffset;
|
||||
@@ -73,15 +116,22 @@ public class AutoHidePanel : INotifyPropertyChanged
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Заголовок панели (обычно берется из содержимого).
|
||||
/// Получает заголовок панели.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Заголовок, взятый из содержимого панели.
|
||||
/// Если содержимое не установлено, возвращает "Auto-hide Panel".
|
||||
/// </value>
|
||||
public string Title => Content?.Title ?? "Auto-hide Panel";
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр автоскрываемой панели.
|
||||
/// Инициализирует новый экземпляр класса <see cref="AutoHidePanel"/>.
|
||||
/// </summary>
|
||||
/// <param name="content">Содержимое панели.</param>
|
||||
/// <param name="side">Сторона окна для прикрепления.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="content"/> равен null.
|
||||
/// </exception>
|
||||
public AutoHidePanel(Abstractions.IDockContent content, DockSide side)
|
||||
{
|
||||
Content = content ?? throw new ArgumentNullException(nameof(content));
|
||||
@@ -91,6 +141,9 @@ public class AutoHidePanel : INotifyPropertyChanged
|
||||
/// <summary>
|
||||
/// Переключает видимость панели.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Если панель была видимой, становится скрытой, и наоборот.
|
||||
/// </remarks>
|
||||
public void Toggle()
|
||||
{
|
||||
IsVisible = !IsVisible;
|
||||
@@ -111,4 +164,15 @@ public class AutoHidePanel : INotifyPropertyChanged
|
||||
{
|
||||
IsVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывает событие <see cref="PropertyChanged"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// Имя изменившегося свойства. Если не указано, определяется автоматически.
|
||||
/// </param>
|
||||
protected void OnPropertyChanged([CallerMemberName] string? name = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
using Lattice.Core.Docking.Abstractions;
|
||||
using Lattice.Core.DragDrop.Abstractions;
|
||||
using Lattice.Core.DragDrop.Enums;
|
||||
using Lattice.Core.DragDrop.Models;
|
||||
using Lattice.Core.Geometry;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -14,32 +10,25 @@ namespace Lattice.Core.Docking.Models;
|
||||
/// элементом для создания сложных макетов с разделителями.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <see cref="DockGroup"/> реализует как <see cref="IDragSource"/> (для
|
||||
/// возможности перетаскивания всей группы), так и <see cref="IDropTarget"/>
|
||||
/// (для возможности сброса на группу), что делает его полностью интегрированным
|
||||
/// в систему перетаскивания док-системы.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Каждая группа содержит два дочерних элемента (<see cref="First"/> и
|
||||
/// <see cref="Second"/>), которые могут быть либо другими группами (для
|
||||
/// создания вложенной структуры), либо листами (<see cref="DockLeaf"/>)
|
||||
/// с контентом. Направление разделения определяется свойством
|
||||
/// <see cref="Orientation"/>.
|
||||
/// </para>
|
||||
/// Каждая группа содержит два дочерних элемента (<see cref="First"/> и <see cref="Second"/>),
|
||||
/// которые могут быть либо другими группами (для создания вложенной структуры),
|
||||
/// либо листами (<see cref="DockLeaf"/>) с контентом.
|
||||
/// Направление разделения определяется свойством <see cref="Orientation"/>.
|
||||
/// </remarks>
|
||||
public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyPropertyChanged
|
||||
public class DockGroup : IDockElement, INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Событие, возникающее при изменении значения свойства.
|
||||
/// Происходит при изменении значения свойства.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private double _splitRatio = 0.5;
|
||||
private string _id;
|
||||
private IDockElement _first;
|
||||
private IDockElement _second;
|
||||
|
||||
/// <summary>
|
||||
/// Получает уникальный идентификатор группы.
|
||||
/// Получает или задает уникальный идентификатор группы.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Строковый идентификатор, уникальный в пределах дерева компоновки.
|
||||
@@ -86,7 +75,19 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
|
||||
/// При установке нового значения автоматически обновляется свойство
|
||||
/// <see cref="Parent"/> у дочернего элемента.
|
||||
/// </remarks>
|
||||
public IDockElement First { get; set; }
|
||||
public IDockElement First
|
||||
{
|
||||
get => _first;
|
||||
set
|
||||
{
|
||||
if (_first != value)
|
||||
{
|
||||
_first = value ?? throw new ArgumentNullException(nameof(value));
|
||||
_first.Parent = this;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает второй дочерний элемент (правую или нижнюю область).
|
||||
@@ -101,7 +102,19 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
|
||||
/// При установке нового значения автоматически обновляется свойство
|
||||
/// <see cref="Parent"/> у дочернего элемента.
|
||||
/// </remarks>
|
||||
public IDockElement Second { get; set; }
|
||||
public IDockElement Second
|
||||
{
|
||||
get => _second;
|
||||
set
|
||||
{
|
||||
if (_second != value)
|
||||
{
|
||||
_second = value ?? throw new ArgumentNullException(nameof(value));
|
||||
_second.Parent = this;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает направление разделения данной группы.
|
||||
@@ -111,12 +124,10 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
|
||||
/// как разделена область: горизонтально или вертикально.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <see cref="SplitDirection.Horizontal"/> создает левую и правую области.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="SplitDirection.Vertical"/> создает верхнюю и нижнюю области.
|
||||
/// </para>
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="SplitDirection.Horizontal"/> создает левую и правую области</item>
|
||||
/// <item><see cref="SplitDirection.Vertical"/> создает верхнюю и нижнюю области</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public SplitDirection Orientation { get; set; }
|
||||
|
||||
@@ -218,7 +229,8 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
|
||||
/// у дочерних элементов на текущую группу и генерирует уникальный идентификатор,
|
||||
/// если он не был предоставлен.
|
||||
/// </remarks>
|
||||
public DockGroup(IDockElement first, IDockElement second, SplitDirection orientation, string? id = 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));
|
||||
@@ -239,206 +251,4 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
}
|
||||
|
||||
#region Реализация IDragSource
|
||||
|
||||
/// <summary>
|
||||
/// Определяет, может ли группа начать операцию перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">
|
||||
/// При успешном возврате содержит информацию о перетаскивании;
|
||||
/// в противном случае — null.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true, если группа может начать перетаскивание; в противном случае — false.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Группа может быть перетащена только если она не является корневым
|
||||
/// элементом дерева (имеет родителя).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// При успешной проверке метод заполняет <paramref name="dragInfo"/>
|
||||
/// данными типа <see cref="DockElementDragData"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool CanStartDrag(out DragInfo? dragInfo)
|
||||
{
|
||||
dragInfo = null;
|
||||
|
||||
// DockGroup можно перетаскивать только если он не является корневым элементом
|
||||
if (Parent == null)
|
||||
return false;
|
||||
|
||||
// Создаем данные для перетаскивания
|
||||
var data = new DockElementDragData
|
||||
{
|
||||
ElementId = Id,
|
||||
ElementType = GetType().Name,
|
||||
IsGroup = true
|
||||
};
|
||||
|
||||
dragInfo = new DragInfo(data, DragDropEffects.Move, Point.Zero, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Начинает операцию перетаскивания для группы.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">
|
||||
/// Информация о перетаскивании, полученная из <see cref="CanStartDrag"/>.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Всегда возвращает true, так как группа не требует специальной подготовки
|
||||
/// для начала перетаскивания.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Для <see cref="DockGroup"/> этот метод не выполняет дополнительных действий,
|
||||
/// так как все необходимые данные уже содержатся в <paramref name="dragInfo"/>.
|
||||
/// </remarks>
|
||||
public bool StartDrag(DragInfo dragInfo)
|
||||
{
|
||||
// DockGroup не требует дополнительной подготовки для перетаскивания
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при завершении операции перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">
|
||||
/// Исходная информация о перетаскивании.
|
||||
/// </param>
|
||||
/// <param name="effects">
|
||||
/// Эффекты, которые были применены при сбросе.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод вызывается после того, как операция перетаскивания была
|
||||
/// завершена (успешно или неуспешно).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для <see cref="DockGroup"/> этот метод не выполняет действий, так как
|
||||
/// все изменения в структуре дерева уже обработаны <see cref="LayoutManager"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
|
||||
{
|
||||
// Если группа была перемещена, ничего не делаем - LayoutManager уже обработал изменение
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при отмене операции перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">
|
||||
/// Исходная информация о перетаскивании.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Для <see cref="DockGroup"/> отмена перетаскивания не требует специальных
|
||||
/// действий, так как структура дерева не была изменена.
|
||||
/// </remarks>
|
||||
public void DragCancelled(DragInfo dragInfo)
|
||||
{
|
||||
// Отмена перетаскивания не требует действий
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Реализация IDropTarget
|
||||
|
||||
/// <summary>
|
||||
/// Определяет, может ли группа принять сбрасываемые данные.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">
|
||||
/// Информация о потенциальном сбросе.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true, если группа может принять данные; в противном случае — false.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Группа может принимать только данные типа <see cref="DockElementDragData"/>
|
||||
/// для элементов док-системы (<see cref="DockGroup"/> или <see cref="DockLeaf"/>).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Группа не может принять сброс самой себя (проверяется по идентификатору).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool CanAcceptDrop(DropInfo dropInfo)
|
||||
{
|
||||
if (dropInfo.Data is not DockElementDragData dragData)
|
||||
return false;
|
||||
|
||||
// Нельзя сбросить элемент на самого себя
|
||||
if (dragData.ElementId == Id)
|
||||
return false;
|
||||
|
||||
// Можно принимать только элементы док-системы
|
||||
return dragData.ElementType == nameof(DockGroup) || dragData.ElementType == nameof(DockLeaf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается, когда перетаскиваемый объект находится над группой.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">
|
||||
/// Информация о текущем положении перетаскивания.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод вызывается постоянно, пока пользователь перемещает объект
|
||||
/// над целью. Для группы он устанавливает предлагаемые эффекты в
|
||||
/// <see cref="DropInfo.SuggestedEffects"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Если группа может принять сброс, предлагается эффект перемещения;
|
||||
/// в противном случае эффекты не предлагаются.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void DragOver(DropInfo dropInfo)
|
||||
{
|
||||
if (CanAcceptDrop(dropInfo))
|
||||
{
|
||||
dropInfo.SuggestedEffects = DragDropEffects.Move;
|
||||
}
|
||||
else
|
||||
{
|
||||
dropInfo.SuggestedEffects = DragDropEffects.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается, когда пользователь сбрасывает данные на группу.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">
|
||||
/// Информация о сбросе.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Для <see cref="DockGroup"/> обработка сброса делегируется
|
||||
/// <see cref="LayoutManager"/>, поэтому метод просто помечает операцию
|
||||
/// как обработанную.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Фактическое изменение структуры дерева выполняется менеджером макета
|
||||
/// на основе данных из <paramref name="dropInfo"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Drop(DropInfo dropInfo)
|
||||
{
|
||||
// Обработка сброса делегируется LayoutManager
|
||||
dropInfo.MarkAsHandled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается, когда перетаскиваемый объект покидает область группы.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Для группы этот метод не выполняет действий, так как очистка визуальной
|
||||
/// обратной связи выполняется в UI-слое.
|
||||
/// </remarks>
|
||||
public void DragLeave()
|
||||
{
|
||||
// Очистка визуальной обратной связи (будет выполнена в UI слое)
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
using Lattice.Core.Docking.Abstractions;
|
||||
using Lattice.Core.DragDrop.Abstractions;
|
||||
using Lattice.Core.DragDrop.Enums;
|
||||
using Lattice.Core.DragDrop.Models;
|
||||
using Lattice.Core.Geometry;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -15,34 +11,24 @@ namespace Lattice.Core.Docking.Models;
|
||||
/// отображаемого пользователю содержимого.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <see cref="DockLeaf"/> реализует интерфейсы <see cref="IDockContainer"/>,
|
||||
/// <see cref="IDragSource"/> и <see cref="IDropTarget"/>, что позволяет ему:
|
||||
/// </para>
|
||||
/// <list type="bullet">
|
||||
/// <item>Управлять коллекцией вкладок</item>
|
||||
/// <item>Быть источником перетаскивания (как всего листа, так и отдельных вкладок)</item>
|
||||
/// <item>Принимать сброс других элементов или вкладок</item>
|
||||
/// </list>
|
||||
/// <para>
|
||||
/// Лист является основным элементом, с которым взаимодействует пользователь
|
||||
/// при работе с документами или инструментальными панелями в IDE-подобных
|
||||
/// приложениях.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDropTarget
|
||||
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>
|
||||
/// Строковый идентификатор, уникальный в пределах дерева компоновки.
|
||||
@@ -88,13 +74,9 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
|
||||
/// Активная вкладка или null, если в контейнере нет вкладок.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// При установке нового значения проверяется, что вкладка действительно
|
||||
/// содержится в коллекции <see cref="Children"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Изменение этого свойства вызывает событие <see cref="PropertyChanged"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public IDockContent? ActiveContent
|
||||
{
|
||||
@@ -152,7 +134,18 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
|
||||
/// <remarks>
|
||||
/// Поддерживаются все четыре стороны: верх, низ, лево, право.
|
||||
/// </remarks>
|
||||
public TabPlacement TabPlacement { get; set; } = TabPlacement.Bottom;
|
||||
public TabPlacement TabPlacement
|
||||
{
|
||||
get => _tabPlacement;
|
||||
set
|
||||
{
|
||||
if (_tabPlacement != value)
|
||||
{
|
||||
_tabPlacement = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="DockLeaf"/>.
|
||||
@@ -187,17 +180,15 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
|
||||
/// Контент для добавления.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Если контент уже содержится в коллекции, он не добавляется повторно,
|
||||
/// но становится активным.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Этот метод обновляет свойство <see cref="ActiveContent"/> и вызывает
|
||||
/// соответствующее событие изменения свойства.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void AddContent(IDockContent content)
|
||||
{
|
||||
if (content == null) return;
|
||||
|
||||
if (!_items.Contains(content))
|
||||
{
|
||||
_items.Add(content);
|
||||
@@ -212,18 +203,16 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
|
||||
/// Контент для удаления.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Если удаляемый контент является активным, автоматически выбирается
|
||||
/// новая активная вкладка (следующая в списке или предыдущая, если удалена
|
||||
/// последняя).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Если после удаления контейнер становится пустым, он может быть удален
|
||||
/// из дерева макета системой компоновки.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void RemoveContent(IDockContent content)
|
||||
{
|
||||
if (content == null) return;
|
||||
|
||||
int index = _items.IndexOf(content);
|
||||
if (index == -1) return;
|
||||
|
||||
@@ -237,344 +226,4 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
|
||||
ActiveContent = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Реализация IDragSource
|
||||
|
||||
/// <summary>
|
||||
/// Определяет, может ли лист начать операцию перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">
|
||||
/// При успешном возврате содержит информацию о перетаскивании;
|
||||
/// в противном случае — null.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true, если лист может начать перетаскивание; в противном случае — false.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Лист может быть перетащен, если:
|
||||
/// </para>
|
||||
/// <list type="bullet">
|
||||
/// <item>Он имеет родителя (не является корневым)</item>
|
||||
/// <item>Или имеет хотя бы одну вкладку (не пустой)</item>
|
||||
/// </list>
|
||||
/// <para>
|
||||
/// В зависимости от наличия активного контента создаются разные данные:
|
||||
/// </para>
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// Если есть активный контент - создается <see cref="ContentDragData"/>
|
||||
/// для перетаскивания конкретной вкладки
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Если нет активного контента - создается <see cref="DockElementDragData"/>
|
||||
/// для перетаскивания всего листа
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public bool CanStartDrag(out DragInfo? dragInfo)
|
||||
{
|
||||
dragInfo = null;
|
||||
|
||||
// DockLeaf можно перетаскивать
|
||||
if (Parent == null && Children.Count == 0)
|
||||
return false; // Не перетаскиваем пустые корневые листья
|
||||
|
||||
object data;
|
||||
|
||||
// Если есть активный контент, перетаскиваем контент, иначе перетаскиваем весь лист
|
||||
if (ActiveContent != null)
|
||||
{
|
||||
data = new ContentDragData
|
||||
{
|
||||
ElementId = Id,
|
||||
ContentId = ActiveContent.Id,
|
||||
ContentTitle = ActiveContent.Title,
|
||||
ContentType = ActiveContent.GetType().Name
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
data = new DockElementDragData
|
||||
{
|
||||
ElementId = Id,
|
||||
ElementType = GetType().Name,
|
||||
IsGroup = false,
|
||||
Width = Width,
|
||||
Height = Height
|
||||
};
|
||||
}
|
||||
|
||||
dragInfo = new DragInfo(data, DragDropEffects.Move | DragDropEffects.Copy, Point.Zero, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Начинает операцию перетаскивания для листа.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">
|
||||
/// Информация о перетаскивании.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Всегда возвращает true.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Для <see cref="DockLeaf"/> этот метод не выполняет дополнительных действий.
|
||||
/// </remarks>
|
||||
public bool StartDrag(DragInfo dragInfo)
|
||||
{
|
||||
// DockLeaf не требует дополнительной подготовки
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при завершении операции перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">
|
||||
/// Исходная информация о перетаскивании.
|
||||
/// </param>
|
||||
/// <param name="effects">
|
||||
/// Эффекты, которые были применены при сбросе.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Для <see cref="DockLeaf"/> этот метод не выполняет действий.
|
||||
/// </remarks>
|
||||
public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
|
||||
{
|
||||
// Если лист был перемещен или скопирован, LayoutManager уже обработал это
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при отмене операции перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">
|
||||
/// Исходная информация о перетаскивании.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Для <see cref="DockLeaf"/> отмена перетаскивания не требует действий.
|
||||
/// </remarks>
|
||||
public void DragCancelled(DragInfo dragInfo)
|
||||
{
|
||||
// Отмена не требует действий
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Реализация IDropTarget
|
||||
|
||||
/// <summary>
|
||||
/// Определяет, может ли лист принять сбрасываемые данные.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">
|
||||
/// Информация о потенциальном сбросе.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true, если лист может принять данные; в противном случае — false.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Лист может принимать:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <see cref="DockElementDragData"/> для других листов и групп
|
||||
/// (для объединения или разделения)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <see cref="ContentDragData"/> для вкладок (для объединения вкладок)
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public bool CanAcceptDrop(DropInfo dropInfo)
|
||||
{
|
||||
if (dropInfo.Data is DockElementDragData elementData)
|
||||
{
|
||||
// Можно принимать другие листы и группы
|
||||
return elementData.ElementType == nameof(DockLeaf) ||
|
||||
elementData.ElementType == nameof(DockGroup);
|
||||
}
|
||||
else if (dropInfo.Data is ContentDragData contentData)
|
||||
{
|
||||
// Можно принимать контент для объединения вкладок
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается, когда перетаскиваемый объект находится над листом.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">
|
||||
/// Информация о текущем положении перетаскивания.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// В зависимости от типа данных устанавливаются разные предлагаемые эффекты:
|
||||
/// </para>
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// Для <see cref="ContentDragData"/> - эффект копирования (объединение вкладок)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Для <see cref="DockElementDragData"/> - эффект перемещения
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public void DragOver(DropInfo dropInfo)
|
||||
{
|
||||
if (CanAcceptDrop(dropInfo))
|
||||
{
|
||||
if (dropInfo.Data is ContentDragData)
|
||||
{
|
||||
// Для контента предлагаем копирование (объединение вкладок)
|
||||
dropInfo.SuggestedEffects = DragDropEffects.Copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Для элементов предлагаем перемещение
|
||||
dropInfo.SuggestedEffects = DragDropEffects.Move;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dropInfo.SuggestedEffects = DragDropEffects.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается, когда пользователь сбрасывает данные на лист.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">
|
||||
/// Информация о сбросе.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Обработка сброса делегируется <see cref="LayoutManager"/>.
|
||||
/// </remarks>
|
||||
public void Drop(DropInfo dropInfo)
|
||||
{
|
||||
// Обработка делегируется LayoutManager
|
||||
dropInfo.MarkAsHandled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается, когда перетаскиваемый объект покидает область листа.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Очистка визуальной обратной связи выполняется в UI-слое.
|
||||
/// </remarks>
|
||||
public void DragLeave()
|
||||
{
|
||||
// Очистка визуальной обратной связи
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Представляет данные для перетаскивания элементов док-системы (групп или листов).
|
||||
/// Используется при перетаскивании целых структурных элементов дерева компоновки.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Этот класс сериализуется и передается между компонентами системы перетаскивания
|
||||
/// для идентификации перетаскиваемого элемента и его свойств.
|
||||
/// </remarks>
|
||||
public class DockElementDragData
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает или задает уникальный идентификатор элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Идентификатор элемента, соответствующий свойству <see cref="IDockElement.Id"/>.
|
||||
/// </value>
|
||||
public string ElementId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает тип элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Имя типа элемента (обычно "DockGroup" или "DockLeaf").
|
||||
/// </value>
|
||||
public string ElementType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает значение, указывающее, является ли элемент группой.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если элемент является <see cref="DockGroup"/>; false, если <see cref="DockLeaf"/>.
|
||||
/// </value>
|
||||
public bool IsGroup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает идентификатор родительского элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Идентификатор родительского элемента или null, если элемент корневой.
|
||||
/// </value>
|
||||
public string? ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает ширину элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Текущая ширина элемента в пикселях.
|
||||
/// </value>
|
||||
public double Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает высоту элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Текущая высота элемента в пикселях.
|
||||
/// </value>
|
||||
public double Height { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Представляет данные для перетаскивания контента (вкладок).
|
||||
/// Используется при перетаскивании отдельных вкладок между контейнерами.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Этот класс позволяет идентифицировать конкретную вкладку для операций
|
||||
/// объединения или перемещения между контейнерами.
|
||||
/// </remarks>
|
||||
public class ContentDragData
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает или задает идентификатор контейнера (листа), содержащего контент.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Идентификатор <see cref="DockLeaf"/>, в котором находится перетаскиваемая вкладка.
|
||||
/// </value>
|
||||
public string ElementId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает уникальный идентификатор контента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Идентификатор контента, соответствующий свойству <see cref="IDockContent.Id"/>.
|
||||
/// </value>
|
||||
public string ContentId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает заголовок контента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Текст, отображаемый на вкладке.
|
||||
/// </value>
|
||||
public string ContentTitle { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает тип контента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Имя типа контента (например, "TextEditor", "Toolbox", и т.д.).
|
||||
/// </value>
|
||||
public string ContentType { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает значение, указывающее, можно ли закрыть контент.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если контент можно закрыть; в противном случае — false.
|
||||
/// </value>
|
||||
public bool CanClose { get; set; } = true;
|
||||
}
|
||||
@@ -1,13 +1,33 @@
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Определяет позицию вставки при операции Drag-and-Drop.
|
||||
/// Определяет позицию вставки элемента относительно целевого элемента.
|
||||
/// Используется при операциях перемещения и вставки элементов в дерево компоновки.
|
||||
/// </summary>
|
||||
public enum DockPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// Слева от целевого элемента.
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// Справа от целевого элемента.
|
||||
/// </summary>
|
||||
Right,
|
||||
|
||||
/// <summary>
|
||||
/// Сверху от целевого элемента.
|
||||
/// </summary>
|
||||
Top,
|
||||
|
||||
/// <summary>
|
||||
/// Снизу от целевого элемента.
|
||||
/// </summary>
|
||||
Bottom,
|
||||
|
||||
/// <summary>
|
||||
/// В центре целевого элемента (для объединения вкладок).
|
||||
/// </summary>
|
||||
Center,
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,23 @@
|
||||
/// </summary>
|
||||
public enum DockSide
|
||||
{
|
||||
/// <summary> Левая сторона окна. </summary>
|
||||
/// <summary>
|
||||
/// Левая сторона окна.
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary> Правая сторона окна. </summary>
|
||||
/// <summary>
|
||||
/// Правая сторона окна.
|
||||
/// </summary>
|
||||
Right,
|
||||
|
||||
/// <summary> Верхняя сторона окна. </summary>
|
||||
/// <summary>
|
||||
/// Верхняя сторона окна.
|
||||
/// </summary>
|
||||
Top,
|
||||
|
||||
/// <summary> Нижняя сторона окна. </summary>
|
||||
/// <summary>
|
||||
/// Нижняя сторона окна.
|
||||
/// </summary>
|
||||
Bottom
|
||||
}
|
||||
@@ -3,21 +3,66 @@
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Описывает состояние плавающего окна в системе Lattice.
|
||||
/// Представляет плавающее окно в системе докинга.
|
||||
/// Плавающие окна могут перемещаться по экрану независимо от главного окна.
|
||||
/// </summary>
|
||||
public class DockWindow
|
||||
{
|
||||
/// <summary> Уникальный ID окна для сохранения его позиции в конфиге. </summary>
|
||||
/// <summary>
|
||||
/// Получает уникальный идентификатор окна.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Строковый идентификатор, сгенерированный с помощью GUID.
|
||||
/// Используется для сохранения позиции и размера окна в конфигурации.
|
||||
/// </value>
|
||||
public string Id { get; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary> Корневой элемент макета внутри данного окна. </summary>
|
||||
/// <summary>
|
||||
/// Получает или задает корневой элемент макета внутри данного окна.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Корневой элемент дерева компоновки плавающего окна.
|
||||
/// </value>
|
||||
public IDockElement? Root { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает позицию X окна на экране.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Координата X левого верхнего угла окна в пикселях.
|
||||
/// </value>
|
||||
public double X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает позицию Y окна на экране.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Координата Y левого верхнего угла окна в пикселях.
|
||||
/// </value>
|
||||
public double Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает ширину окна.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Ширина окна в пикселях. Значение по умолчанию: 800.
|
||||
/// </value>
|
||||
public double Width { get; set; } = 800;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает высоту окна.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Высота окна в пикселях. Значение по умолчанию: 600.
|
||||
/// </value>
|
||||
public double Height { get; set; } = 600;
|
||||
|
||||
/// <summary> Заголовок окна (обычно берется из активного контента). </summary>
|
||||
/// <summary>
|
||||
/// Получает или задает заголовок окна.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Текст заголовка окна. Обычно берется из активного контента.
|
||||
/// Значение по умолчанию: "Lattice Tool Window".
|
||||
/// </value>
|
||||
public string Title { get; set; } = "Lattice Tool Window";
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Перечисление направлений разделения пространства внутри группы.
|
||||
/// Определяет направление разделения пространства внутри группы.
|
||||
/// </summary>
|
||||
public enum SplitDirection
|
||||
{
|
||||
/// <summary> Разделение по горизонтали (создает левую и правую области). </summary>
|
||||
/// <summary>
|
||||
/// Разделение по горизонтали (создает левую и правую области).
|
||||
/// </summary>
|
||||
Horizontal,
|
||||
/// <summary> Разделение по вертикали (создает верхнюю и нижнюю области). </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Разделение по вертикали (создает верхнюю и нижнюю области).
|
||||
/// </summary>
|
||||
Vertical
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,23 @@
|
||||
/// </summary>
|
||||
public enum TabPlacement
|
||||
{
|
||||
/// <summary>
|
||||
/// Вкладки располагаются сверху.
|
||||
/// </summary>
|
||||
Top,
|
||||
|
||||
/// <summary>
|
||||
/// Вкладки располагаются снизу.
|
||||
/// </summary>
|
||||
Bottom,
|
||||
|
||||
/// <summary>
|
||||
/// Вкладки располагаются слева.
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// Вкладки располагаются справа.
|
||||
/// </summary>
|
||||
Right,
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace Lattice.Core.Docking.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Абстракция для сериализации и десериализации состояния макета док-системы.
|
||||
/// Определяет контракт для сериализации и десериализации состояния макета док-системы.
|
||||
/// Позволяет сохранять и восстанавливать расположение панелей, окон и их состояние.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -14,7 +14,12 @@ public interface ILayoutSerializer
|
||||
/// Сериализует состояние менеджера макета в строку.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для сериализации.</param>
|
||||
/// <returns>Строковое представление состояния макета.</returns>
|
||||
/// <returns>
|
||||
/// Строковое представление состояния макета.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="manager"/> равен null.
|
||||
/// </exception>
|
||||
string Serialize(Engine.LayoutManager manager);
|
||||
|
||||
/// <summary>
|
||||
@@ -26,6 +31,10 @@ public interface ILayoutSerializer
|
||||
/// Функция разрешения контента по идентификатору, используемая для восстановления
|
||||
/// ссылок на контент в десериализованном состоянии.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="manager"/> или <paramref name="serializedLayout"/>
|
||||
/// равны null.
|
||||
/// </exception>
|
||||
void Deserialize(Engine.LayoutManager manager, string serializedLayout,
|
||||
Func<string, Abstractions.IDockContent?> contentResolver);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,24 @@
|
||||
namespace Lattice.Core.Docking.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Контракт для объектов, которые могут предоставлять состояние для сериализации.
|
||||
/// Определяет контракт для объектов, которые могут предоставлять состояние для сериализации.
|
||||
/// </summary>
|
||||
public interface ISerializableLayout
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает состояние для сериализации.
|
||||
/// Получает состояние объекта для сериализации.
|
||||
/// </summary>
|
||||
/// <returns>Объект состояния, готовый к сериализации.</returns>
|
||||
/// <returns>
|
||||
/// Объект состояния, готовый к сериализации.
|
||||
/// </returns>
|
||||
object GetSerializableState();
|
||||
|
||||
/// <summary>
|
||||
/// Восстанавливает состояние из десериализованного объекта.
|
||||
/// Восстанавливает состояние объекта из десериализованного объекта.
|
||||
/// </summary>
|
||||
/// <param name="state">Десериализованное состояние.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="state"/> равен null.
|
||||
/// </exception>
|
||||
void RestoreFromState(object state);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/// <summary>
|
||||
/// Реестр типов содержимого, который позволяет создавать экземпляры контента по типу.
|
||||
/// Этот сервис является центральным для динамического создания панелей инструментов и документов в IDE.
|
||||
/// Этот сервис является центральным для динамического создания панелей инструментов и документов.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Реализует шаблон "Фабрика" для создания экземпляров <see cref="Abstractions.IDockContent"/>.
|
||||
@@ -16,12 +16,19 @@ public class ContentRegistry
|
||||
/// <summary>
|
||||
/// Регистрирует фабричный метод для создания контента указанного типа.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Тип контента, реализующий <see cref="Abstractions.IDockContent"/>.</typeparam>
|
||||
/// <typeparam name="T">
|
||||
/// Тип контента, реализующий <see cref="Abstractions.IDockContent"/>.
|
||||
/// </typeparam>
|
||||
/// <param name="contentTypeId">Уникальный идентификатор типа контента.</param>
|
||||
/// <param name="factory">Фабричный метод для создания экземпляров контента.</param>
|
||||
/// <param name="metadata">Метаданные типа контента (опционально).</param>
|
||||
/// <exception cref="ArgumentNullException">Выбрасывается, если contentTypeId или factory равны null.</exception>
|
||||
/// <exception cref="ArgumentException">Выбрасывается, если contentTypeId уже зарегистрирован.</exception>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="contentTypeId"/> или <paramref name="factory"/>
|
||||
/// равны null.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Выбрасывается, если <paramref name="contentTypeId"/> уже зарегистрирован.
|
||||
/// </exception>
|
||||
public void Register<T>(string contentTypeId, Func<T> factory, ContentMetadata? metadata = null)
|
||||
where T : Abstractions.IDockContent
|
||||
{
|
||||
@@ -31,7 +38,7 @@ public class ContentRegistry
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
if (_contentTypes.ContainsKey(contentTypeId))
|
||||
throw new ArgumentException($"Content type '{contentTypeId}' is already registered.");
|
||||
throw new ArgumentException($"Тип контента '{contentTypeId}' уже зарегистрирован.");
|
||||
|
||||
_contentTypes[contentTypeId] = new ContentDescriptor(
|
||||
typeof(T),
|
||||
@@ -45,14 +52,25 @@ public class ContentRegistry
|
||||
/// </summary>
|
||||
/// <param name="contentTypeId">Идентификатор типа контента.</param>
|
||||
/// <param name="id">Уникальный идентификатор для создаваемого экземпляра контента.</param>
|
||||
/// <returns>Новый экземпляр контента.</returns>
|
||||
/// <exception cref="KeyNotFoundException">Выбрасывается, если тип контента не зарегистрирован.</exception>
|
||||
/// <returns>
|
||||
/// Новый экземпляр контента.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="contentTypeId"/> равен null или пустой строке.
|
||||
/// </exception>
|
||||
/// <exception cref="KeyNotFoundException">
|
||||
/// Выбрасывается, если тип контента не зарегистрирован.
|
||||
/// </exception>
|
||||
public Abstractions.IDockContent CreateContent(string contentTypeId, string id)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(contentTypeId))
|
||||
throw new ArgumentNullException(nameof(contentTypeId));
|
||||
|
||||
if (!_contentTypes.TryGetValue(contentTypeId, out var descriptor))
|
||||
throw new KeyNotFoundException($"Content type '{contentTypeId}' is not registered.");
|
||||
throw new KeyNotFoundException($"Тип контента '{contentTypeId}' не зарегистрирован.");
|
||||
|
||||
var content = descriptor.Factory();
|
||||
|
||||
// Устанавливаем ID через рефлексию, если есть свойство Id
|
||||
var property = content.GetType().GetProperty("Id");
|
||||
if (property != null && property.CanWrite)
|
||||
@@ -67,9 +85,14 @@ public class ContentRegistry
|
||||
/// Получает метаданные для указанного типа контента.
|
||||
/// </summary>
|
||||
/// <param name="contentTypeId">Идентификатор типа контента.</param>
|
||||
/// <returns>Метаданные типа контента или null, если тип не найден.</returns>
|
||||
/// <returns>
|
||||
/// Метаданные типа контента или null, если тип не найден.
|
||||
/// </returns>
|
||||
public ContentMetadata? GetMetadata(string contentTypeId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(contentTypeId))
|
||||
return null;
|
||||
|
||||
return _contentTypes.TryGetValue(contentTypeId, out var descriptor)
|
||||
? descriptor.Metadata
|
||||
: null;
|
||||
@@ -78,24 +101,54 @@ public class ContentRegistry
|
||||
/// <summary>
|
||||
/// Получает все зарегистрированные типы контента.
|
||||
/// </summary>
|
||||
/// <returns>Коллекция идентификаторов зарегистрированных типов контента.</returns>
|
||||
/// <returns>
|
||||
/// Коллекция идентификаторов зарегистрированных типов контента.
|
||||
/// </returns>
|
||||
public IEnumerable<string> GetRegisteredTypes() => _contentTypes.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, зарегистрирован ли указанный тип контента.
|
||||
/// </summary>
|
||||
public bool IsRegistered(string contentTypeId) => _contentTypes.ContainsKey(contentTypeId);
|
||||
/// <param name="contentTypeId">Идентификатор типа контента.</param>
|
||||
/// <returns>
|
||||
/// true, если тип контента зарегистрирован; в противном случае false.
|
||||
/// </returns>
|
||||
public bool IsRegistered(string contentTypeId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(contentTypeId))
|
||||
return false;
|
||||
|
||||
return _contentTypes.ContainsKey(contentTypeId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Дескриптор типа контента, содержащий информацию о фабричном методе и метаданных.
|
||||
/// Представляет дескриптор типа контента, содержащий информацию о фабричном методе и метаданных.
|
||||
/// </summary>
|
||||
private class ContentDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает тип контента.
|
||||
/// </summary>
|
||||
public Type ContentType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает фабричный метод для создания экземпляров контента.
|
||||
/// </summary>
|
||||
public Func<Abstractions.IDockContent> Factory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает метаданные типа контента.
|
||||
/// </summary>
|
||||
public ContentMetadata Metadata { get; }
|
||||
|
||||
public ContentDescriptor(Type contentType, Func<Abstractions.IDockContent> factory, ContentMetadata metadata)
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="ContentDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="contentType">Тип контента.</param>
|
||||
/// <param name="factory">Фабричный метод.</param>
|
||||
/// <param name="metadata">Метаданные.</param>
|
||||
public ContentDescriptor(Type contentType, Func<Abstractions.IDockContent> factory,
|
||||
ContentMetadata metadata)
|
||||
{
|
||||
ContentType = contentType;
|
||||
Factory = factory;
|
||||
@@ -105,54 +158,80 @@ public class ContentRegistry
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Метаданные типа контента, предоставляющие дополнительную информацию для отображения в UI.
|
||||
/// Представляет метаданные типа контента, предоставляющие дополнительную информацию для отображения в UI.
|
||||
/// </summary>
|
||||
public class ContentMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Идентификатор типа контента.
|
||||
/// Получает идентификатор типа контента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Уникальный строковый идентификатор типа контента.
|
||||
/// </value>
|
||||
public string ContentTypeId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Отображаемое имя типа контента.
|
||||
/// Получает или задает отображаемое имя типа контента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Имя типа контента, отображаемое пользователю.
|
||||
/// </value>
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Описание типа контента.
|
||||
/// Получает или задает описание типа контента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Текстовое описание функциональности контента.
|
||||
/// </value>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Имя ресурса для иконки (опционально).
|
||||
/// Получает или задает имя ресурса для иконки типа контента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Имя ресурса иконки или null, если иконка не определена.
|
||||
/// </value>
|
||||
public string? IconResource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Признак того, что контент является документом (а не инструментальной панелью).
|
||||
/// Получает или задает значение, указывающее, является ли контент документом
|
||||
/// (а не инструментальной панелью).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если контент является документом; в противном случае false.
|
||||
/// </value>
|
||||
public bool IsDocument { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Минимальная ширина контента в пикселях.
|
||||
/// Получает или задает ширину контента по умолчанию.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Ширина контента в пикселях. Значение по умолчанию: 300.
|
||||
/// </value>
|
||||
public double DefaultWidth { get; set; } = 300;
|
||||
|
||||
/// <summary>
|
||||
/// Минимальная высота контента в пикселях.
|
||||
/// Получает или задает высоту контента по умолчанию.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Высота контента в пикселях. Значение по умолчанию: 200.
|
||||
/// </value>
|
||||
public double DefaultHeight { get; set; } = 200;
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр метаданных контента.
|
||||
/// Инициализирует новый экземпляр класса <see cref="ContentMetadata"/>.
|
||||
/// </summary>
|
||||
/// <param name="contentTypeId">Идентификатор типа контента.</param>
|
||||
/// <param name="displayName">Отображаемое имя.</param>
|
||||
/// <param name="displayName">Отображаемое имя типа контента.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="contentTypeId"/> или <paramref name="displayName"/>
|
||||
/// равны null.
|
||||
/// </exception>
|
||||
public ContentMetadata(string contentTypeId, string displayName)
|
||||
{
|
||||
ContentTypeId = contentTypeId;
|
||||
DisplayName = displayName;
|
||||
ContentTypeId = contentTypeId ?? throw new ArgumentNullException(nameof(contentTypeId));
|
||||
DisplayName = displayName ?? throw new ArgumentNullException(nameof(displayName));
|
||||
Description = string.Empty;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user