From 40977e545ba746c7aacbbd2ce560b22aecf5deaa Mon Sep 17 00:00:00 2001 From: "Brian E. Granger" Date: Wed, 10 Jul 2013 17:14:30 -0700 Subject: [PATCH] Semi working version of basic dual mode UX. As of this point there are lots of things that don't work, but most of the basic dual mode interactions do work fine. --- IPython/html/static/notebook/js/cell.js | 21 ++-- IPython/html/static/notebook/js/codecell.js | 7 +- IPython/html/static/notebook/js/notebook.js | 102 ++++++++++---------- IPython/html/static/notebook/js/textcell.js | 48 ++++++--- 4 files changed, 93 insertions(+), 85 deletions(-) diff --git a/IPython/html/static/notebook/js/cell.js b/IPython/html/static/notebook/js/cell.js index 568eb1ead..fb1e971c0 100644 --- a/IPython/html/static/notebook/js/cell.js +++ b/IPython/html/static/notebook/js/cell.js @@ -112,12 +112,17 @@ var IPython = (function (IPython) { that.element.click(function (event) { if (that.selected === false) { $([IPython.events]).trigger('select.Cell', {'cell':that}); - } + }; }); that.element.focusin(function (event) { if (that.selected === false) { $([IPython.events]).trigger('select.Cell', {'cell':that}); - } + }; + }); + that.element.focusout(function (event) { + if (that.mode === 'edit') { + $([IPython.events]).trigger('command_mode.Cell', {'cell':that}); + }; }); if (this.code_mirror) { this.code_mirror.on("change", function(cm, change) { @@ -148,14 +153,12 @@ var IPython = (function (IPython) { * @return is the action being taken */ Cell.prototype.select = function () { - console.log('Cell.select'); if (!this.selected) { this.element.addClass('selected'); this.element.removeClass('unselected'); this.selected = true; return true; } else { - console.log('WARNING: select'); return false; }; }; @@ -166,14 +169,12 @@ var IPython = (function (IPython) { * @return is the action being taken */ Cell.prototype.unselect = function () { - console.log('Cell.unselect'); if (this.selected) { this.element.addClass('unselected'); this.element.removeClass('selected'); this.selected = false; return true; } else { - console.log('WARNING: unselect'); return false; }; }; @@ -184,14 +185,12 @@ var IPython = (function (IPython) { * @return is the action being taken */ Cell.prototype.render = function () { - console.log('Cell.render'); if (!this.rendered) { this.element.addClass('rendered'); this.element.removeClass('unrendered'); this.rendered = true; return true; } else { - console.log('WARNING: render'); return false; }; }; @@ -202,14 +201,12 @@ var IPython = (function (IPython) { * @return is the action being taken */ Cell.prototype.unrender = function () { - console.log('Cell.unrender'); if (this.rendered) { this.element.addClass('unrendered'); this.element.removeClass('rendered'); this.rendered = false; return true; } else { - console.log('WARNING: unrender'); return false; }; }; @@ -220,14 +217,12 @@ var IPython = (function (IPython) { * @return is the action being taken */ Cell.prototype.command_mode = function () { - console.log('Cell.command_mode:', this.mode); if (this.mode !== 'command') { this.element.addClass('command_mode'); this.element.removeClass('edit_mode'); this.mode = 'command'; return true; } else { - console.log('WARNING: command_mode'); return false; }; }; @@ -238,14 +233,12 @@ var IPython = (function (IPython) { * @return is the action being taken */ Cell.prototype.edit_mode = function () { - console.log('Cell.edit_mode:', this.mode); if (this.mode !== 'edit') { this.element.addClass('edit_mode'); this.element.removeClass('command_mode'); this.mode = 'edit'; return true; } else { - console.log('WARNING: edit_mode'); return false; }; } diff --git a/IPython/html/static/notebook/js/codecell.js b/IPython/html/static/notebook/js/codecell.js index 9f20e2f01..164c72da2 100644 --- a/IPython/html/static/notebook/js/codecell.js +++ b/IPython/html/static/notebook/js/codecell.js @@ -157,7 +157,6 @@ var IPython = (function (IPython) { * @method handle_codemirror_keyevent */ CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) { - var that = this; if (this.mode === 'command') { @@ -174,7 +173,7 @@ var IPython = (function (IPython) { this.auto_highlight(); } - if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) { + if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey || event.altKey)) { // Always ignore shift-enter in CodeMirror as we handle it. return true; @@ -319,7 +318,6 @@ var IPython = (function (IPython) { CodeCell.prototype.select = function () { var cont = IPython.Cell.prototype.select.apply(this); - console.log('CodeCell.select', cont); if (cont) { this.code_mirror.refresh(); this.auto_highlight(); @@ -329,7 +327,6 @@ var IPython = (function (IPython) { CodeCell.prototype.render = function () { var cont = IPython.Cell.prototype.render.apply(this); - console.log('CodeCell.render'); // Always execute, even if we are already in the rendered state return cont; }; @@ -341,7 +338,6 @@ var IPython = (function (IPython) { CodeCell.prototype.command_mode = function () { var cont = IPython.Cell.prototype.command_mode.apply(this); - console.log('CodeCell.command_mode'); if (cont) { this.focus_cell(); }; @@ -350,7 +346,6 @@ var IPython = (function (IPython) { CodeCell.prototype.edit_mode = function () { var cont = IPython.Cell.prototype.edit_mode.apply(this); - console.log('CodeCell.edit_mode'); if (cont) { this.focus_editor(); }; diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js index d97a42627..0905c1543 100644 --- a/IPython/html/static/notebook/js/notebook.js +++ b/IPython/html/static/notebook/js/notebook.js @@ -40,7 +40,6 @@ var IPython = (function (IPython) { this.undelete_below = false; this.paste_enabled = false; this.mode = 'command'; - this.edit_index = null; this.set_dirty(false); this.metadata = {}; this._checkpoint_after_save = false; @@ -139,7 +138,11 @@ var IPython = (function (IPython) { that.select(index); that.edit_mode(); }); - + + $([IPython.events]).on('command_mode.Cell', function (event, data) { + that.command_mode(); + }); + $([IPython.events]).on('status_autorestarting.Kernel', function () { IPython.dialog.modal({ title: "Kernel Restarting", @@ -660,10 +663,10 @@ var IPython = (function (IPython) { if (this.is_valid_cell_index(index)) { var sindex = this.get_selected_index() if (sindex !== null && index !== sindex) { + this.command_mode(); this.get_cell(sindex).unselect(); }; var cell = this.get_cell(index); - console.log('Notebook.select', index); cell.select(); if (cell.cell_type === 'heading') { $([IPython.events]).trigger('selected_cell_type_changed.Notebook', @@ -705,41 +708,36 @@ var IPython = (function (IPython) { // Edit/Command mode - /** - * Enter command mode for the currently selected cell - * - * @method command_mode - */ + Notebook.prototype.get_edit_index = function () { + var result = null; + this.get_cell_elements().filter(function (index) { + if ($(this).data("cell").mode === 'edit') { + result = index; + }; + }); + return result; + }; + Notebook.prototype.command_mode = function () { - console.log('Notebook.command_mode', this.mode, this.edit_index); if (this.mode !== 'command') { - var cell = this.get_cell(this.edit_index); + var index = this.get_edit_index(); + var cell = this.get_cell(index); if (cell) { cell.command_mode(); this.mode = 'command'; - this.edit_index = null; }; }; }; - /** - * Enter edit mode for the currently selected cell - * - * @method editmode - */ Notebook.prototype.edit_mode = function () { - var index = this.get_selected_index(); - console.log('Notebook.edit_mode', this.mode, index); - if (index !== this.edit_index) { - if (this.edit_index !== null) { - var old_cell = this.get_cell(this.edit_index) - old_cell.command_mode(); - } + if (this.mode !== 'edit') { + // We are in command mode so get_edit_index() is null!!! + var index = this.get_selected_index(); + if (index === null) {return;} // No cell is selected var cell = this.get_cell(index); if (cell) { cell.edit_mode(); this.mode = 'edit'; - this.edit_index = index; }; }; }; @@ -763,6 +761,7 @@ var IPython = (function (IPython) { tomove.detach(); pivot.before(tomove); this.select(i-1); + }; this.set_dirty(true); }; @@ -779,7 +778,7 @@ var IPython = (function (IPython) { **/ Notebook.prototype.move_cell_down = function (index) { var i = this.index_or_selected(index); - if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) { + if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) { var pivot = this.get_cell_element(i+1); var tomove = this.get_cell_element(i); if (pivot !== null && tomove !== null) { @@ -1000,7 +999,7 @@ var IPython = (function (IPython) { if (text === source_cell.placeholder) { text = ''; }; - // The edit must come before the set_text. + // We must show the editor before setting its contents target_cell.unrender(); target_cell.set_text(text); // make this value the starting point, so that we can only undo @@ -1032,13 +1031,15 @@ var IPython = (function (IPython) { if (text === source_cell.placeholder) { text = ''; }; - // The edit must come before the set_text. + // We must show the editor before setting its contents target_cell.unrender(); target_cell.set_text(text); // make this value the starting point, so that we can only undo // to this state, instead of a blank cell target_cell.code_mirror.clearHistory(); source_element.remove(); + this.select(i); + this.edit_mode(); this.set_dirty(true); }; }; @@ -1066,7 +1067,7 @@ var IPython = (function (IPython) { if (text === source_cell.placeholder) { text = ''; }; - // The edit must come before the set_text. + // We must show the editor before setting its contents target_cell.set_level(level); target_cell.unrender(); target_cell.set_text(text); @@ -1074,6 +1075,8 @@ var IPython = (function (IPython) { // to this state, instead of a blank cell target_cell.code_mirror.clearHistory(); source_element.remove(); + this.select(i); + this.edit_mode(); this.set_dirty(true); }; $([IPython.events]).trigger('selected_cell_type_changed.Notebook', @@ -1500,34 +1503,35 @@ var IPython = (function (IPython) { Notebook.prototype.execute_selected_cell = function (mode) { // mode = shift, ctrl, alt mode = mode || 'shift' - var that = this; - var cell = that.get_selected_cell(); - var cell_index = that.find_cell_index(cell); + var cell = this.get_selected_cell(); + var cell_index = this.find_cell_index(cell); cell.execute(); - console.log('Notebook.execute_selected_cell', mode); + + // If we are at the end always insert a new cell and return + if (cell_index === (this.ncells()-1)) { + this.insert_cell_below('code'); + this.select(cell_index+1); + this.edit_mode(); + this.scroll_to_bottom(); + this.set_dirty(true); + return; + } + if (mode === 'shift') { - if (cell_index === (that.ncells()-1)) { - that.insert_cell_below('code'); - that.select(cell_index+1); - that.edit_mode(); - that.scroll_to_bottom(); - } else { - that.command_mode(); - } + this.command_mode(); } else if (mode === 'ctrl') { - that.select(cell_index+1); - that.get_cell(cell_index+1).focus_cell(); + this.select(cell_index+1); + this.get_cell(cell_index+1).focus_cell(); } else if (mode === 'alt') { // Only insert a new cell, if we ended up in an already populated cell - if (/\S/.test(that.get_next_cell().get_text()) == true) { - that.insert_cell_below('code'); + var next_text = this.get_cell(cell_index+1).get_text(); + if (/\S/.test(next_text) === true) { + this.insert_cell_below('code'); } - var next_index = cell_index+1; - that.select(cell_index+1); - that.edit_mode(); + this.select(cell_index+1); + this.edit_mode(); } - this.set_dirty(true); }; @@ -1651,7 +1655,7 @@ var IPython = (function (IPython) { cell_data.cell_type = 'raw'; } - new_cell = this.insert_cell_at_bottom(cell_data.cell_type); + new_cell = this.insert_cell_at_index(cell_data.cell_type, i); new_cell.fromJSON(cell_data); }; }; diff --git a/IPython/html/static/notebook/js/textcell.js b/IPython/html/static/notebook/js/textcell.js index 3c50fde59..5bc05f2a2 100644 --- a/IPython/html/static/notebook/js/textcell.js +++ b/IPython/html/static/notebook/js/textcell.js @@ -101,18 +101,11 @@ var IPython = (function (IPython) { IPython.Cell.prototype.bind_events.apply(this); var that = this; - // TODO: move this to the notebook event handler - this.element.keydown(function (event) { - if (event.which === 13 && !event.shiftKey) { - if (that.rendered) { - that.unrender(); - return false; - }; - }; - }); - this.element.dblclick(function () { - that.unrender(); + if (that.selected === false) { + $([IPython.events]).trigger('select.Cell', {'cell':that}); + }; + $([IPython.events]).trigger('edit_mode.Cell', {cell: that}); }); }; @@ -129,13 +122,32 @@ var IPython = (function (IPython) { * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise */ TextCell.prototype.handle_codemirror_keyevent = function (editor, event) { + var that = this; if (this.mode === 'command') { return false } else if (this.mode === 'edit') { - if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { + if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) { // Always ignore shift-enter in CodeMirror as we handle it. return true; - }; + } else if (event.which === key.UPARROW && event.type === 'keydown') { + // If we are not at the top, let CM handle the up arrow and + // prevent the global keydown handler from handling it. + if (!that.at_top()) { + event.stop(); + return false; + } else { + return true; + }; + } else if (event.which === key.DOWNARROW && event.type === 'keydown') { + // If we are not at the bottom, let CM handle the down arrow and + // prevent the global keydown handler from handling it. + if (!that.at_bottom()) { + event.stop(); + return false; + } else { + return true; + }; + } return false; }; return false; @@ -186,6 +198,7 @@ var IPython = (function (IPython) { TextCell.prototype.edit_mode = function () { var cont = IPython.Cell.prototype.edit_mode.apply(this); if (cont) { + this.unrender(); this.focus_editor(); }; return cont; @@ -234,6 +247,7 @@ var IPython = (function (IPython) { if (this.rendered) { return true; } else { + var cursor = this.code_mirror.getCursor(); if (cursor.line === 0 && cursor.ch === 0) { return true; } else { @@ -369,10 +383,12 @@ var IPython = (function (IPython) { * @extends IPython.TextCell */ var RawCell = function (options) { - options = this.mergeopt(RawCell, options); - - this.cell_type = 'raw'; + + options = this.mergeopt(RawCell,options) TextCell.apply(this, [options]); + this.cell_type = 'raw'; + // RawCell should always hide its rendered div + this.element.find('div.text_cell_render').hide(); }; RawCell.options_default = {