diff --git a/IPython/html/static/notebook/js/widgets/widget.js b/IPython/html/static/notebook/js/widgets/widget.js index 4b3fe06..95fca66 100644 --- a/IPython/html/static/notebook/js/widgets/widget.js +++ b/IPython/html/static/notebook/js/widgets/widget.js @@ -226,31 +226,18 @@ function(widget_manager, underscore, backbone){ // triggered on model change }, - child_view: function(child_model, options) { + create_child_view: function(child_model, options) { // create and return a child view, given a model and (optionally) a view name // if the view name is not given, it defaults to the model's default view attribute var child_view = this.model.widget_manager.create_view(child_model, options); this.child_views[child_model.id] = child_view; return child_view; }, - - update_child_views: function(old_list, new_list) { - // this function takes an old list and new list of models - // views in child_views that correspond to deleted ids are deleted - // views corresponding to added ids are added child_views - - // delete old views - _.each(_.difference(old_list, new_list), function(element, index, list) { - var view = this.child_views[element.id]; - delete this.child_views[element.id]; - view.remove(); - }, this); - - // add new views - _.each(_.difference(new_list, old_list), function(element, index, list) { - // this function adds the view to the child_views dictionary - this.child_view(element); - }, this); + + delete_child_view: function(child_model, options) { + var view = this.child_views[child_model.id]; + delete this.child_views[child_model.id]; + view.remove(); }, do_diff: function(old_list, new_list, removed_callback, added_callback) { diff --git a/IPython/html/static/notebook/js/widgets/widget_container.js b/IPython/html/static/notebook/js/widgets/widget_container.js index e69ff36..88ad035 100644 --- a/IPython/html/static/notebook/js/widgets/widget_container.js +++ b/IPython/html/static/notebook/js/widgets/widget_container.js @@ -29,11 +29,20 @@ define(["notebook/js/widgets/widget"], function(widget_manager) { }, update_children: function(old_list, new_list) { - this.$el.empty(); - this.update_child_views(old_list, new_list); - _.each(new_list, function(element, index, list) { - this.$el.append(this.child_views[element.id].$el); - }, this) + this.do_diff(old_list, + new_list, + $.proxy(this.remove_child_model, this), + $.proxy(this.add_child_model, this)); + }, + + remove_child_model: function(model) { + this.child_views[model.id].remove(); + this.delete_child_view(model); + }, + + add_child_model: function(model) { + var view = this.create_child_view(model); + this.$el.append(view.$el); }, update: function(){ @@ -188,11 +197,20 @@ define(["notebook/js/widgets/widget"], function(widget_manager) { }, update_children: function(old_list, new_list) { - this.$el.empty(); - this.update_child_views(old_list, new_list); - _.each(new_list, function(element, index, list) { - this.$body.append(this.child_views[element].$el); - }, this) + this.do_diff(old_list, + new_list, + $.proxy(this.remove_child_model, this), + $.proxy(this.add_child_model, this)); + }, + + remove_child_model: function(model) { + this.child_views[model.id].remove(); + this.delete_child_view(model); + }, + + add_child_model: function(model) { + var view = this.create_child_view(model); + this.$body.append(view.$el); }, update: function(){ diff --git a/IPython/html/static/notebook/js/widgets/widget_selectioncontainer.js b/IPython/html/static/notebook/js/widgets/widget_selectioncontainer.js index 46dc3e4..e0a4e31 100644 --- a/IPython/html/static/notebook/js/widgets/widget_selectioncontainer.js +++ b/IPython/html/static/notebook/js/widgets/widget_selectioncontainer.js @@ -23,24 +23,13 @@ define(["notebook/js/widgets/widget"], function(widget_manager){ .attr('id', guid) .addClass('accordion'); this.containers = []; + this.model_containers = {}; this.update_children([], this.model.get('children')); this.model.on('change:children', function(model, value, options) { this.update_children(model.previous('children'), value); }, this); }, - update_children: function(old_list, new_list) { - _.each(this.containers, function(element, index, list) { - element.remove(); - }, this); - this.containers = []; - this.update_child_views(old_list, new_list); - _.each(new_list, function(element, index, list) { - this.add_child_view(this.child_views[element]); - }, this) - }, - - update: function(options) { // Update the contents of this view // @@ -76,9 +65,24 @@ define(["notebook/js/widgets/widget"], function(widget_manager){ } return AccordionView.__super__.update.apply(this); }, + + update_children: function(old_list, new_list) { + this.do_diff(old_list, + new_list, + $.proxy(this.remove_child_model, this), + $.proxy(this.add_child_model, this)); + }, - add_child_view: function(view) { + remove_child_model: function(model) { + var accordion_group = this.model_containers[model.id]; + this.containers.splice(accordion_group.container_index, 1); + delete this.model_containers[model.id]; + accordion_group.remove(); + this.delete_child_view(model); + }, + add_child_model: function(model) { + var view = this.create_child_view(model); var index = this.containers.length; var uuid = IPython.utils.uuid(); var accordion_group = $('
') @@ -108,7 +112,9 @@ define(["notebook/js/widgets/widget"], function(widget_manager){ var accordion_inner = $('
') .addClass('accordion-inner') .appendTo(accordion_body); - this.containers.push(accordion_group); + var container_index = this.containers.push(accordion_group) - 1; + accordion_group.container_index = container_index; + this.model_containers[model.id] = accordion_group; accordion_inner.append(view.$el); this.update(); @@ -157,31 +163,25 @@ define(["notebook/js/widgets/widget"], function(widget_manager){ this.add_child_view(this.child_views[element]); }, this) }, + + update_children: function(old_list, new_list) { + this.do_diff(old_list, + new_list, + $.proxy(this.remove_child_model, this), + $.proxy(this.add_child_model, this)); + }, - update: function(options) { - // Update the contents of this view - // - // Called when the model is changed. The model may have been - // changed by another view or by a state update from the back-end. - if (options === undefined || options.updated_view != this) { - // Set tab titles - var titles = this.model.get('_titles'); - for (var page_index in titles) { - var tab_text = this.containers[page_index]; - if (tab_text !== undefined) { - tab_text.html(titles[page_index]); - } - } - - var selected_index = this.model.get('selected_index'); - if (0 <= selected_index && selected_index < this.containers.length) { - this.select_page(selected_index); - } - } - return TabView.__super__.update.apply(this); + remove_child_model: function(model) { + var view = this.child_views[model.id]; + this.containers.splice(view.parent_tab.tab_text_index, 1); + view.parent_tab.remove(); + view.parent_container.remove(); + view.remove(); + this.delete_child_view(model); }, - add_child_view: function(view) { + add_child_model: function(model) { + var view = this.create_child_view(model); var index = this.containers.length; var uuid = IPython.utils.uuid(); @@ -189,6 +189,8 @@ define(["notebook/js/widgets/widget"], function(widget_manager){ var tab = $('
  • ') .css('list-style-type', 'none') .appendTo(this.$tabs); + view.parent_tab = tab; + var tab_text = $('') .attr('href', '#' + uuid) .attr('data-toggle', 'tab') @@ -202,13 +204,37 @@ define(["notebook/js/widgets/widget"], function(widget_manager){ that.touch(); that.select_page(index); }); - this.containers.push(tab_text); + tab.tab_text_index = this.containers.push(tab_text) - 1; var contents_div = $('
    ', {id: uuid}) .addClass('tab-pane') .addClass('fade') .append(view.$el) .appendTo(this.$tab_contents); + view.parent_container = contents_div; + }, + + update: function(options) { + // Update the contents of this view + // + // Called when the model is changed. The model may have been + // changed by another view or by a state update from the back-end. + if (options === undefined || options.updated_view != this) { + // Set tab titles + var titles = this.model.get('_titles'); + for (var page_index in titles) { + var tab_text = this.containers[page_index]; + if (tab_text !== undefined) { + tab_text.html(titles[page_index]); + } + } + + var selected_index = this.model.get('selected_index'); + if (0 <= selected_index && selected_index < this.containers.length) { + this.select_page(selected_index); + } + } + return TabView.__super__.update.apply(this); }, select_page: function(index) { diff --git a/IPython/html/widgets/widget_selectioncontainer.py b/IPython/html/widgets/widget_selectioncontainer.py index 3283e31..d20dcad 100644 --- a/IPython/html/widgets/widget_selectioncontainer.py +++ b/IPython/html/widgets/widget_selectioncontainer.py @@ -27,7 +27,7 @@ class AccordionWidget(DOMWidget): _titles = Dict(help="Titles of the pages", sync=True) selected_index = Int(0, sync=True) - children = List(Instance(DOMWidget)) + children = List(Instance(DOMWidget), sync=True) # Public methods def set_title(self, index, title):