DragAndDrop core
This commit is contained in:
368
Lattice.UI.Docking.WinUI/Controls/LatticeDockGroup.cs
Normal file
368
Lattice.UI.Docking.WinUI/Controls/LatticeDockGroup.cs
Normal file
@@ -0,0 +1,368 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Визуальный контрол для отображения группы разделения (сплиттера).
|
||||
/// Реализует интерфейс <see cref="IDockGroupControl"/> для интеграции с системой докинга.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="LatticeDockGroup"/>.
|
||||
/// </summary>
|
||||
public LatticeDockGroup()
|
||||
{
|
||||
this.DefaultStyleKey = typeof(LatticeDockGroup);
|
||||
_modelPropertyChangedHandler = OnModelPropertyChanged;
|
||||
this.DataContextChanged += OnDataContextChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDockElement? Model
|
||||
{
|
||||
get => _model;
|
||||
set
|
||||
{
|
||||
if (_model == value) return;
|
||||
DetachModel();
|
||||
_model = value as DockGroup;
|
||||
AttachModel();
|
||||
OnPropertyChanged(nameof(Model));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public LayoutManager? LayoutManager
|
||||
{
|
||||
get => _layoutManager;
|
||||
set
|
||||
{
|
||||
if (_layoutManager == value) return;
|
||||
_layoutManager = value;
|
||||
OnPropertyChanged(nameof(LayoutManager));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDockDragDropService? DragDropService
|
||||
{
|
||||
get => _dragDropService;
|
||||
set
|
||||
{
|
||||
if (_dragDropService == value) return;
|
||||
_dragDropService = value;
|
||||
OnPropertyChanged(nameof(DragDropService));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDockContextManager? ContextManager
|
||||
{
|
||||
get => _contextManager;
|
||||
set
|
||||
{
|
||||
if (_contextManager == value) return;
|
||||
_contextManager = value;
|
||||
OnPropertyChanged(nameof(ContextManager));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set
|
||||
{
|
||||
if (_isSelected == value) return;
|
||||
_isSelected = value;
|
||||
OnPropertyChanged(nameof(IsSelected));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsActive
|
||||
{
|
||||
get => _isActive;
|
||||
set
|
||||
{
|
||||
if (_isActive == value) return;
|
||||
_isActive = value;
|
||||
OnPropertyChanged(nameof(IsActive));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool CanDrag
|
||||
{
|
||||
get => _canDrag;
|
||||
set
|
||||
{
|
||||
if (_canDrag == value) return;
|
||||
_canDrag = value;
|
||||
OnPropertyChanged(nameof(CanDrag));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool CanDrop
|
||||
{
|
||||
get => _canDrop;
|
||||
set
|
||||
{
|
||||
if (_canDrop == value) return;
|
||||
_canDrop = value;
|
||||
OnPropertyChanged(nameof(CanDrop));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SplitDirection Orientation
|
||||
{
|
||||
get => _model?.Orientation ?? SplitDirection.Horizontal;
|
||||
set
|
||||
{
|
||||
if (_model != null && _model.Orientation != value)
|
||||
{
|
||||
_model.Orientation = value;
|
||||
UpdateLayoutDefinitions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double SplitterSize
|
||||
{
|
||||
get => _splitterSize;
|
||||
set
|
||||
{
|
||||
if (Math.Abs(_splitterSize - value) > 0.001)
|
||||
{
|
||||
_splitterSize = value;
|
||||
OnPropertyChanged(nameof(SplitterSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDockControl? FirstChild => _firstChildControl?.Content as IDockControl;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDockControl? SecondChild => _secondChildControl?.Content as IDockControl;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<SplitRatioChangedEventArgs>? SplitRatioChanged;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetChildren(IDockControl? firstChild, IDockControl? secondChild)
|
||||
{
|
||||
if (_firstChildControl != null)
|
||||
_firstChildControl.Content = firstChild;
|
||||
|
||||
if (_secondChildControl != null)
|
||||
_secondChildControl.Content = secondChild;
|
||||
|
||||
UpdateLayoutDefinitions();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Refresh()
|
||||
{
|
||||
UpdateLayoutDefinitions();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ApplyTheme(IDockTheme theme)
|
||||
{
|
||||
// Применение темы к контролу
|
||||
if (theme != null)
|
||||
{
|
||||
// TODO: Реализовать применение темы к стилям контрола
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
DetachModel();
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user