#nullable enable using System.ComponentModel; using Terminal.Gui.Resources; namespace Terminal.Gui; /// /// A -derived object to be used as a menu item in a . Has title, an /// A -derived object to be used as a menu item in a . Has title, an /// associated help text, and an action to execute on activation. /// public class MenuItemv2 : Shortcut { /// /// Creates a new instance of . /// public MenuItemv2 () : base (Key.Empty, null, null) { } /// /// Creates a new instance of , binding it to and /// . The Key /// has bound to will be used as . /// /// /// /// /// The View that will be invoked on when user does something that causes the Shortcut's /// Accept /// event to be raised. /// /// /// The Command to invoke on . The Key /// has bound to will be used as /// /// The text to display for the command. /// The help text to display. /// The submenu to display when the user selects this menu item. public MenuItemv2 (View? targetView, Command command, string? commandText = null, string? helpText = null, Menuv2? subMenu = null) : base ( targetView?.HotKeyBindings.GetFirstFromCommands (command)!, string.IsNullOrEmpty (commandText) ? GlobalResources.GetString ($"cmd.{command}") : commandText, null, string.IsNullOrEmpty (helpText) ? GlobalResources.GetString ($"cmd.{command}.Help") : helpText ) { TargetView = targetView; Command = command; SubMenu = subMenu; } /// public MenuItemv2 (string? commandText = null, string? helpText = null, Action? action = null, Key? key = null) : base (key ?? Key.Empty, commandText, action, helpText) { } /// public MenuItemv2 (string commandText, Key key, Action? action = null) : base (key ?? Key.Empty, commandText, action, null) { } /// public MenuItemv2 (string? commandText = null, string? helpText = null, Menuv2? subMenu = null) : base (Key.Empty, commandText, null, helpText) { SubMenu = subMenu; } // TODO: Consider moving TargetView and Command to Shortcut? /// /// Gets the target that the will be invoked on. /// public View? TargetView { get; set; } private Command _command; /// /// Gets the that will be invoked on when the MenuItem is selected. /// public Command Command { get => _command; set { if (_command == value) { return; } _command = value; if (string.IsNullOrEmpty (Title)) { Title = GlobalResources.GetString ($"cmd.{_command}") ?? string.Empty; } if (string.IsNullOrEmpty (HelpText)) { HelpText = GlobalResources.GetString ($"cmd.{_command}.Help") ?? string.Empty; } } } internal override bool? DispatchCommand (ICommandContext? commandContext) { Logging.Debug ($"{Title} - {commandContext?.Source?.Title} Command: {commandContext?.Command}"); bool? ret = null; bool quit = false; if (commandContext is CommandContext keyCommandContext) { if (keyCommandContext.Binding.Key is { } && keyCommandContext.Binding.Key == Application.QuitKey && SuperView is { Visible: true }) { // This supports a MenuItem with Key = Application.QuitKey/Command = Command.Quit Logging.Debug ($"{Title} - Ignoring Key = Application.QuitKey/Command = Command.Quit"); quit = true; //ret = true; } } // Translate the incoming command to Command if (Command != Command.NotBound && commandContext is { }) { commandContext.Command = Command; } if (!quit) { if (TargetView is { }) { Logging.Debug ($"{Title} - InvokeCommand on TargetView ({TargetView.Title})..."); ret = TargetView.InvokeCommand (Command, commandContext); } else { // Is this an Application-bound command? Logging.Debug ($"{Title} - Application.InvokeCommandsBoundToKey ({Key})..."); ret = Application.InvokeCommandsBoundToKey (Key); } } if (ret is not true) { Logging.Debug ($"{Title} - calling base.DispatchCommand..."); // Base will Raise Selected, then Accepting, then invoke the Action, if any ret = base.DispatchCommand (commandContext); } if (ret is true) { Logging.Debug ($"{Title} - Calling RaiseAccepted"); RaiseAccepted (commandContext); } return ret; } ///// //protected override bool OnAccepting (CommandEventArgs e) //{ // Logging.Debug ($"{Title} - calling base.OnAccepting: {e.Context?.Command}"); // bool? ret = base.OnAccepting (e); // if (ret is true || e.Cancel) // { // return true; // } // //RaiseAccepted (e.Context); // return ret is true; //} private Menuv2? _subMenu; /// /// The submenu to display when the user selects this menu item. /// public Menuv2? SubMenu { get => _subMenu; set { _subMenu = value; if (_subMenu is { }) { SubMenu!.Visible = false; // TODO: This is a temporary hack - add a flag or something instead KeyView.Text = $"{Glyphs.RightArrow}"; _subMenu.SuperMenuItem = this; } } } /// protected override bool OnMouseEnter (CancelEventArgs eventArgs) { // When the mouse enters a menuitem, we set focus to it automatically. // Logging.Trace($"OnEnter {Title}"); SetFocus (); return base.OnMouseEnter (eventArgs); } // TODO: Consider moving Accepted to Shortcut? /// /// Raises the / event indicating this item (or submenu) /// was accepted. This is used to determine when to hide the menu. /// /// /// protected void RaiseAccepted (ICommandContext? ctx) { //Logging.Trace ($"RaiseAccepted: {ctx}"); CommandEventArgs args = new () { Context = ctx }; OnAccepted (args); Accepted?.Invoke (this, args); } /// /// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the /// menu. /// /// /// /// protected virtual void OnAccepted (CommandEventArgs args) { } /// /// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the /// menu. /// /// /// /// See for more information. /// /// public event EventHandler? Accepted; /// protected override void Dispose (bool disposing) { if (disposing) { SubMenu?.Dispose (); SubMenu = null; } base.Dispose (disposing); } }