Przeglądaj źródła

Clean up the input area and add prompt handling

Steven Silvester 9 lat temu
rodzic
commit
9b30def9c4

+ 5 - 0
example/index.css

@@ -1,3 +1,8 @@
+/*-----------------------------------------------------------------------------
+| Copyright (c) 2014-2016, Jupyter Development Team.
+|
+| Distributed under the terms of the Modified BSD License.
+|----------------------------------------------------------------------------*/
 body {
   margin: 0;
   padding: 0;

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

@@ -353,6 +353,7 @@ class CodeCellModel extends BaseCellModel implements ICodeCellModel {
   constructor(options?: ICellOptions) {
     super(options);
     Private.outputProperty.set(this, new OutputAreaModel());
+    this.input.prompt = 'In[ ]:';
   }
 
   /**
@@ -374,6 +375,11 @@ class CodeCellModel extends BaseCellModel implements ICodeCellModel {
    */
   set executionCount(value: number) {
     Private.executionCountProperty.set(this, value);
+    if (value !== void 0 && value !== null) {
+      this.input.prompt = `In[${value}]:`;
+    } else {
+      this.input.prompt = 'In[ ]:';
+    }
   }
 
   /**

+ 7 - 0
src/notebook/cells/widget.ts

@@ -64,6 +64,11 @@ const MARKDOWN_CELL_CLASS = 'jp-MarkdownCell';
  */
 const RAW_CELL_CLASS = 'jp-RawCell';
 
+/**
+ * The class name added to a rendered markdown cell.
+ */
+const RENDERED_CLASS = 'jp-mod-rendered';
+
 
 /**
  * A base cell widget.
@@ -209,9 +214,11 @@ class MarkdownCellWidget extends BaseCellWidget {
       this.rendered.node.innerHTML = marked(model.input.textEditor.text);
       this.input.parent = null;
       (this.layout as PanelLayout).addChild(this.rendered);
+      this.addClass(RENDERED_CLASS);
     } else {
       this.rendered.parent = null;
       (this.layout as PanelLayout).addChild(this.input);
+      this.removeClass(RENDERED_CLASS);
     }
     this._dirty = false;
   }

+ 13 - 0
src/notebook/index.css

@@ -3,4 +3,17 @@
 |
 | Distributed under the terms of the Modified BSD License.
 |----------------------------------------------------------------------------*/
+.jp-InputArea {
+  display: flex;
+  flex-direction: row;
+}
 
+
+.jp-InputArea-prompt {
+  flex: 0 0 auto;
+}
+
+
+.jp-InputArea-editor {
+  flex: 1 1 auto;
+}

+ 23 - 61
src/notebook/input-area/model.ts

@@ -20,7 +20,6 @@ import {
  */
 export
 interface IInputAreaModel {
-
   /**
    * A signal emitted when state of the input area changes.
    */
@@ -33,20 +32,13 @@ interface IInputAreaModel {
 
   /**
    * Whether the input area should be collapsed (hidden) or expanded.
-   *
-   * // TODO: this should probably be a property on the cell, not the input area.
    */
   collapsed: boolean;
 
   /**
-   * The prompt number to display for the input area.
+   * The prompt text to display for the input area.
    */
-  promptNumber: number;
-
-  /**
-   * The execution count.
-   */
-  executionCount: number;
+  prompt: string;
 
   /**
    * The dirty state of the input cell.
@@ -64,7 +56,7 @@ interface IInputAreaModel {
  * The options for creating an input area.
  */
 export
-interface IInputAreaOptions extends IEditorOptions {}
+interface IInputAreaOptions extends IEditorOptions { }
 
 
 /**
@@ -76,9 +68,8 @@ class InputAreaModel implements IInputAreaModel {
    * Construct a new input area model.
    */
   constructor(options?: IInputAreaOptions) {
-    let editor = new EditorModel(options);
-    InputAreaModelPrivate.textEditorProperty.set(this, editor);
-    editor.stateChanged.connect(this._textEditorChanged, this);
+    this._editor = new EditorModel(options);
+    this._editor.stateChanged.connect(this.onEditorChanged, this);
   }
 
   /**
@@ -103,31 +94,17 @@ class InputAreaModel implements IInputAreaModel {
   }
 
   /**
-   * Get the prompt number.
-   */
-  get promptNumber() {
-    return InputAreaModelPrivate.promptNumberProperty.get(this);
-  }
-
-  /**
-   * Set the prompt number.
-   */
-  set promptNumber(value: number) {
-    InputAreaModelPrivate.promptNumberProperty.set(this, value);
-  }
-
-  /**
-   * Get the execution count of the input area.
+   * Get the prompt text.
    */
-  get executionCount() {
-    return InputAreaModelPrivate.executionCountProperty.get(this);
+  get prompt() {
+    return InputAreaModelPrivate.promptProperty.get(this);
   }
 
   /**
-   * Set the execution count of the input area.
+   * Set the prompt text.
    */
-  set executionCount(value: number) {
-    InputAreaModelPrivate.executionCountProperty.set(this, value);
+  set prompt(value: string) {
+    InputAreaModelPrivate.promptProperty.set(this, value);
   }
 
   /**
@@ -137,14 +114,14 @@ class InputAreaModel implements IInputAreaModel {
    * This is a read-only property.
    */
   get textEditor(): EditorModel {
-    return InputAreaModelPrivate.textEditorProperty.get(this);
+    return this._editor;
   }
 
   /**
    * Get the dirty state.
    *
    * #### Notest
-   * This is a pure delegate to the dirty state of the [textEditor].
+   * This is a delegate to the dirty state of the [textEditor].
    */
   get dirty(): boolean {
     return this.textEditor.dirty;
@@ -154,7 +131,7 @@ class InputAreaModel implements IInputAreaModel {
    * Set the dirty state.
    *
    * #### Notest
-   * This is a pure delegate to the dirty state of the [textEditor].
+   * This is a delegate to the dirty state of the [textEditor].
    */
   set dirty(value: boolean) {
     this.textEditor.dirty = value;
@@ -175,13 +152,16 @@ class InputAreaModel implements IInputAreaModel {
   }
 
   /**
-   * Re-emit changes to the text editor dirty state.
+   * Handle changes to the editor state.
    */
-  private _textEditorChanged(editor: EditorModel, args: IChangedArgs<any>): void {
-    if (editor === this.textEditor && args.name === 'dirty') {
+  protected onEditorChanged(editor: EditorModel, args: IChangedArgs<any>): void {
+    if (args.name === 'dirty') {
+      // Re-emit dirty state changes from the editor.
       this.stateChanged.emit(args);
     }
   }
+
+  private _editor: EditorModel = null;
 }
 
 
@@ -205,29 +185,11 @@ namespace InputAreaModelPrivate {
   });
 
   /**
-  * A property descriptor containing the prompt number.
-  */
-  export
-  const promptNumberProperty = new Property<InputAreaModel, number>({
-    name: 'promptNumber',
-    notify: stateChangedSignal,
-  });
-
-  /**
-  * A property descriptor containing the execution count of the input area.
-  */
-  export
-  const executionCountProperty = new Property<InputAreaModel, number>({
-    name: 'executionCount',
-    notify: stateChangedSignal,
-  });
-
-  /**
-  * A property descriptor containing the text editor Model.
+  * A property descriptor containing the prompt.
   */
   export
-  const textEditorProperty = new Property<InputAreaModel, EditorModel>({
-    name: 'textEditor',
+  const promptProperty = new Property<InputAreaModel, string>({
+    name: 'prompt',
     notify: stateChangedSignal,
   });
 }

+ 91 - 19
src/notebook/input-area/widget.ts

@@ -31,52 +31,124 @@ import {
 } from './model';
 
 
+/**
+ * The class name added to input area widgets.
+ */
+const INPUT_CLASS = 'jp-InputArea';
+
+/**
+ * The class name added to the prompt area of the input area.
+ */
+const PROMPT_CLASS = 'jp-InputArea-prompt';
+
+/**
+ * The class name added to the editor area of the input area.
+ */
+const EDITOR_CLASS = 'jp-InputArea-editor';
+
+/**
+ * The class name added to the input area when collapsed.
+ */
+const COLLAPSED_CLASS = 'jp-mod-collapsed';
+
+/**
+ * The class name added to to the input area when dirty.
+ */
+const DIRTY_CLASS = 'jp-mod-dirty';
+
+/**
+ * The class name added to to the input area when readonly.
+ */
+const READONLY_CLASS = 'jp-mod-readOnly';
+
+
 /**
  * An input area widget, which hosts an editor widget.
  */
 export
 class InputAreaWidget extends Widget {
-
   /**
    * Construct an input area widget.
    */
   constructor(model: IInputAreaModel) {
     super();
-    this.addClass('jp-InputArea');
+    this.addClass(INPUT_CLASS);
     this._model = model;
     this.layout = new PanelLayout();
-    this.updateTextEditor(model.textEditor);
-    model.stateChanged.connect(this._modelUpdate, this);
+    this._prompt = new Widget();
+    this._prompt.addClass(PROMPT_CLASS);
+    this._prompt.node.textContent = model.prompt;
+    this._editor = new CodeMirrorWidget(model.textEditor);
+    this._editor.addClass(EDITOR_CLASS);
+    let layout = this.layout as PanelLayout;
+    layout.addChild(this._prompt);
+    layout.addChild(this._editor);
+    model.stateChanged.connect(this.onModelUpdated, this);
   }
 
   /**
-   * Update the text editor model, creating a new text editor
-   * widget and detaching the old one.
+   * Get the model used by the widget.
+   *
+   * #### Notes
+   * This is a read-only property.
    */
-  updateTextEditor(editor: IEditorModel) {
-    let layout = this.layout as PanelLayout;
-    if (layout.childCount() > 0) {
-      layout.childAt(0).dispose();
-    }
-    layout.addChild(new CodeMirrorWidget(editor));
+  get model(): IInputAreaModel {
+    return this._model;
+  }
+
+  /**
+   * Get the editor widget used by the widget.
+   *
+   * #### Notes
+   * This is a read-only property.
+   */
+  get editor(): CodeMirrorWidget {
+    return this._editor;
+  }
+
+  /**
+   * Get the prompt widget for the input area.
+   *
+   * #### Notes
+   * This is a read-only property.
+   */
+  get prompt(): Widget {
+    return this._prompt;
   }
 
   /**
    * Change handler for model updates.
    */
-  private _modelUpdate(sender: IInputAreaModel, args: IChangedArgs<any>) {
+  protected onModelUpdated(sender: IInputAreaModel, args: IChangedArgs<any>) {
     switch(args.name) {
-    case 'textEditor':
-      this.updateTextEditor(args.newValue);
-      break;
     case 'collapsed':
+      if (args.newValue) {
+        this.addClass(COLLAPSED_CLASS);
+      } else {
+        this.removeClass(COLLAPSED_CLASS);
+      }
+      break;
+    case 'dirty':
+      if (args.newValue) {
+        this.addClass(DIRTY_CLASS);
+      } else {
+        this.removeClass(DIRTY_CLASS);
+      }
       break;
-    case 'promptNumber':
+    case 'readOnly':
+      if (args.newValue) {
+        this.addClass(READONLY_CLASS);
+      } else {
+        this.removeClass(READONLY_CLASS);
+      }
       break;
-    case 'executionCount':
+    case 'prompt':
+      this.prompt.node.textContent = args.newValue;
       break;
     }
   }
 
-  private _model: IInputAreaModel;
+  private _model: IInputAreaModel = null;
+  private _editor: CodeMirrorWidget = null;
+  private _prompt: Widget = null;
 }

+ 5 - 1
src/notebook/notebook/model.ts

@@ -1,6 +1,6 @@
 
 import {
-  INotebookSession
+  INotebookSession, IExecuteReply
 } from 'jupyter-js-services';
 
 import {
@@ -471,6 +471,7 @@ class NotebookModel implements INotebookModel {
     if (!session) {
       return;
     }
+    cell.input.prompt = 'In[*]:';
     let exRequest = {
       code: cell.input.textEditor.text,
       silent: false,
@@ -488,6 +489,9 @@ class NotebookModel implements INotebookModel {
         output.add(model)
       }
     });
+    ex.onReply = (msg => {
+      cell.executionCount = (msg.content as IExecuteReply).execution_count;
+    });
   }
 
   /**

+ 30 - 2
src/notebook/theme.css

@@ -1,3 +1,8 @@
+/*-----------------------------------------------------------------------------
+| Copyright (c) 2014-2016, Jupyter Development Team.
+|
+| Distributed under the terms of the Modified BSD License.
+|----------------------------------------------------------------------------*/
 /* Increased specificity in case phosphor css is loaded later. */
 .p-Widget.jp-Notebook {  
   min-width: 50px;
@@ -5,12 +10,29 @@
   overflow-y: auto;
 }
 
+.jp-InputArea-prompt {
+  flex-basis: 90px;
+  color: #303F9F;
+  font-family: monospace;
+  padding: 0.4em;
+  text-align: right;
+  line-height: 20px;
+  font-size: 14px;
+}
+
+
+.jp-InputArea-editor {
+  height: auto;
+  min-height: 2em;
+}
+
 
-.jp-InputArea > .jp-CodeMirror {
+.jp-InputArea-editor.jp-CodeMirror {
   border: 1px solid #cfcfcf;
   border-radius: 2px;
   background: #f7f7f7;
   line-height: 1.21429em;
+  padding: 4px;
 }
 
 
@@ -19,6 +41,11 @@
 }
 
 
+.jp-MarkdownCell.jp-mod-rendered {
+  margin-left: 90px;
+}
+
+
 .jp-OutputArea {
   background: #ffffff;
 }
@@ -26,6 +53,7 @@
 
 .jp-OutputArea-output {
   padding: 4px;
+  margin-left: 90px;
 }
 
 
@@ -64,7 +92,7 @@
 }
 
 
-.jp-CodeMirror > .CodeMirror.cm-s-default {
+.jp-CodeMirror > .CodeMirror {
   line-height: 1.21429em;
   /* Changed from 1em to our global default */
   font-size: 14px;