From 2dd3d63da9973820904e1f6ea5f99e86fc047877 2014-01-07 22:42:34 From: Brian E. Granger Date: 2014-01-07 22:42:34 Subject: [PATCH] More work on the dual mode UX. --- diff --git a/IPython/html/static/notebook/js/cell.js b/IPython/html/static/notebook/js/cell.js index 7d8506a..568eb1e 100644 --- a/IPython/html/static/notebook/js/cell.js +++ b/IPython/html/static/notebook/js/cell.js @@ -123,7 +123,12 @@ var IPython = (function (IPython) { this.code_mirror.on("change", function(cm, change) { $([IPython.events]).trigger("set_dirty.Notebook", {value: true}); }); - } + }; + if (this.code_mirror) { + this.code_mirror.on('focus', function(cm, change) { + $([IPython.events]).trigger('edit_mode.Cell', {cell: that}); + }); + }; }; /** @@ -143,12 +148,14 @@ 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; }; }; @@ -159,12 +166,14 @@ 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; }; }; @@ -175,12 +184,14 @@ 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; }; }; @@ -191,12 +202,14 @@ 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; }; }; @@ -207,12 +220,14 @@ 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; }; }; @@ -223,12 +238,14 @@ 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; }; } @@ -246,26 +263,11 @@ var IPython = (function (IPython) { * @method focus_editor */ Cell.prototype.focus_editor = function () { - this.code_mirror.refresh(); + this.refresh(); this.code_mirror.focus(); } /** - * should be overritten by subclass - * @method get_text - */ - Cell.prototype.get_text = function () { - }; - - /** - * should be overritten by subclass - * @method set_text - * @param {string} text - */ - Cell.prototype.set_text = function (text) { - }; - - /** * Refresh codemirror instance * @method refresh */ @@ -273,20 +275,19 @@ var IPython = (function (IPython) { this.code_mirror.refresh(); }; - /** * should be overritten by subclass - * @method edit - **/ - Cell.prototype.edit = function () { + * @method get_text + */ + Cell.prototype.get_text = function () { }; - /** * should be overritten by subclass - * @method render - **/ - Cell.prototype.render = function () { + * @method set_text + * @param {string} text + */ + Cell.prototype.set_text = function (text) { }; /** diff --git a/IPython/html/static/notebook/js/codecell.js b/IPython/html/static/notebook/js/codecell.js index 9069279..9f20e2f 100644 --- a/IPython/html/static/notebook/js/codecell.js +++ b/IPython/html/static/notebook/js/codecell.js @@ -139,6 +139,16 @@ var IPython = (function (IPython) { this.completer = new IPython.Completer(this); }; + /** @method bind_events */ + CodeCell.prototype.bind_events = function () { + IPython.Cell.prototype.bind_events.apply(this); + var that = this; + + this.element.focusout( + function() { that.auto_highlight(); } + ); + }; + /** * This method gets called in CodeMirror's onKeyDown/onKeyPress * handlers and is used to provide custom key handling. Its return @@ -308,44 +318,43 @@ var IPython = (function (IPython) { // Basic cell manipulation. CodeCell.prototype.select = function () { - var continue = IPython.Cell.prototype.select.apply(this); - if (continue) { + var cont = IPython.Cell.prototype.select.apply(this); + console.log('CodeCell.select', cont); + if (cont) { this.code_mirror.refresh(); this.auto_highlight(); }; - return continue; + return cont; }; CodeCell.prototype.render = function () { - var continue = IPython.Cell.prototype.render.apply(this); - if (continue) { - this.execute(); - }; - return continue; + 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; }; - + CodeCell.prototype.unrender = function () { - var continue = IPython.Cell.prototype.unrender.apply(this); - if (continue) { - this.clear_output(true, true, true); - }; - return continue; + // CodeCell is always rendered + return false; }; CodeCell.prototype.command_mode = function () { - var continue = IPython.Cell.prototype.command_mode.apply(this); - if (continue) { + var cont = IPython.Cell.prototype.command_mode.apply(this); + console.log('CodeCell.command_mode'); + if (cont) { this.focus_cell(); }; - return continue; + return cont; } CodeCell.prototype.edit_mode = function () { - var continue = IPython.Cell.prototype.edit_mode.apply(this); - if (continue) { + var cont = IPython.Cell.prototype.edit_mode.apply(this); + console.log('CodeCell.edit_mode'); + if (cont) { this.focus_editor(); }; - return continue; + return cont; } CodeCell.prototype.select_all = function () { diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js index 3eebb2f..d97a426 100644 --- a/IPython/html/static/notebook/js/notebook.js +++ b/IPython/html/static/notebook/js/notebook.js @@ -39,6 +39,8 @@ var IPython = (function (IPython) { this.undelete_index = null; 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; @@ -74,7 +76,7 @@ var IPython = (function (IPython) { * @method baseProjectUrl * @return {String} The base project URL */ - Notebook.prototype.baseProjectUrl = function(){ + Notebook.prototype.baseProjectUrl = function() { return this._baseProjectUrl || $('body').data('baseProjectUrl'); }; @@ -131,6 +133,12 @@ var IPython = (function (IPython) { var index = that.find_cell_index(data.cell); that.select(index); }); + + $([IPython.events]).on('edit_mode.Cell', function (event, data) { + var index = that.find_cell_index(data.cell); + that.select(index); + that.edit_mode(); + }); $([IPython.events]).on('status_autorestarting.Kernel', function () { IPython.dialog.modal({ @@ -144,220 +152,222 @@ var IPython = (function (IPython) { }); }); - $(document).keydown(function (event) { - // Save (CTRL+S) or (AppleKey+S) - //metaKey = applekey on mac + // Event handlers for both command and edit mode if ((event.ctrlKey || event.metaKey) && event.keyCode==83) { + // Save (CTRL+S) or (Command+S on Mac) that.save_checkpoint(); event.preventDefault(); return false; } else if (event.which === key.ESC) { // Intercept escape at highest level to avoid closing // websocket connection with firefox - IPython.pager.collapse(); event.preventDefault(); + // Don't return yet to allow edit/command modes to handle } else if (event.which === key.SHIFT) { // ignore shift keydown return true; - } - if (event.which === key.UPARROW && !event.shiftKey) { - var cell = that.get_selected_cell(); - if (cell && cell.at_top()) { - event.preventDefault(); - that.select_prev(); - }; - } else if (event.which === key.DOWNARROW && !event.shiftKey) { - var cell = that.get_selected_cell(); - if (cell && cell.at_bottom()) { - event.preventDefault(); - that.select_next(); - }; } else if (event.which === key.ENTER && event.shiftKey) { - that.execute_selected_cell(); + that.execute_selected_cell('shift'); return false; } else if (event.which === key.ENTER && event.altKey) { // Execute code cell, and insert new in place - that.execute_selected_cell(); - // Only insert a new cell, if we ended up in an already populated cell - if (/\S/.test(that.get_selected_cell().get_text()) == true) { - that.insert_cell_above('code'); - } + that.execute_selected_cell('alt'); return false; } else if (event.which === key.ENTER && event.ctrlKey) { - that.execute_selected_cell({terminal:true}); - return false; - } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) { - that.control_key_active = true; - return false; - } else if (event.which === 88 && that.control_key_active) { - // Cut selected cell = x - that.cut_cell(); - that.control_key_active = false; - return false; - } else if (event.which === 67 && that.control_key_active) { - // Copy selected cell = c - that.copy_cell(); - that.control_key_active = false; - return false; - } else if (event.which === 86 && that.control_key_active) { - // Paste below selected cell = v - that.paste_cell_below(); - that.control_key_active = false; - return false; - } else if (event.which === 68 && that.control_key_active) { - // Delete selected cell = d - that.delete_cell(); - that.control_key_active = false; - return false; - } else if (event.which === 65 && that.control_key_active) { - // Insert code cell above selected = a - that.insert_cell_above('code'); - that.control_key_active = false; - return false; - } else if (event.which === 66 && that.control_key_active) { - // Insert code cell below selected = b - that.insert_cell_below('code'); - that.control_key_active = false; - return false; - } else if (event.which === 89 && that.control_key_active) { - // To code = y - that.to_code(); - that.control_key_active = false; - return false; - } else if (event.which === 77 && that.control_key_active) { - // To markdown = m - that.to_markdown(); - that.control_key_active = false; - return false; - } else if (event.which === 84 && that.control_key_active) { - // To Raw = t - that.to_raw(); - that.control_key_active = false; + that.execute_selected_cell('ctrl'); return false; - } else if (event.which === 49 && that.control_key_active) { - // To Heading 1 = 1 - that.to_heading(undefined, 1); - that.control_key_active = false; - return false; - } else if (event.which === 50 && that.control_key_active) { - // To Heading 2 = 2 - that.to_heading(undefined, 2); - that.control_key_active = false; - return false; - } else if (event.which === 51 && that.control_key_active) { - // To Heading 3 = 3 - that.to_heading(undefined, 3); - that.control_key_active = false; - return false; - } else if (event.which === 52 && that.control_key_active) { - // To Heading 4 = 4 - that.to_heading(undefined, 4); - that.control_key_active = false; - return false; - } else if (event.which === 53 && that.control_key_active) { - // To Heading 5 = 5 - that.to_heading(undefined, 5); - that.control_key_active = false; - return false; - } else if (event.which === 54 && that.control_key_active) { - // To Heading 6 = 6 - that.to_heading(undefined, 6); - that.control_key_active = false; - return false; - } else if (event.which === 79 && that.control_key_active) { - // Toggle output = o - if (event.shiftKey){ - that.toggle_output_scroll(); - } else { - that.toggle_output(); - } - that.control_key_active = false; - return false; - } else if (event.which === 83 && that.control_key_active) { - // Save notebook = s - that.save_checkpoint(); - that.control_key_active = false; - return false; - } else if (event.which === 74 && that.control_key_active) { - // Move cell down = j - that.move_cell_down(); - that.control_key_active = false; - return false; - } else if (event.which === 75 && that.control_key_active) { - // Move cell up = k - that.move_cell_up(); - that.control_key_active = false; - return false; - } else if (event.which === 80 && that.control_key_active) { - // Select previous = p - that.select_prev(); - that.control_key_active = false; - return false; - } else if (event.which === 78 && that.control_key_active) { - // Select next = n - that.select_next(); - that.control_key_active = false; - return false; - } else if (event.which === 76 && that.control_key_active) { - // Toggle line numbers = l - that.cell_toggle_line_numbers(); - that.control_key_active = false; - return false; - } else if (event.which === 73 && that.control_key_active) { - // Interrupt kernel = i - that.session.interrupt_kernel(); - that.control_key_active = false; - return false; - } else if (event.which === 190 && that.control_key_active) { - // Restart kernel = . # matches qt console - that.restart_kernel(); - that.control_key_active = false; - return false; - } else if (event.which === 72 && that.control_key_active) { - // Show keyboard shortcuts = h - IPython.quick_help.show_keyboard_shortcuts(); - that.control_key_active = false; - return false; - } else if (event.which === 90 && that.control_key_active) { - // Undo last cell delete = z - that.undelete(); - that.control_key_active = false; - return false; - } else if ((event.which === 189 || event.which === 173) && - that.control_key_active) { - // how fun! '-' is 189 in Chrome, but 173 in FF and Opera - // Split cell = - - that.split_cell(); - that.control_key_active = false; - return false; - } else if (that.control_key_active) { - that.control_key_active = false; - return true; } + + // Event handlers for edit mode + if (that.mode === 'edit') { + if (event.which === key.ESC) { + // ESC + that.command_mode(); + return false; + } else if (event.which === 77 && event.ctrlKey) { + // Ctrl-m + that.command_mode(); + return false; + } else if (event.which === key.UPARROW && !event.shiftKey) { + var cell = that.get_selected_cell(); + if (cell && cell.at_top()) { + event.preventDefault(); + that.command_mode() + that.select_prev(); + that.edit_mode(); + return false; + }; + } else if (event.which === key.DOWNARROW && !event.shiftKey) { + var cell = that.get_selected_cell(); + if (cell && cell.at_bottom()) { + event.preventDefault(); + that.command_mode() + that.select_next(); + that.edit_mode(); + return false; + }; + }; + // Event handlers for command mode + } else if (that.mode === 'command' && !(event.ctrlKey || event.altKey)) { + if (event.which === key.ENTER && !(event.ctrlKey || event.altKey || event.shiftKey)) { + // Enter edit mode = ENTER alone + that.edit_mode(); + return false + } else if (event.which === key.UPARROW && !event.shiftKey) { + var index = that.get_selected_index(); + if (index !== 0 && index !== null) { + that.select_prev(); + var cell = that.get_selected_cell(); + cell.focus_cell(); + }; + return false; + } else if (event.which === key.DOWNARROW && !event.shiftKey) { + var index = that.get_selected_index(); + if (index !== (that.ncells()-1) && index !== null) { + that.select_next(); + var cell = that.get_selected_cell(); + cell.focus_cell(); + }; + return false; + } else if (event.which === 88) { + // Cut selected cell = x + that.cut_cell(); + return false; + } else if (event.which === 67) { + // Copy selected cell = c + that.copy_cell(); + return false; + } else if (event.which === 86) { + // Paste below selected cell = v + that.paste_cell_below(); + return false; + } else if (event.which === 68) { + // Delete selected cell = d + that.delete_cell(); + return false; + } else if (event.which === 65) { + // Insert code cell above selected = a + that.insert_cell_above('code'); + that.select_prev(); + return false; + } else if (event.which === 66) { + // Insert code cell below selected = b + that.insert_cell_below('code'); + that.select_next(); + return false; + } else if (event.which === 89) { + // To code = y + that.to_code(); + return false; + } else if (event.which === 77) { + // To markdown = m + that.to_markdown(); + return false; + } else if (event.which === 84) { + // To Raw = t + that.to_raw(); + return false; + } else if (event.which === 49) { + // To Heading 1 = 1 + that.to_heading(undefined, 1); + return false; + } else if (event.which === 50) { + // To Heading 2 = 2 + that.to_heading(undefined, 2); + return false; + } else if (event.which === 51) { + // To Heading 3 = 3 + that.to_heading(undefined, 3); + return false; + } else if (event.which === 52) { + // To Heading 4 = 4 + that.to_heading(undefined, 4); + return false; + } else if (event.which === 53) { + // To Heading 5 = 5 + that.to_heading(undefined, 5); + return false; + } else if (event.which === 54) { + // To Heading 6 = 6 + that.to_heading(undefined, 6); + return false; + } else if (event.which === 79) { + // Toggle output = o + if (event.shiftKey) { + that.toggle_output_scroll(); + } else { + that.toggle_output(); + }; + return false; + } else if (event.which === 83) { + // Save notebook = s + that.save_checkpoint(); + that.control_key_active = false; + return false; + } else if (event.which === 74) { + // Move cell down = j + that.move_cell_down(); + return false; + } else if (event.which === 75) { + // Move cell up = k + that.move_cell_up(); + return false; + } else if (event.which === 80) { + // Select previous = p + that.select_prev(); + return false; + } else if (event.which === 78) { + // Select next = n + that.select_next(); + return false; + } else if (event.which === 76) { + // Toggle line numbers = l + that.cell_toggle_line_numbers(); + return false; + } else if (event.which === 73) { + // Interrupt kernel = i + that.kernel.interrupt(); + return false; + } else if (event.which === 190) { + // Restart kernel = . # matches qt console + that.restart_kernel(); + return false; + } else if (event.which === 72) { + // Show keyboard shortcuts = h + IPython.quick_help.show_keyboard_shortcuts(); + return false; + } else if (event.which === 90) { + // Undo last cell delete = z + that.undelete(); + return false; + }; + }; + + // If we havn't handled it, let someone else. return true; }); - var collapse_time = function(time){ + var collapse_time = function (time) { var app_height = $('#ipython-main-app').height(); // content height var splitter_height = $('div#pager_splitter').outerHeight(true); var new_height = app_height - splitter_height; that.element.animate({height : new_height + 'px'}, time); - } + }; - this.element.bind('collapse_pager', function (event,extrap) { + this.element.bind('collapse_pager', function (event, extrap) { var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast'; collapse_time(time); }); - var expand_time = function(time) { + var expand_time = function (time) { var app_height = $('#ipython-main-app').height(); // content height var splitter_height = $('div#pager_splitter').outerHeight(true); var pager_height = $('div#pager').outerHeight(true); var new_height = app_height - pager_height - splitter_height; that.element.animate({height : new_height + 'px'}, time); - } + }; this.element.bind('expand_pager', function (event, extrap) { var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast'; @@ -653,6 +663,7 @@ var IPython = (function (IPython) { 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', @@ -692,6 +703,48 @@ var IPython = (function (IPython) { }; + // Edit/Command mode + + /** + * Enter command mode for the currently selected cell + * + * @method command_mode + */ + 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); + 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(); + } + var cell = this.get_cell(index); + if (cell) { + cell.edit_mode(); + this.mode = 'edit'; + this.edit_index = index; + }; + }; + }; + + // Cell movement /** @@ -804,10 +857,12 @@ var IPython = (function (IPython) { cell = new IPython.HeadingCell(); } - if(this._insert_element_at_index(cell.element,index)){ + if(this._insert_element_at_index(cell.element,index)) { cell.render(); - this.select(this.find_cell_index(cell)); $([IPython.events]).trigger('create.Cell', {'cell': cell, 'index': index}); + cell.refresh(); + // TODO: should we really get rid of this? + //this.select(this.find_cell_index(cell)); this.set_dirty(true); } } @@ -952,6 +1007,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); }; }; @@ -1440,31 +1497,41 @@ var IPython = (function (IPython) { * @method execute_selected_cell * @param {Object} options Customize post-execution behavior */ - Notebook.prototype.execute_selected_cell = function (options) { - // add_new: should a new cell be added if we are at the end of the nb - // terminal: execute in terminal mode, which stays in the current cell - var default_options = {terminal: false, add_new: true}; - $.extend(default_options, options); + 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); - if (cell instanceof IPython.CodeCell) { - cell.execute(); - } - if (default_options.terminal) { - cell.select_all(); - } else { - if ((cell_index === (that.ncells()-1)) && default_options.add_new) { + + cell.execute(); + console.log('Notebook.execute_selected_cell', mode); + if (mode === 'shift') { + if (cell_index === (that.ncells()-1)) { that.insert_cell_below('code'); - // If we are adding a new cell at the end, scroll down to show it. + that.select(cell_index+1); + that.edit_mode(); that.scroll_to_bottom(); } else { - that.select(cell_index+1); - }; - }; + that.command_mode(); + } + } else if (mode === 'ctrl') { + that.select(cell_index+1); + that.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_index = cell_index+1; + that.select(cell_index+1); + that.edit_mode(); + } + this.set_dirty(true); }; + /** * Execute all cells below the selected cell. * @@ -1909,9 +1976,13 @@ var IPython = (function (IPython) { this.fromJSON(data); if (this.ncells() === 0) { this.insert_cell_below('code'); + this.select(0); + this.edit_mode(); + } else { + this.select(0); + this.command_mode(); }; this.set_dirty(false); - this.select(0); this.scroll_to_top(); if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) { var msg = "This notebook has been converted from an older " + diff --git a/IPython/html/static/notebook/js/textcell.js b/IPython/html/static/notebook/js/textcell.js index e8cbd35..3c50fde 100644 --- a/IPython/html/static/notebook/js/textcell.js +++ b/IPython/html/static/notebook/js/textcell.js @@ -64,7 +64,6 @@ var IPython = (function (IPython) { }; - /** * Create the DOM element of the TextCell * @method create_element @@ -101,6 +100,8 @@ var IPython = (function (IPython) { TextCell.prototype.bind_events = function () { 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) { @@ -109,11 +110,13 @@ var IPython = (function (IPython) { }; }; }); + this.element.dblclick(function () { that.unrender(); }); }; + /** * This method gets called in CodeMirror's onKeyDown/onKeyPress * handlers and is used to provide custom key handling. @@ -126,38 +129,34 @@ var IPython = (function (IPython) { * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise */ TextCell.prototype.handle_codemirror_keyevent = function (editor, event) { - - if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { - // Always ignore shift-enter in CodeMirror as we handle it. - return true; - } + if (this.mode === 'command') { + return false + } else if (this.mode === 'edit') { + if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { + // Always ignore shift-enter in CodeMirror as we handle it. + return true; + }; + return false; + }; return false; }; // Cell level actions TextCell.prototype.select = function () { - var continue = IPython.Cell.prototype.select.apply(this); - if (continue) { + var cont = IPython.Cell.prototype.select.apply(this); + if (cont) { if (this.mode === 'edit') { this.code_mirror.refresh(); } }; - return continue; - }; - - TextCell.prototype.render = function () { - var continue = IPython.Cell.prototype.render.apply(this); - if (continue) { - this.execute(); - }; - return continue; + return cont; }; TextCell.prototype.unrender = function () { if (this.read_only) return; - var continue = IPython.Cell.prototype.unrender.apply(this); - if (continue) { + var cont = IPython.Cell.prototype.unrender.apply(this); + if (cont) { var text_cell = this.element; var output = text_cell.find("div.text_cell_render"); output.hide(); @@ -169,23 +168,27 @@ var IPython = (function (IPython) { } }; - return continue; + return cont; + }; + + TextCell.prototype.execute = function () { + this.render(); }; TextCell.prototype.command_mode = function () { - var continue = IPython.Cell.prototype.command_mode.apply(this); - if (continue) { + var cont = IPython.Cell.prototype.command_mode.apply(this); + if (cont) { this.focus_cell(); }; - return continue; + return cont; } TextCell.prototype.edit_mode = function () { - var continue = IPython.Cell.prototype.edit_mode.apply(this); - if (continue) { + var cont = IPython.Cell.prototype.edit_mode.apply(this); + if (cont) { this.focus_editor(); }; - return continue; + return cont; } /** @@ -224,36 +227,51 @@ var IPython = (function (IPython) { }; /** - * not deprecated, but implementation wrong * @method at_top - * @deprecated - * @return {Boolean} true is cell rendered, false otherwise - * I doubt this is what it is supposed to do - * this implementation is completly false + * @return {Boolean} */ TextCell.prototype.at_top = function () { if (this.rendered) { return true; } else { - return false; - } + if (cursor.line === 0 && cursor.ch === 0) { + return true; + } else { + return false; + }; + }; }; + /** @method at_bottom **/ + TextCell.prototype.at_bottom = function () { + if (this.rendered) { + return true + } else { + var cursor = this.code_mirror.getCursor(); + if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) { + return true; + } else { + return false; + }; + }; + }; + /** - * not deprecated, but implementation wrong * @method at_bottom - * @deprecated - * @return {Boolean} true is cell rendered, false otherwise - * I doubt this is what it is supposed to do - * this implementation is completly false + * @return {Boolean} * */ TextCell.prototype.at_bottom = function () { if (this.rendered) { return true; } else { - return false; - } + var cursor = this.code_mirror.getCursor(); + if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) { + return true; + } else { + return false; + }; + }; }; /** @@ -308,16 +326,14 @@ var IPython = (function (IPython) { placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$" } - - - MarkdownCell.prototype = new TextCell(); /** * @method render */ MarkdownCell.prototype.render = function () { - if (this.rendered === false) { + var cont = IPython.TextCell.prototype.render.apply(this); + if (cont) { var text = this.get_text(); var math = null; if (text === "") { text = this.placeholder; } @@ -339,9 +355,9 @@ var IPython = (function (IPython) { } this.element.find('div.text_cell_input').hide(); this.element.find("div.text_cell_render").show(); - this.typeset(); - this.rendered = true; - } + this.typeset() + }; + return cont; }; @@ -357,11 +373,6 @@ var IPython = (function (IPython) { this.cell_type = 'raw'; TextCell.apply(this, [options]); - - var that = this; - this.element.focusout( - function() { that.auto_highlight(); } - ); }; RawCell.options_default = { @@ -370,10 +381,17 @@ var IPython = (function (IPython) { "When passing through nbconvert, a Raw Cell's content is added to the output unmodified." }; - - RawCell.prototype = new TextCell(); + /** @method bind_events **/ + RawCell.prototype.bind_events = function () { + TextCell.prototype.bind_events.apply(this); + var that = this + this.element.focusout(function() { + that.auto_highlight(); + }); + }; + /** * Trigger autodetection of highlight scheme for current cell * @method auto_highlight @@ -384,12 +402,13 @@ var IPython = (function (IPython) { /** @method render **/ RawCell.prototype.render = function () { - - this.rendered = true; + // Make sure that this cell type can never be rendered + if (this.rendered) { + this.unrender(); + } var text = this.get_text(); if (text === "") { text = this.placeholder; } this.set_text(text); - this.unrender(); }; @@ -397,55 +416,34 @@ var IPython = (function (IPython) { RawCell.prototype.handle_codemirror_keyevent = function (editor, event) { var that = this; - 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; + if (this.mode === 'command') { + return false + } else if (this.mode === 'edit') { + // TODO: review these handlers... + 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; }; - /** @method select **/ - RawCell.prototype.select = function () { - IPython.Cell.prototype.select.apply(this); - this.edit(); - }; - - /** @method at_top **/ - RawCell.prototype.at_top = function () { - var cursor = this.code_mirror.getCursor(); - if (cursor.line === 0 && cursor.ch === 0) { - return true; - } else { - return false; - } - }; - - - /** @method at_bottom **/ - RawCell.prototype.at_bottom = function () { - var cursor = this.code_mirror.getCursor(); - if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) { - return true; - } else { - return false; - } - }; - /** * @class HeadingCell @@ -541,7 +539,8 @@ var IPython = (function (IPython) { HeadingCell.prototype.render = function () { - if (this.rendered === false) { + var cont = IPython.TextCell.prototype.render.apply(this); + if (cont) { var text = this.get_text(); var math = null; // Markdown headings must be a single line @@ -567,8 +566,9 @@ var IPython = (function (IPython) { this.typeset(); this.element.find('div.text_cell_input').hide(); this.element.find("div.text_cell_render").show(); - this.rendered = true; + }; + return cont; }; IPython.TextCell = TextCell; diff --git a/IPython/html/static/notebook/less/cell.less b/IPython/html/static/notebook/less/cell.less index 0cf8078..ed279f4 100644 --- a/IPython/html/static/notebook/less/cell.less +++ b/IPython/html/static/notebook/less/cell.less @@ -6,6 +6,14 @@ div.cell { .corner-all; border : thin @border_color solid; } + + &.edit_mode { + .corner-all; + border : thin green solid; + } +} + +div.cell { width: 100%; padding: 5px 5px 5px 0px; /* This acts as a spacer between cells, that is outside the border */ diff --git a/IPython/html/static/style/ipython.min.css b/IPython/html/static/style/ipython.min.css index a26bd1f..7d7e790 100644 --- a/IPython/html/static/style/ipython.min.css +++ b/IPython/html/static/style/ipython.min.css @@ -54,7 +54,9 @@ input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto .ansibgpurple{background-color:magenta;} .ansibgcyan{background-color:cyan;} .ansibggray{background-color:gray;} -div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;} +div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}div.cell.selected{border-radius:4px;border:thin #ababab solid;} +div.cell.edit_mode{border-radius:4px;border:thin green solid;} +div.cell{width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;} div.prompt{min-width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;} div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;} div.prompt:empty{padding-top:0;padding-bottom:0;} diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index 19f14f5..88d3332 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -1435,7 +1435,9 @@ input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto .ansibgpurple{background-color:magenta;} .ansibgcyan{background-color:cyan;} .ansibggray{background-color:gray;} -div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;} +div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}div.cell.selected{border-radius:4px;border:thin #ababab solid;} +div.cell.edit_mode{border-radius:4px;border:thin green solid;} +div.cell{width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;} div.prompt{min-width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;} div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;} div.prompt:empty{padding-top:0;padding-bottom:0;}