Move some logic and dependencies from Shell to plugin

pull/6487/head
Nicolas Brichet 4 years ago committed by foo
parent 2145e66ab6
commit 116f5a5b8c

@ -37,16 +37,23 @@ import { ITranslator } from '@jupyterlab/translation';
import {
NotebookApp,
NotebookShell,
INotebookShell
INotebookShell,
SideBarPanel
} from '@jupyter-notebook/application';
import { jupyterIcon } from '@jupyter-notebook/ui-components';
import { each } from '@lumino/algorithm';
import { PromiseDelegate } from '@lumino/coreutils';
import { DisposableDelegate, DisposableSet } from '@lumino/disposable';
import {
DisposableDelegate,
DisposableSet,
IDisposable
} from '@lumino/disposable';
import { Widget } from '@lumino/widgets';
import { Menu, Widget } from '@lumino/widgets';
/**
* The default notebook factory.
@ -673,28 +680,58 @@ const sidebarVisibility: JupyterFrontEndPlugin<void> = {
}
});
app.restored.then(() => {
// Add the notebook tools in right area.
if (notebookTools) notebookShell.add(notebookTools, 'right');
/**
* The function which adds entries to the View menu for each widget of a sidebar.
*
* @param area - 'left' or 'right', the area of the side bar.
* @param entryLabel - the name of the main entry in the View menu for that sidebar.
* @returns - The disposable menu added to the View menu or null.
*/
const updateMenu: SideBarPanel.UpdateSideBarMenuFn = (area, entryLabel) => {
if (menu === null) {
return null;
}
// Create a menu entry for left and right panel and update it with current widgets.
if (menu) {
notebookShell.leftHandler.createMenuEntry({
mainMenuEntry: menu.viewMenu,
commandRegistry: app.commands,
entryLabel: trans.__('Left Sidebar'),
command: CommandIDs.togglePanel
let disposableMenu: IDisposable | null = null;
const newMenu = new Menu({ commands: app.commands });
newMenu.title.label = entryLabel;
const widgets = notebookShell.widgets(area);
let menuToAdd = false;
each(widgets, widget => {
newMenu.addItem({
command: CommandIDs.togglePanel,
args: {
side: area,
title: `Show ${widget.title.caption}`,
id: widget.id
}
});
menuToAdd = true;
});
notebookShell.rightHandler.createMenuEntry({
mainMenuEntry: menu.viewMenu,
commandRegistry: app.commands,
entryLabel: trans.__('Right Sidebar'),
command: CommandIDs.togglePanel
// If there are widgets, add the menu to the main menu entry.
if (menuToAdd) {
disposableMenu = menu.viewMenu.addItem({
type: 'submenu',
submenu: newMenu
});
}
return disposableMenu;
};
notebookShell.leftHandler.updateMenu();
notebookShell.rightHandler.updateMenu();
app.restored.then(() => {
// Add the notebook tools in right area.
if (notebookTools) {
notebookShell.add(notebookTools, 'right');
}
// Create menu entries for left and right panel.
if (menu) {
notebookShell.leftHandler.addUpdateMenuFn(updateMenu);
notebookShell.rightHandler.addUpdateMenuFn(updateMenu);
}
// Add palette entries associated to the side panels.

@ -4,10 +4,8 @@
import { JupyterFrontEnd } from '@jupyterlab/application';
import { ICommandPalette } from '@jupyterlab/apputils';
import { DocumentRegistry } from '@jupyterlab/docregistry';
import { IRankedMenu } from '@jupyterlab/ui-components';
import { ArrayExt, find } from '@lumino/algorithm';
import { CommandRegistry } from '@lumino/commands';
import { PromiseDelegate, Token } from '@lumino/coreutils';
import { IDisposable } from '@lumino/disposable';
import { Message, MessageLoop, IMessageHandler } from '@lumino/messaging';
@ -16,7 +14,6 @@ import { ISignal, Signal } from '@lumino/signaling';
import {
BoxLayout,
Menu,
Panel,
SplitPanel,
StackedPanel,
@ -211,12 +208,20 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
const widget = find(this.widgets(area), w => w.id === id);
if (widget) {
if (area === 'left') {
if (this.leftCollapsed) this.expandLeft(id);
else this.collapseLeft();
if (this.leftCollapsed) {
this.expandLeft(id);
} else {
this.collapseLeft();
}
} else if (area === 'right') {
if (this.rightCollapsed) this.expandRight(id);
else this.collapseRight();
} else widget.activate();
if (this.rightCollapsed) {
this.expandRight(id);
} else {
this.collapseRight();
}
} else {
widget.activate();
}
}
}
}
@ -395,6 +400,19 @@ export namespace Shell {
export type Area = 'main' | 'top' | 'left' | 'right' | 'menu';
}
/**
*
*/
export namespace SideBarPanel {
/**
* The function to update the sidebar menu.
*/
export type UpdateSideBarMenuFn = (
area: 'left' | 'right',
entryLabel: string
) => IDisposable | null;
}
/**
* A namespace for private module data.
*/
@ -499,8 +517,8 @@ namespace Private {
this._current = null;
this._lastCurrent = null;
this._stackedPanel.widgetRemoved.connect(this._onWidgetRemoved, this);
this._sideBarMenu = null;
this._sideBarPalette = null;
this._menuEntryLabel = `${area[0].toUpperCase()}${area.slice(1)} Sidebar`;
}
get current(): Widget | null {
@ -533,10 +551,12 @@ namespace Private {
}
/**
* Associate a menu entry to the sidebar.
* Add a function to update the menus with the side bar widgets.
* @param updateMenu - the function to call to update the menu.
*/
createMenuEntry(options: SideBarMenuOption): void {
this._sideBarMenu = new SideBarMenu(options);
addUpdateMenuFn(updateMenu: SideBarPanel.UpdateSideBarMenuFn): void {
this._updateMenu = updateMenu;
this.updateMenu();
}
/**
@ -557,8 +577,9 @@ namespace Private {
* if there is no most recently used.
*/
expand(id?: string): void {
if (id) this.activate(id);
else {
if (id) {
this.activate(id);
} else {
const visibleWidget = this.current;
if (visibleWidget) {
this._current = visibleWidget;
@ -636,10 +657,15 @@ namespace Private {
* Update menu entries
*/
updateMenu(): void {
if (!this._sideBarMenu) return;
const widgets = this.stackedPanel.widgets;
this._sideBarMenu?.updateMenu(widgets, this._area);
if (!this._updateMenu) {
return;
}
if (this._disposableMenu) {
this._disposableMenu.dispose();
}
this._disposableMenu = this._updateMenu(this._area, this._menuEntryLabel);
}
/**
* Find the insertion index for a rank item.
*/
@ -694,92 +720,12 @@ namespace Private {
private _current: Widget | null;
private _lastCurrent: Widget | null;
private _updated: Signal<SideBarHandler, void> = new Signal(this);
private _sideBarMenu: SideBarMenu | null;
private _sideBarPalette: SideBarPalette | null;
private _menuEntryLabel: string;
private _disposableMenu: IDisposable | null = null;
private _updateMenu: SideBarPanel.UpdateSideBarMenuFn | null = null;
}
/**
* A class to manage the menu entries associated to the side bar.
*/
export class SideBarMenu {
/**
* Construct a new side bar menu.
*/
constructor(options: SideBarMenuOption) {
this._commandRegistry = options.commandRegistry;
this._command = options.command;
this._mainMenuEntry = options.mainMenuEntry;
this._entryLabel = options.entryLabel;
}
/**
* Update the menu by disposing the previous one and rebuilding a new one from widgets list.
*/
updateMenu(widgets: Readonly<Widget[]>, area: 'left' | 'right'): void {
// Remove the previous menu entry.
if (this._menu) this._menu.dispose();
// Build the new menu entry from widgets list.
let menu = new Menu({ commands: this._commandRegistry });
menu.title.label = this._entryLabel;
widgets.forEach(widget => {
menu.addItem({
command: this._command,
args: {
side: area,
title: `Show ${widget.title.caption}`,
id: widget.id
}
});
});
// If there are widgets, add the menu to the main menu entry.
if (widgets.length > 0) {
this._menu = this._mainMenuEntry.addItem({
type: 'submenu',
submenu: menu
});
}
}
_entryLabel: string;
_command: string;
_mainMenuEntry: IRankedMenu;
_commandRegistry: CommandRegistry;
_menu: IDisposable | null = null;
}
/**
* An interface for the options to include in SideBarMenu constructor.
*/
type SideBarMenuOption = {
/**
* The main menu entry where the sidebar menu should be added.
*/
mainMenuEntry: IRankedMenu;
/**
* The label of the sidebar menu.
*/
entryLabel: string;
/**
* The application command registry, necessary when updating the sidebar menu.
*/
commandRegistry: CommandRegistry;
/**
* The command to call from each sidebar menu entry.
*
* ### Notes
* That command required 3 args :
* side: 'left' | 'right', the area to toggle
* title: string, label of the command
* id: string, id of the widget to activate
*/
command: string;
};
/**
* A class to manages the palette entries associated to the side bar.
*/
@ -801,7 +747,7 @@ namespace Private {
): SideBarPaletteItem | null {
const itemList = this._items;
for (let i = 0; i < itemList.length; i++) {
let item = itemList[i];
const item = itemList[i];
if (item.widgetId == widget.id && item.area == area) {
return item;
break;
@ -815,7 +761,9 @@ namespace Private {
*/
addItem(widget: Readonly<Widget>, area: 'left' | 'right'): void {
// Check if the item does not already exist.
if (this.getItem(widget, area)) return;
if (this.getItem(widget, area)) {
return;
}
// Add a new item in command palette.
const disposableDelegate = this._commandPalette.addItem({
@ -842,7 +790,9 @@ namespace Private {
*/
removeItem(widget: Readonly<Widget>, area: 'left' | 'right'): void {
const item = this.getItem(widget, area);
if (item) item.disposable.dispose();
if (item) {
item.disposable.dispose();
}
}
_command: string;

Loading…
Cancel
Save