##// END OF EJS Templates
Refactor the do_diff and manual child view lists into a separate ViewList object
Jason Grout -
Show More
@@ -325,6 +325,7 b' define(["widgets/js/manager",'
325
325
326 pop_child_view: function(child_model) {
326 pop_child_view: function(child_model) {
327 // Delete a child view that was previously created using create_child_view.
327 // Delete a child view that was previously created using create_child_view.
328 console.error("Deprecated use of WidgetView.pop_child_view; use a ViewList instead");
328 var view_ids = this.child_model_views[child_model.id];
329 var view_ids = this.child_model_views[child_model.id];
329 if (view_ids !== undefined) {
330 if (view_ids !== undefined) {
330
331
@@ -358,6 +359,8 b' define(["widgets/js/manager",'
358 // Callback that is called for each item added.
359 // Callback that is called for each item added.
359
360
360 // Walk the lists until an unequal entry is found.
361 // Walk the lists until an unequal entry is found.
362 console.error("Deprecated _do_diff; use a ViewList or related class instead");
363
361 var i;
364 var i;
362 for (i = 0; i < new_list.length; i++) {
365 for (i = 0; i < new_list.length; i++) {
363 if (i >= old_list.length || new_list[i] !== old_list[i]) {
366 if (i >= old_list.length || new_list[i] !== old_list[i]) {
@@ -588,11 +591,87 b' define(["widgets/js/manager",'
588 },
591 },
589 });
592 });
590
593
591
594
595 var ViewList = function(create_view, remove_view, context) {
596 // * create_view and remove_view are default functions called when adding or removing views
597 // * create_view takes a model and creates a view or a promise for a view for that model
598 // * remove_view takes a view and destroys it (including calling `.remove()`)
599 // * each time the update() function is called with a new list, the create and destroy
600 // callbacks will be called in an order so that if you append the views created in the
601 // create callback, you will duplicate the order of the list.
602 // * the remove callback defaults to just removing the view (if you pass in null)
603 // * the context defaults to the ViewList. If you pass another context, the create and remove
604 // will be called in that context.
605
606 this.initialize.apply(this, arguments);
607 this.state_change = Promise.resolve();
608 }
609
610 _.extend(ViewList.prototype, {
611 initialize: function(create_view, remove_view, context) {
612 this._handler_context = context || this;
613 this._models = [];
614 this.views = [];
615 this._create_view = create_view;
616 this._remove_view = remove_view || function(view) {view.remove()};
617 },
618
619 update: function(new_models, create_view, remove_view, context) {
620 // the create_view, remove_view, and context arguments override the defaults
621 // specified when the list is created.
622 // returns a promise that resolves after this update is done
623 var remove = remove_view || this._remove_view;
624 var create = create_view || this._create_view;
625 var context = context || this._handler_context;
626 var added_views = [];
627 var that = this;
628 this.state_change = this.state_change.then(function() {
629 var i;
630 // first, skip past the beginning of the lists if they are identical
631 for (i = 0; i < new_models.length; i++) {
632 if (i >= that._models.length || new_models[i] !== that._models[i]) {
633 break;
634 }
635 }
636 var first_removed = i;
637 // Remove the non-matching items from the old list.
638 for (var j = first_removed; j < that._models.length; j++) {
639 remove.call(context, that.views[j]);
640 }
641
642 // Add the rest of the new list items.
643 for (; i < new_models.length; i++) {
644 added_views.push(create.call(context, new_models[i]));
645 }
646 that._models = new_models;
647 return Promise.all(added_views, function(added) {
648 that.views = that.views.slice(0,first_removed).concat(added);
649 });
650 });
651 return this.state_change;
652 },
653
654 remove: function() {
655 // removes every view in our list; convenience function for `.update([])`
656 // that should be faster
657 // returns a promise that resolves after this removal is done
658 var that = this;
659 this.state_change = this.state_change.then(function() {
660 for (var i = 0, len=that.views.length; i <len; i++) {
661 that._remove_view.call(that._handler_context, that.views[i]);
662 };
663 that._models = [];
664 that.views = [];
665 });
666 return this.state_change;
667 },
668 });
669
592 var widget = {
670 var widget = {
593 'WidgetModel': WidgetModel,
671 'WidgetModel': WidgetModel,
594 'WidgetView': WidgetView,
672 'WidgetView': WidgetView,
595 'DOMWidgetView': DOMWidgetView,
673 'DOMWidgetView': DOMWidgetView,
674 'ViewList': ViewList,
596 };
675 };
597
676
598 // For backwards compatability.
677 // For backwards compatability.
@@ -12,16 +12,17 b' define(['
12 initialize: function(){
12 initialize: function(){
13 // Public constructor
13 // Public constructor
14 BoxView.__super__.initialize.apply(this, arguments);
14 BoxView.__super__.initialize.apply(this, arguments);
15 this.model.on('change:children', function(model, value) {
15 this.children_views = new widget.ViewList(this.add_child_model, null, this);
16 this.update_children(model.previous('children'), value);
16 this.listenTo(this.model, 'change:children', function(model, value) {
17 this.children_views.update(value);
17 }, this);
18 }, this);
18 this.model.on('change:overflow_x', function(model, value) {
19 this.listenTo(this.model, 'change:overflow_x', function(model, value) {
19 this.update_overflow_x();
20 this.update_overflow_x();
20 }, this);
21 }, this);
21 this.model.on('change:overflow_y', function(model, value) {
22 this.listenTo(this.model, 'change:overflow_y', function(model, value) {
22 this.update_overflow_y();
23 this.update_overflow_y();
23 }, this);
24 }, this);
24 this.model.on('change:box_style', function(model, value) {
25 this.listenTo(this.model, 'change:box_style', function(model, value) {
25 this.update_box_style();
26 this.update_box_style();
26 }, this);
27 }, this);
27 },
28 },
@@ -35,7 +36,7 b' define(['
35 // Called when view is rendered.
36 // Called when view is rendered.
36 this.$box = this.$el;
37 this.$box = this.$el;
37 this.$box.addClass('widget-box');
38 this.$box.addClass('widget-box');
38 this.update_children([], this.model.get('children'));
39 this.children_views.update(this.model.get('children'));
39 this.update_overflow_x();
40 this.update_overflow_x();
40 this.update_overflow_y();
41 this.update_overflow_y();
41 this.update_box_style('');
42 this.update_box_style('');
@@ -61,18 +62,6 b' define(['
61 this.update_mapped_classes(class_map, 'box_style', previous_trait_value, this.$box);
62 this.update_mapped_classes(class_map, 'box_style', previous_trait_value, this.$box);
62 },
63 },
63
64
64 update_children: function(old_list, new_list) {
65 // Called when the children list changes.
66 this.do_diff(old_list, new_list,
67 $.proxy(this.remove_child_model, this),
68 $.proxy(this.add_child_model, this));
69 },
70
71 remove_child_model: function(model) {
72 // Called when a model is removed from the children list.
73 this.pop_child_view(model).remove();
74 },
75
76 add_child_model: function(model) {
65 add_child_model: function(model) {
77 // Called when a model is added to the children list.
66 // Called when a model is added to the children list.
78 var that = this;
67 var that = this;
@@ -88,16 +77,21 b' define(['
88 return view;
77 return view;
89 }, utils.reject("Couldn't add child view to box", true));
78 }, utils.reject("Couldn't add child view to box", true));
90 },
79 },
80
81 remove: function() {
82 BoxView.__super__.remove.apply(this, arguments);
83 this.children_views.remove();
84 },
91 });
85 });
92
86
93
87
94 var FlexBoxView = BoxView.extend({
88 var FlexBoxView = BoxView.extend({
95 render: function(){
89 render: function(){
96 FlexBoxView.__super__.render.apply(this);
90 FlexBoxView.__super__.render.apply(this);
97 this.model.on('change:orientation', this.update_orientation, this);
91 this.listenTo(this.model, 'change:orientation', this.update_orientation, this);
98 this.model.on('change:flex', this._flex_changed, this);
92 this.listenTo(this.model, 'change:flex', this._flex_changed, this);
99 this.model.on('change:pack', this._pack_changed, this);
93 this.listenTo(this.model, 'change:pack', this._pack_changed, this);
100 this.model.on('change:align', this._align_changed, this);
94 this.listenTo(this.model, 'change:align', this._align_changed, this);
101 this._flex_changed();
95 this._flex_changed();
102 this._pack_changed();
96 this._pack_changed();
103 this._align_changed();
97 this._align_changed();
@@ -244,10 +238,7 b' define(['
244 this._shown_once = false;
238 this._shown_once = false;
245 this.popped_out = true;
239 this.popped_out = true;
246
240
247 this.update_children([], this.model.get('children'));
241 this.children_views.update(this.model.get('children'))
248 this.model.on('change:children', function(model, value) {
249 this.update_children(model.previous('children'), value);
250 }, this);
251 },
242 },
252
243
253 hide: function() {
244 hide: function() {
General Comments 0
You need to be logged in to leave comments. Login now