MenuBarv2.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. #nullable enable
  2. namespace Terminal.Gui;
  3. /// <summary>
  4. /// A horizontal list of <see cref="MenuBarItemv2"/>s. Each <see cref="MenuBarItemv2"/> can have a
  5. /// <see cref="PopoverMenu"/> that is shown when the <see cref="MenuBarItemv2"/> is selected.
  6. /// </summary>
  7. /// <remarks>
  8. /// MenuBars may be hosted by any View and will, by default, be positioned the full width across the top of the View's
  9. /// Viewport.
  10. /// </remarks>
  11. public class MenuBarv2 : Menuv2, IDesignable
  12. {
  13. /// <inheritdoc/>
  14. public MenuBarv2 () : this ([]) { }
  15. /// <inheritdoc/>
  16. public MenuBarv2 (IEnumerable<MenuBarItemv2> menuBarItems) : base (menuBarItems)
  17. {
  18. TabStop = TabBehavior.TabGroup;
  19. Y = 0;
  20. Width = Dim.Fill ();
  21. Orientation = Orientation.Horizontal;
  22. AddCommand (Command.Right, MoveRight);
  23. KeyBindings.Add (Key.CursorRight, Command.Right);
  24. AddCommand (Command.Left, MoveLeft);
  25. KeyBindings.Add (Key.CursorLeft, Command.Left);
  26. return;
  27. bool? MoveLeft (ICommandContext? ctx) { return AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop); }
  28. bool? MoveRight (ICommandContext? ctx) { return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); }
  29. }
  30. /// <inheritdoc/>
  31. protected override void OnSelectedMenuItemChanged (MenuItemv2? selected)
  32. {
  33. if (selected is MenuBarItemv2 { } selectedMenuBarItem)
  34. {
  35. ShowPopover (selectedMenuBarItem);
  36. }
  37. }
  38. /// <inheritdoc/>
  39. public override void EndInit ()
  40. {
  41. base.EndInit ();
  42. if (Border is { })
  43. {
  44. Border.Thickness = new (0);
  45. Border.LineStyle = LineStyle.None;
  46. }
  47. // TODO: This needs to be done whenever a menuitem in any memubaritem changes
  48. foreach (MenuBarItemv2? mbi in SubViews.Select(s => s as MenuBarItemv2))
  49. {
  50. Application.Popover?.Register (mbi?.PopoverMenu);
  51. }
  52. }
  53. /// <inheritdoc/>
  54. protected override bool OnAccepting (CommandEventArgs args)
  55. {
  56. if (args.Context?.Source is MenuBarItemv2 { PopoverMenu: { } } menuBarItem)
  57. {
  58. ShowPopover (menuBarItem);
  59. }
  60. return base.OnAccepting (args);
  61. }
  62. private void ShowPopover (MenuBarItemv2? menuBarItem)
  63. {
  64. if (menuBarItem?.PopoverMenu is { IsInitialized: false })
  65. {
  66. menuBarItem.PopoverMenu.BeginInit ();
  67. menuBarItem.PopoverMenu.EndInit ();
  68. }
  69. // If the active popover is a PopoverMenu and part of this MenuBar...
  70. if (menuBarItem?.PopoverMenu is null
  71. && Application.Popover?.GetActivePopover () is PopoverMenu popoverMenu
  72. && popoverMenu?.Root?.SuperMenuItem?.SuperView == this)
  73. {
  74. Application.Popover?.HidePopover (popoverMenu);
  75. }
  76. menuBarItem?.PopoverMenu?.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom));
  77. if (menuBarItem?.PopoverMenu?.Root is { })
  78. {
  79. menuBarItem.PopoverMenu.Root.SuperMenuItem = menuBarItem;
  80. }
  81. }
  82. /// <inheritdoc/>
  83. public bool EnableForDesign<TContext> (ref readonly TContext context) where TContext : notnull
  84. {
  85. Add (
  86. new MenuBarItemv2 (
  87. "_File",
  88. [
  89. new MenuItemv2 (this, Command.New),
  90. new MenuItemv2 (this, Command.Open),
  91. new MenuItemv2 (this, Command.Save),
  92. new MenuItemv2 (this, Command.SaveAs),
  93. new Line (),
  94. new MenuItemv2
  95. {
  96. Title = "_Preferences",
  97. SubMenu = new (
  98. [
  99. new MenuItemv2
  100. {
  101. CommandView = new CheckBox ()
  102. {
  103. Title = "O_ption",
  104. },
  105. HelpText = "Toggle option"
  106. },
  107. new MenuItemv2
  108. {
  109. Title = "_Settings...",
  110. HelpText = "More settings",
  111. Action = () => MessageBox.Query ("Settings", "This is the Settings Dialog\n", ["_Ok", "_Cancel"])
  112. }
  113. ]
  114. )
  115. },
  116. new Line (),
  117. new MenuItemv2 (this, Command.Quit)
  118. ]
  119. )
  120. );
  121. Add (
  122. new MenuBarItemv2 (
  123. "_Edit",
  124. [
  125. new MenuItemv2 (this, Command.Cut),
  126. new MenuItemv2 (this, Command.Copy),
  127. new MenuItemv2 (this, Command.Paste),
  128. new Line (),
  129. new MenuItemv2 (this, Command.SelectAll)
  130. ]
  131. )
  132. );
  133. Add (
  134. new MenuBarItemv2 (
  135. "_Help",
  136. [
  137. new MenuItemv2
  138. {
  139. Title = "_Online Help...",
  140. Action = () => MessageBox.Query ("Online Help", "https://gui-cs.github.io/Terminal.GuiV2Docs", "Ok")
  141. },
  142. new MenuItemv2
  143. {
  144. Title = "About...",
  145. Action = () => MessageBox.Query ("About", "Something About Mary.", "Ok")
  146. }
  147. ]
  148. )
  149. );
  150. // if (context is not Func<string, bool> actionFn)
  151. // {
  152. // actionFn = (_) => true;
  153. // }
  154. // View? targetView = context as View;
  155. // Add (new MenuItemv2 (targetView,
  156. // Command.NotBound,
  157. // "_File",
  158. // new MenuItem []
  159. // {
  160. // new (
  161. // "_New",
  162. // "",
  163. // () => actionFn ("New"),
  164. // null,
  165. // null,
  166. // KeyCode.CtrlMask | KeyCode.N
  167. // ),
  168. // new (
  169. // "_Open",
  170. // "",
  171. // () => actionFn ("Open"),
  172. // null,
  173. // null,
  174. // KeyCode.CtrlMask | KeyCode.O
  175. // ),
  176. // new (
  177. // "_Save",
  178. // "",
  179. // () => actionFn ("Save"),
  180. // null,
  181. // null,
  182. // KeyCode.CtrlMask | KeyCode.S
  183. // ),
  184. //#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
  185. // null,
  186. //#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
  187. // // Don't use Application.Quit so we can disambiguate between quitting and closing the toplevel
  188. // new (
  189. // "_Quit",
  190. // "",
  191. // () => actionFn ("Quit"),
  192. // null,
  193. // null,
  194. // KeyCode.CtrlMask | KeyCode.Q
  195. // )
  196. // }
  197. // ),
  198. // new MenuBarItem (
  199. // "_Edit",
  200. // new MenuItem []
  201. // {
  202. // new (
  203. // "_Copy",
  204. // "",
  205. // () => actionFn ("Copy"),
  206. // null,
  207. // null,
  208. // KeyCode.CtrlMask | KeyCode.C
  209. // ),
  210. // new (
  211. // "C_ut",
  212. // "",
  213. // () => actionFn ("Cut"),
  214. // null,
  215. // null,
  216. // KeyCode.CtrlMask | KeyCode.X
  217. // ),
  218. // new (
  219. // "_Paste",
  220. // "",
  221. // () => actionFn ("Paste"),
  222. // null,
  223. // null,
  224. // KeyCode.CtrlMask | KeyCode.V
  225. // ),
  226. // new MenuBarItem (
  227. // "_Find and Replace",
  228. // new MenuItem []
  229. // {
  230. // new (
  231. // "F_ind",
  232. // "",
  233. // () => actionFn ("Find"),
  234. // null,
  235. // null,
  236. // KeyCode.CtrlMask | KeyCode.F
  237. // ),
  238. // new (
  239. // "_Replace",
  240. // "",
  241. // () => actionFn ("Replace"),
  242. // null,
  243. // null,
  244. // KeyCode.CtrlMask | KeyCode.H
  245. // ),
  246. // new MenuBarItem (
  247. // "_3rd Level",
  248. // new MenuItem []
  249. // {
  250. // new (
  251. // "_1st",
  252. // "",
  253. // () => actionFn (
  254. // "1"
  255. // ),
  256. // null,
  257. // null,
  258. // KeyCode.F1
  259. // ),
  260. // new (
  261. // "_2nd",
  262. // "",
  263. // () => actionFn (
  264. // "2"
  265. // ),
  266. // null,
  267. // null,
  268. // KeyCode.F2
  269. // )
  270. // }
  271. // ),
  272. // new MenuBarItem (
  273. // "_4th Level",
  274. // new MenuItem []
  275. // {
  276. // new (
  277. // "_5th",
  278. // "",
  279. // () => actionFn (
  280. // "5"
  281. // ),
  282. // null,
  283. // null,
  284. // KeyCode.CtrlMask
  285. // | KeyCode.D5
  286. // ),
  287. // new (
  288. // "_6th",
  289. // "",
  290. // () => actionFn (
  291. // "6"
  292. // ),
  293. // null,
  294. // null,
  295. // KeyCode.CtrlMask
  296. // | KeyCode.D6
  297. // )
  298. // }
  299. // )
  300. // }
  301. // ),
  302. // new (
  303. // "_Select All",
  304. // "",
  305. // () => actionFn ("Select All"),
  306. // null,
  307. // null,
  308. // KeyCode.CtrlMask
  309. // | KeyCode.ShiftMask
  310. // | KeyCode.S
  311. // )
  312. // }
  313. // ),
  314. // new MenuBarItem ("_About", "Top-Level", () => actionFn ("About"))
  315. // ];
  316. return true;
  317. }
  318. }