From 0e14026de3a77c805b590d493348ae1056074922 2014-03-24 22:18:45 From: Paul Ivanov Date: 2014-03-24 22:18:45 Subject: [PATCH] Merge pull request #5320 from jdfreder/focusbug_tt Fix for Tooltip & completer click focus bug. --- diff --git a/IPython/html/static/notebook/js/cell.js b/IPython/html/static/notebook/js/cell.js index 514144f..032b413 100644 --- a/IPython/html/static/notebook/js/cell.js +++ b/IPython/html/static/notebook/js/cell.js @@ -147,10 +147,7 @@ var IPython = (function (IPython) { } if (this.code_mirror) { this.code_mirror.on('blur', function(cm, change) { - // Check if this unfocus event is legit. - if (!that.should_cancel_blur()) { - $([IPython.events]).trigger('command_mode.Cell', {cell: that}); - } + $([IPython.events]).trigger('command_mode.Cell', {cell: that}); }); } }; @@ -331,18 +328,7 @@ var IPython = (function (IPython) { return false; } }; - - /** - * Determine whether or not the unfocus event should be aknowledged. - * - * @method should_cancel_blur - * - * @return results {bool} Whether or not to ignore the cell's blur event. - **/ - Cell.prototype.should_cancel_blur = function () { - return false; - }; - + /** * Focus the cell in the DOM sense * @method focus_cell diff --git a/IPython/html/static/notebook/js/codecell.js b/IPython/html/static/notebook/js/codecell.js index c799681..44d4c79 100644 --- a/IPython/html/static/notebook/js/codecell.js +++ b/IPython/html/static/notebook/js/codecell.js @@ -358,21 +358,6 @@ var IPython = (function (IPython) { return false; }; - /** - * Determine whether or not the unfocus event should be aknowledged. - * - * @method should_cancel_blur - * - * @return results {bool} Whether or not to ignore the cell's blur event. - **/ - CodeCell.prototype.should_cancel_blur = function () { - // Cancel this unfocus event if the base wants to cancel or the cell - // completer is open or the tooltip is open. - return IPython.Cell.prototype.should_cancel_blur.apply(this) || - (this.completer && this.completer.is_visible()) || - (IPython.tooltip && IPython.tooltip.is_visible()); - }; - CodeCell.prototype.select_all = function () { var start = {line: 0, ch: 0}; var nlines = this.code_mirror.lineCount(); @@ -508,6 +493,23 @@ var IPython = (function (IPython) { return data; }; + /** + * handle cell level logic when a cell is unselected + * @method unselect + * @return is the action being taken + */ + CodeCell.prototype.unselect = function () { + var cont = IPython.Cell.prototype.unselect.apply(this); + if (cont) { + // When a code cell is usnelected, make sure that the corresponding + // tooltip and completer to that cell is closed. + IPython.tooltip.remove_and_cancel_tooltip(true); + if (this.completer !== null) { + this.completer.close(); + } + } + return cont; + }; IPython.CodeCell = CodeCell; diff --git a/IPython/html/static/notebook/js/completer.js b/IPython/html/static/notebook/js/completer.js index 3fd7245..14e13b6 100644 --- a/IPython/html/static/notebook/js/completer.js +++ b/IPython/html/static/notebook/js/completer.js @@ -73,7 +73,6 @@ var IPython = (function (IPython) { var Completer = function (cell) { - this._visible = false; this.cell = cell; this.editor = cell.code_mirror; var that = this; @@ -85,15 +84,9 @@ var IPython = (function (IPython) { }); }; - Completer.prototype.is_visible = function () { - // Return whether or not the completer is visible. - return this._visible; - }; - Completer.prototype.startCompletion = function () { // call for a 'first' completion, that will set the editor and do some - // special behaviour like autopicking if only one completion availlable - // + // special behavior like autopicking if only one completion available. if (this.editor.somethingSelected()) return; this.done = false; // use to get focus back on opera @@ -221,17 +214,37 @@ var IPython = (function (IPython) { } } - this.complete = $('
').addClass('completions'); - this.complete.attr('id', 'complete'); - - // Currently webkit doesn't use the size attr correctly. See: - // https://code.google.com/p/chromium/issues/detail?id=4579 - this.sel = $('') + .attr('tabindex', -1) + .attr('multiple', 'true'); + this.complete.append(this.sel); + this.visible = true; + $('body').append(this.complete); + + //build the container + var that = this; + this.sel.dblclick(function () { + that.pick(); + }); + this.sel.focus(function () { + that.editor.focus(); + }); + this._handle_keydown = function (cm, event) { + that.keydown(event); + }; + this.editor.on('keydown', this._handle_keydown); + this._handle_keypress = function (cm, event) { + that.keypress(event); + }; + this.editor.on('keypress', this._handle_keypress); + } + this.sel.attr('size', Math.min(10, this.raw_result.length)); // After everything is on the page, compute the postion. // We put it above the code if it is too close to the bottom of the page. @@ -249,28 +262,9 @@ var IPython = (function (IPython) { this.complete.css('left', left + 'px'); this.complete.css('top', top + 'px'); - - //build the container - var that = this; - this.sel.dblclick(function () { - that.pick(); - }); - this.sel.blur(this.close); - this.sel.keydown(function (event) { - that.keydown(event); - }); - this.sel.keypress(function (event) { - that.keypress(event); - }); - + // Clear and fill the list. + this.sel.text(''); this.build_gui_list(this.raw_result); - - this.sel.focus(); - IPython.keyboard_manager.disable(); - // Opera sometimes ignores focusing a freshly created node - if (window.opera) setTimeout(function () { - if (!this.done) this.sel.focus(); - }, 100); return true; }; @@ -288,20 +282,16 @@ var IPython = (function (IPython) { }; Completer.prototype.close = function () { - this._visible = false; - if (this.done) return; this.done = true; - $('.completions').remove(); - IPython.keyboard_manager.enable(); + $('#complete').remove(); + this.editor.off('keydown', this._handle_keydown); + this.editor.off('keypress', this._handle_keypress); + this.visible = false; }; Completer.prototype.pick = function () { this.insert(this.raw_result[this.sel[0].selectedIndex]); this.close(); - var that = this; - setTimeout(function () { - that.editor.focus(); - }, 50); }; Completer.prototype.keydown = function (event) { @@ -312,16 +302,10 @@ var IPython = (function (IPython) { if (code == keycodes.enter) { CodeMirror.e_stop(event); this.pick(); - } // Escape or backspace - else if (code == keycodes.esc) { + } else if (code == keycodes.esc || code == keycodes.backspace) { CodeMirror.e_stop(event); this.close(); - this.editor.focus(); - - } else if (code == keycodes.backspace) { - this.close(); - this.editor.focus(); } else if (code == keycodes.tab) { //all the fastforwarding operation, //Check that shared start is not null which can append with prefixed completion @@ -332,8 +316,6 @@ var IPython = (function (IPython) { this.insert(sh); } this.close(); - CodeMirror.e_stop(event); - this.editor.focus(); //reinvoke self setTimeout(function () { that.carry_on_completion(); @@ -341,10 +323,23 @@ var IPython = (function (IPython) { } else if (code == keycodes.up || code == keycodes.down) { // need to do that to be able to move the arrow // when on the first or last line ofo a code cell - event.stopPropagation(); + CodeMirror.e_stop(event); + + var options = this.sel.find('option'); + var index = this.sel[0].selectedIndex; + if (code == keycodes.up) { + index--; + } + if (code == keycodes.down) { + index++; + } + index = Math.min(Math.max(index, 0), options.length-1); + this.sel[0].selectedIndex = index; + } else if (code == keycodes.left || code == keycodes.right) { + this.close(); } }; - + Completer.prototype.keypress = function (event) { // FIXME: This is a band-aid. // on keypress, trigger insertion of a single character. @@ -358,26 +353,16 @@ var IPython = (function (IPython) { // don't handle keypress if it's not a character (arrows on FF) // or ENTER/TAB if (event.charCode === 0 || - code == keycodes.enter || - code == keycodes.tab + code == keycodes.tab || + code == keycodes.enter ) return; - var cur = this.editor.getCursor(); - var completion = { - str: String.fromCharCode(event.which), - type: "introspection", - from: cur, - to: cur, - }; - this.insert(completion); - this.close(); this.editor.focus(); setTimeout(function () { that.carry_on_completion(); }, 50); }; - IPython.Completer = Completer; return IPython; diff --git a/IPython/html/static/notebook/less/completer.less b/IPython/html/static/notebook/less/completer.less index 6f633de..1e564a2 100644 --- a/IPython/html/static/notebook/less/completer.less +++ b/IPython/html/static/notebook/less/completer.less @@ -17,6 +17,7 @@ font-family: @monoFontFamily; font-size: 110%; color: @textColor; + width: auto; } .completions select option.context { diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index 3c01e1d..36563fd 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -1,7 +1,3 @@ -.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0} -.clearfix:after{clear:both} -.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0} -.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block} audio,canvas,video{display:inline-block;*display:inline;*zoom:1} audio:not([controls]){display:none} @@ -856,6 +852,10 @@ a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decorati .show{display:block} .invisible{visibility:hidden} .affix{position:fixed} +.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0} +.clearfix:after{clear:both} +.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0} +.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} @-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden} .visible-phone{display:none !important} .visible-tablet{display:none !important} @@ -1497,7 +1497,7 @@ p{margin-bottom:0} .celltoolbar input[type=checkbox]{margin:0;margin-left:4px;margin-right:4px} .celltoolbar .ui-button{border:none;vertical-align:top;height:20px;min-width:30px} .completions{position:absolute;z-index:10;overflow:hidden;border:1px solid #ababab;border-radius:4px;-webkit-box-shadow:0 6px 10px -1px #adadad;-moz-box-shadow:0 6px 10px -1px #adadad;box-shadow:0 6px 10px -1px #adadad} -.completions select{background:#fff;outline:none;border:none;padding:0;margin:0;overflow:auto;font-family:monospace;font-size:110%;color:#000} +.completions select{background:#fff;outline:none;border:none;padding:0;margin:0;overflow:auto;font-family:monospace;font-size:110%;color:#000;width:auto} .completions select option.context{color:#0064cd} #menubar .navbar-inner{min-height:28px;border-top:1px;border-radius:0 0 4px 4px} #menubar .navbar{margin-bottom:8px}