Files
Lattice/Lattice.UI.Docking.WinUI/Controls/LatticeDockHost.cs
2026-02-01 09:26:13 +03:00

383 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<IFloatingWindowControl> _floatingWindows = new();
private readonly ObservableCollection<IAutoHidePanelControl> _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<IFloatingWindowControl> FloatingWindows => _floatingWindows;
public IEnumerable<IAutoHidePanelControl> 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<FloatingWindowCreatedEventArgs>? FloatingWindowCreated;
public event EventHandler<FloatingWindowClosedEventArgs>? FloatingWindowClosed;
public event PropertyChangedEventHandler? PropertyChanged;
/// <inheritdoc/>
public FrameworkElement? DragDropElement => this;
/// <inheritdoc/>
public void SetupDragDropHandlers()
{
this.AllowDrop = true;
this.CanDrag = true;
// Настройка обработчиков для хоста
this.Drop += OnHostDrop;
this.DragOver += OnHostDragOver;
}
/// <inheritdoc/>
public void StartDrag() { /* Реализация */ }
/// <inheritdoc/>
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;
}
}