Explorar el Código

Clean up notebook widget api

Steven Silvester hace 9 años
padre
commit
6afb830f40
Se han modificado 3 ficheros con 116 adiciones y 104 borrados
  1. 25 25
      src/notebook/notebook/actions.ts
  2. 6 6
      src/notebook/notebook/panel.ts
  3. 85 73
      src/notebook/notebook/widget.ts

+ 25 - 25
src/notebook/notebook/actions.ts

@@ -23,7 +23,7 @@ import {
 } from './nbformat';
 
 import {
-  ActiveNotebook
+  Notebook
 } from './widget';
 
 
@@ -42,7 +42,7 @@ namespace NotebookActions {
    * Split the active cell into two cells.
    */
   export
-  function splitCell(widget: ActiveNotebook): void {
+  function splitCell(widget: Notebook): void {
     Private.deselectCells(widget);
     let nbModel = widget.model;
     let index = widget.activeCellIndex;
@@ -68,7 +68,7 @@ namespace NotebookActions {
    * Merge selected cells.
    */
   export
-  function mergeCells(widget: ActiveNotebook): void {
+  function mergeCells(widget: Notebook): void {
     let toMerge: string[] = [];
     let toDelete: ICellModel[] = [];
     let model = widget.model;
@@ -130,7 +130,7 @@ namespace NotebookActions {
    * Delete the selected cells.
    */
   export
-  function deleteCells(widget: ActiveNotebook): void {
+  function deleteCells(widget: Notebook): void {
     let model = widget.model;
     let cells = model.cells;
     // Delete the cells as one undo event.
@@ -154,7 +154,7 @@ namespace NotebookActions {
    * Insert a new code cell above the current cell.
    */
   export
-  function insertAbove(widget: ActiveNotebook): void {
+  function insertAbove(widget: Notebook): void {
     let cell = widget.model.createCodeCell();
     widget.model.cells.insert(widget.activeCellIndex, cell);
     Private.deselectCells(widget);
@@ -164,7 +164,7 @@ namespace NotebookActions {
    * Insert a node code cell below the current cell.
    */
   export
-  function insertBelow(widget: ActiveNotebook): void {
+  function insertBelow(widget: Notebook): void {
     let cell = widget.model.createCodeCell();
     widget.model.cells.insert(widget.activeCellIndex + 1, cell);
     Private.deselectCells(widget);
@@ -174,7 +174,7 @@ namespace NotebookActions {
    * Change the selected cell type(s).
    */
   export
-  function changeCellType(widget: ActiveNotebook, value: string): void {
+  function changeCellType(widget: Notebook, value: string): void {
     let model = widget.model;
     model.cells.beginCompoundOperation();
     for (let i = 0; i < widget.childCount(); i++) {
@@ -211,7 +211,7 @@ namespace NotebookActions {
    * Run the selected cell(s).
    */
   export
-  function run(widget: ActiveNotebook, kernel?: IKernel): void {
+  function run(widget: Notebook, kernel?: IKernel): void {
     let selected: BaseCellWidget[] = [];
     for (let i = 0; i < widget.childCount(); i++) {
       let child = widget.childAt(i);
@@ -238,7 +238,7 @@ namespace NotebookActions {
    * edit mode and selected.
    */
   export
-  function runAndAdvance(widget: ActiveNotebook, kernel?: IKernel): void {
+  function runAndAdvance(widget: Notebook, kernel?: IKernel): void {
     run(widget, kernel);
     let model = widget.model;
     if (widget.activeCellIndex === widget.childCount() - 1) {
@@ -256,7 +256,7 @@ namespace NotebookActions {
    * Run the selected cell(s) and insert a new code cell below in edit mode.
    */
   export
-  function runAndInsert(widget: ActiveNotebook, kernel?: IKernel): void {
+  function runAndInsert(widget: Notebook, kernel?: IKernel): void {
     run(widget, kernel);
     let model = widget.model;
     let cell = model.createCodeCell();
@@ -270,7 +270,7 @@ namespace NotebookActions {
    * Run all of the cells in the notebook.
    */
   export
-  function runAll(widget: ActiveNotebook, kernel?: IKernel): void {
+  function runAll(widget: Notebook, kernel?: IKernel): void {
     for (let i = 0; i < widget.childCount(); i++) {
       Private.runCell(widget.childAt(i), kernel);
     }
@@ -282,7 +282,7 @@ namespace NotebookActions {
    * Select the cell below the active cell.
    */
   export
-  function selectBelow(widget: ActiveNotebook): void {
+  function selectBelow(widget: Notebook): void {
     if (widget.activeCellIndex === widget.childCount() - 1) {
       return;
     }
@@ -295,7 +295,7 @@ namespace NotebookActions {
    * Select the above the active cell.
    */
   export
-  function selectAbove(widget: ActiveNotebook): void {
+  function selectAbove(widget: Notebook): void {
     if (widget.activeCellIndex === 0) {
       return;
     }
@@ -308,7 +308,7 @@ namespace NotebookActions {
    * Extend the selection to the cell above.
    */
   export
-  function extendSelectionAbove(widget: ActiveNotebook): void {
+  function extendSelectionAbove(widget: Notebook): void {
     // Do not wrap around.
     if (widget.activeCellIndex === 0) {
       return;
@@ -336,7 +336,7 @@ namespace NotebookActions {
    * Extend the selection to the cell below.
    */
   export
-  function extendSelectionBelow(widget: ActiveNotebook): void {
+  function extendSelectionBelow(widget: Notebook): void {
     // Do not wrap around.
     if (widget.activeCellIndex === widget.childCount() - 1) {
       return;
@@ -364,7 +364,7 @@ namespace NotebookActions {
    * Copy the selected cells to a clipboard.
    */
   export
-  function copy(widget: ActiveNotebook, clipboard: IClipboard): void {
+  function copy(widget: Notebook, clipboard: IClipboard): void {
     clipboard.clear();
     let data: nbformat.IBaseCell[] = [];
     for (let i = 0; i < widget.childCount(); i++) {
@@ -381,7 +381,7 @@ namespace NotebookActions {
    * Cut the selected cells to a clipboard.
    */
   export
-  function cut(widget: ActiveNotebook, clipboard: IClipboard): void {
+  function cut(widget: Notebook, clipboard: IClipboard): void {
     clipboard.clear();
     let data: nbformat.IBaseCell[] = [];
     let model = widget.model;
@@ -408,7 +408,7 @@ namespace NotebookActions {
    * Paste cells from a clipboard.
    */
   export
-  function paste(widget: ActiveNotebook, clipboard: IClipboard): void {
+  function paste(widget: Notebook, clipboard: IClipboard): void {
     if (!clipboard.hasData(JUPYTER_CELL_MIME)) {
       return;
     }
@@ -437,7 +437,7 @@ namespace NotebookActions {
    * Undo a cell action.
    */
   export
-  function undo(widget: ActiveNotebook): void {
+  function undo(widget: Notebook): void {
     widget.mode = 'command';
     widget.model.cells.undo();
   }
@@ -446,7 +446,7 @@ namespace NotebookActions {
    * Redo a cell action.
    */
   export
-  function redo(widget: ActiveNotebook): void {
+  function redo(widget: Notebook): void {
     widget.mode = 'command';
     widget.model.cells.redo();
   }
@@ -455,7 +455,7 @@ namespace NotebookActions {
    * Toggle line numbers on the selected cell(s).
    */
   export
-  function toggleLineNumbers(widget: ActiveNotebook): void {
+  function toggleLineNumbers(widget: Notebook): void {
     let cell = widget.childAt(widget.activeCellIndex);
     let editor = cell.editor.editor;
     let lineNumbers = editor.getOption('lineNumbers');
@@ -472,7 +472,7 @@ namespace NotebookActions {
    * Toggle the line number of all cells.
    */
   export
-  function toggleAllLineNumbers(widget: ActiveNotebook): void {
+  function toggleAllLineNumbers(widget: Notebook): void {
     let cell = widget.childAt(widget.activeCellIndex);
     let editor = cell.editor.editor;
     let lineNumbers = editor.getOption('lineNumbers');
@@ -487,7 +487,7 @@ namespace NotebookActions {
    * Clear the outputs of the currently selected cells.
    */
   export
-  function clearOutputs(widget: ActiveNotebook): void {
+  function clearOutputs(widget: Notebook): void {
     let cells = widget.model.cells;
     for (let i = 0; i < cells.length; i++) {
       let cell = cells.get(i) as CodeCellModel;
@@ -503,7 +503,7 @@ namespace NotebookActions {
    * Clear the code outputs on the widget.
    */
   export
-  function clearAllOutputs(widget: ActiveNotebook): void {
+  function clearAllOutputs(widget: Notebook): void {
     let cells = widget.model.cells;
     for (let i = 0; i < cells.length; i++) {
       let cell = cells.get(i) as CodeCellModel;
@@ -524,7 +524,7 @@ namespace Private {
    * Deselect all of the cells.
    */
   export
-  function deselectCells(widget: ActiveNotebook): void {
+  function deselectCells(widget: Notebook): void {
     for (let i = 0; i < widget.childCount(); i++) {
       let child = widget.childAt(i);
       widget.deselect(child);

+ 6 - 6
src/notebook/notebook/panel.ts

@@ -50,7 +50,7 @@ import {
 } from './toolbar';
 
 import {
-  ActiveNotebook
+  Notebook
 } from './widget';
 
 
@@ -82,8 +82,8 @@ class NotebookPanel extends Widget {
   /**
    * Create a new content area for the notebook.
    */
-  static createContent(model: INotebookModel, rendermime: RenderMime<Widget>): ActiveNotebook {
-    let widget = new ActiveNotebook(rendermime);
+  static createContent(model: INotebookModel, rendermime: RenderMime<Widget>): Notebook {
+    let widget = new Notebook(rendermime);
     widget.model = model;
     return widget;
   }
@@ -180,7 +180,7 @@ class NotebookPanel extends Widget {
    * #### Notes
    * This is a read-only property.
    */
-  get content(): ActiveNotebook {
+  get content(): Notebook {
     return this._content;
   }
 
@@ -283,7 +283,7 @@ class NotebookPanel extends Widget {
   /**
    * Handle a change in the content area.
    */
-  protected onContentChanged(sender: ActiveNotebook, args: IChangedArgs<any>): void {
+  protected onContentChanged(sender: Notebook, args: IChangedArgs<any>): void {
     switch (args.name) {
     case 'activeCellIndex':
       let cell = this._content.childAt(args.oldValue);
@@ -373,7 +373,7 @@ class NotebookPanel extends Widget {
   private _rendermime: RenderMime<Widget> = null;
   private _context: IDocumentContext = null;
   private _model: INotebookModel = null;
-  private _content: ActiveNotebook = null;
+  private _content: Notebook = null;
   private _toolbar: NotebookToolbar = null;
   private _clipboard: IClipboard = null;
   private _completion: CompletionWidget = null;

+ 85 - 73
src/notebook/notebook/widget.ts

@@ -30,7 +30,7 @@ import {
 } from 'phosphor-signaling';
 
 import {
-  Widget
+  ChildMessage, Widget
 } from 'phosphor-widget';
 
 import {
@@ -99,10 +99,10 @@ type NotebookMode = 'command' | 'edit';
 
 
 /**
- * A widget which renders notebooks.
+ * A widget which renders static non-interactive notebooks.
  */
 export
-class NotebookRenderer extends Widget {
+class StaticNotebook extends Widget {
   /**
    * Create a new cell widget given a cell model.
    */
@@ -130,6 +130,13 @@ class NotebookRenderer extends Widget {
     this.layout = new PanelLayout();
   }
 
+  /**
+   * A signal emitted when the model of the notebook changes.
+   */
+  get modelChanged(): ISignal<StaticNotebook, INotebookModel> {
+    return Private.modelChangedSignal.bind(this);
+  }
+
   /**
    * Get the model for the widget.
    */
@@ -140,21 +147,16 @@ class NotebookRenderer extends Widget {
   /**
    * Set the model for the widget.
    *
-   * #### Notes
-   * The model is single-use only. It cannot be set to `null` and it
-   * cannot be changed after the first assignment.
-   *
    */
-  set model(value: INotebookModel) {
-    value = value || null;
-    if (this._model === value) {
+  set model(newValue: INotebookModel) {
+    newValue = newValue || null;
+    if (this._model === newValue) {
       return;
     }
-    if (this._model) {
-      throw new Error('Cannot change widget model.');
-    }
-    this._model = value;
-    this.initialize(value);
+    let oldValue = this._model;
+    this._model = newValue;
+    this.changeModel(oldValue, newValue);
+    this.modelChanged.emit(newValue);
   }
 
   /**
@@ -197,42 +199,74 @@ class NotebookRenderer extends Widget {
   }
 
   /**
-   * Initialize the widget based on the model.
+   * Handle a new model on the widget.
    *
    * #### Notes
    * Creates child widgets for each of the cells in the model.
    * If there are no cells, adds a single code cell.
    */
-  protected initialize(model: INotebookModel): void {
-    let rendermime = this.rendermime;
-    let cells = model.cells;
-
-    // Add the current cells.
-    if (cells.length === 0) {
-      // Add a new code cell if there are no cells.
-      let cell = model.createCodeCell();
-      cells.add(cell);
-    }
+  protected changeModel(oldValue: INotebookModel, newValue: INotebookModel): void {
     let layout = this.layout as PanelLayout;
-    let constructor = this.constructor as typeof NotebookRenderer;
+    if (oldValue) {
+      oldValue.cells.changed.disconnect(this._onCellsChanged, this);
+      oldValue.metadataChanged.disconnect(this._onMetadataChanged, this);
+      // TODO: reuse existing cell widgets if possible.
+      for (let i = 0; i < layout.childCount(); i++) {
+        layout.removeChild(layout.childAt(0));
+      }
+    }
+    let rendermime = this.rendermime;
+    let cells = newValue.cells;
+    let constructor = this.constructor as typeof StaticNotebook;
     let factory = constructor.createCell;
     for (let i = 0; i < cells.length; i++) {
       let widget = factory(cells.get(i), rendermime);
-      this.initializeCellWidget(widget);
       layout.addChild(widget);
     }
-    this.updateMimetypes();
-    cells.changed.connect(this.onCellsChanged, this);
-    model.metadataChanged.connect(this.onMetadataChanged, this);
+    this.setChildMimetypes();
+    cells.changed.connect(this._onCellsChanged, this);
+    newValue.metadataChanged.connect(this._onMetadataChanged, this);
+  }
+
+  /**
+   * Handle a `child-added` message.
+   */
+  protected onChildAdded(msg: ChildMessage): void {
+    msg.child.addClass(NB_CLASS);
+    this.update();
+  }
+
+  /**
+   * Handle a `child-removed` message.
+   */
+  protected onChildRemoved(msg: ChildMessage): void {
+    msg.child.dispose();
+    this.update();
+  }
+
+  /**
+   * Set the mimetype of the child code widgets.
+   */
+  protected setChildMimetypes(): void {
+    let cursor = this.model.getMetadata('language_info');
+    let info = cursor.getValue() as nbformat.ILanguageInfoMetadata;
+    let mimetype = mimetypeForLanguage(info as IKernelLanguageInfo);
+    let layout = this.layout as PanelLayout;
+    for (let i = 0; i < layout.childCount(); i++) {
+      let widget = layout.childAt(i) as CodeCellWidget;
+      if (widget instanceof CodeCellWidget) {
+        widget.mimetype = mimetype;
+      }
+    }
   }
 
   /**
    * Handle changes to the notebook model.
    */
-  protected onMetadataChanged(model: INotebookModel, args: IChangedArgs<any>): void {
+  private _onMetadataChanged(model: INotebookModel, args: IChangedArgs<any>): void {
     switch (args.name) {
     case 'language_info':
-      this.updateMimetypes();
+      this.setChildMimetypes();
       break;
     default:
       break;
@@ -242,15 +276,14 @@ class NotebookRenderer extends Widget {
   /**
    * Handle a change cells event.
    */
-  protected onCellsChanged(sender: IObservableList<ICellModel>, args: IListChangedArgs<ICellModel>) {
+  private _onCellsChanged(sender: IObservableList<ICellModel>, args: IListChangedArgs<ICellModel>) {
     let layout = this.layout as PanelLayout;
-    let constructor = this.constructor as typeof NotebookRenderer;
+    let constructor = this.constructor as typeof StaticNotebook;
     let factory = constructor.createCell;
     let widget: BaseCellWidget;
     switch (args.type) {
     case ListChangeType.Add:
       widget = factory(args.newValue as ICellModel, this._rendermime);
-      this.initializeCellWidget(widget);
       layout.insertChild(args.newIndex, widget);
       break;
     case ListChangeType.Move:
@@ -259,57 +292,28 @@ class NotebookRenderer extends Widget {
     case ListChangeType.Remove:
       widget = layout.childAt(args.oldIndex) as BaseCellWidget;
       layout.removeChild(widget);
-      widget.dispose();
       break;
     case ListChangeType.Replace:
       let oldValues = args.oldValue as ICellModel[];
       for (let i = 0; i < oldValues.length; i++) {
         widget = layout.childAt(args.oldIndex) as BaseCellWidget;
         layout.removeChild(widget);
-        widget.dispose();
       }
       let newValues = args.newValue as ICellModel[];
       for (let i = newValues.length; i > 0; i--) {
         widget = factory(newValues[i - 1], this._rendermime);
-        this.initializeCellWidget(widget);
         layout.insertChild(args.newIndex, widget);
       }
       break;
     case ListChangeType.Set:
       widget = layout.childAt(args.newIndex) as BaseCellWidget;
       layout.removeChild(widget);
-      widget.dispose();
       widget = factory(args.newValue as ICellModel, this._rendermime);
       layout.insertChild(args.newIndex, widget);
-      this.initializeCellWidget(widget);
       break;
     default:
       return;
     }
-    this.update();
-  }
-
-  /**
-   * Initialize a cell widget.
-   */
-  protected initializeCellWidget(widget: BaseCellWidget): void {
-    widget.addClass(NB_CELL_CLASS);
-  }
-
-  /**
-   * Update the mimetype of code widgets.
-   */
-  protected updateMimetypes(): void {
-    let cursor = this.model.getMetadata('language_info');
-    let info = cursor.getValue() as nbformat.ILanguageInfoMetadata;
-    let mimetype = mimetypeForLanguage(info as IKernelLanguageInfo);
-    let layout = this.layout as PanelLayout;
-    for (let i = 0; i < layout.childCount(); i++) {
-      let widget = layout.childAt(i) as CodeCellWidget;
-      if (widget instanceof CodeCellWidget) {
-        widget.mimetype = mimetype;
-      }
-    }
   }
 
   private _model: INotebookModel = null;
@@ -321,11 +325,11 @@ class NotebookRenderer extends Widget {
  * A notebook widget that supports interactivity.
  */
 export
-class ActiveNotebook extends NotebookRenderer {
+class Notebook extends StaticNotebook {
   /**
    * A signal emitted when the state of the notebook changes.
    */
-  get stateChanged(): ISignal<ActiveNotebook, IChangedArgs<any>> {
+  get stateChanged(): ISignal<Notebook, IChangedArgs<any>> {
     return Private.stateChangedSignal.bind(this);
   }
 
@@ -503,17 +507,18 @@ class ActiveNotebook extends NotebookRenderer {
   }
 
   /**
-   * Initialize a cell widget.
+   * Handle a `child-added` message.
    */
-  protected initializeCellWidget(widget: BaseCellWidget): void {
-    super.initializeCellWidget(widget);
-    widget.editor.edgeRequested.connect(this.onEdgeRequest, this);
+  protected onChildAdded(msg: ChildMessage): void {
+    super.onChildAdded(msg);
+    let widget = msg.child as BaseCellWidget;
+    widget.editor.edgeRequested.connect(this._onEdgeRequest, this);
   }
 
   /**
    * Handle edge request signals from cells.
    */
-  protected onEdgeRequest(widget: Widget, location: EdgeLocation): void {
+  private _onEdgeRequest(widget: Widget, location: EdgeLocation): void {
     if (location === 'top') {
       this.activeCellIndex--;
     } else {
@@ -609,11 +614,18 @@ namespace Private {
     value: false
   });
 
+  /**
+   * A signal emitted when the model changes on the notebook.
+   */
+  export
+  const modelChangedSignal = new Signal<StaticNotebook, INotebookModel>();
+
+
   /**
    * A signal emitted when the state changes on the notebook.
    */
   export
-  const stateChangedSignal = new Signal<ActiveNotebook, IChangedArgs<any>>();
+  const stateChangedSignal = new Signal<Notebook, IChangedArgs<any>>();
 
  /**
   * Scroll an element into view if needed.