mainmenu.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { TranslationBundle } from '@jupyterlab/translation';
  4. import { IRankedMenu, MenuSvg, RankedMenu } from '@jupyterlab/ui-components';
  5. import { ArrayExt } from '@lumino/algorithm';
  6. import { CommandRegistry } from '@lumino/commands';
  7. import { Menu, MenuBar } from '@lumino/widgets';
  8. import { EditMenu } from './edit';
  9. import { FileMenu } from './file';
  10. import { HelpMenu } from './help';
  11. import { KernelMenu } from './kernel';
  12. import { RunMenu } from './run';
  13. import { SettingsMenu } from './settings';
  14. import { TabsMenu } from './tabs';
  15. import { IMainMenu } from './tokens';
  16. import { ViewMenu } from './view';
  17. /**
  18. * The main menu class. It is intended to be used as a singleton.
  19. */
  20. export class MainMenu extends MenuBar implements IMainMenu {
  21. /**
  22. * Construct the main menu bar.
  23. */
  24. constructor(commands: CommandRegistry) {
  25. super();
  26. this._commands = commands;
  27. }
  28. /**
  29. * The application "Edit" menu.
  30. */
  31. get editMenu(): EditMenu {
  32. if (!this._editMenu) {
  33. this._editMenu = new EditMenu({
  34. commands: this._commands,
  35. rank: 2,
  36. renderer: MenuSvg.defaultRenderer
  37. });
  38. }
  39. return this._editMenu;
  40. }
  41. /**
  42. * The application "File" menu.
  43. */
  44. get fileMenu(): FileMenu {
  45. if (!this._fileMenu) {
  46. this._fileMenu = new FileMenu({
  47. commands: this._commands,
  48. rank: 1,
  49. renderer: MenuSvg.defaultRenderer
  50. });
  51. }
  52. return this._fileMenu;
  53. }
  54. /**
  55. * The application "Help" menu.
  56. */
  57. get helpMenu(): HelpMenu {
  58. if (!this._helpMenu) {
  59. this._helpMenu = new HelpMenu({
  60. commands: this._commands,
  61. rank: 1000,
  62. renderer: MenuSvg.defaultRenderer
  63. });
  64. }
  65. return this._helpMenu;
  66. }
  67. /**
  68. * The application "Kernel" menu.
  69. */
  70. get kernelMenu(): KernelMenu {
  71. if (!this._kernelMenu) {
  72. this._kernelMenu = new KernelMenu({
  73. commands: this._commands,
  74. rank: 5,
  75. renderer: MenuSvg.defaultRenderer
  76. });
  77. }
  78. return this._kernelMenu;
  79. }
  80. /**
  81. * The application "Run" menu.
  82. */
  83. get runMenu(): RunMenu {
  84. if (!this._runMenu) {
  85. this._runMenu = new RunMenu({
  86. commands: this._commands,
  87. rank: 4,
  88. renderer: MenuSvg.defaultRenderer
  89. });
  90. }
  91. return this._runMenu;
  92. }
  93. /**
  94. * The application "Settings" menu.
  95. */
  96. get settingsMenu(): SettingsMenu {
  97. if (!this._settingsMenu) {
  98. this._settingsMenu = new SettingsMenu({
  99. commands: this._commands,
  100. rank: 999,
  101. renderer: MenuSvg.defaultRenderer
  102. });
  103. }
  104. return this._settingsMenu;
  105. }
  106. /**
  107. * The application "View" menu.
  108. */
  109. get viewMenu(): ViewMenu {
  110. if (!this._viewMenu) {
  111. this._viewMenu = new ViewMenu({
  112. commands: this._commands,
  113. rank: 3,
  114. renderer: MenuSvg.defaultRenderer
  115. });
  116. }
  117. return this._viewMenu;
  118. }
  119. /**
  120. * The application "Tabs" menu.
  121. */
  122. get tabsMenu(): TabsMenu {
  123. if (!this._tabsMenu) {
  124. this._tabsMenu = new TabsMenu({
  125. commands: this._commands,
  126. rank: 500,
  127. renderer: MenuSvg.defaultRenderer
  128. });
  129. }
  130. return this._tabsMenu;
  131. }
  132. /**
  133. * Add a new menu to the main menu bar.
  134. */
  135. addMenu(menu: Menu, options: IMainMenu.IAddOptions = {}): void {
  136. if (ArrayExt.firstIndexOf(this.menus, menu) > -1) {
  137. return;
  138. }
  139. // override default renderer with svg-supporting renderer
  140. MenuSvg.overrideDefaultRenderer(menu);
  141. const rank =
  142. 'rank' in options
  143. ? options.rank
  144. : 'rank' in menu
  145. ? (menu as any).rank
  146. : IRankedMenu.DEFAULT_RANK;
  147. const rankItem = { menu, rank };
  148. const index = ArrayExt.upperBound(this._items, rankItem, Private.itemCmp);
  149. // Upon disposal, remove the menu and its rank reference.
  150. menu.disposed.connect(this._onMenuDisposed, this);
  151. ArrayExt.insert(this._items, index, rankItem);
  152. /**
  153. * Create a new menu.
  154. */
  155. this.insertMenu(index, menu);
  156. // Link the menu to the API - backward compatibility when switching to menu description in settings
  157. switch (menu.id) {
  158. case 'jp-mainmenu-file':
  159. if (!this._fileMenu && menu instanceof FileMenu) {
  160. this._fileMenu = menu;
  161. }
  162. break;
  163. case 'jp-mainmenu-edit':
  164. if (!this._editMenu && menu instanceof EditMenu) {
  165. this._editMenu = menu;
  166. }
  167. break;
  168. case 'jp-mainmenu-view':
  169. if (!this._viewMenu && menu instanceof ViewMenu) {
  170. this._viewMenu = menu;
  171. }
  172. break;
  173. case 'jp-mainmenu-run':
  174. if (!this._runMenu && menu instanceof RunMenu) {
  175. this._runMenu = menu;
  176. }
  177. break;
  178. case 'jp-mainmenu-kernel':
  179. if (!this._kernelMenu && menu instanceof KernelMenu) {
  180. this._kernelMenu = menu;
  181. }
  182. break;
  183. case 'jp-mainmenu-tabs':
  184. if (!this._tabsMenu && menu instanceof TabsMenu) {
  185. this._tabsMenu = menu;
  186. }
  187. break;
  188. case 'jp-mainmenu-settings':
  189. if (!this._settingsMenu && menu instanceof SettingsMenu) {
  190. this._settingsMenu = menu;
  191. }
  192. break;
  193. case 'jp-mainmenu-help':
  194. if (!this._helpMenu && menu instanceof HelpMenu) {
  195. this._helpMenu = menu;
  196. }
  197. break;
  198. }
  199. }
  200. /**
  201. * Dispose of the resources held by the menu bar.
  202. */
  203. dispose(): void {
  204. this._editMenu?.dispose();
  205. this._fileMenu?.dispose();
  206. this._helpMenu?.dispose();
  207. this._kernelMenu?.dispose();
  208. this._runMenu?.dispose();
  209. this._settingsMenu?.dispose();
  210. this._viewMenu?.dispose();
  211. this._tabsMenu?.dispose();
  212. super.dispose();
  213. }
  214. /**
  215. * Generate the menu.
  216. *
  217. * @param commands The command registry
  218. * @param options The main menu options.
  219. * @param trans - The application language translator.
  220. */
  221. static generateMenu(
  222. commands: CommandRegistry,
  223. options: IMainMenu.IMenuOptions,
  224. trans: TranslationBundle
  225. ): RankedMenu {
  226. let menu: RankedMenu;
  227. const { id, label, rank } = options;
  228. switch (id) {
  229. case 'jp-mainmenu-file':
  230. menu = new FileMenu({
  231. commands,
  232. rank,
  233. renderer: MenuSvg.defaultRenderer
  234. });
  235. break;
  236. case 'jp-mainmenu-edit':
  237. menu = new EditMenu({
  238. commands,
  239. rank,
  240. renderer: MenuSvg.defaultRenderer
  241. });
  242. break;
  243. case 'jp-mainmenu-view':
  244. menu = new ViewMenu({
  245. commands,
  246. rank,
  247. renderer: MenuSvg.defaultRenderer
  248. });
  249. break;
  250. case 'jp-mainmenu-run':
  251. menu = new RunMenu({
  252. commands,
  253. rank,
  254. renderer: MenuSvg.defaultRenderer
  255. });
  256. break;
  257. case 'jp-mainmenu-kernel':
  258. menu = new KernelMenu({
  259. commands,
  260. rank,
  261. renderer: MenuSvg.defaultRenderer
  262. });
  263. break;
  264. case 'jp-mainmenu-tabs':
  265. menu = new TabsMenu({
  266. commands,
  267. rank,
  268. renderer: MenuSvg.defaultRenderer
  269. });
  270. break;
  271. case 'jp-mainmenu-settings':
  272. menu = new SettingsMenu({
  273. commands,
  274. rank,
  275. renderer: MenuSvg.defaultRenderer
  276. });
  277. break;
  278. case 'jp-mainmenu-help':
  279. menu = new HelpMenu({
  280. commands,
  281. rank,
  282. renderer: MenuSvg.defaultRenderer
  283. });
  284. break;
  285. default:
  286. menu = new RankedMenu({
  287. commands,
  288. rank,
  289. renderer: MenuSvg.defaultRenderer
  290. });
  291. }
  292. if (label) {
  293. menu.title.label = trans._p('menu', label);
  294. }
  295. return menu;
  296. }
  297. /**
  298. * Handle the disposal of a menu.
  299. */
  300. private _onMenuDisposed(menu: Menu): void {
  301. this.removeMenu(menu);
  302. const index = ArrayExt.findFirstIndex(
  303. this._items,
  304. item => item.menu === menu
  305. );
  306. if (index !== -1) {
  307. ArrayExt.removeAt(this._items, index);
  308. }
  309. }
  310. private _commands: CommandRegistry;
  311. private _items: Private.IRankItem[] = [];
  312. private _editMenu: EditMenu;
  313. private _fileMenu: FileMenu;
  314. private _helpMenu: HelpMenu;
  315. private _kernelMenu: KernelMenu;
  316. private _runMenu: RunMenu;
  317. private _settingsMenu: SettingsMenu;
  318. private _viewMenu: ViewMenu;
  319. private _tabsMenu: TabsMenu;
  320. }
  321. /**
  322. * A namespace for private data.
  323. */
  324. namespace Private {
  325. /**
  326. * An object which holds a menu and its sort rank.
  327. */
  328. export interface IRankItem {
  329. /**
  330. * The menu for the item.
  331. */
  332. menu: Menu;
  333. /**
  334. * The sort rank of the menu.
  335. */
  336. rank: number;
  337. }
  338. /**
  339. * A comparator function for menu rank items.
  340. */
  341. export function itemCmp(first: IRankItem, second: IRankItem): number {
  342. return first.rank - second.rank;
  343. }
  344. }