|
@@ -136,7 +136,9 @@ class StaticNotebook extends Widget {
|
|
}
|
|
}
|
|
let oldValue = this._model;
|
|
let oldValue = this._model;
|
|
this._model = newValue;
|
|
this._model = newValue;
|
|
- this._changeModel(oldValue, newValue);
|
|
|
|
|
|
+ // Trigger private, protected, and public changes.
|
|
|
|
+ this._onModelChanged(oldValue, newValue);
|
|
|
|
+ this.onModelChanged(oldValue, newValue);
|
|
this.modelChanged.emit(newValue);
|
|
this.modelChanged.emit(newValue);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -151,13 +153,13 @@ class StaticNotebook extends Widget {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * The cell widget factory used by the widget.
|
|
|
|
|
|
+ * Get the mime type for code cells.
|
|
*
|
|
*
|
|
* #### Notes
|
|
* #### Notes
|
|
* This is a read-only property.
|
|
* This is a read-only property.
|
|
*/
|
|
*/
|
|
- get factory(): StaticNotebook.IFactory {
|
|
|
|
- return this._factory;
|
|
|
|
|
|
+ get codeMimetype(): string {
|
|
|
|
+ return this._mimetype;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -190,87 +192,120 @@ class StaticNotebook extends Widget {
|
|
super.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Handle a new model on the widget.
|
|
|
|
+ *
|
|
|
|
+ * #### Notes
|
|
|
|
+ * The default implementation is a no-op.
|
|
|
|
+ */
|
|
|
|
+ protected onModelChanged(oldValue: INotebookModel, newValue: INotebookModel): void {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Handle a `child-added` message.
|
|
|
|
|
|
+ * Handle changes to the notebook model metadata.
|
|
*/
|
|
*/
|
|
- protected onChildAdded(msg: ChildMessage): void {
|
|
|
|
- msg.child.addClass(NB_CELL_CLASS);
|
|
|
|
|
|
+ protected onMetadataChanged(model: INotebookModel, args: IChangedArgs<any>): void {
|
|
|
|
+ switch (args.name) {
|
|
|
|
+ case 'language_info':
|
|
|
|
+ this._mimetype = this.getMimetype();
|
|
|
|
+ this._updateChildren();
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Handle a `child-removed` message.
|
|
|
|
|
|
+ * Get the preferred mime type for code cells in the notebook.
|
|
|
|
+ *
|
|
|
|
+ * #### Notes
|
|
|
|
+ * The default implementation uses the `language_info` metadata.
|
|
*/
|
|
*/
|
|
- protected onChildRemoved(msg: ChildMessage): void {
|
|
|
|
- msg.child.dispose();
|
|
|
|
|
|
+ protected getMimetype(): string {
|
|
|
|
+ if (!this.model) {
|
|
|
|
+ return 'text/plain';
|
|
|
|
+ }
|
|
|
|
+ let cursor = this.model.getMetadata('language_info');
|
|
|
|
+ let info = cursor.getValue() as nbformat.ILanguageInfoMetadata;
|
|
|
|
+ return mimetypeForLanguage(info as IKernelLanguageInfo);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Update a child widget.
|
|
|
|
+ *
|
|
|
|
+ * #### Notes
|
|
|
|
+ * The default implementation updates the code cell mimetypes.
|
|
|
|
+ */
|
|
|
|
+ protected updateChild(index: number): void {
|
|
|
|
+ let layout = this.layout as PanelLayout;
|
|
|
|
+ let child = layout.childAt(index);
|
|
|
|
+ if (child instanceof CodeCellWidget) {
|
|
|
|
+ child.mimetype = this._mimetype;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Handle a new model on the widget.
|
|
* Handle a new model on the widget.
|
|
*/
|
|
*/
|
|
- private _changeModel(oldValue: INotebookModel, newValue: INotebookModel): void {
|
|
|
|
|
|
+ private _onModelChanged(oldValue: INotebookModel, newValue: INotebookModel): void {
|
|
let layout = this.layout as PanelLayout;
|
|
let layout = this.layout as PanelLayout;
|
|
if (oldValue) {
|
|
if (oldValue) {
|
|
oldValue.cells.changed.disconnect(this._onCellsChanged, this);
|
|
oldValue.cells.changed.disconnect(this._onCellsChanged, this);
|
|
- oldValue.metadataChanged.disconnect(this._onMetadataChanged, this);
|
|
|
|
|
|
+ oldValue.metadataChanged.disconnect(this.onMetadataChanged, this);
|
|
// TODO: reuse existing cell widgets if possible.
|
|
// TODO: reuse existing cell widgets if possible.
|
|
for (let i = 0; i < layout.childCount(); i++) {
|
|
for (let i = 0; i < layout.childCount(); i++) {
|
|
- layout.removeChild(layout.childAt(0));
|
|
|
|
|
|
+ this._removeChild(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ this._mimetype = this.getMimetype();
|
|
let cells = newValue.cells;
|
|
let cells = newValue.cells;
|
|
for (let i = 0; i < cells.length; i++) {
|
|
for (let i = 0; i < cells.length; i++) {
|
|
- layout.addChild(this._createWidget(cells.get(i)));
|
|
|
|
|
|
+ this._insertChild(i, cells.get(i));
|
|
}
|
|
}
|
|
- this._setChildMimetypes();
|
|
|
|
cells.changed.connect(this._onCellsChanged, this);
|
|
cells.changed.connect(this._onCellsChanged, this);
|
|
- newValue.metadataChanged.connect(this._onMetadataChanged, this);
|
|
|
|
|
|
+ newValue.metadataChanged.connect(this.onMetadataChanged, this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Create a widget from a model using the appropriate factory.
|
|
|
|
|
|
+ * Create a child widget and insert into to the notebook.
|
|
*/
|
|
*/
|
|
- private _createWidget(model: ICellModel): BaseCellWidget {
|
|
|
|
- switch (model.type) {
|
|
|
|
|
|
+ private _insertChild(index: number, cell: ICellModel): void {
|
|
|
|
+ let widget: BaseCellWidget;
|
|
|
|
+ switch (cell.type) {
|
|
case 'code':
|
|
case 'code':
|
|
let codeFactory = this._factory.createCodeCell;
|
|
let codeFactory = this._factory.createCodeCell;
|
|
- return codeFactory(model as CodeCellModel, this._rendermime);
|
|
|
|
|
|
+ widget = codeFactory(cell as CodeCellModel, this._rendermime);
|
|
|
|
+ break;
|
|
case 'markdown':
|
|
case 'markdown':
|
|
let mdFactory = this._factory.createMarkdownCell;
|
|
let mdFactory = this._factory.createMarkdownCell;
|
|
- return mdFactory(model as MarkdownCellModel, this._rendermime);
|
|
|
|
|
|
+ widget = mdFactory(cell as MarkdownCellModel, this._rendermime);
|
|
|
|
+ break;
|
|
default:
|
|
default:
|
|
- let rawFactory = this._factory.createRawCell;
|
|
|
|
- return rawFactory(model as RawCellModel);
|
|
|
|
|
|
+ widget = this._factory.createRawCell(cell as RawCellModel);
|
|
}
|
|
}
|
|
|
|
+ widget.addClass(NB_CELL_CLASS);
|
|
|
|
+ let layout = this.layout as PanelLayout;
|
|
|
|
+ layout.insertChild(index, widget);
|
|
|
|
+ this.updateChild(index);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Set the mimetype of the child code widgets.
|
|
|
|
|
|
+ * Update the child widgets.
|
|
*/
|
|
*/
|
|
- private _setChildMimetypes(): void {
|
|
|
|
- let cursor = this.model.getMetadata('language_info');
|
|
|
|
- let info = cursor.getValue() as nbformat.ILanguageInfoMetadata;
|
|
|
|
- let mimetype = mimetypeForLanguage(info as IKernelLanguageInfo);
|
|
|
|
|
|
+ private _updateChildren(): void {
|
|
let layout = this.layout as PanelLayout;
|
|
let layout = this.layout as PanelLayout;
|
|
for (let i = 0; i < layout.childCount(); i++) {
|
|
for (let i = 0; i < layout.childCount(); i++) {
|
|
- let widget = layout.childAt(i) as CodeCellWidget;
|
|
|
|
- if (widget instanceof CodeCellWidget) {
|
|
|
|
- widget.mimetype = mimetype;
|
|
|
|
- }
|
|
|
|
|
|
+ this.updateChild(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Handle changes to the notebook model.
|
|
|
|
|
|
+ * Remove a child widget.
|
|
*/
|
|
*/
|
|
- private _onMetadataChanged(model: INotebookModel, args: IChangedArgs<any>): void {
|
|
|
|
- switch (args.name) {
|
|
|
|
- case 'language_info':
|
|
|
|
- this._setChildMimetypes();
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
+ private _removeChild(index: number): void {
|
|
|
|
+ let layout = this.layout as PanelLayout;
|
|
|
|
+ layout.childAt(index).dispose();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -278,42 +313,41 @@ class StaticNotebook extends Widget {
|
|
*/
|
|
*/
|
|
private _onCellsChanged(sender: IObservableList<ICellModel>, args: IListChangedArgs<ICellModel>) {
|
|
private _onCellsChanged(sender: IObservableList<ICellModel>, args: IListChangedArgs<ICellModel>) {
|
|
let layout = this.layout as PanelLayout;
|
|
let layout = this.layout as PanelLayout;
|
|
- let model: ICellModel;
|
|
|
|
switch (args.type) {
|
|
switch (args.type) {
|
|
case ListChangeType.Add:
|
|
case ListChangeType.Add:
|
|
- model = args.newValue as ICellModel;
|
|
|
|
- layout.insertChild(args.newIndex, this._createWidget(model));
|
|
|
|
|
|
+ this._insertChild(args.newIndex, args.newValue as ICellModel);
|
|
break;
|
|
break;
|
|
case ListChangeType.Move:
|
|
case ListChangeType.Move:
|
|
layout.insertChild(args.newIndex, layout.childAt(args.oldIndex));
|
|
layout.insertChild(args.newIndex, layout.childAt(args.oldIndex));
|
|
break;
|
|
break;
|
|
case ListChangeType.Remove:
|
|
case ListChangeType.Remove:
|
|
- layout.childAt(args.oldIndex).parent = null;
|
|
|
|
|
|
+ this._removeChild(args.oldIndex);
|
|
break;
|
|
break;
|
|
case ListChangeType.Replace:
|
|
case ListChangeType.Replace:
|
|
|
|
+ // TODO: reuse existing cell widgets if possible.
|
|
let oldValues = args.oldValue as ICellModel[];
|
|
let oldValues = args.oldValue as ICellModel[];
|
|
for (let i = 0; i < oldValues.length; i++) {
|
|
for (let i = 0; i < oldValues.length; i++) {
|
|
- layout.childAt(args.oldIndex).parent = null;
|
|
|
|
|
|
+ this._removeChild(args.oldIndex);
|
|
}
|
|
}
|
|
let newValues = args.newValue as ICellModel[];
|
|
let newValues = args.newValue as ICellModel[];
|
|
for (let i = newValues.length; i > 0; i--) {
|
|
for (let i = newValues.length; i > 0; i--) {
|
|
- model = newValues[i - 1];
|
|
|
|
- layout.insertChild(args.newIndex, this._createWidget(model));
|
|
|
|
|
|
+ this._insertChild(args.newIndex, newValues[i - 1]);
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case ListChangeType.Set:
|
|
case ListChangeType.Set:
|
|
- layout.childAt(args.newIndex).parent = null;
|
|
|
|
- model = args.newValue as ICellModel;
|
|
|
|
- layout.insertChild(args.newIndex, this._createWidget(model));
|
|
|
|
|
|
+ // TODO: reuse existing widget if possible.
|
|
|
|
+ this._removeChild(args.newIndex);
|
|
|
|
+ this._insertChild(args.newIndex, args.newValue as ICellModel);
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private _mimetype = 'text/plain';
|
|
private _model: INotebookModel = null;
|
|
private _model: INotebookModel = null;
|
|
private _rendermime: RenderMime<Widget> = null;
|
|
private _rendermime: RenderMime<Widget> = null;
|
|
- private _factory: StaticNotebook.IFactory = null;
|
|
|
|
|
|
+ private _factory: StaticNotebook.ICellWidgetFactory = null;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -342,14 +376,14 @@ namespace StaticNotebook {
|
|
*
|
|
*
|
|
* The default is a shared factory instance.
|
|
* The default is a shared factory instance.
|
|
*/
|
|
*/
|
|
- factory?: IFactory;
|
|
|
|
|
|
+ factory?: ICellWidgetFactory;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* A factory for creating code cell widgets.
|
|
* A factory for creating code cell widgets.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
- interface IFactory {
|
|
|
|
|
|
+ interface ICellWidgetFactory {
|
|
/**
|
|
/**
|
|
* Create a new code cell widget.
|
|
* Create a new code cell widget.
|
|
*/
|
|
*/
|
|
@@ -367,20 +401,37 @@ namespace StaticNotebook {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * The default `IFactory` instance.
|
|
|
|
|
|
+ * The default implementation of a cell widget factory.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
- const defaultFactory: IFactory = {
|
|
|
|
- createCodeCell: (model: ICodeCellModel, rendermime: RenderMime<Widget>) => {
|
|
|
|
|
|
+ class CellWidgetFactory implements ICellWidgetFactory {
|
|
|
|
+ /**
|
|
|
|
+ * Create a new code cell widget.
|
|
|
|
+ */
|
|
|
|
+ createCodeCell(model: ICodeCellModel, rendermime: RenderMime<Widget>): CodeCellWidget {
|
|
return new CodeCellWidget(model, rendermime);
|
|
return new CodeCellWidget(model, rendermime);
|
|
- },
|
|
|
|
- createMarkdownCell: (model: IMarkdownCellModel, rendermime: RenderMime<Widget>) => {
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create a new markdown cell widget.
|
|
|
|
+ */
|
|
|
|
+ createMarkdownCell(model: IMarkdownCellModel, rendermime: RenderMime<Widget>): MarkdownCellWidget {
|
|
return new MarkdownCellWidget(model, rendermime);
|
|
return new MarkdownCellWidget(model, rendermime);
|
|
- },
|
|
|
|
- createRawCell: (model: IRawCellModel) => {
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create a new raw cell widget.
|
|
|
|
+ */
|
|
|
|
+ createRawCell(model: IRawCellModel): RawCellWidget {
|
|
return new RawCellWidget(model);
|
|
return new RawCellWidget(model);
|
|
}
|
|
}
|
|
- };
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The default `ICellWidgetFactory` instance.
|
|
|
|
+ */
|
|
|
|
+ export
|
|
|
|
+ const defaultFactory = new CellWidgetFactory();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -573,7 +624,6 @@ class Notebook extends StaticNotebook {
|
|
* Handle a `child-added` message.
|
|
* Handle a `child-added` message.
|
|
*/
|
|
*/
|
|
protected onChildAdded(msg: ChildMessage): void {
|
|
protected onChildAdded(msg: ChildMessage): void {
|
|
- super.onChildAdded(msg);
|
|
|
|
let widget = msg.child as BaseCellWidget;
|
|
let widget = msg.child as BaseCellWidget;
|
|
widget.editor.edgeRequested.connect(this._onEdgeRequest, this);
|
|
widget.editor.edgeRequested.connect(this._onEdgeRequest, this);
|
|
this.update();
|
|
this.update();
|
|
@@ -583,7 +633,6 @@ class Notebook extends StaticNotebook {
|
|
* Handle a `child-removed` message.
|
|
* Handle a `child-removed` message.
|
|
*/
|
|
*/
|
|
protected onChildRemoved(msg: ChildMessage): void {
|
|
protected onChildRemoved(msg: ChildMessage): void {
|
|
- msg.child.dispose();
|
|
|
|
this.update();
|
|
this.update();
|
|
}
|
|
}
|
|
|
|
|