2
0

MenuItemv2.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #nullable enable
  2. using System.ComponentModel;
  3. using Terminal.Gui.Resources;
  4. namespace Terminal.Gui;
  5. /// <summary>
  6. /// A <see cref="Shortcut"/>-derived object to be used as a menu item in a <see cref="Menuv2"/>. Has title, an
  7. /// A <see cref="Shortcut"/>-derived object to be used as a menu item in a <see cref="Menuv2"/>. Has title, an
  8. /// associated help text, and an action to execute on activation.
  9. /// </summary>
  10. public class MenuItemv2 : Shortcut
  11. {
  12. /// <summary>
  13. /// Creates a new instance of <see cref="MenuItemv2"/>.
  14. /// </summary>
  15. public MenuItemv2 () : base (Key.Empty, null, null) { }
  16. /// <summary>
  17. /// Creates a new instance of <see cref="MenuItemv2"/>, binding it to <paramref name="targetView"/> and
  18. /// <paramref name="command"/>. The Key <paramref name="targetView"/>
  19. /// has bound to <paramref name="command"/> will be used as <see cref="Key"/>.
  20. /// </summary>
  21. /// <remarks>
  22. /// </remarks>
  23. /// <param name="targetView">
  24. /// The View that <paramref name="command"/> will be invoked on when user does something that causes the Shortcut's
  25. /// Accept
  26. /// event to be raised.
  27. /// </param>
  28. /// <param name="command">
  29. /// The Command to invoke on <paramref name="targetView"/>. The Key <paramref name="targetView"/>
  30. /// has bound to <paramref name="command"/> will be used as <see cref="Key"/>
  31. /// </param>
  32. /// <param name="commandText">The text to display for the command.</param>
  33. /// <param name="helpText">The help text to display.</param>
  34. /// <param name="subMenu">The submenu to display when the user selects this menu item.</param>
  35. public MenuItemv2 (View? targetView, Command command, string? commandText = null, string? helpText = null, Menuv2? subMenu = null)
  36. : base (
  37. targetView?.HotKeyBindings.GetFirstFromCommands (command)!,
  38. string.IsNullOrEmpty (commandText) ? GlobalResources.GetString ($"cmd.{command}") : commandText,
  39. null,
  40. string.IsNullOrEmpty (helpText) ? GlobalResources.GetString ($"cmd.{command}.Help") : helpText
  41. )
  42. {
  43. TargetView = targetView;
  44. Command = command;
  45. SubMenu = subMenu;
  46. }
  47. /// <inheritdoc/>
  48. public MenuItemv2 (string? commandText = null, string? helpText = null, Action? action = null, Key? key = null)
  49. : base (key ?? Key.Empty, commandText, action, helpText)
  50. { }
  51. /// <inheritdoc/>
  52. public MenuItemv2 (string commandText, Key key, Action? action = null)
  53. : base (key ?? Key.Empty, commandText, action, null)
  54. { }
  55. /// <inheritdoc/>
  56. public MenuItemv2 (string? commandText = null, string? helpText = null, Menuv2? subMenu = null)
  57. : base (Key.Empty, commandText, null, helpText)
  58. {
  59. SubMenu = subMenu;
  60. }
  61. // TODO: Consider moving TargetView and Command to Shortcut?
  62. /// <summary>
  63. /// Gets the target <see cref="View"/> that the <see cref="Command"/> will be invoked on.
  64. /// </summary>
  65. public View? TargetView { get; set; }
  66. private Command _command;
  67. /// <summary>
  68. /// Gets the <see cref="Command"/> that will be invoked on <see cref="TargetView"/> when the MenuItem is selected.
  69. /// </summary>
  70. public Command Command
  71. {
  72. get => _command;
  73. set
  74. {
  75. if (_command == value)
  76. {
  77. return;
  78. }
  79. _command = value;
  80. if (string.IsNullOrEmpty (Title))
  81. {
  82. Title = GlobalResources.GetString ($"cmd.{_command}") ?? string.Empty;
  83. }
  84. if (string.IsNullOrEmpty (HelpText))
  85. {
  86. HelpText = GlobalResources.GetString ($"cmd.{_command}.Help") ?? string.Empty;
  87. }
  88. }
  89. }
  90. internal override bool? DispatchCommand (ICommandContext? commandContext)
  91. {
  92. Logging.Debug ($"{Title} - {commandContext?.Source?.Title} Command: {commandContext?.Command}");
  93. bool? ret = null;
  94. bool quit = false;
  95. if (commandContext is CommandContext<KeyBinding> keyCommandContext)
  96. {
  97. if (keyCommandContext.Binding.Key is { } && keyCommandContext.Binding.Key == Application.QuitKey && SuperView is { Visible: true })
  98. {
  99. // This supports a MenuItem with Key = Application.QuitKey/Command = Command.Quit
  100. Logging.Debug ($"{Title} - Ignoring Key = Application.QuitKey/Command = Command.Quit");
  101. quit = true;
  102. //ret = true;
  103. }
  104. }
  105. // Translate the incoming command to Command
  106. if (Command != Command.NotBound && commandContext is { })
  107. {
  108. commandContext.Command = Command;
  109. }
  110. if (!quit)
  111. {
  112. if (TargetView is { })
  113. {
  114. Logging.Debug ($"{Title} - InvokeCommand on TargetView ({TargetView.Title})...");
  115. ret = TargetView.InvokeCommand (Command, commandContext);
  116. }
  117. else
  118. {
  119. // Is this an Application-bound command?
  120. Logging.Debug ($"{Title} - Application.InvokeCommandsBoundToKey ({Key})...");
  121. ret = Application.InvokeCommandsBoundToKey (Key);
  122. }
  123. }
  124. if (ret is not true)
  125. {
  126. Logging.Debug ($"{Title} - calling base.DispatchCommand...");
  127. // Base will Raise Selected, then Accepting, then invoke the Action, if any
  128. ret = base.DispatchCommand (commandContext);
  129. }
  130. if (ret is true)
  131. {
  132. Logging.Debug ($"{Title} - Calling RaiseAccepted");
  133. RaiseAccepted (commandContext);
  134. }
  135. return ret;
  136. }
  137. ///// <inheritdoc />
  138. //protected override bool OnAccepting (CommandEventArgs e)
  139. //{
  140. // Logging.Debug ($"{Title} - calling base.OnAccepting: {e.Context?.Command}");
  141. // bool? ret = base.OnAccepting (e);
  142. // if (ret is true || e.Cancel)
  143. // {
  144. // return true;
  145. // }
  146. // //RaiseAccepted (e.Context);
  147. // return ret is true;
  148. //}
  149. private Menuv2? _subMenu;
  150. /// <summary>
  151. /// The submenu to display when the user selects this menu item.
  152. /// </summary>
  153. public Menuv2? SubMenu
  154. {
  155. get => _subMenu;
  156. set
  157. {
  158. _subMenu = value;
  159. if (_subMenu is { })
  160. {
  161. SubMenu!.Visible = false;
  162. // TODO: This is a temporary hack - add a flag or something instead
  163. KeyView.Text = $"{Glyphs.RightArrow}";
  164. _subMenu.SuperMenuItem = this;
  165. }
  166. }
  167. }
  168. /// <inheritdoc/>
  169. protected override bool OnMouseEnter (CancelEventArgs eventArgs)
  170. {
  171. // When the mouse enters a menuitem, we set focus to it automatically.
  172. // Logging.Trace($"OnEnter {Title}");
  173. SetFocus ();
  174. return base.OnMouseEnter (eventArgs);
  175. }
  176. // TODO: Consider moving Accepted to Shortcut?
  177. /// <summary>
  178. /// Raises the <see cref="OnAccepted"/>/<see cref="Accepted"/> event indicating this item (or submenu)
  179. /// was accepted. This is used to determine when to hide the menu.
  180. /// </summary>
  181. /// <param name="ctx"></param>
  182. /// <returns></returns>
  183. protected void RaiseAccepted (ICommandContext? ctx)
  184. {
  185. //Logging.Trace ($"RaiseAccepted: {ctx}");
  186. CommandEventArgs args = new () { Context = ctx };
  187. OnAccepted (args);
  188. Accepted?.Invoke (this, args);
  189. }
  190. /// <summary>
  191. /// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the
  192. /// menu.
  193. /// </summary>
  194. /// <remarks>
  195. /// </remarks>
  196. /// <param name="args"></param>
  197. protected virtual void OnAccepted (CommandEventArgs args) { }
  198. /// <summary>
  199. /// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the
  200. /// menu.
  201. /// </summary>
  202. /// <remarks>
  203. /// <para>
  204. /// See <see cref="RaiseAccepted"/> for more information.
  205. /// </para>
  206. /// </remarks>
  207. public event EventHandler<CommandEventArgs>? Accepted;
  208. /// <inheritdoc/>
  209. protected override void Dispose (bool disposing)
  210. {
  211. if (disposing)
  212. {
  213. SubMenu?.Dispose ();
  214. SubMenu = null;
  215. }
  216. base.Dispose (disposing);
  217. }
  218. }