From 3dd14b756b00dbe86438b623f8bfc2b5a7cd8d72 2014-01-16 10:57:04 From: Jonathan Frederic Date: 2014-01-16 10:57:04 Subject: [PATCH] Made scroll to bottom use msgs allow multiple msg handlers added send to view to automatically hookup callbacks changed send signature to optionally accept a cell to connect callbacks --- diff --git a/IPython/html/static/notebook/js/widget.js b/IPython/html/static/notebook/js/widget.js index d985b68..d6ef81f 100644 --- a/IPython/html/static/notebook/js/widget.js +++ b/IPython/html/static/notebook/js/widget.js @@ -36,6 +36,7 @@ define(["components/underscore/underscore-min", this.msg_throttle = 3; this.msg_buffer = null; this.views = {}; + this._custom_msg_callbacks = []; // Remember comm associated with the model. this.comm = comm; @@ -64,15 +65,17 @@ define(["components/underscore/underscore-min", } }, - send: function(content) { + + send: function(content, cell) { // Used the last modified view as the sender of the message. This // will insure that any python code triggered by the sent message // can create and display widgets and output. - var cell = null; - if (this.last_modified_view != undefined && - this.last_modified_view.cell != undefined) { - cell = this.last_modified_view.cell; + if (cell === undefined) { + if (this.last_modified_view != undefined && + this.last_modified_view.cell != undefined) { + cell = this.last_modified_view.cell; + } } var callbacks = this._make_callbacks(cell); var data = {'custom_content': content}; @@ -90,15 +93,29 @@ define(["components/underscore/underscore-min", }, - on_msg: function (callback) { - this._msg_callback = callback; + on_msg: function (callback, remove) { + if (remove) { + var found_index = -1; + for (var index in this._custom_msg_callbacks) { + if (callback === this._custom_msg_callbacks[index]) { + found_index = index; + break; + } + } + + if (found_index >= 0) { + this._custom_msg_callbacks.splice(found_index, 1); + } + } else { + this._custom_msg_callbacks.push(callback) + } }, _handle_custom_msg: function (content) { - if (this._msg_callback) { + for (var index in this._custom_msg_callbacks) { try { - this._msg_callback(content); + this._custom_msg_callbacks[index](content); } catch (e) { console.log("Exception in widget model msg callback", e, content); } @@ -465,6 +482,11 @@ define(["components/underscore/underscore-min", elements.removeClass(class_list); } }, + + + send: function(content) { + this.model.send({event: 'click'}, this.cell); + }, update: function() { if (this.model.get('visible') != undefined) { diff --git a/IPython/html/static/notebook/js/widgets/button.js b/IPython/html/static/notebook/js/widgets/button.js index 9e0f452..d450984 100644 --- a/IPython/html/static/notebook/js/widgets/button.js +++ b/IPython/html/static/notebook/js/widgets/button.js @@ -50,8 +50,7 @@ define(["notebook/js/widget"], function(widget_manager){ }, _handle_click: function(){ - this.model.last_modified_view = this; // For callbacks. - this.model.send({event: 'click'}); + this.send({event: 'click'}); }, }); diff --git a/IPython/html/static/notebook/js/widgets/string.js b/IPython/html/static/notebook/js/widgets/string.js index 45255bc..a943017 100644 --- a/IPython/html/static/notebook/js/widgets/string.js +++ b/IPython/html/static/notebook/js/widgets/string.js @@ -39,7 +39,7 @@ define(["notebook/js/widget"], function(widget_manager){ var TextAreaView = IPython.WidgetView.extend({ // Called when view is rendered. - render : function(){ + render: function(){ this.$el .addClass('widget-hbox') .html(''); @@ -53,23 +53,30 @@ define(["notebook/js/widget"], function(widget_manager){ .appendTo(this.$el); this.$el_to_style = this.$textbox; // Set default element to style this.update(); // Set defaults. + + this.on_msg(); + }, + + + _handle_textarea_msg: function (content){ + if (content.method == "scroll_to_bottom") { + this.scroll_to_bottom(); + } + }, + + + scroll_to_bottom: function (){ + this.$textbox.scrollTop(this.$textbox[0].scrollHeight); }, + // Handles: Backend -> Frontend Sync // Frontent -> Frontend Sync - update : function(){ + update: function(){ if (!this.user_invoked_update) { this.$textbox.val(this.model.get('value')); } - if (this.last_scroll_to_bottom == undefined) { - this.last_scroll_to_bottom = 0; - } - if (this.last_scroll_to_bottom < this.model.get('scroll_to_bottoms')) { - this.last_scroll_to_bottom < this.model.get('scroll_to_bottoms'); - this.$textbox.scrollTop(this.$textbox[0].scrollHeight); - } - var disabled = this.model.get('disabled'); this.$textbox.prop('disabled', disabled); @@ -83,9 +90,9 @@ define(["notebook/js/widget"], function(widget_manager){ return IPython.WidgetView.prototype.update.call(this); }, - events: {"keyup textarea" : "handleChanging", - "paste textarea" : "handleChanging", - "cut textarea" : "handleChanging"}, + events: {"keyup textarea": "handleChanging", + "paste textarea": "handleChanging", + "cut textarea": "handleChanging"}, // Handles and validates user input. handleChanging: function(e) { @@ -101,7 +108,7 @@ define(["notebook/js/widget"], function(widget_manager){ var TextBoxView = IPython.WidgetView.extend({ // Called when view is rendered. - render : function(){ + render: function(){ this.$el .addClass('widget-hbox-single') .html(''); @@ -119,7 +126,7 @@ define(["notebook/js/widget"], function(widget_manager){ // Handles: Backend -> Frontend Sync // Frontent -> Frontend Sync - update : function(){ + update: function(){ if (this.$textbox.val() != this.model.get('value')) { this.$textbox.val(this.model.get('value')); } @@ -137,10 +144,10 @@ define(["notebook/js/widget"], function(widget_manager){ return IPython.WidgetView.prototype.update.call(this); }, - events: {"keyup input" : "handleChanging", - "paste input" : "handleChanging", - "cut input" : "handleChanging", - "keypress input" : "handleKeypress"}, + events: {"keyup input": "handleChanging", + "paste input": "handleChanging", + "cut input": "handleChanging", + "keypress input": "handleKeypress"}, // Handles and validates user input. handleChanging: function(e) { @@ -151,8 +158,7 @@ define(["notebook/js/widget"], function(widget_manager){ // Handles text submition handleKeypress: function(e) { if (e.keyCode == 13) { // Return key - this.model.last_modified_view = this; // For callbacks. - this.model.send({event: 'submit'}); + this.send({event: 'submit'}); } }, }); diff --git a/IPython/html/widgets/widget_string.py b/IPython/html/widgets/widget_string.py index 89c4d95..7b81715 100644 --- a/IPython/html/widgets/widget_string.py +++ b/IPython/html/widgets/widget_string.py @@ -27,11 +27,10 @@ class StringWidget(Widget): default_view_name = Unicode('TextBoxView') # Keys - _keys = ['value', 'disabled', 'description', 'scroll_to_bottoms'] + _keys = ['value', 'disabled', 'description'] value = Unicode(help="String value") disabled = Bool(False, help="Enable or disable user changes") description = Unicode(help="Description of the value this widget represents") - scroll_to_bottoms = Int(0, help="Used to scroll a TextAreaView to the bottom") def __init__(self, **kwargs): @@ -41,7 +40,7 @@ class StringWidget(Widget): def scroll_to_bottom(self): - self.scroll_to_bottoms += 1 + self._comm.send({"method": "scroll_to_bottom"}) def on_click(self, callback, remove=False):