@@ -14,9 +14,11 @@ import {
14
14
FocusTracker ,
15
15
Panel ,
16
16
SplitPanel ,
17
+ TabPanel ,
17
18
Widget ,
18
19
} from '@lumino/widgets' ;
19
20
import { PanelHandler , SidePanelHandler } from './panelhandler' ;
21
+ import { TabPanelSvg } from '@jupyterlab/ui-components' ;
20
22
21
23
/**
22
24
* The Jupyter Notebook application shell token.
@@ -37,7 +39,7 @@ export namespace INotebookShell {
37
39
/**
38
40
* The areas of the application shell where widgets can reside.
39
41
*/
40
- export type Area = 'main' | 'top' | 'menu' | 'left' | 'right' ;
42
+ export type Area = 'main' | 'top' | 'menu' | 'left' | 'right' | 'down' ;
41
43
42
44
/**
43
45
* Widget position
@@ -134,6 +136,18 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
134
136
middlePanel . addWidget ( this . _spacer_bottom ) ;
135
137
middlePanel . layout = middleLayout ;
136
138
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
+
137
151
// TODO: Consider storing this as an attribute this._hsplitPanel if saving/restoring layout needed
138
152
const hsplitPanel = new SplitPanel ( ) ;
139
153
hsplitPanel . id = 'main-split-panel' ;
@@ -153,8 +167,21 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
153
167
// panel.
154
168
hsplitPanel . setRelativeSizes ( [ 1 , 2.5 , 1 ] ) ;
155
169
170
+ vsplitPanel . addWidget ( hsplitPanel ) ;
171
+ vsplitPanel . addWidget ( downPanel ) ;
172
+
156
173
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
+ ) ;
158
185
159
186
this . layout = rootLayout ;
160
187
@@ -267,7 +294,7 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
267
294
*/
268
295
activateById ( id : string ) : void {
269
296
// 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' ] ) {
271
298
const widget = find (
272
299
this . widgets ( area as INotebookShell . Area ) ,
273
300
( w ) => w . id === id
@@ -277,6 +304,9 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
277
304
this . expandLeft ( id ) ;
278
305
} else if ( area === 'right' ) {
279
306
this . expandRight ( id ) ;
307
+ } else if ( area === 'down' ) {
308
+ this . _downPanel . show ( ) ;
309
+ widget . activate ( ) ;
280
310
} else {
281
311
widget . activate ( ) ;
282
312
}
@@ -342,6 +372,8 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
342
372
return this . _leftHandler . addWidget ( widget , rank ) ;
343
373
case 'right' :
344
374
return this . _rightHandler . addWidget ( widget , rank ) ;
375
+ case 'down' :
376
+ return this . _downPanel . addWidget ( widget ) ;
345
377
default :
346
378
console . warn ( `Cannot add widget to area: ${ area } ` ) ;
347
379
}
@@ -385,6 +417,9 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
385
417
case 'right' :
386
418
yield * this . _rightHandler . widgets ;
387
419
return ;
420
+ case 'down' :
421
+ yield * this . _downPanel . widgets ;
422
+ return ;
388
423
default :
389
424
console . error ( `This shell has no area called "${ area } "` ) ;
390
425
return ;
@@ -432,6 +467,15 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
432
467
this . _userLayout = configuration ;
433
468
}
434
469
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
+
435
479
private _topWrapper : Panel ;
436
480
private _topHandler : PanelHandler ;
437
481
private _menuWrapper : Panel ;
@@ -442,6 +486,7 @@ export class NotebookShell extends Widget implements JupyterFrontEnd.IShell {
442
486
private _spacer_bottom : Widget ;
443
487
private _skipLinkWidgetHandler : Private . SkipLinkWidgetHandler ;
444
488
private _main : Panel ;
489
+ private _downPanel : TabPanel ;
445
490
private _translator : ITranslator = nullTranslator ;
446
491
private _currentChanged = new Signal < this, FocusTracker . IChangedArgs < Widget > > (
447
492
this
0 commit comments