| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- using System.ComponentModel;
- namespace Terminal.Gui.Views;
- /// <summary>
- /// A <see cref="Shortcut"/>-derived object to be used as a menu item in a <see cref="Menu"/>. Has title, an
- /// A <see cref="Shortcut"/>-derived object to be used as a menu item in a <see cref="Menu"/>. Has title, an
- /// associated help text, and an action to execute on activation.
- /// </summary>
- public class MenuItem : Shortcut
- {
- /// <summary>
- /// Creates a new instance of <see cref="MenuItem"/>.
- /// </summary>
- public MenuItem () : base (Key.Empty, null, null) { }
- /// <summary>
- /// Creates a new instance of <see cref="MenuItem"/>, binding it to <paramref name="targetView"/> and
- /// <paramref name="command"/>. The Key <paramref name="targetView"/>
- /// has bound to <paramref name="command"/> will be used as <see cref="Key"/>.
- /// </summary>
- /// <remarks>
- /// </remarks>
- /// <param name="targetView">
- /// The View that <paramref name="command"/> will be invoked on when user does something that causes the Shortcut's
- /// Accept
- /// event to be raised.
- /// </param>
- /// <param name="command">
- /// The Command to invoke on <paramref name="targetView"/>. The Key <paramref name="targetView"/>
- /// has bound to <paramref name="command"/> will be used as <see cref="Key"/>
- /// </param>
- /// <param name="commandText">The text to display for the command.</param>
- /// <param name="helpText">The help text to display.</param>
- /// <param name="subMenu">The submenu to display when the user selects this menu item.</param>
- public MenuItem (View? targetView, Command command, string? commandText = null, string? helpText = null, Menu? 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;
- }
- /// <inheritdoc/>
- public MenuItem (string? commandText = null, string? helpText = null, Action? action = null, Key? key = null)
- : base (key ?? Key.Empty, commandText, action, helpText)
- { }
- /// <inheritdoc/>
- public MenuItem (string commandText, Key key, Action? action = null)
- : base (key ?? Key.Empty, commandText, action, null)
- { }
- /// <inheritdoc/>
- public MenuItem (string? commandText = null, string? helpText = null, Menu? subMenu = null)
- : base (Key.Empty, commandText, null, helpText)
- {
- SubMenu = subMenu;
- }
- // TODO: Consider moving TargetView and Command to Shortcut?
- /// <summary>
- /// Gets the target <see cref="View"/> that the <see cref="Command"/> will be invoked on.
- /// </summary>
- public View? TargetView { get; set; }
- private Command _command;
- /// <summary>
- /// Gets the <see cref="Command"/> that will be invoked on <see cref="TargetView"/> when the MenuItem is selected.
- /// </summary>
- 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<KeyBinding> 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 = App?.Keyboard.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;
- }
- ///// <inheritdoc />
- //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 Menu? _subMenu;
- /// <summary>
- /// The submenu to display when the user selects this menu item.
- /// </summary>
- public Menu? SubMenu
- {
- get => _subMenu;
- set
- {
- _subMenu = value;
- if (_subMenu is { })
- {
- SubMenu!.App ??= App;
- SubMenu!.Visible = false;
- // TODO: This is a temporary hack - add a flag or something instead
- KeyView.Text = $"{Glyphs.RightArrow}";
- _subMenu.SuperMenuItem = this;
- }
- }
- }
- /// <inheritdoc/>
- 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?
- /// <summary>
- /// Raises the <see cref="OnAccepted"/>/<see cref="Accepted"/> event indicating this item (or submenu)
- /// was accepted. This is used to determine when to hide the menu.
- /// </summary>
- /// <param name="ctx"></param>
- /// <returns></returns>
- protected void RaiseAccepted (ICommandContext? ctx)
- {
- //Logging.Trace ($"RaiseAccepted: {ctx}");
- CommandEventArgs args = new () { Context = ctx };
- OnAccepted (args);
- Accepted?.Invoke (this, args);
- }
- /// <summary>
- /// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the
- /// menu.
- /// </summary>
- /// <remarks>
- /// </remarks>
- /// <param name="args"></param>
- protected virtual void OnAccepted (CommandEventArgs args) { }
- /// <summary>
- /// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the
- /// menu.
- /// </summary>
- /// <remarks>
- /// <para>
- /// See <see cref="RaiseAccepted"/> for more information.
- /// </para>
- /// </remarks>
- public event EventHandler<CommandEventArgs>? Accepted;
- /// <inheritdoc/>
- protected override void Dispose (bool disposing)
- {
- if (disposing)
- {
- SubMenu?.Dispose ();
- SubMenu = null;
- }
- base.Dispose (disposing);
- }
- }
|