MenuBarItemv2.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. namespace Terminal.Gui.Views;
  2. /// <summary>
  3. /// A <see cref="Shortcut"/>-derived object to be used as items in a <see cref="MenuBarv2"/>.
  4. /// MenuBarItems hold a <see cref="PopoverMenu"/> instead of a <see cref="SubMenu"/>.
  5. /// </summary>
  6. public class MenuBarItemv2 : MenuItemv2
  7. {
  8. /// <summary>
  9. /// Creates a new instance of <see cref="MenuBarItemv2"/>.
  10. /// </summary>
  11. public MenuBarItemv2 () : base (null, Command.NotBound) { }
  12. /// <summary>
  13. /// Creates a new instance of <see cref="MenuBarItemv2"/>. Each MenuBarItem typically has a <see cref="PopoverMenu"/>
  14. /// that is
  15. /// shown when the item is selected.
  16. /// </summary>
  17. /// <remarks>
  18. /// </remarks>
  19. /// <param name="targetView">
  20. /// The View that <paramref name="command"/> will be invoked on when user does something that causes the MenuBarItems's
  21. /// Accept event to be raised.
  22. /// </param>
  23. /// <param name="command">
  24. /// The Command to invoke on <paramref name="targetView"/>. The Key <paramref name="targetView"/>
  25. /// has bound to <paramref name="command"/> will be used as <see cref="Key"/>
  26. /// </param>
  27. /// <param name="commandText">The text to display for the command.</param>
  28. /// <param name="popoverMenu">The Popover Menu that will be displayed when this item is selected.</param>
  29. public MenuBarItemv2 (View? targetView, Command command, string? commandText, PopoverMenu? popoverMenu = null)
  30. : base (
  31. targetView,
  32. command,
  33. commandText)
  34. {
  35. TargetView = targetView;
  36. Command = command;
  37. PopoverMenu = popoverMenu;
  38. }
  39. /// <summary>
  40. /// Creates a new instance of <see cref="MenuBarItemv2"/> with the specified <paramref name="popoverMenu"/>. This is a
  41. /// helper for the most common MenuBar use-cases.
  42. /// </summary>
  43. /// <remarks>
  44. /// </remarks>
  45. /// <param name="commandText">The text to display for the command.</param>
  46. /// <param name="popoverMenu">The Popover Menu that will be displayed when this item is selected.</param>
  47. public MenuBarItemv2 (string commandText, PopoverMenu? popoverMenu = null)
  48. : this (
  49. null,
  50. Command.NotBound,
  51. commandText,
  52. popoverMenu)
  53. { }
  54. /// <summary>
  55. /// Creates a new instance of <see cref="MenuBarItemv2"/> with the <paramref name="menuItems"/> automatcialy added to a
  56. /// <see cref="PopoverMenu"/>.
  57. /// This is a helper for the most common MenuBar use-cases.
  58. /// </summary>
  59. /// <remarks>
  60. /// </remarks>
  61. /// <param name="commandText">The text to display for the command.</param>
  62. /// <param name="menuItems">
  63. /// The menu items that will be added to the Popover Menu that will be displayed when this item is
  64. /// selected.
  65. /// </param>
  66. public MenuBarItemv2 (string commandText, IEnumerable<View> menuItems)
  67. : this (
  68. null,
  69. Command.NotBound,
  70. commandText,
  71. new (menuItems) { Title = $"PopoverMenu for {commandText}" })
  72. { }
  73. /// <summary>
  74. /// Do not use this property. MenuBarItem does not support SubMenu. Use <see cref="PopoverMenu"/> instead.
  75. /// </summary>
  76. /// <exception cref="InvalidOperationException"></exception>
  77. public new Menuv2? SubMenu
  78. {
  79. get => null;
  80. set => throw new InvalidOperationException ("MenuBarItem does not support SubMenu. Use PopoverMenu instead.");
  81. }
  82. private PopoverMenu? _popoverMenu;
  83. /// <summary>
  84. /// The Popover Menu that will be displayed when this item is selected.
  85. /// </summary>
  86. public PopoverMenu? PopoverMenu
  87. {
  88. get => _popoverMenu;
  89. set
  90. {
  91. if (_popoverMenu == value)
  92. {
  93. return;
  94. }
  95. if (_popoverMenu is { })
  96. {
  97. _popoverMenu.VisibleChanged -= OnPopoverVisibleChanged;
  98. _popoverMenu.Accepted -= OnPopoverMenuOnAccepted;
  99. }
  100. _popoverMenu = value;
  101. if (_popoverMenu is { })
  102. {
  103. PopoverMenuOpen = _popoverMenu.Visible;
  104. _popoverMenu.VisibleChanged += OnPopoverVisibleChanged;
  105. _popoverMenu.Accepted += OnPopoverMenuOnAccepted;
  106. }
  107. return;
  108. void OnPopoverVisibleChanged (object? sender, EventArgs args)
  109. {
  110. // Logging.Debug ($"OnPopoverVisibleChanged - {Title} - Visible = {_popoverMenu?.Visible} ");
  111. PopoverMenuOpen = _popoverMenu?.Visible ?? false;
  112. }
  113. void OnPopoverMenuOnAccepted (object? sender, CommandEventArgs args)
  114. {
  115. // Logging.Debug ($"OnPopoverMenuOnAccepted - {Title} - {args.Context?.Source?.Title} - {args.Context?.Command}");
  116. RaiseAccepted (args.Context);
  117. }
  118. }
  119. }
  120. private bool _popoverMenuOpen;
  121. /// <summary>
  122. /// Gets or sets whether the MenuBarItem is active. This is used to determine if the MenuBarItem should be
  123. /// </summary>
  124. public bool PopoverMenuOpen
  125. {
  126. get => _popoverMenuOpen;
  127. set
  128. {
  129. if (_popoverMenuOpen == value)
  130. {
  131. return;
  132. }
  133. _popoverMenuOpen = value;
  134. RaisePopoverMenuOpenChanged();
  135. }
  136. }
  137. /// <summary>
  138. ///
  139. /// </summary>
  140. public void RaisePopoverMenuOpenChanged ()
  141. {
  142. OnPopoverMenuOpenChanged();
  143. PopoverMenuOpenChanged?.Invoke (this, new EventArgs<bool> (PopoverMenuOpen));
  144. }
  145. /// <summary>
  146. ///
  147. /// </summary>
  148. protected virtual void OnPopoverMenuOpenChanged () {}
  149. /// <summary>
  150. ///
  151. /// </summary>
  152. public event EventHandler<EventArgs<bool>>? PopoverMenuOpenChanged;
  153. /// <inheritdoc />
  154. protected override bool OnKeyDownNotHandled (Key key)
  155. {
  156. Logging.Trace ($"{key}");
  157. if (PopoverMenu is { Visible: true } && HotKeyBindings.TryGet (key, out _))
  158. {
  159. // If the user presses the hotkey for a menu item that is already open,
  160. // it should close the menu item (Test: MenuBarItem_HotKey_DeActivates)
  161. if (SuperView is MenuBarv2 { } menuBar)
  162. {
  163. menuBar.HideActiveItem ();
  164. }
  165. return true;
  166. }
  167. return false;
  168. }
  169. /// <inheritdoc/>
  170. protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? focusedView)
  171. {
  172. // Logging.Debug ($"CanFocus = {CanFocus}, HasFocus = {HasFocus}");
  173. }
  174. /// <inheritdoc/>
  175. protected override void Dispose (bool disposing)
  176. {
  177. if (disposing)
  178. {
  179. PopoverMenu?.Dispose ();
  180. PopoverMenu = null;
  181. }
  182. base.Dispose (disposing);
  183. }
  184. }