MenuBarItemv2.cs 6.7 KB

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