using Lattice.Core.Docking.Abstractions; using Lattice.Core.Docking.Engine; using Lattice.Core.Docking.Models; using Lattice.UI.Docking.Abstractions; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; namespace Lattice.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; } 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)); } } public bool IsSelected { get => _isSelected; set { if (_isSelected == value) return; _isSelected = value; OnPropertyChanged(nameof(IsSelected)); } } public bool IsActive { get => _isActive; set { if (_isActive == value) return; _isActive = value; OnPropertyChanged(nameof(IsActive)); } } public bool CanDrag { get => _canDrag; set { if (_canDrag == value) return; _canDrag = value; OnPropertyChanged(nameof(CanDrag)); } } public bool CanDrop { get => _canDrop; set { if (_canDrop == value) return; _canDrop = value; OnPropertyChanged(nameof(CanDrop)); } } public IEnumerable FloatingWindows => _floatingWindows; public IEnumerable AutoHidePanels => _autoHidePanels; public bool ShowToolbox { get => _showToolbox; set { if (_showToolbox == value) return; _showToolbox = value; OnPropertyChanged(nameof(ShowToolbox)); } } public bool ShowStatusBar { get => _showStatusBar; set { if (_showStatusBar == value) return; _showStatusBar = value; OnPropertyChanged(nameof(ShowStatusBar)); } } 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; /// public FrameworkElement? DragDropElement => this; /// public void SetupDragDropHandlers() { this.AllowDrop = true; this.CanDrag = true; // Настройка обработчиков для хоста this.Drop += OnHostDrop; this.DragOver += OnHostDragOver; } /// public void StartDrag() { /* Реализация */ } /// public void EndDrag() { /* Реализация */ } private void OnHostDragOver(object sender, DragEventArgs args) { args.AcceptedOperation = CanDrop ? Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move : Windows.ApplicationModel.DataTransfer.DataPackageOperation.None; args.DragUIOverride.IsGlyphVisible = true; args.DragUIOverride.Caption = "Закрепить здесь"; } 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; 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 = Lattice.UI.Docking.LatticeUIFramework.ControlFactory; if (factory != null) { var control = factory.CreateControlForElement(_model); _rootContainer.Content = control; } } } public IFloatingWindowControl CreateFloatingWindow(IDockElement element, string title) { throw new NotImplementedException("Floating windows not implemented yet"); } public void CloseFloatingWindow(IFloatingWindowControl window) { if (_floatingWindows.Remove(window)) { FloatingWindowClosed?.Invoke(this, new FloatingWindowClosedEventArgs(window)); } } public IAutoHidePanelControl AddAutoHidePanel(Core.Docking.Abstractions.IDockContent content, DockSide side) { if (_layoutManager != null) { var panel = _layoutManager.AddAutoHidePanel(content, side); throw new NotImplementedException("Auto-hide panels not implemented yet"); } throw new InvalidOperationException("LayoutManager is not set"); } public void RemoveAutoHidePanel(IAutoHidePanelControl panel) { throw new NotImplementedException("Auto-hide panels not implemented yet"); } public object? PrepareDragData() => Model; public bool HandleDrop(object data, DockPosition position) => false; public void Refresh() => UpdateRootContent(); public void ApplyTheme(IDockTheme theme) { // TODO: Реализовать применение темы } 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); } } private DockPosition GetDropPosition(Windows.Foundation.Point point) { if (ActualWidth <= 0 || ActualHeight <= 0) return DockPosition.Center; var relativeX = point.X / ActualWidth; var relativeY = point.Y / ActualHeight; // Определяем регионы для докирования const double edgeThreshold = 0.2; // 20% от краев const double centerThreshold = 0.4; // Центральная область // Проверяем края if (relativeX < edgeThreshold) return DockPosition.Left; if (relativeX > (1 - edgeThreshold)) return DockPosition.Right; if (relativeY < edgeThreshold) return DockPosition.Top; if (relativeY > (1 - edgeThreshold)) return DockPosition.Bottom; // Если в центральной области if (relativeX > centerThreshold && relativeX < (1 - centerThreshold) && relativeY > centerThreshold && relativeY < (1 - centerThreshold)) { return DockPosition.Center; } // По умолчанию - центр return DockPosition.Center; } private void OnHostDrop(object sender, DragEventArgs args) { if (CanDrop && args.DataView.Properties.TryGetValue("LatticeDockElement", out var data)) { // Получаем позицию сброса var position = GetDropPosition(args.GetPosition(this)); // Определяем целевой элемент IDockElement? target = null; if (args.OriginalSource is FrameworkElement element) { // Находим соответствующий контрол докинга var dockControl = FindDockControl(element); target = dockControl?.Model; } // Если цель не найдена, используем корневой элемент target ??= LayoutManager?.Root; if (data is IDockElement source && target != null) { LayoutManager?.Move(source, target, position); } } } private IDockControl? FindDockControl(FrameworkElement element) { // Поднимаемся по дереву элементов, чтобы найти контрол докинга var current = element; while (current != null) { if (current is IDockControl dockControl) return dockControl; current = VisualTreeHelper.GetParent(current) as FrameworkElement; } return null; } }