using Lattice.Core.Docking.Abstractions; using Lattice.Core.Docking.Engine; using Lattice.Core.Docking.Models; using Lattice.UI.Docking.Abstractions; using Lattice.UI.Docking.Services; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; using System.ComponentModel; using System.Runtime.CompilerServices; namespace Lattice.UI; /// /// Визуальный контрол для отображения группы разделения (сплиттера). /// Реализует интерфейс для интеграции с системой докинга. /// public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable { private readonly PropertyChangedEventHandler _modelPropertyChangedHandler; private bool _disposed; private DockGroup? _model; private Grid? _rootGrid; private ContentControl? _firstChildControl; private ContentControl? _secondChildControl; private LayoutManager? _layoutManager; private DockDragDropService? _dragDropService; private IDockContextManager? _contextManager; private bool _isSelected; private bool _isActive; private bool _canDrag = true; private bool _canDrop = true; private double _splitRatio = 0.5; private double _splitterSize = 4.0; /// /// Инициализирует новый экземпляр класса . /// public LatticeDockGroup() { this.DefaultStyleKey = typeof(LatticeDockGroup); _modelPropertyChangedHandler = OnModelPropertyChanged; this.DataContextChanged += OnDataContextChanged; } /// public IDockElement? Model { get => _model; set { if (_model == value) return; DetachModel(); _model = value as DockGroup; AttachModel(); OnPropertyChanged(nameof(Model)); } } /// public LayoutManager? LayoutManager { get => _layoutManager; set { if (_layoutManager == value) return; _layoutManager = value; OnPropertyChanged(nameof(LayoutManager)); } } /// public IDockDragDropService? DragDropService { get => _dragDropService; set { if (_dragDropService == value) return; _dragDropService = value; OnPropertyChanged(nameof(DragDropService)); } } /// 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 SplitDirection Orientation { get => _model?.Orientation ?? SplitDirection.Horizontal; set { if (_model != null && _model.Orientation != value) { _model.Orientation = value; UpdateLayoutDefinitions(); } } } /// public double SplitRatio { get => _splitRatio; set { if (Math.Abs(_splitRatio - value) > 0.001) { _splitRatio = value; UpdateLayoutDefinitions(); OnPropertyChanged(nameof(SplitRatio)); SplitRatioChanged?.Invoke(this, new SplitRatioChangedEventArgs(value, SplitRatioChangeSource.Programmatic)); } } } /// public double SplitterSize { get => _splitterSize; set { if (Math.Abs(_splitterSize - value) > 0.001) { _splitterSize = value; OnPropertyChanged(nameof(SplitterSize)); } } } /// public IDockControl? FirstChild => _firstChildControl?.Content as IDockControl; /// public IDockControl? SecondChild => _secondChildControl?.Content as IDockControl; /// public event EventHandler? SplitRatioChanged; /// public event PropertyChangedEventHandler? PropertyChanged; /// protected override void OnApplyTemplate() { base.OnApplyTemplate(); _rootGrid = GetTemplateChild("PART_Grid") as Grid; _firstChildControl = GetTemplateChild("PART_First") as ContentControl; _secondChildControl = GetTemplateChild("PART_Second") as ContentControl; UpdateLayoutDefinitions(); } private void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) { Model = args.NewValue as DockGroup; } private void AttachModel() { if (_model != null) { _model.PropertyChanged += _modelPropertyChangedHandler; this.DataContext = _model; // Инициализируем свойства из модели _splitRatio = _model.SplitRatio; UpdateLayoutDefinitions(); } } private void DetachModel() { if (_model != null) { _model.PropertyChanged -= _modelPropertyChangedHandler; this.DataContext = null; } } private void OnModelPropertyChanged(object? sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(DockGroup.Orientation): OnPropertyChanged(nameof(Orientation)); UpdateLayoutDefinitions(); break; case nameof(DockGroup.SplitRatio): if (_model != null) { _splitRatio = _model.SplitRatio; OnPropertyChanged(nameof(SplitRatio)); UpdateLayoutDefinitions(); } break; } } private void UpdateLayoutDefinitions() { if (_rootGrid == null || _model == null) return; _rootGrid.ColumnDefinitions.Clear(); _rootGrid.RowDefinitions.Clear(); if (_model.Orientation == SplitDirection.Horizontal) { // Горизонтальное разделение _rootGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(_model.SplitRatio, GridUnitType.Star) }); _rootGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); _rootGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1 - _model.SplitRatio, GridUnitType.Star) }); // Устанавливаем позиции элементов if (_firstChildControl != null) { Grid.SetColumn(_firstChildControl, 0); Grid.SetRow(_firstChildControl, 0); } if (_secondChildControl != null) { Grid.SetColumn(_secondChildControl, 2); Grid.SetRow(_secondChildControl, 0); } } else { // Вертикальное разделение _rootGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(_model.SplitRatio, GridUnitType.Star) }); _rootGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); _rootGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1 - _model.SplitRatio, GridUnitType.Star) }); // Устанавливаем позиции элементов if (_firstChildControl != null) { Grid.SetRow(_firstChildControl, 0); Grid.SetColumn(_firstChildControl, 0); } if (_secondChildControl != null) { Grid.SetRow(_secondChildControl, 2); Grid.SetColumn(_secondChildControl, 0); } } } /// public void SetChildren(IDockControl? firstChild, IDockControl? secondChild) { if (_firstChildControl != null) _firstChildControl.Content = firstChild; if (_secondChildControl != null) _secondChildControl.Content = secondChild; UpdateLayoutDefinitions(); } /// public void Refresh() { UpdateLayoutDefinitions(); } /// public void ApplyTheme(IDockTheme theme) { // Применение темы к контролу if (theme != null) { // 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(); _disposed = true; GC.SuppressFinalize(this); } } }