Skip to content

Commit 844154f

Browse files
committed
Update the menu entries when widgets are added or removed from side panels
1 parent 5376fc6 commit 844154f

File tree

2 files changed

+126
-43
lines changed

2 files changed

+126
-43
lines changed

packages/application-extension/src/index.ts

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import { PromiseDelegate } from '@lumino/coreutils';
4444

4545
import { DisposableDelegate, DisposableSet } from '@lumino/disposable';
4646

47-
import { Menu, Widget } from '@lumino/widgets';
47+
import { Widget } from '@lumino/widgets';
4848

4949
/**
5050
* The default notebook factory.
@@ -670,47 +670,25 @@ const sidebarVisibility: JupyterFrontEndPlugin<void> = {
670670
}
671671
});
672672

673-
const leftSidebarMenu = new Menu({ commands: app.commands });
674-
leftSidebarMenu.title.label = trans.__('Show Left Sidebar');
675-
676-
const rightSidebarMenu = new Menu({ commands: app.commands });
677-
rightSidebarMenu.title.label = trans.__('Show Right Sidebar');
678-
679673
app.restored.then(() => {
680-
const leftWidgets = notebookShell.widgetsList('left');
681-
leftWidgets.forEach(widget => {
682-
leftSidebarMenu.addItem({
683-
command: CommandIDs.togglePanel,
684-
args: {
685-
side: 'left',
686-
title: widget.title.caption,
687-
id: widget.id
688-
}
674+
// Create a menu entry for left and right panel and update it with current widgets
675+
if (menu) {
676+
notebookShell.leftHandler.createMenuEntry({
677+
mainMenuEntry: menu.viewMenu,
678+
commandRegistry: app.commands,
679+
entryLabel: trans.__('Show Left Sidebar'),
680+
command: CommandIDs.togglePanel
689681
});
690-
});
691682

692-
const rightWidgets = notebookShell.widgetsList('right');
693-
rightWidgets.forEach(widget => {
694-
rightSidebarMenu.addItem({
695-
command: CommandIDs.togglePanel,
696-
args: {
697-
side: 'right',
698-
title: widget.title.caption,
699-
id: widget.id
700-
}
683+
notebookShell.rightHandler.createMenuEntry({
684+
mainMenuEntry: menu.viewMenu,
685+
commandRegistry: app.commands,
686+
entryLabel: trans.__('Show Right Sidebar'),
687+
command: CommandIDs.togglePanel
701688
});
702-
});
703-
704-
const menuItemsToAdd: Menu.IItemOptions[] = [];
705-
if (leftWidgets.length > 0) {
706-
menuItemsToAdd.push({ type: 'submenu', submenu: leftSidebarMenu });
707-
}
708-
if (rightWidgets.length > 0) {
709-
menuItemsToAdd.push({ type: 'submenu', submenu: rightSidebarMenu });
710-
}
711689

712-
if (menu && menuItemsToAdd) {
713-
menu.viewMenu.addGroup(menuItemsToAdd, 2);
690+
notebookShell.leftHandler.updateMenu();
691+
notebookShell.rightHandler.updateMenu();
714692
}
715693
});
716694
},

packages/application/src/shell.ts

Lines changed: 111 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,20 @@
44
import { JupyterFrontEnd } from '@jupyterlab/application';
55
import { PageConfig } from '@jupyterlab/coreutils';
66
import { DocumentRegistry } from '@jupyterlab/docregistry';
7+
import { IRankedMenu } from '@jupyterlab/ui-components';
78

89
import { ArrayExt, find, IIterator, iter } from '@lumino/algorithm';
10+
import { CommandRegistry } from '@lumino/commands';
911
import { PromiseDelegate, Token } from '@lumino/coreutils';
12+
import { IDisposable } from '@lumino/disposable';
1013
import { Message, MessageLoop, IMessageHandler } from '@lumino/messaging';
1114
import { Debouncer } from '@lumino/polling';
1215
import { ISignal, Signal } from '@lumino/signaling';
1316

1417
import {
1518
BoxLayout,
1619
Layout,
20+
Menu,
1721
Panel,
1822
SplitPanel,
1923
StackedPanel,
@@ -47,8 +51,8 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
4751

4852
this._topHandler = new Private.PanelHandler();
4953
this._menuHandler = new Private.PanelHandler();
50-
this._leftHandler = new Private.SideBarHandler();
51-
this._rightHandler = new Private.SideBarHandler();
54+
this._leftHandler = new Private.SideBarHandler('left');
55+
this._rightHandler = new Private.SideBarHandler('right');
5256
this._main = new Panel();
5357
const topWrapper = (this._topWrapper = new Panel());
5458
const menuWrapper = (this._menuWrapper = new Panel());
@@ -526,12 +530,14 @@ namespace Private {
526530
/**
527531
* Construct a new side bar handler.
528532
*/
529-
constructor() {
533+
constructor(area: 'left' | 'right') {
534+
this._area = area;
530535
this._stackedPanel = new StackedPanel();
531536
this._stackedPanel.hide();
532537
this._current = null;
533538
this._lastCurrent = null;
534539
this._stackedPanel.widgetRemoved.connect(this._onWidgetRemoved, this);
540+
this._sideBarMenu = null;
535541
}
536542

537543
get current(): Widget | null {
@@ -563,6 +569,13 @@ namespace Private {
563569
return this._updated;
564570
}
565571

572+
/**
573+
* Associate a menu entry to the sidebar.
574+
*/
575+
createMenuEntry(options: SideBarMenuOption): void {
576+
this._sideBarMenu = new SideBarMenu(options);
577+
}
578+
566579
/**
567580
* Expand the sidebar.
568581
*
@@ -619,8 +632,7 @@ namespace Private {
619632
ArrayExt.insert(this._items, index, item);
620633
this._stackedPanel.insertWidget(index, widget);
621634

622-
// TODO: Update menu to include widget in appropriate position
623-
635+
this.updateMenu();
624636
this._refreshVisibility();
625637
}
626638

@@ -640,6 +652,14 @@ namespace Private {
640652
this._refreshVisibility();
641653
}
642654

655+
/**
656+
* Update menu entries
657+
*/
658+
updateMenu(): void {
659+
if (!this._sideBarMenu) return;
660+
const widgets = this.stackedPanel.widgets;
661+
this._sideBarMenu?.updateMenu(widgets, this._area);
662+
}
643663
/**
644664
* Find the insertion index for a rank item.
645665
*/
@@ -678,15 +698,100 @@ namespace Private {
678698
this._lastCurrent = null;
679699
}
680700
ArrayExt.removeAt(this._items, this._findWidgetIndex(widget));
681-
// TODO: Remove the widget from the menu
701+
702+
this.updateMenu();
682703
this._refreshVisibility();
683704
}
684705

706+
private _area: 'left' | 'right';
685707
private _isHiddenByUser = false;
686708
private _items = new Array<Private.IRankItem>();
687709
private _stackedPanel: StackedPanel;
688710
private _current: Widget | null;
689711
private _lastCurrent: Widget | null;
690712
private _updated: Signal<SideBarHandler, void> = new Signal(this);
713+
private _sideBarMenu: SideBarMenu | null;
714+
}
715+
716+
/**
717+
* A class which manages the menu entry associated to the side bar.
718+
*/
719+
export class SideBarMenu {
720+
/**
721+
* Construct a new side bar handler.
722+
*/
723+
constructor(options: SideBarMenuOption) {
724+
this._commandRegistry = options.commandRegistry;
725+
this._command = options.command;
726+
this._mainMenuEntry = options.mainMenuEntry;
727+
this._entryLabel = options.entryLabel;
728+
}
729+
730+
/**
731+
* Update the menu by disposing the previous one and rebuilding a new one from widgets list.
732+
*/
733+
updateMenu(widgets: Readonly<Widget[]>, area: 'left' | 'right'): void {
734+
// Remove the previous menu entry.
735+
if (this._menu) this._menu.dispose();
736+
737+
// Build the new menu entry from widgets list.
738+
let menu = new Menu({ commands: this._commandRegistry });
739+
menu.title.label = this._entryLabel;
740+
widgets.forEach(widget => {
741+
menu.addItem({
742+
command: this._command,
743+
args: {
744+
side: area,
745+
title: widget.title.caption,
746+
id: widget.id
747+
}
748+
});
749+
});
750+
751+
// If there are widgets, add the menu to the main menu entry.
752+
if (widgets.length > 0) {
753+
this._menu = this._mainMenuEntry.addItem({
754+
type: 'submenu',
755+
submenu: menu
756+
});
757+
}
758+
}
759+
760+
_entryLabel: string;
761+
_command: string;
762+
_mainMenuEntry: IRankedMenu;
763+
_commandRegistry: CommandRegistry;
764+
_menu: IDisposable | null = null;
691765
}
766+
767+
/**
768+
* An interface for the options to include in SideBarMenu constructor.
769+
*/
770+
type SideBarMenuOption = {
771+
/**
772+
* The main menu entry where the sidebar menu should be added.
773+
*/
774+
mainMenuEntry: IRankedMenu;
775+
776+
/**
777+
* Tha label of the sidebar menu.
778+
*/
779+
entryLabel: string;
780+
781+
/**
782+
* The application command registry, necessary when updating the sidebar menu.
783+
*/
784+
commandRegistry: CommandRegistry;
785+
786+
/**
787+
* The command to call from each sidebar menu entry.
788+
*
789+
* ### Notes
790+
* That command required 3 args :
791+
* side: 'left' | 'right', the area to toggle
792+
* title: string, label of the command
793+
* id: string, id of the widget to activate
794+
*/
795+
command: string;
796+
};
692797
}

0 commit comments

Comments
 (0)