MenuBarv2.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. #nullable enable
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. namespace Terminal.Gui.Views;
  5. /// <summary>
  6. /// A horizontal list of <see cref="MenuBarItemv2"/>s. Each <see cref="MenuBarItemv2"/> can have a
  7. /// <see cref="PopoverMenu"/> that is shown when the <see cref="MenuBarItemv2"/> is selected.
  8. /// </summary>
  9. /// <remarks>
  10. /// MenuBars may be hosted by any View and will, by default, be positioned the full width across the top of the View's
  11. /// Viewport.
  12. /// </remarks>
  13. public class MenuBarv2 : Menuv2, IDesignable
  14. {
  15. /// <inheritdoc/>
  16. public MenuBarv2 () : this ([]) { }
  17. /// <inheritdoc/>
  18. public MenuBarv2 (IEnumerable<MenuBarItemv2> menuBarItems) : base (menuBarItems)
  19. {
  20. CanFocus = false;
  21. TabStop = TabBehavior.TabGroup;
  22. Y = 0;
  23. Width = Dim.Fill ();
  24. Height = Dim.Auto ();
  25. Orientation = Orientation.Horizontal;
  26. Key = DefaultKey;
  27. AddCommand (
  28. Command.HotKey,
  29. (ctx) =>
  30. {
  31. // Logging.Debug ($"{Title} - Command.HotKey");
  32. if (RaiseHandlingHotKey (ctx) is true)
  33. {
  34. return true;
  35. }
  36. if (HideActiveItem ())
  37. {
  38. return true;
  39. }
  40. if (SubViews.OfType<MenuBarItemv2> ().FirstOrDefault (mbi => mbi.PopoverMenu is { }) is { } first)
  41. {
  42. Active = true;
  43. ShowItem (first);
  44. return true;
  45. }
  46. return false;
  47. });
  48. // If we're not focused, Key activates/deactivates
  49. HotKeyBindings.Add (Key, Command.HotKey);
  50. KeyBindings.Add (Key, Command.Quit);
  51. KeyBindings.ReplaceCommands (Application.QuitKey, Command.Quit);
  52. AddCommand (
  53. Command.Quit,
  54. ctx =>
  55. {
  56. // Logging.Debug ($"{Title} - Command.Quit");
  57. if (HideActiveItem ())
  58. {
  59. return true;
  60. }
  61. if (CanFocus)
  62. {
  63. CanFocus = false;
  64. Active = false;
  65. return true;
  66. }
  67. return false; //RaiseAccepted (ctx);
  68. });
  69. AddCommand (Command.Right, MoveRight);
  70. KeyBindings.Add (Key.CursorRight, Command.Right);
  71. AddCommand (Command.Left, MoveLeft);
  72. KeyBindings.Add (Key.CursorLeft, Command.Left);
  73. BorderStyle = DefaultBorderStyle;
  74. ConfigurationManager.Applied += OnConfigurationManagerApplied;
  75. SuperViewChanged += OnSuperViewChanged;
  76. return;
  77. bool? MoveLeft (ICommandContext? ctx) { return AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop); }
  78. bool? MoveRight (ICommandContext? ctx) { return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); }
  79. }
  80. private void OnSuperViewChanged (object? sender, SuperViewChangedEventArgs e)
  81. {
  82. if (SuperView is null)
  83. {
  84. // BUGBUG: This is a hack for avoiding a race condition in ConfigurationManager.Apply
  85. // BUGBUG: For some reason in some unit tests, when Top is disposed, MenuBar.Dispose does not get called.
  86. // BUGBUG: Yet, the MenuBar does get Removed from Top (and it's SuperView set to null).
  87. // BUGBUG: Related: https://github.com/gui-cs/Terminal.Gui/issues/4021
  88. ConfigurationManager.Applied -= OnConfigurationManagerApplied;
  89. }
  90. }
  91. private void OnConfigurationManagerApplied (object? sender, ConfigurationManagerEventArgs e) { BorderStyle = DefaultBorderStyle; }
  92. /// <inheritdoc/>
  93. protected override bool OnBorderStyleChanged ()
  94. {
  95. //HideActiveItem ();
  96. return base.OnBorderStyleChanged ();
  97. }
  98. /// <summary>
  99. /// Gets or sets the default Border Style for the MenuBar. The default is <see cref="LineStyle.None"/>.
  100. /// </summary>
  101. [ConfigurationProperty (Scope = typeof (ThemeScope))]
  102. public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.None;
  103. private Key _key = DefaultKey;
  104. /// <summary>Specifies the key that will activate the context menu.</summary>
  105. public Key Key
  106. {
  107. get => _key;
  108. set
  109. {
  110. Key oldKey = _key;
  111. _key = value;
  112. KeyChanged?.Invoke (this, new (oldKey, _key));
  113. }
  114. }
  115. /// <summary>
  116. /// Sets the Menu Bar Items for this Menu Bar. This will replace any existing Menu Bar Items.
  117. /// </summary>
  118. /// <remarks>
  119. /// <para>
  120. /// This is a convenience property to help porting from the v1 MenuBar.
  121. /// </para>
  122. /// </remarks>
  123. public MenuBarItemv2 []? Menus
  124. {
  125. set
  126. {
  127. RemoveAll ();
  128. if (value is null)
  129. {
  130. return;
  131. }
  132. foreach (MenuBarItemv2 mbi in value)
  133. {
  134. Add (mbi);
  135. }
  136. }
  137. }
  138. /// <inheritdoc/>
  139. protected override void OnSubViewAdded (View view)
  140. {
  141. base.OnSubViewAdded (view);
  142. if (view is MenuBarItemv2 mbi)
  143. {
  144. mbi.Accepted += OnMenuBarItemAccepted;
  145. mbi.PopoverMenuOpenChanged += OnMenuBarItemPopoverMenuOpenChanged;
  146. }
  147. }
  148. /// <inheritdoc/>
  149. protected override void OnSubViewRemoved (View view)
  150. {
  151. base.OnSubViewRemoved (view);
  152. if (view is MenuBarItemv2 mbi)
  153. {
  154. mbi.Accepted -= OnMenuBarItemAccepted;
  155. mbi.PopoverMenuOpenChanged -= OnMenuBarItemPopoverMenuOpenChanged;
  156. }
  157. }
  158. private void OnMenuBarItemPopoverMenuOpenChanged (object? sender, EventArgs<bool> e)
  159. {
  160. if (sender is MenuBarItemv2 mbi)
  161. {
  162. if (e.Value)
  163. {
  164. Active = true;
  165. }
  166. }
  167. }
  168. private void OnMenuBarItemAccepted (object? sender, CommandEventArgs e)
  169. {
  170. // Logging.Debug ($"{Title} ({e.Context?.Source?.Title}) Command: {e.Context?.Command}");
  171. RaiseAccepted (e.Context);
  172. }
  173. /// <summary>Raised when <see cref="Key"/> is changed.</summary>
  174. public event EventHandler<KeyChangedEventArgs>? KeyChanged;
  175. /// <summary>The default key for activating menu bars.</summary>
  176. [ConfigurationProperty (Scope = typeof (SettingsScope))]
  177. public static Key DefaultKey { get; set; } = Key.F9;
  178. /// <summary>
  179. /// Gets whether any of the menu bar items have a visible <see cref="PopoverMenu"/>.
  180. /// </summary>
  181. /// <exception cref="NotImplementedException"></exception>
  182. public bool IsOpen () { return SubViews.OfType<MenuBarItemv2> ().Count (sv => sv is { PopoverMenuOpen: true }) > 0; }
  183. private bool _active;
  184. /// <summary>
  185. /// Gets or sets whether the menu bar is active or not. When active, the MenuBar can focus and moving the mouse
  186. /// over a MenuBarItem will switch focus to that item. Use <see cref="IsOpen"/> to determine if a PopoverMenu of
  187. /// a MenuBarItem is open.
  188. /// </summary>
  189. /// <returns></returns>
  190. public bool Active
  191. {
  192. get => _active;
  193. internal set
  194. {
  195. if (_active == value)
  196. {
  197. return;
  198. }
  199. _active = value;
  200. // Logging.Debug ($"Active set to {_active} - CanFocus: {CanFocus}, HasFocus: {HasFocus}");
  201. if (!_active)
  202. {
  203. // Hide open Popovers
  204. HideActiveItem ();
  205. }
  206. CanFocus = value;
  207. // Logging.Debug ($"Set CanFocus: {CanFocus}, HasFocus: {HasFocus}");
  208. }
  209. }
  210. /// <inheritdoc/>
  211. protected override bool OnMouseEnter (CancelEventArgs eventArgs)
  212. {
  213. // If the MenuBar does not have focus and the mouse enters: Enable CanFocus
  214. // But do NOT show a Popover unless the user clicks or presses a hotkey
  215. // Logging.Debug ($"CanFocus = {CanFocus}, HasFocus = {HasFocus}");
  216. if (!HasFocus)
  217. {
  218. Active = true;
  219. }
  220. return base.OnMouseEnter (eventArgs);
  221. }
  222. /// <inheritdoc/>
  223. protected override void OnMouseLeave ()
  224. {
  225. // Logging.Debug ($"CanFocus = {CanFocus}, HasFocus = {HasFocus}");
  226. if (!IsOpen ())
  227. {
  228. Active = false;
  229. }
  230. base.OnMouseLeave ();
  231. }
  232. /// <inheritdoc/>
  233. protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? focusedView)
  234. {
  235. // Logging.Debug ($"CanFocus = {CanFocus}, HasFocus = {HasFocus}");
  236. if (!newHasFocus)
  237. {
  238. Active = false;
  239. }
  240. }
  241. /// <inheritdoc/>
  242. protected override void OnSelectedMenuItemChanged (MenuItemv2? selected)
  243. {
  244. // Logging.Debug ($"{Title} ({selected?.Title}) - IsOpen: {IsOpen ()}");
  245. if (IsOpen () && selected is MenuBarItemv2 { PopoverMenuOpen: false } selectedMenuBarItem)
  246. {
  247. ShowItem (selectedMenuBarItem);
  248. }
  249. }
  250. /// <inheritdoc/>
  251. public override void EndInit ()
  252. {
  253. base.EndInit ();
  254. if (Border is { })
  255. {
  256. Border.Thickness = new (0);
  257. Border.LineStyle = LineStyle.None;
  258. }
  259. // TODO: This needs to be done whenever a menuitem in any MenuBarItem changes
  260. foreach (MenuBarItemv2? mbi in SubViews.Select (s => s as MenuBarItemv2))
  261. {
  262. Application.Popover?.Register (mbi?.PopoverMenu);
  263. }
  264. }
  265. /// <inheritdoc/>
  266. protected override bool OnAccepting (CommandEventArgs args)
  267. {
  268. // Logging.Debug ($"{Title} ({args.Context?.Source?.Title})");
  269. // TODO: Ensure sourceMenuBar is actually one of our bar items
  270. if (Visible && Enabled && args.Context?.Source is MenuBarItemv2 { PopoverMenuOpen: false } sourceMenuBarItem)
  271. {
  272. if (!CanFocus)
  273. {
  274. Debug.Assert (!Active);
  275. // We are not Active; change that
  276. Active = true;
  277. ShowItem (sourceMenuBarItem);
  278. if (!sourceMenuBarItem.HasFocus)
  279. {
  280. sourceMenuBarItem.SetFocus ();
  281. }
  282. }
  283. else
  284. {
  285. Debug.Assert (Active);
  286. ShowItem (sourceMenuBarItem);
  287. }
  288. return true;
  289. }
  290. return false;
  291. }
  292. /// <inheritdoc/>
  293. protected override void OnAccepted (CommandEventArgs args)
  294. {
  295. // Logging.Debug ($"{Title} ({args.Context?.Source?.Title}) Command: {args.Context?.Command}");
  296. base.OnAccepted (args);
  297. if (SubViews.OfType<MenuBarItemv2> ().Contains (args.Context?.Source))
  298. {
  299. return;
  300. }
  301. Active = false;
  302. }
  303. /// <summary>
  304. /// Shows the specified popover, but only if the menu bar is active.
  305. /// </summary>
  306. /// <param name="menuBarItem"></param>
  307. private void ShowItem (MenuBarItemv2? menuBarItem)
  308. {
  309. // Logging.Debug ($"{Title} - {menuBarItem?.Id}");
  310. if (!Active || !Visible)
  311. {
  312. // Logging.Debug ($"{Title} - {menuBarItem?.Id} - Not Active, not showing.");
  313. return;
  314. }
  315. // TODO: We should init the PopoverMenu in a smarter way
  316. if (menuBarItem?.PopoverMenu is { IsInitialized: false })
  317. {
  318. menuBarItem.PopoverMenu.BeginInit ();
  319. menuBarItem.PopoverMenu.EndInit ();
  320. }
  321. // If the active Application Popover is part of this MenuBar, hide it.
  322. if (Application.Popover?.GetActivePopover () is PopoverMenu popoverMenu
  323. && popoverMenu.Root?.SuperMenuItem?.SuperView == this)
  324. {
  325. // Logging.Debug ($"{Title} - Calling Application.Popover?.Hide ({popoverMenu.Title})");
  326. Application.Popover.Hide (popoverMenu);
  327. }
  328. if (menuBarItem is null)
  329. {
  330. // Logging.Debug ($"{Title} - menuBarItem is null.");
  331. return;
  332. }
  333. Active = true;
  334. menuBarItem.SetFocus ();
  335. if (menuBarItem.PopoverMenu?.Root is { })
  336. {
  337. menuBarItem.PopoverMenu.Root.SuperMenuItem = menuBarItem;
  338. menuBarItem.PopoverMenu.Root.SchemeName = SchemeName;
  339. }
  340. // Logging.Debug ($"{Title} - \"{menuBarItem.PopoverMenu?.Title}\".MakeVisible");
  341. menuBarItem.PopoverMenu?.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom));
  342. menuBarItem.Accepting += OnMenuItemAccepted;
  343. return;
  344. void OnMenuItemAccepted (object? sender, EventArgs args)
  345. {
  346. // Logging.Debug ($"{Title} - OnMenuItemAccepted");
  347. if (menuBarItem.PopoverMenu is { })
  348. {
  349. menuBarItem.PopoverMenu.VisibleChanged -= OnMenuItemAccepted;
  350. }
  351. if (Active && menuBarItem.PopoverMenu is { Visible: false })
  352. {
  353. Active = false;
  354. HasFocus = false;
  355. }
  356. }
  357. }
  358. private MenuBarItemv2? GetActiveItem () { return SubViews.OfType<MenuBarItemv2> ().FirstOrDefault (sv => sv is { PopoverMenu: { Visible: true } }); }
  359. /// <summary>
  360. /// Hides the popover menu associated with the active menu bar item and updates the focus state.
  361. /// </summary>
  362. /// <returns><see langword="true"/> if the popover was hidden</returns>
  363. public bool HideActiveItem () { return HideItem (GetActiveItem ()); }
  364. /// <summary>
  365. /// Hides popover menu associated with the specified menu bar item and updates the focus state.
  366. /// </summary>
  367. /// <param name="activeItem"></param>
  368. /// <returns><see langword="true"/> if the popover was hidden</returns>
  369. public bool HideItem (MenuBarItemv2? activeItem)
  370. {
  371. // Logging.Debug ($"{Title} ({activeItem?.Title}) - Active: {Active}, CanFocus: {CanFocus}, HasFocus: {HasFocus}");
  372. if (activeItem is null || !activeItem.PopoverMenu!.Visible)
  373. {
  374. // Logging.Debug ($"{Title} No active item.");
  375. return false;
  376. }
  377. // IMPORTANT: Set Visible false before setting Active to false (Active changes Can/HasFocus)
  378. activeItem.PopoverMenu!.Visible = false;
  379. Active = false;
  380. HasFocus = false;
  381. return true;
  382. }
  383. /// <summary>
  384. /// Gets all menu items with the specified Title, anywhere in the menu hierarchy.
  385. /// </summary>
  386. /// <param name="title"></param>
  387. /// <returns></returns>
  388. public IEnumerable<MenuItemv2> GetMenuItemsWithTitle (string title)
  389. {
  390. List<MenuItemv2> menuItems = new ();
  391. if (string.IsNullOrEmpty (title))
  392. {
  393. return menuItems;
  394. }
  395. foreach (MenuBarItemv2 mbi in SubViews.OfType<MenuBarItemv2> ())
  396. {
  397. if (mbi.PopoverMenu is { })
  398. {
  399. menuItems.AddRange (mbi.PopoverMenu.GetMenuItemsOfAllSubMenus ());
  400. }
  401. }
  402. return menuItems.Where (mi => mi.Title == title);
  403. }
  404. /// <inheritdoc/>
  405. public bool EnableForDesign<TContext> (ref TContext context) where TContext : notnull
  406. {
  407. // Note: This menu is used by unit tests. If you modify it, you'll likely have to update
  408. // unit tests.
  409. Id = "DemoBar";
  410. var bordersCb = new CheckBox
  411. {
  412. Title = "_Borders",
  413. CheckedState = CheckState.Checked
  414. };
  415. var autoSaveCb = new CheckBox
  416. {
  417. Title = "_Auto Save"
  418. };
  419. var enableOverwriteCb = new CheckBox
  420. {
  421. Title = "Enable _Overwrite"
  422. };
  423. var mutuallyExclusiveOptionsSelector = new OptionSelector
  424. {
  425. Options = ["G_ood", "_Bad", "U_gly"],
  426. SelectedItem = 0
  427. };
  428. var menuBgColorCp = new ColorPicker
  429. {
  430. Width = 30
  431. };
  432. menuBgColorCp.ColorChanged += (sender, args) =>
  433. {
  434. // BUGBUG: This is weird.
  435. SetScheme (
  436. GetScheme () with
  437. {
  438. Normal = new (
  439. GetAttributeForRole (VisualRole.Normal).Foreground,
  440. args.Result,
  441. GetAttributeForRole (VisualRole.Normal).Style)
  442. });
  443. };
  444. Add (
  445. new MenuBarItemv2 (
  446. "_File",
  447. [
  448. new MenuItemv2 (context as View, Command.New),
  449. new MenuItemv2 (context as View, Command.Open),
  450. new MenuItemv2 (context as View, Command.Save),
  451. new MenuItemv2 (context as View, Command.SaveAs),
  452. new Line (),
  453. new MenuItemv2
  454. {
  455. Title = "_File Options",
  456. SubMenu = new (
  457. [
  458. new ()
  459. {
  460. Id = "AutoSave",
  461. Text = "(no Command)",
  462. Key = Key.F10,
  463. CommandView = autoSaveCb
  464. },
  465. new ()
  466. {
  467. Text = "Overwrite",
  468. Id = "Overwrite",
  469. Key = Key.W.WithCtrl,
  470. CommandView = enableOverwriteCb,
  471. Command = Command.EnableOverwrite,
  472. TargetView = context as View
  473. },
  474. new ()
  475. {
  476. Title = "_File Settings...",
  477. HelpText = "More file settings",
  478. Action = () => MessageBox.Query (
  479. "File Settings",
  480. "This is the File Settings Dialog\n",
  481. "_Ok",
  482. "_Cancel")
  483. }
  484. ]
  485. )
  486. },
  487. new Line (),
  488. new MenuItemv2
  489. {
  490. Title = "_Preferences",
  491. SubMenu = new (
  492. [
  493. new MenuItemv2
  494. {
  495. CommandView = bordersCb,
  496. HelpText = "Toggle Menu Borders",
  497. Action = ToggleMenuBorders
  498. },
  499. new MenuItemv2
  500. {
  501. HelpText = "3 Mutually Exclusive Options",
  502. CommandView = mutuallyExclusiveOptionsSelector,
  503. Key = Key.F7
  504. },
  505. new Line (),
  506. new MenuItemv2
  507. {
  508. HelpText = "MenuBar BG Color",
  509. CommandView = menuBgColorCp,
  510. Key = Key.F8
  511. }
  512. ]
  513. )
  514. },
  515. new Line (),
  516. new MenuItemv2
  517. {
  518. TargetView = context as View,
  519. Key = Application.QuitKey,
  520. Command = Command.Quit
  521. }
  522. ]
  523. )
  524. );
  525. Add (
  526. new MenuBarItemv2 (
  527. "_Edit",
  528. [
  529. new MenuItemv2 (context as View, Command.Cut),
  530. new MenuItemv2 (context as View, Command.Copy),
  531. new MenuItemv2 (context as View, Command.Paste),
  532. new Line (),
  533. new MenuItemv2 (context as View, Command.SelectAll),
  534. new Line (),
  535. new MenuItemv2
  536. {
  537. Title = "_Details",
  538. SubMenu = new (ConfigureDetailsSubMenu ())
  539. }
  540. ]
  541. )
  542. );
  543. Add (
  544. new MenuBarItemv2 (
  545. "_Help",
  546. [
  547. new MenuItemv2
  548. {
  549. Title = "_Online Help...",
  550. Action = () => MessageBox.Query ("Online Help", "https://gui-cs.github.io/Terminal.Gui", "Ok")
  551. },
  552. new MenuItemv2
  553. {
  554. Title = "About...",
  555. Action = () => MessageBox.Query ("About", "Something About Mary.", "Ok")
  556. }
  557. ]
  558. )
  559. );
  560. return true;
  561. void ToggleMenuBorders ()
  562. {
  563. foreach (MenuBarItemv2 mbi in SubViews.OfType<MenuBarItemv2> ())
  564. {
  565. if (mbi is not { PopoverMenu: { } })
  566. {
  567. continue;
  568. }
  569. foreach (Menuv2? subMenu in mbi.PopoverMenu.GetAllSubMenus ())
  570. {
  571. if (bordersCb.CheckedState == CheckState.Checked)
  572. {
  573. subMenu.Border!.Thickness = new (1);
  574. }
  575. else
  576. {
  577. subMenu.Border!.Thickness = new (0);
  578. }
  579. }
  580. }
  581. }
  582. MenuItemv2 [] ConfigureDetailsSubMenu ()
  583. {
  584. var detail = new MenuItemv2
  585. {
  586. Title = "_Detail 1",
  587. Text = "Some detail #1"
  588. };
  589. var nestedSubMenu = new MenuItemv2
  590. {
  591. Title = "_Moar Details",
  592. SubMenu = new (ConfigureMoreDetailsSubMenu ())
  593. };
  594. var editMode = new MenuItemv2
  595. {
  596. Text = "App Binding to Command.Edit",
  597. Id = "EditMode",
  598. Command = Command.Edit,
  599. CommandView = new CheckBox
  600. {
  601. Title = "E_dit Mode"
  602. }
  603. };
  604. return [detail, nestedSubMenu, null!, editMode];
  605. View [] ConfigureMoreDetailsSubMenu ()
  606. {
  607. var deeperDetail = new MenuItemv2
  608. {
  609. Title = "_Deeper Detail",
  610. Text = "Deeper Detail",
  611. Action = () => { MessageBox.Query ("Deeper Detail", "Lots of details", "_Ok"); }
  612. };
  613. var belowLineDetail = new MenuItemv2
  614. {
  615. Title = "_Even more detail",
  616. Text = "Below the line"
  617. };
  618. // This ensures the checkbox state toggles when the hotkey of Title is pressed.
  619. //shortcut4.Accepting += (sender, args) => args.Cancel = true;
  620. return [deeperDetail, new Line (), belowLineDetail];
  621. }
  622. }
  623. }
  624. /// <inheritdoc/>
  625. protected override void Dispose (bool disposing)
  626. {
  627. base.Dispose (disposing);
  628. if (disposing)
  629. {
  630. SuperViewChanged += OnSuperViewChanged;
  631. ConfigurationManager.Applied -= OnConfigurationManagerApplied;
  632. }
  633. }
  634. }