Browse Source

Finish initial cell implementation

Steven Silvester 9 years ago
parent
commit
c55dff9b2a
2 changed files with 178 additions and 46 deletions
  1. 1 0
      src/notebook/cells/model.ts
  2. 177 46
      src/notebook/cells/widget.ts

+ 1 - 0
src/notebook/cells/model.ts

@@ -67,6 +67,7 @@ interface ICellModel extends IDisposable {
 }
 
 
+
 /**
  * The definition of a code cell.
  */

+ 177 - 46
src/notebook/cells/widget.ts

@@ -2,6 +2,14 @@
 // Distributed under the terms of the Modified BSD License.
 'use strict';
 
+import {
+  loadModeByMIME
+} from 'jupyter-js-ui/lib/codemirror';
+
+import {
+  CodeMirrorWidget
+} from 'jupyter-js-ui/lib/codemirror/widget';
+
 import {
   RenderMime
 } from 'jupyter-js-ui/lib/rendermime';
@@ -18,10 +26,6 @@ import {
   Widget
 } from 'phosphor-widget';
 
-import {
-  IEditorWidget, IEditorModel, EditorWidget
-} from '../editor';
-
 import {
   MimeBundle
 } from '../notebook/nbformat';
@@ -44,6 +48,31 @@ import {
  */
 const CELL_CLASS = 'jp-Cell';
 
+/**
+ * The class name added to input area widgets.
+ */
+const INPUT_CLASS = 'jp-InputArea';
+
+/**
+ * The class name added to the prompt area of cell.
+ */
+const PROMPT_CLASS = 'jp-InputArea-prompt';
+
+/**
+ * The class name added to the editor area of the cell.
+ */
+const EDITOR_CLASS = 'jp-InputArea-editor';
+
+/**
+ * The class name added to the cell when collapsed.
+ */
+const COLLAPSED_CLASS = 'jp-mod-collapsed';
+
+/**
+ * The class name added to the cell when readonly.
+ */
+const READONLY_CLASS = 'jp-mod-readOnly';
+
 /**
  * The class name added to code cells.
  */
@@ -80,13 +109,6 @@ const DEFAULT_MARKDOWN_TEXT = 'Type Markdown and LaTeX: $ α^2 $';
  */
 export
 class BaseCellWidget extends Widget {
-  /**
-   * Create a new editor widget.
-   */
-  static createEditor(model: IEditorModel): IEditorWidget {
-    return new EditorWidget(model);
-  }
-
   /**
    * Construct a new base cell widget.
    */
@@ -94,12 +116,12 @@ class BaseCellWidget extends Widget {
     super();
     this.addClass(CELL_CLASS);
     this._model = model;
-    let constructor = this.constructor as typeof BaseCellWidget;
-    this._editor = constructor.createEditor(model.editor);
-    // TODO: Create the input area.
-    this._input = new Widget();
+    this._editor = new CodeMirrorWidget();
+    this._input = new InputAreaWidget(this._editor);
     this.layout = new PanelLayout();
     (this.layout as PanelLayout).addChild(this._input);
+    this._initializeEditor();
+    model.contentChanged.connect(this.onModelChanged, this);
   }
 
   /**
@@ -113,13 +135,59 @@ class BaseCellWidget extends Widget {
   }
 
   /**
-   * Get the editor widget used by the widget.
+   * Get the editor used by the widget.
    *
    * #### Notes
-   * This is a read-only property.
+   * This is a ready-only property.
+   */
+   get editor(): CodeMirror.Editor {
+     return this._editor.editor;
+   }
+
+  /**
+   * The mimetype used by the cell.
+   */
+  get mimetype(): string {
+    return this._mimetype;
+  }
+  set mimetype(value: string) {
+    if (this._mimetype === value) {
+      return;
+    }
+    this._mimetype = value;
+    loadModeByMIME(this.editor, value);
+  }
+
+  /**
+   * The read only state of the cell.
+   */
+  get readOnly(): boolean {
+    return this._readOnly;
+  }
+  set readOnly(value: boolean) {
+    if (value === this._readOnly) {
+      return;
+    }
+    let option = value ? 'nocursor' : false;
+    this.editor.setOption('readOnly', option);
+  }
+  /**
+   * Set the prompt for the widget.
+   */
+  setPrompt(value: string): void {
+    this._input.setPrompt(value);
+  }
+
+  /**
+   * Toggle whether the input is shown.
    */
-  get editor(): IEditorWidget {
-    return this._editor;
+  toggleInput(value: boolean): void {
+    if (value) {
+      this._input.show();
+      this.editor.focus();
+    } else {
+      this._input.hide();
+    }
   }
 
   /**
@@ -131,12 +199,42 @@ class BaseCellWidget extends Widget {
       return;
     }
     this._model = null;
+    this._input = null;
+    this._editor = null;
     super.dispose();
   }
 
-  private _input: Widget = null;
-  private _editor: IEditorWidget = null;
+  /**
+   * Handle changes in the model.
+   */
+  protected onModelChanged(model: ICellModel, change: string): void {
+    switch (change) {
+    case 'source':
+      this.editor.getDoc().setValue(model.source);
+      break;
+    default:
+      break;
+    }
+  }
+
+  /**
+   * Initialize the codemirror editor.
+   */
+  private _initializeEditor(): void {
+    let doc = this.editor.getDoc();
+    CodeMirror.on(doc, 'change', (instance, change) => {
+      if (change.origin === 'setValue') {
+        return;
+      }
+      this._model.source = instance.getValue();
+    });
+  }
+
+  private _input: InputAreaWidget = null;
+  private _editor: CodeMirrorWidget = null;
   private _model: ICellModel = null;
+  private _mimetype = 'text/plain';
+  private _readOnly = false;
 }
 
 
@@ -194,27 +292,31 @@ class MarkdownCellWidget extends BaseCellWidget {
   /**
    * Construct a Markdown cell widget.
    */
-  constructor(model: IMarkdownCellModel, rendermime: RenderMime<Widget>) {
+  constructor(model: ICellModel, rendermime: RenderMime<Widget>) {
     super(model);
     this._rendermime = rendermime;
     this.addClass(MARKDOWN_CELL_CLASS);
     // Insist on the Github-flavored markdown mode.
-    model.mimetype = 'text/x-ipythongfm';
-    this._rendered = new Widget();
-    this._rendered.addClass(RENDERER_CLASS);
-    (this.layout as PanelLayout).addChild(this._rendered);
-    this.model.stateChanged.connect(this.onModelChanged, this);
+    this.mimetype = 'text/x-ipythongfm';
+    this._renderer = new Widget();
+    this._renderer.addClass(RENDERER_CLASS);
+    (this.layout as PanelLayout).addChild(this._renderer);
+    this.model.contentChanged.connect(this.onModelChanged, this);
   }
 
   /**
-   * Get the rendering widget used by the widget.
-   *
-   * #### Notes
-   * This is a read-only property.
+   * Whether the cell is rendered.
    */
-  get rendered(): Widget {
+  get rendered(): boolean {
     return this._rendered;
   }
+  set rendered(value: boolean) {
+    if (value === this._rendered) {
+      return;
+    }
+    this._rendered = value;
+    this.update();
+  }
 
   /**
    * Handle `after-attach` messages.
@@ -227,24 +329,23 @@ class MarkdownCellWidget extends BaseCellWidget {
    * Handle `update_request` messages.
    */
   protected onUpdateRequest(message: Message): void {
-    let model = this.model as IMarkdownCellModel;
-    if (model.rendered) {
+    let model = this.model;
+    if (this.rendered) {
       if (this._dirty) {
-        let text = model.input.textEditor.text || DEFAULT_MARKDOWN_TEXT;
+        let text = model.source || DEFAULT_MARKDOWN_TEXT;
         text = sanitize(text);
         let bundle: MimeBundle = { 'text/markdown': text };
-        this._rendered.dispose();
-        this._rendered = this._rendermime.render(bundle) || new Widget();
-        this._rendered.addClass(RENDERER_CLASS);
-        (this.layout as PanelLayout).addChild(this._rendered);
+        this._renderer.dispose();
+        this._renderer = this._rendermime.render(bundle) || new Widget();
+        this._renderer.addClass(RENDERER_CLASS);
+        (this.layout as PanelLayout).addChild(this._renderer);
       }
-      this._rendered.show();
-      this.input.hide();
+      this._renderer.show();
+      this.toggleInput(false);
       this.addClass(RENDERED_CLASS);
     } else {
-      this._rendered.hide();
-      this.input.show();
-      this.input.editor.focus();
+      this._renderer.hide();
+      this.toggleInput(true);
       this.removeClass(RENDERED_CLASS);
     }
     this._dirty = false;
@@ -252,7 +353,8 @@ class MarkdownCellWidget extends BaseCellWidget {
   }
 
   private _rendermime: RenderMime<Widget> = null;
-  private _rendered: Widget = null;
+  private _renderer: Widget = null;
+  private _rendered = true;
   private _dirty = true;
 }
 
@@ -265,8 +367,37 @@ class RawCellWidget extends BaseCellWidget {
   /**
    * Construct a raw cell widget.
    */
-  constructor(model: IRawCellModel) {
+  constructor(model: ICellModel) {
     super(model);
     this.addClass(RAW_CELL_CLASS);
   }
 }
+
+
+/**
+ * An input area widget, which hosts an editor widget.
+ */
+class InputAreaWidget extends Widget {
+  /**
+   * Construct an input area widget.
+   */
+  constructor(editor: CodeMirrorWidget) {
+    super();
+    this.addClass(INPUT_CLASS);
+    editor.addClass(EDITOR_CLASS);
+    this.layout = new PanelLayout();
+    let prompt = new Widget();
+    prompt.addClass(PROMPT_CLASS);
+    let layout = this.layout as PanelLayout;
+    layout.addChild(prompt);
+    layout.addChild(editor);
+  }
+
+  /**
+   * Set the prompt of the input area.
+   */
+  setPrompt(value: string): void {
+    let prompt = (this.layout as PanelLayout).childAt(0);
+    prompt.node.textContent = value;
+  }
+}