DragAndDrop core
This commit is contained in:
114
Lattice.Core.Docking/Models/AutoHidePanel.cs
Normal file
114
Lattice.Core.Docking/Models/AutoHidePanel.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Представляет автоскрываемую панель, которая может быть прикреплена к одной из сторон окна.
|
||||
/// Автоскрываемые панели скрываются, оставляя только заголовок, и появляются при наведении курсора.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Автоскрываемые панели являются ключевым элементом интерфейса современных IDE,
|
||||
/// позволяя экономить пространство экрана при сохранении быстрого доступа к инструментам.
|
||||
/// </remarks>
|
||||
public class AutoHidePanel : INotifyPropertyChanged
|
||||
{
|
||||
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>
|
||||
public string Id { get; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Содержимое панели.
|
||||
/// </summary>
|
||||
public Abstractions.IDockContent Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Сторона окна, к которой прикреплена панель.
|
||||
/// </summary>
|
||||
public DockSide Side { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ширина панели (для левой/правой сторон) или высота (для верхней/нижней сторон).
|
||||
/// </summary>
|
||||
public double Size { get; set; } = 300;
|
||||
|
||||
/// <summary>
|
||||
/// Признак видимости панели.
|
||||
/// </summary>
|
||||
public bool IsVisible
|
||||
{
|
||||
get => _isVisible;
|
||||
set
|
||||
{
|
||||
if (_isVisible != value)
|
||||
{
|
||||
_isVisible = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Смещение для анимации выезда/заезда панели (0-1).
|
||||
/// </summary>
|
||||
public double SlideOffset
|
||||
{
|
||||
get => _slideOffset;
|
||||
set
|
||||
{
|
||||
if (Math.Abs(_slideOffset - value) > 0.001)
|
||||
{
|
||||
_slideOffset = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Заголовок панели (обычно берется из содержимого).
|
||||
/// </summary>
|
||||
public string Title => Content?.Title ?? "Auto-hide Panel";
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр автоскрываемой панели.
|
||||
/// </summary>
|
||||
/// <param name="content">Содержимое панели.</param>
|
||||
/// <param name="side">Сторона окна для прикрепления.</param>
|
||||
public AutoHidePanel(Abstractions.IDockContent content, DockSide side)
|
||||
{
|
||||
Content = content ?? throw new ArgumentNullException(nameof(content));
|
||||
Side = side;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Переключает видимость панели.
|
||||
/// </summary>
|
||||
public void Toggle()
|
||||
{
|
||||
IsVisible = !IsVisible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Показывает панель.
|
||||
/// </summary>
|
||||
public void Show()
|
||||
{
|
||||
IsVisible = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Скрывает панель.
|
||||
/// </summary>
|
||||
public void Hide()
|
||||
{
|
||||
IsVisible = false;
|
||||
}
|
||||
}
|
||||
444
Lattice.Core.Docking/Models/DockGroup.cs
Normal file
444
Lattice.Core.Docking/Models/DockGroup.cs
Normal file
@@ -0,0 +1,444 @@
|
||||
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;
|
||||
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Представляет узел дерева компоновки, который разделяет доступную область
|
||||
/// между двумя дочерними элементами. Этот класс является основным структурным
|
||||
/// элементом для создания сложных макетов с разделителями.
|
||||
/// </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>
|
||||
/// </remarks>
|
||||
public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Событие, возникающее при изменении значения свойства.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private double _splitRatio = 0.5;
|
||||
private string _id;
|
||||
|
||||
/// <summary>
|
||||
/// Получает уникальный идентификатор группы.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Строковый идентификатор, уникальный в пределах дерева компоновки.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Идентификатор используется для сериализации/десериализации макета,
|
||||
/// поиска элементов и отслеживания изменений в дереве.
|
||||
/// </remarks>
|
||||
public string Id
|
||||
{
|
||||
get => _id;
|
||||
internal set
|
||||
{
|
||||
if (_id != value)
|
||||
{
|
||||
_id = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает родительский элемент в иерархии дерева компоновки.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Родительский элемент или null, если эта группа является корневой.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Это свойство управляется системой компоновки при добавлении или
|
||||
/// удалении элементов из дерева.
|
||||
/// </remarks>
|
||||
public IDockElement? Parent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает первый дочерний элемент (левую или верхнюю область).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Элемент, занимающий первую часть разделенной области.
|
||||
/// </value>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается при попытке установить значение null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// При установке нового значения автоматически обновляется свойство
|
||||
/// <see cref="Parent"/> у дочернего элемента.
|
||||
/// </remarks>
|
||||
public IDockElement First { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает второй дочерний элемент (правую или нижнюю область).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Элемент, занимающий вторую часть разделенной области.
|
||||
/// </value>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается при попытке установить значение null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// При установке нового значения автоматически обновляется свойство
|
||||
/// <see cref="Parent"/> у дочернего элемента.
|
||||
/// </remarks>
|
||||
public IDockElement Second { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает направление разделения данной группы.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Значение перечисления <see cref="SplitDirection"/>, указывающее,
|
||||
/// как разделена область: горизонтально или вертикально.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <see cref="SplitDirection.Horizontal"/> создает левую и правую области.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="SplitDirection.Vertical"/> создает верхнюю и нижнюю области.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public SplitDirection Orientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает соотношение разделения между первым и вторым элементами.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Значение от 0.0 до 1.0, где:
|
||||
/// <list type="bullet">
|
||||
/// <item>0.0 - вся область принадлежит второму элементу</item>
|
||||
/// <item>0.5 - область разделена поровну</item>
|
||||
/// <item>1.0 - вся область принадлежит первому элементу</item>
|
||||
/// </list>
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Изменение этого свойства вызывает событие <see cref="PropertyChanged"/>
|
||||
/// и может привести к перерисовке пользовательского интерфейса.
|
||||
/// </remarks>
|
||||
public double SplitRatio
|
||||
{
|
||||
get => _splitRatio;
|
||||
set
|
||||
{
|
||||
if (Math.Abs(_splitRatio - value) > double.Epsilon)
|
||||
{
|
||||
_splitRatio = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает желаемую ширину элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Ширина в пикселях или относительных единицах.
|
||||
/// </value>
|
||||
public double Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает желаемую высоту элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Высота в пикселях или относительных единицах.
|
||||
/// </value>
|
||||
public double Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает минимально допустимую ширину элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Минимальная ширина в пикселях, при которой элемент сохраняет функциональность.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Для группы минимальная ширина вычисляется как сумма минимальных ширин
|
||||
/// дочерних элементов при горизонтальной ориентации или максимум минимальных
|
||||
/// ширин при вертикальной ориентации.
|
||||
/// </remarks>
|
||||
public double MinWidth => Orientation == SplitDirection.Horizontal
|
||||
? First.MinWidth + Second.MinWidth
|
||||
: Math.Max(First.MinWidth, Second.MinWidth);
|
||||
|
||||
/// <summary>
|
||||
/// Получает минимально допустимую высоту элемента.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Минимальная высота в пикселях, при которой элемент сохраняет функциональность.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Для группы минимальная высота вычисляется как сумма минимальных высот
|
||||
/// дочерних элементов при вертикальной ориентации или максимум минимальных
|
||||
/// высот при горизонтальной ориентации.
|
||||
/// </remarks>
|
||||
public double MinHeight => Orientation == SplitDirection.Vertical
|
||||
? First.MinHeight + Second.MinHeight
|
||||
: Math.Max(First.MinHeight, Second.MinHeight);
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="DockGroup"/>.
|
||||
/// </summary>
|
||||
/// <param name="first">
|
||||
/// Первый дочерний элемент (левая или верхняя область).
|
||||
/// </param>
|
||||
/// <param name="second">
|
||||
/// Второй дочерний элемент (правая или нижняя область).
|
||||
/// </param>
|
||||
/// <param name="orientation">
|
||||
/// Направление разделения между дочерними элементами.
|
||||
/// </param>
|
||||
/// <param name="id">
|
||||
/// Уникальный идентификатор группы. Если не указан, генерируется новый GUID.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="first"/> или <paramref name="second"/>
|
||||
/// равны null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Конструктор автоматически устанавливает свойство <see cref="Parent"/>
|
||||
/// у дочерних элементов на текущую группу и генерирует уникальный идентификатор,
|
||||
/// если он не был предоставлен.
|
||||
/// </remarks>
|
||||
public DockGroup(IDockElement first, IDockElement second, SplitDirection orientation, string? id = null)
|
||||
{
|
||||
First = first ?? throw new ArgumentNullException(nameof(first));
|
||||
Second = second ?? throw new ArgumentNullException(nameof(second));
|
||||
Orientation = orientation;
|
||||
Id = id ?? Guid.NewGuid().ToString();
|
||||
|
||||
First.Parent = this;
|
||||
Second.Parent = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывает событие <see cref="PropertyChanged"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// Имя изменившегося свойства. Если не указано, определяется автоматически.
|
||||
/// </param>
|
||||
protected void OnPropertyChanged([CallerMemberName] string? name = null)
|
||||
{
|
||||
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
|
||||
}
|
||||
580
Lattice.Core.Docking/Models/DockLeaf.cs
Normal file
580
Lattice.Core.Docking/Models/DockLeaf.cs
Normal file
@@ -0,0 +1,580 @@
|
||||
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;
|
||||
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Представляет конечный узел (лист) дерева компоновки, который непосредственно
|
||||
/// содержит коллекцию вкладок с контентом. Этот класс является контейнером для
|
||||
/// отображаемого пользователю содержимого.
|
||||
/// </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
|
||||
{
|
||||
/// <summary>
|
||||
/// Событие, возникающее при изменении значения свойства.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private readonly ObservableCollection<IDockContent> _items = new();
|
||||
private IDockContent? _activeContent;
|
||||
private string _id;
|
||||
|
||||
/// <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>
|
||||
/// <para>
|
||||
/// При установке нового значения проверяется, что вкладка действительно
|
||||
/// содержится в коллекции <see cref="Children"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Изменение этого свойства вызывает событие <see cref="PropertyChanged"/>.
|
||||
/// </para>
|
||||
/// </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; set; } = TabPlacement.Bottom;
|
||||
|
||||
/// <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>
|
||||
/// <para>
|
||||
/// Если контент уже содержится в коллекции, он не добавляется повторно,
|
||||
/// но становится активным.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Этот метод обновляет свойство <see cref="ActiveContent"/> и вызывает
|
||||
/// соответствующее событие изменения свойства.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void AddContent(IDockContent content)
|
||||
{
|
||||
if (!_items.Contains(content))
|
||||
{
|
||||
_items.Add(content);
|
||||
}
|
||||
ActiveContent = content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет контент из контейнера.
|
||||
/// </summary>
|
||||
/// <param name="content">
|
||||
/// Контент для удаления.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Если удаляемый контент является активным, автоматически выбирается
|
||||
/// новая активная вкладка (следующая в списке или предыдущая, если удалена
|
||||
/// последняя).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Если после удаления контейнер становится пустым, он может быть удален
|
||||
/// из дерева макета системой компоновки.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void RemoveContent(IDockContent content)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
13
Lattice.Core.Docking/Models/DockPosition.cs
Normal file
13
Lattice.Core.Docking/Models/DockPosition.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Определяет позицию вставки при операции Drag-and-Drop.
|
||||
/// </summary>
|
||||
public enum DockPosition
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Top,
|
||||
Bottom,
|
||||
Center,
|
||||
}
|
||||
19
Lattice.Core.Docking/Models/DockSide.cs
Normal file
19
Lattice.Core.Docking/Models/DockSide.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Определяет стороны окна, к которым могут быть прикреплены автоскрываемые панели.
|
||||
/// </summary>
|
||||
public enum DockSide
|
||||
{
|
||||
/// <summary> Левая сторона окна. </summary>
|
||||
Left,
|
||||
|
||||
/// <summary> Правая сторона окна. </summary>
|
||||
Right,
|
||||
|
||||
/// <summary> Верхняя сторона окна. </summary>
|
||||
Top,
|
||||
|
||||
/// <summary> Нижняя сторона окна. </summary>
|
||||
Bottom
|
||||
}
|
||||
23
Lattice.Core.Docking/Models/DockWindow.cs
Normal file
23
Lattice.Core.Docking/Models/DockWindow.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Lattice.Core.Docking.Abstractions;
|
||||
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Описывает состояние плавающего окна в системе Lattice.
|
||||
/// </summary>
|
||||
public class DockWindow
|
||||
{
|
||||
/// <summary> Уникальный ID окна для сохранения его позиции в конфиге. </summary>
|
||||
public string Id { get; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary> Корневой элемент макета внутри данного окна. </summary>
|
||||
public IDockElement? Root { get; set; }
|
||||
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
public double Width { get; set; } = 800;
|
||||
public double Height { get; set; } = 600;
|
||||
|
||||
/// <summary> Заголовок окна (обычно берется из активного контента). </summary>
|
||||
public string Title { get; set; } = "Lattice Tool Window";
|
||||
}
|
||||
12
Lattice.Core.Docking/Models/SplitDirection.cs
Normal file
12
Lattice.Core.Docking/Models/SplitDirection.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Перечисление направлений разделения пространства внутри группы.
|
||||
/// </summary>
|
||||
public enum SplitDirection
|
||||
{
|
||||
/// <summary> Разделение по горизонтали (создает левую и правую области). </summary>
|
||||
Horizontal,
|
||||
/// <summary> Разделение по вертикали (создает верхнюю и нижнюю области). </summary>
|
||||
Vertical
|
||||
}
|
||||
12
Lattice.Core.Docking/Models/TabPlacement.cs
Normal file
12
Lattice.Core.Docking/Models/TabPlacement.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Lattice.Core.Docking.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Определяет положение полосы вкладок в контейнере.
|
||||
/// </summary>
|
||||
public enum TabPlacement
|
||||
{
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
Reference in New Issue
Block a user