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

223 lines
7.2 KiB
C#

using Lattice.UI.Docking.Abstractions;
using Lattice.UI.Docking.Services;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
namespace Lattice.UI.Docking.WinUI.Services;
/// <summary>
/// Реализация менеджера контекстных меню для WinUI.
/// </summary>
public sealed class WinUIDockContextManager : DockContextManagerBase, IDisposable
{
private readonly ConcurrentDictionary<string, IDockCommand> _commands = new();
private MenuFlyout? _currentFlyout;
private IDockControl? _currentContextTarget;
/// <summary>
/// Инициализирует новый экземпляр менеджера контекстных меню.
/// </summary>
public WinUIDockContextManager()
{
// Регистрируем стандартные команды
RegisterDefaultCommands();
}
private void RegisterDefaultCommands()
{
// Пример регистрации стандартных команд
RegisterCommand("Close", new DockCommand("Close", "Close", "Close the selected content", () => "", () => true, OnCloseCommand));
RegisterCommand("Float", new DockCommand("Float", "Float", "Float the window", () => "", () => true, OnFloatCommand));
RegisterCommand("Dock", new DockCommand("Dock", "Dock", "Dock the window", () => "", () => true, OnDockCommand));
}
private void OnCloseCommand()
{
if (_currentContextTarget is Lattice.UI.LatticeDockLeaf leafControl && leafControl.ActiveContent != null)
{
leafControl.CloseContent(leafControl.ActiveContent);
}
}
private void OnFloatCommand()
{
// TODO: Реализовать плавающее окно
System.Diagnostics.Debug.WriteLine("Float command triggered");
}
private void OnDockCommand()
{
// TODO: Реализовать закрепление окна
System.Diagnostics.Debug.WriteLine("Dock command triggered");
}
public override void ShowContextMenu(IDockControl element, double x, double y)
{
if (element is not FrameworkElement uiElement) return;
// Создаем контекстное меню
var flyout = new MenuFlyout();
// Получаем команды для элемента
var commands = GetCommandsForElement(element);
foreach (var command in commands)
{
var item = new MenuFlyoutItem
{
Text = command.Name,
Tag = command,
Command = new RelayCommand(() => ExecuteCommand(command, element),
() => command.CanExecute(element))
};
// Устанавливаем иконку, если есть
if (!string.IsNullOrEmpty(command.Icon))
{
var icon = new FontIcon
{
Glyph = command.Icon,
FontSize = 12
};
item.Icon = icon;
}
// Добавляем подсказку, если есть описание
if (!string.IsNullOrEmpty(command.Description))
{
ToolTipService.SetToolTip(item, command.Description);
}
flyout.Items.Add(item);
}
// Если команд нет, не показываем меню
if (flyout.Items.Count == 0) return;
// Закрываем предыдущее меню, если оно открыто
HideContextMenu();
// Сохраняем ссылки
_currentFlyout = flyout;
_currentContextTarget = element;
// Показываем меню
flyout.ShowAt(uiElement, new Windows.Foundation.Point(x, y));
// Вызываем событие
OnContextMenuShown(element, x, y);
}
public override void HideContextMenu()
{
if (_currentFlyout != null)
{
_currentFlyout.Hide();
_currentFlyout = null;
}
if (_currentContextTarget != null)
{
OnContextMenuHidden();
_currentContextTarget = null;
}
}
public override void RegisterCommand(string commandId, IDockCommand command)
{
if (string.IsNullOrEmpty(commandId))
throw new ArgumentNullException(nameof(commandId));
_commands[commandId] = command ?? throw new ArgumentNullException(nameof(command));
}
public override void UnregisterCommand(string commandId)
{
_commands.TryRemove(commandId, out _);
}
/// <summary>
/// Получает команду по идентификатору.
/// </summary>
protected override IDockCommand? GetCommand(string commandId)
{
_commands.TryGetValue(commandId, out var command);
return command;
}
/// <summary>
/// Получает все доступные команды для указанного элемента.
/// </summary>
protected override IEnumerable<IDockCommand> GetCommandsForElement(IDockControl element)
{
return _commands.Values.Where(c => CanExecuteCommand(c, element));
}
/// <summary>
/// Класс для реализации ICommand.
/// </summary>
private sealed class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public event EventHandler? CanExecuteChanged;
public RelayCommand(Action execute, Func<bool> canExecute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object? parameter) => _canExecute();
public void Execute(object? parameter) => _execute();
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Базовая реализация команды докинга.
/// </summary>
private class DockCommand : IDockCommand
{
public string Id { get; }
public string Name { get; }
public string Description { get; }
private readonly Func<string> _getIcon;
private readonly Func<bool> _canExecute;
private readonly Action _execute;
public DockCommand(string id, string name, string description, Func<string> getIcon, Func<bool> canExecute, Action execute)
{
Id = id;
Name = name;
Description = description;
_getIcon = getIcon;
_canExecute = canExecute;
_execute = execute;
}
public string Icon => _getIcon();
public string Shortcut => "";
public bool CanExecute(object? parameter) => _canExecute();
public void Execute(object? parameter) => _execute();
public event EventHandler? CanExecuteChanged;
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
public void Dispose()
{
HideContextMenu();
_commands.Clear();
}
}