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; /// /// Реализация менеджера контекстных меню для WinUI. /// public sealed class WinUIDockContextManager : DockContextManagerBase, IDisposable { private readonly ConcurrentDictionary _commands = new(); private MenuFlyout? _currentFlyout; private IDockControl? _currentContextTarget; /// /// Инициализирует новый экземпляр менеджера контекстных меню. /// 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 _); } /// /// Получает команду по идентификатору. /// protected override IDockCommand? GetCommand(string commandId) { _commands.TryGetValue(commandId, out var command); return command; } /// /// Получает все доступные команды для указанного элемента. /// protected override IEnumerable GetCommandsForElement(IDockControl element) { return _commands.Values.Where(c => CanExecuteCommand(c, element)); } /// /// Класс для реализации ICommand. /// private sealed class RelayCommand : ICommand { private readonly Action _execute; private readonly Func _canExecute; public event EventHandler? CanExecuteChanged; public RelayCommand(Action execute, Func 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); } /// /// Базовая реализация команды докинга. /// private class DockCommand : IDockCommand { public string Id { get; } public string Name { get; } public string Description { get; } private readonly Func _getIcon; private readonly Func _canExecute; private readonly Action _execute; public DockCommand(string id, string name, string description, Func getIcon, Func 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(); } }