@@ -14,9 +14,11 @@ import {
1414 FocusTracker ,
1515 Panel ,
1616 SplitPanel ,
17+ TabPanel ,
1718 Widget ,
1819} from '@lumino/widgets' ;
1920import { PanelHandler , SidePanelHandler } from './panelhandler' ;
21+ import { TabPanelSvg } from '@jupyterlab/ui-components' ;
2022
2123/**
2224 * The Jupyter Notebook application shell token.
@@ -37,7 +39,7 @@ export namespace INotebookShell {
3739 /**
3840 * The areas of the application shell where widgets can reside.
3941 */
40- export type Area = 'main' | 'top' | 'menu' | 'left' | 'right' ;
42+ export type Area = 'main' | 'top' | 'menu' | 'left' | 'right' | 'down' ;
4143
4244 /**
4345 * Widget position
@@ -134,6 +136,18 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
134136 middlePanel . addWidget ( this . _spacer_bottom ) ;
135137 middlePanel . layout = middleLayout ;
136138
139+ const vsplitPanel = new SplitPanel ( ) ;
140+ vsplitPanel . id = 'jp-main-vsplit-panel' ;
141+ vsplitPanel . spacing = 1 ;
142+ vsplitPanel . orientation = 'vertical' ;
143+ SplitPanel . setStretch ( vsplitPanel , 1 ) ;
144+
145+ const downPanel = new TabPanelSvg ( {
146+ tabsMovable : true ,
147+ } ) ;
148+ this . _downPanel = downPanel ;
149+ this . _downPanel . id = 'jp-down-stack' ;
150+
137151 // TODO: Consider storing this as an attribute this._hsplitPanel if saving/restoring layout needed
138152 const hsplitPanel = new SplitPanel ( ) ;
139153 hsplitPanel . id = 'main-split-panel' ;
@@ -153,8 +167,21 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
153167 // panel.
154168 hsplitPanel . setRelativeSizes ( [ 1 , 2.5 , 1 ] ) ;
155169
170+ vsplitPanel . addWidget ( hsplitPanel ) ;
171+ vsplitPanel . addWidget ( downPanel ) ;
172+
156173 rootLayout . spacing = 0 ;
157- rootLayout . addWidget ( hsplitPanel ) ;
174+ rootLayout . addWidget ( vsplitPanel ) ;
175+
176+ // initially hiding the down panel
177+ this . _downPanel . hide ( ) ;
178+
179+ // Connect down panel change listeners
180+ this . _downPanel . tabBar . tabMoved . connect ( this . _onTabPanelChanged , this ) ;
181+ this . _downPanel . stackedPanel . widgetRemoved . connect (
182+ this . _onTabPanelChanged ,
183+ this
184+ ) ;
158185
159186 this . layout = rootLayout ;
160187
@@ -267,7 +294,7 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
267294 */
268295 activateById ( id : string ) : void {
269296 // Search all areas that can have widgets for this widget, starting with main.
270- for ( const area of [ 'main' , 'top' , 'left' , 'right' , 'menu' ] ) {
297+ for ( const area of [ 'main' , 'top' , 'left' , 'right' , 'menu' , 'down' ] ) {
271298 const widget = find (
272299 this . widgets ( area as INotebookShell . Area ) ,
273300 ( w ) => w . id === id
@@ -277,6 +304,9 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
277304 this . expandLeft ( id ) ;
278305 } else if ( area === 'right' ) {
279306 this . expandRight ( id ) ;
307+ } else if ( area === 'down' ) {
308+ this . _downPanel . show ( ) ;
309+ widget . activate ( ) ;
280310 } else {
281311 widget . activate ( ) ;
282312 }
@@ -342,6 +372,8 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
342372 return this . _leftHandler . addWidget ( widget , rank ) ;
343373 case 'right' :
344374 return this . _rightHandler . addWidget ( widget , rank ) ;
375+ case 'down' :
376+ return this . _downPanel . addWidget ( widget ) ;
345377 default :
346378 console . warn ( `Cannot add widget to area: ${ area } ` ) ;
347379 }
@@ -385,6 +417,9 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
385417 case 'right' :
386418 yield * this . _rightHandler . widgets ;
387419 return ;
420+ case 'down' :
421+ yield * this . _downPanel . widgets ;
422+ return ;
388423 default :
389424 console . error ( `This shell has no area called "${ area } "` ) ;
390425 return ;
@@ -432,6 +467,15 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
432467 this . _userLayout = configuration ;
433468 }
434469
470+ /**
471+ * Handle a change on the down panel widgets
472+ */
473+ private _onTabPanelChanged ( ) : void {
474+ if ( this . _downPanel . stackedPanel . widgets . length === 0 ) {
475+ this . _downPanel . hide ( ) ;
476+ }
477+ }
478+
435479 private _topWrapper : Panel ;
436480 private _topHandler : PanelHandler ;
437481 private _menuWrapper : Panel ;
@@ -442,6 +486,7 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
442486 private _spacer_bottom : Widget ;
443487 private _skipLinkWidgetHandler : Private . SkipLinkWidgetHandler ;
444488 private _main : Panel ;
489+ private _downPanel : TabPanel ;
445490 private _translator : ITranslator = nullTranslator ;
446491 private _currentChanged = new Signal < this, FocusTracker . IChangedArgs < Widget > > (
447492 this
0 commit comments