UNPKG

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