using Lattice.Core.Docking.Abstractions; using Lattice.Core.Docking.Engine; using Lattice.Core.Docking.Models; using Lattice.UI.Docking; using Lattice.UI.Docking.Abstractions; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; namespace Lattice.UI; /// /// Представляет главный контейнер док-системы для WinUI, который служит корневым элементом /// пользовательского интерфейса для размещения всех компонентов системы докинга. /// Этот контрол управляет всем макетом приложения, включая основное дерево компоновки, /// плавающие окна и автоскрываемые панели. /// /// /// /// является центральным координатором UI-слоя док-системы, /// интегрирующим функциональность менеджера макета, системы перетаскивания и контекстных меню. /// Он обеспечивает согласованное отображение всех элементов и обрабатывает пользовательские /// взаимодействия на верхнем уровне. /// /// /// Контрол реализует интерфейс и предоставляет полный набор методов /// для управления структурой док-системы, включая создание/закрытие плавающих окон и /// добавление/удаление автоскрываемых панелей. /// /// public sealed class LatticeDockHost : Control, IDockHost, IDisposable { private readonly PropertyChangedEventHandler _modelPropertyChangedHandler; private readonly ObservableCollection _floatingWindows = new(); private readonly ObservableCollection _autoHidePanels = new(); private bool _disposed; private IDockElement? _model; private LayoutManager? _layoutManager; private IDockContextManager? _contextManager; private bool _isSelected; private bool _isActive; private bool _canDrag = true; private bool _canDrop = true; private bool _showToolbox = true; private bool _showStatusBar = true; private bool _showMenu = true; private ContentControl? _rootContainer; /// /// Инициализирует новый экземпляр класса . /// /// /// Конструктор устанавливает ключ стиля по умолчанию, инициализирует обработчик изменений модели /// и подписывается на событие изменения контекста данных. /// public LatticeDockHost() { this.DefaultStyleKey = typeof(LatticeDockHost); _modelPropertyChangedHandler = OnModelPropertyChanged; this.DataContextChanged += OnDataContextChanged; } /// /// Получает или задает модель данных, связанную с этим контролом. /// /// /// Экземпляр, реализующий , представляющий корневой элемент /// дерева компоновки. Может быть null. /// /// /// Этот элемент является корнем всего макета док-системы. При изменении этого свойства /// происходит перестройка всего пользовательского интерфейса. /// public IDockElement? Model { get => _model; set { if (_model == value) return; DetachModel(); _model = value; AttachModel(); OnPropertyChanged(nameof(Model)); } } /// /// Получает или задает менеджер макета, к которому принадлежит этот контрол. /// /// /// Экземпляр , управляющий структурой док-системы. /// /// /// Менеджер макета используется для выполнения операций с деревом компоновки /// и координации изменений между различными элементами системы. /// public LayoutManager? LayoutManager { get => _layoutManager; set { if (_layoutManager == value) return; _layoutManager = value; OnPropertyChanged(nameof(LayoutManager)); } } /// /// Получает или задает контекстный менеджер для этого контрола. /// /// /// Экземпляр , управляющий контекстными меню и действиями. /// /// /// Контекстный менеджер используется для отображения меню, связанных с этим элементом, /// и выполнения команд, доступных в текущем контексте. /// public IDockContextManager? ContextManager { get => _contextManager; set { if (_contextManager == value) return; _contextManager = value; OnPropertyChanged(nameof(ContextManager)); } } /// /// Получает или задает признак того, что контрол выбран. /// /// /// true, если контрол выбран; в противном случае — false. /// /// /// Выделение контрола обычно визуально выделяет его границы или фон, /// чтобы указать пользователю на активный элемент. /// public bool IsSelected { get => _isSelected; set { if (_isSelected == value) return; _isSelected = value; OnPropertyChanged(nameof(IsSelected)); } } /// /// Получает или задает признак того, что контрол активен. /// /// /// true, если контрол активен; в противном случае — false. /// /// /// Активный контрол обычно получает фокус ввода и может обрабатывать команды клавиатуры. /// public bool IsActive { get => _isActive; set { if (_isActive == value) return; _isActive = value; OnPropertyChanged(nameof(IsActive)); } } /// /// Получает или задает признак того, что контрол можно перетаскивать. /// /// /// true, если контрол можно перетаскивать; в противном случае — false. /// /// /// Этот флаг влияет на возможность инициирования операции перетаскивания /// при взаимодействии пользователя с этим контролом. /// public bool CanDrag { get => _canDrag; set { if (_canDrag == value) return; _canDrag = value; OnPropertyChanged(nameof(CanDrag)); } } /// /// Получает или задает признак того, что контрол может принимать сброс. /// /// /// true, если контрол может принимать сброс; в противном случае — false. /// /// /// Этот флаг влияет на возможность завершения операции перетаскивания /// сбросом данных на этот контрол. /// public bool CanDrop { get => _canDrop; set { if (_canDrop == value) return; _canDrop = value; OnPropertyChanged(nameof(CanDrop)); } } /// /// Получает коллекцию контролов плавающих окон, связанных с этим хостом. /// /// /// Коллекция объектов, реализующих , /// представляющих все активные плавающие окна в системе. /// /// /// Коллекция является наблюдаемой (ObservableCollection), что позволяет автоматически /// обновлять пользовательский интерфейс при добавлении или удалении окон. /// public IEnumerable FloatingWindows => _floatingWindows; /// /// Получает коллекцию контролов автоскрываемых панелей, прикрепленных к краям окна. /// /// /// Коллекция объектов, реализующих , /// представляющих автоскрываемые панели на разных сторонах окна. /// /// /// Коллекция является наблюдаемой (ObservableCollection), что позволяет автоматически /// обновлять пользовательский интерфейс при добавлении или удалении панелей. /// public IEnumerable AutoHidePanels => _autoHidePanels; /// /// Получает или задает значение, указывающее, отображается ли панель инструментов (Toolbox). /// /// /// true, если панель инструментов видима; в противном случае — false. /// /// /// Панель инструментов обычно содержит элементы для быстрого доступа к командам /// или создания новых компонентов в приложении. /// public bool ShowToolbox { get => _showToolbox; set { if (_showToolbox == value) return; _showToolbox = value; OnPropertyChanged(nameof(ShowToolbox)); } } /// /// Получает или задает значение, указывающее, отображается ли строка состояния. /// /// /// true, если строка состояния видима; в противном случае — false. /// /// /// Строка состояния обычно отображает текущий статус приложения, /// информацию о выбранном элементе или прогресс выполнения операций. /// public bool ShowStatusBar { get => _showStatusBar; set { if (_showStatusBar == value) return; _showStatusBar = value; OnPropertyChanged(nameof(ShowStatusBar)); } } /// /// Получает или задает значение, указывающее, отображается ли главное меню приложения. /// /// /// true, если главное меню видимо; в противном случае — false. /// /// /// Главное меню содержит основные команды приложения, организованные в иерархическую структуру. /// public bool ShowMenu { get => _showMenu; set { if (_showMenu == value) return; _showMenu = value; OnPropertyChanged(nameof(ShowMenu)); } } /// /// Событие, возникающее при изменении структуры макета док-системы. /// /// /// Может вызываться при добавлении/удалении элементов, изменении размеров, /// создании/закрытии плавающих окон и других операциях, влияющих на компоновку. /// public event EventHandler? LayoutChanged; /// /// Событие, возникающее при создании нового плавающего окна. /// public event EventHandler? FloatingWindowCreated; /// /// Событие, возникающее при закрытии плавающего окна. /// public event EventHandler? FloatingWindowClosed; /// /// Событие, возникающее при изменении значения свойства. /// public event PropertyChangedEventHandler? PropertyChanged; /// /// Вызывается при применении шаблона контрола. /// /// /// Метод получает ссылки на именованные части шаблона и обновляет отображение /// корневого содержимого в соответствии с текущим состоянием модели. /// protected override void OnApplyTemplate() { base.OnApplyTemplate(); _rootContainer = GetTemplateChild("PART_RootContainer") as ContentControl; UpdateRootContent(); } private void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) { Model = args.NewValue as IDockElement; } private void AttachModel() { if (_model != null && _layoutManager != null) { // Подписываемся на события менеджера макета _layoutManager.LayoutUpdated += OnLayoutUpdated; _layoutManager.AutoHidePanelsChanged += OnAutoHidePanelsChanged; // Устанавливаем DataContext this.DataContext = _model; UpdateRootContent(); } } private void DetachModel() { if (_model != null && _layoutManager != null) { // Отписываемся от событий _layoutManager.LayoutUpdated -= OnLayoutUpdated; _layoutManager.AutoHidePanelsChanged -= OnAutoHidePanelsChanged; this.DataContext = null; } } private void OnModelPropertyChanged(object? sender, PropertyChangedEventArgs e) { // Обработка изменений модели OnPropertyChanged(e.PropertyName); } private void OnLayoutUpdated() { UpdateRootContent(); LayoutChanged?.Invoke(this, EventArgs.Empty); } private void OnAutoHidePanelsChanged(object? sender, EventArgs e) { // Обновление автоскрываемых панелей OnPropertyChanged(nameof(AutoHidePanels)); } private void UpdateRootContent() { if (_rootContainer != null && _model != null && _layoutManager != null) { // Создаем дерево контролов через фабрику var factory = LatticeUIFramework.ControlFactory; if (factory != null) { var control = factory.CreateControlForElement(_model); _rootContainer.Content = control; } } } /// /// Создает новое плавающее окно для размещения указанного элемента док-системы. /// /// /// Элемент док-системы (группа или лист), который будет размещен в плавающем окне. /// /// Заголовок создаваемого окна. /// /// Экземпляр , представляющий созданное плавающее окно. /// /// /// Выбрасывается, если равен null. /// /// /// Выбрасывается, так как метод еще не реализован. /// /// /// Созданное окно может быть перемещено пользователем в любое место экрана, /// изменено в размерах и обычно содержит стандартные элементы управления окном /// (заголовок, кнопки закрытия/сворачивания). /// public IFloatingWindowControl CreateFloatingWindow(IDockElement element, string title) { if (element == null) throw new ArgumentNullException(nameof(element)); // TODO: Реализовать создание плавающего окна через фабрику throw new NotImplementedException(); } /// /// Закрывает указанное плавающее окно и возвращает его содержимое в основной макет. /// /// /// Плавающее окно, которое необходимо закрыть. /// /// /// Выбрасывается, если равен null. /// /// /// При закрытии плавающего окна его содержимое обычно возвращается в то место /// в основном макете, откуда оно было извлечено, или в ближайшую допустимую позицию. /// public void CloseFloatingWindow(IFloatingWindowControl window) { if (window == null) throw new ArgumentNullException(nameof(window)); if (_floatingWindows.Remove(window)) { FloatingWindowClosed?.Invoke(this, new FloatingWindowClosedEventArgs(window)); } } /// /// Добавляет автоскрываемую панель с указанным содержимым к заданной стороне окна. /// /// /// Контент, который будет отображаться в автоскрываемой панели. /// /// /// Сторона окна, к которой будет прикреплена панель. /// /// /// Экземпляр , представляющий созданную панель. /// /// /// Выбрасывается, если равен null. /// /// /// Выбрасывается, если свойство не установлено. /// /// /// Выбрасывается, так как метод еще не реализован. /// /// /// Автоскрываемые панели полезны для инструментов, к которым нужен частый, /// но не постоянный доступ, так как они экономят пространство экрана. /// public IAutoHidePanelControl AddAutoHidePanel(Core.Docking.Abstractions.IDockContent content, DockSide side) { if (content == null) throw new ArgumentNullException(nameof(content)); if (_layoutManager != null) { var panel = _layoutManager.AddAutoHidePanel(content, side); // TODO: Создать UI-контрол для автоскрываемой панели через фабрику throw new NotImplementedException(); } throw new InvalidOperationException("LayoutManager is not set"); } /// /// Удаляет автоскрываемую панель из интерфейса. /// /// /// Автоскрываемая панель, которую необходимо удалить. /// /// /// Выбрасывается, если равен null. /// /// /// Выбрасывается, так как метод еще не реализован. /// /// /// После удаления панели её содержимое обычно либо закрывается полностью, /// либо преобразуется в обычную закрепленную панель, в зависимости от настроек. /// public void RemoveAutoHidePanel(IAutoHidePanelControl panel) { if (panel == null) throw new ArgumentNullException(nameof(panel)); // TODO: Реализовать удаление автоскрываемой панели throw new NotImplementedException(); } /// /// Обновляет внешний вид контрола в соответствии с текущим состоянием модели. /// /// /// Вызывает обновление корневого содержимого и всех дочерних элементов. /// public void Refresh() { UpdateRootContent(); } /// /// Применяет указанную тему к контролу. /// /// Тема для применения. /// /// В текущей реализации метод является заглушкой и должен быть расширен /// для поддержки динамического изменения тем. /// public void ApplyTheme(IDockTheme theme) { // Применение темы к контролу if (theme != null) { // TODO: Реализовать применение темы к стилям контрола } } /// /// Вызывается при изменении состояния модели для обновления UI. /// /// Имя изменившегося свойства модели. /// /// Перенаправляет вызов в обработчик изменений модели. /// public void OnModelPropertyChanged(string propertyName) { if (_model != null) { OnModelPropertyChanged(_model, new PropertyChangedEventArgs(propertyName)); } } private void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } /// /// Освобождает ресурсы, используемые этим экземпляром контрола. /// /// /// Выполняет отписку от событий модели, очистку коллекций и освобождение ресурсов. /// public void Dispose() { if (!_disposed) { DetachModel(); // Очищаем коллекции _floatingWindows.Clear(); _autoHidePanels.Clear(); _disposed = true; GC.SuppressFinalize(this); } } }