##// END OF EJS Templates
Remove unused code and debugging statements
Jason Grout -
Show More
@@ -1,326 +1,322 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Base Widget Model and View classes
9 // Base Widget Model and View classes
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 **/
15 **/
16
16
17 define(["notebook/js/widgetmanager",
17 define(["notebook/js/widgetmanager",
18 "underscore",
18 "underscore",
19 "backbone"],
19 "backbone"],
20 function(widget_manager, underscore, backbone){
20 function(widget_manager, underscore, backbone){
21
21
22 //--------------------------------------------------------------------
22 //--------------------------------------------------------------------
23 // WidgetModel class
23 // WidgetModel class
24 //--------------------------------------------------------------------
24 //--------------------------------------------------------------------
25 var WidgetModel = Backbone.Model.extend({
25 var WidgetModel = Backbone.Model.extend({
26 constructor: function (widget_manager, widget_id, comm) {
26 constructor: function (widget_manager, widget_id, comm) {
27 this.widget_manager = widget_manager;
27 this.widget_manager = widget_manager;
28 this.pending_msgs = 0;
28 this.pending_msgs = 0;
29 this.msg_throttle = 3;
29 this.msg_throttle = 3;
30 this.msg_buffer = null;
30 this.msg_buffer = null;
31 this.id = widget_id;
31 this.id = widget_id;
32
32
33 if (comm !== undefined) {
33 if (comm !== undefined) {
34 // Remember comm associated with the model.
34 // Remember comm associated with the model.
35 this.comm = comm;
35 this.comm = comm;
36 comm.model = this;
36 comm.model = this;
37
37
38 // Hook comm messages up to model.
38 // Hook comm messages up to model.
39 var that = this;
39 var that = this;
40 comm.on_close($.proxy(this._handle_comm_closed, this));
40 comm.on_close($.proxy(this._handle_comm_closed, this));
41 comm.on_msg($.proxy(this._handle_comm_msg, this));
41 comm.on_msg($.proxy(this._handle_comm_msg, this));
42 }
42 }
43
43
44 return Backbone.Model.apply(this);
44 return Backbone.Model.apply(this);
45 },
45 },
46
46
47
47
48 send: function (content, callbacks) {
48 send: function (content, callbacks) {
49 console.log('send',content, callbacks);
50 if (this.comm !== undefined) {
49 if (this.comm !== undefined) {
51 var data = {method: 'custom', custom_content: content};
50 var data = {method: 'custom', custom_content: content};
52 this.comm.send(data, callbacks);
51 this.comm.send(data, callbacks);
53 }
52 }
54 },
53 },
55
54
56 // Handle when a widget is closed.
55 // Handle when a widget is closed.
57 _handle_comm_closed: function (msg) {
56 _handle_comm_closed: function (msg) {
58 // jng: widget manager should observe the comm_close event and delete views when triggered
57 // jng: widget manager should observe the comm_close event and delete views when triggered
59 this.trigger('comm:close');
58 this.trigger('comm:close');
60 if (this._has_comm()) {
59 if (this._has_comm()) {
61 delete this.comm.model; // Delete ref so GC will collect widget model.
60 delete this.comm.model; // Delete ref so GC will collect widget model.
62 delete this.comm;
61 delete this.comm;
63 }
62 }
64 delete this.widget_id; // Delete id from model so widget manager cleans up.
63 delete this.widget_id; // Delete id from model so widget manager cleans up.
65 },
64 },
66
65
67
66
68 // Handle incoming comm msg.
67 // Handle incoming comm msg.
69 _handle_comm_msg: function (msg) {
68 _handle_comm_msg: function (msg) {
70 var method = msg.content.data.method;
69 var method = msg.content.data.method;
71 switch (method) {
70 switch (method) {
72 case 'update':
71 case 'update':
73 this.apply_update(msg.content.data.state);
72 this.apply_update(msg.content.data.state);
74 break;
73 break;
75 case 'custom':
74 case 'custom':
76 this.trigger('msg:custom', msg.content.data.custom_content);
75 this.trigger('msg:custom', msg.content.data.custom_content);
77 break;
76 break;
78 default:
77 default:
79 // pass on to widget manager
78 // pass on to widget manager
80 this.widget_manager.handle_msg(msg, this);
79 this.widget_manager.handle_msg(msg, this);
81 }
80 }
82 },
81 },
83
82
84
83
85 // Handle when a widget is updated via the python side.
84 // Handle when a widget is updated via the python side.
86 apply_update: function (state) {
85 apply_update: function (state) {
87 this.updating = true;
86 this.updating = true;
88 try {
87 try {
89 for (var key in state) {
88 for (var key in state) {
90 if (state.hasOwnProperty(key)) {
89 if (state.hasOwnProperty(key)) {
91 this.set(key, state[key]);
90 this.set(key, state[key]);
92 }
91 }
93 }
92 }
94 this.save();
93 this.save();
95 } finally {
94 } finally {
96 this.updating = false;
95 this.updating = false;
97 }
96 }
98 },
97 },
99
98
100
99
101 _handle_status: function (msg, callbacks) {
100 _handle_status: function (msg, callbacks) {
102 //execution_state : ('busy', 'idle', 'starting')
101 //execution_state : ('busy', 'idle', 'starting')
103 if (this.comm !== undefined) {
102 if (this.comm !== undefined) {
104 if (msg.content.execution_state ==='idle') {
103 if (msg.content.execution_state ==='idle') {
105
104
106 // Send buffer if this message caused another message to be
105 // Send buffer if this message caused another message to be
107 // throttled.
106 // throttled.
108 if (this.msg_buffer !== null &&
107 if (this.msg_buffer !== null &&
109 this.msg_throttle === this.pending_msgs) {
108 this.msg_throttle === this.pending_msgs) {
110 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
109 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
111 this.comm.send(data, callbacks);
110 this.comm.send(data, callbacks);
112 this.msg_buffer = null;
111 this.msg_buffer = null;
113 } else {
112 } else {
114
113
115 // Only decrease the pending message count if the buffer
114 // Only decrease the pending message count if the buffer
116 // doesn't get flushed (sent).
115 // doesn't get flushed (sent).
117 --this.pending_msgs;
116 --this.pending_msgs;
118 }
117 }
119 }
118 }
120 }
119 }
121 },
120 },
122
121
123
122
124 // Custom syncronization logic.
123 // Custom syncronization logic.
125 _handle_sync: function (method, options) {
124 _handle_sync: function (method, options) {
126 var model_json = this.toJSON();
125 var model_json = this.toJSON();
127 var attr;
126 var attr;
128
127
129 // Only send updated state if the state hasn't been changed
128 // Only send updated state if the state hasn't been changed
130 // during an update.
129 // during an update.
131 if (this.comm !== undefined) {
130 if (this.comm !== undefined) {
132 if (!this.updating) {
131 if (!this.updating) {
133 if (this.pending_msgs >= this.msg_throttle) {
132 if (this.pending_msgs >= this.msg_throttle) {
134 // The throttle has been exceeded, buffer the current msg so
133 // The throttle has been exceeded, buffer the current msg so
135 // it can be sent once the kernel has finished processing
134 // it can be sent once the kernel has finished processing
136 // some of the existing messages.
135 // some of the existing messages.
137 if (method=='patch') {
136 if (method=='patch') {
138 if (this.msg_buffer === null) {
137 if (this.msg_buffer === null) {
139 this.msg_buffer = $.extend({}, model_json); // Copy
138 this.msg_buffer = $.extend({}, model_json); // Copy
140 }
139 }
141 for (attr in options.attrs) {
140 for (attr in options.attrs) {
142 this.msg_buffer[attr] = options.attrs[attr];
141 this.msg_buffer[attr] = options.attrs[attr];
143 }
142 }
144 } else {
143 } else {
145 this.msg_buffer = $.extend({}, model_json); // Copy
144 this.msg_buffer = $.extend({}, model_json); // Copy
146 }
145 }
147
146
148 } else {
147 } else {
149 // We haven't exceeded the throttle, send the message like
148 // We haven't exceeded the throttle, send the message like
150 // normal. If this is a patch operation, just send the
149 // normal. If this is a patch operation, just send the
151 // changes.
150 // changes.
152 var send_json = model_json;
151 var send_json = model_json;
153 if (method =='patch') {
152 if (method =='patch') {
154 send_json = {};
153 send_json = {};
155 for (attr in options.attrs) {
154 for (attr in options.attrs) {
156 send_json[attr] = options.attrs[attr];
155 send_json[attr] = options.attrs[attr];
157 }
156 }
158 }
157 }
159
158
160 var data = {method: 'backbone', sync_method: method, sync_data: send_json};
159 var data = {method: 'backbone', sync_method: method, sync_data: send_json};
161 this.comm.send(data, this.cell_callbacks());
160 this.comm.send(data, this.cell_callbacks());
162 this.pending_msgs++;
161 this.pending_msgs++;
163 }
162 }
164 }
163 }
165 }
164 }
166
165
167 // Since the comm is a one-way communication, assume the message
166 // Since the comm is a one-way communication, assume the message
168 // arrived.
167 // arrived.
169 return model_json;
168 return model_json;
170 },
169 },
171
170
172 // Build a callback dict.
171 // Build a callback dict.
173 cell_callbacks: function (cell) {
172 cell_callbacks: function (cell) {
174 var callbacks = {};
173 var callbacks = {};
175 console.log('cell_callbacks A', cell);
176 if (cell === undefined) {
174 if (cell === undefined) {
177 // Used the last modified view as the sender of the message. This
175 // Used the last modified view as the sender of the message. This
178 // will insure that any python code triggered by the sent message
176 // will insure that any python code triggered by the sent message
179 // can create and display widgets and output.
177 // can create and display widgets and output.
180 if (this.last_modified_view !== undefined &&
178 if (this.last_modified_view !== undefined &&
181 this.last_modified_view.cell !== undefined) {
179 this.last_modified_view.cell !== undefined) {
182 cell = this.last_modified_view.cell;
180 cell = this.last_modified_view.cell;
183 } else {
181 } else {
184 cell = null;
182 cell = null;
185 }
183 }
186 }
184 }
187 console.log('cell_callbacks B', cell);
188 if (cell !== null) {
185 if (cell !== null) {
189
186
190 // Try to get output handlers
187 // Try to get output handlers
191 var handle_output = null;
188 var handle_output = null;
192 var handle_clear_output = null;
189 var handle_clear_output = null;
193 if (cell.output_area !== undefined && cell.output_area !== null) {
190 if (cell.output_area !== undefined && cell.output_area !== null) {
194 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
191 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
195 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
192 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
196 }
193 }
197
194
198 // Create callback dict using what is known
195 // Create callback dict using what is known
199 var that = this;
196 var that = this;
200 callbacks = {
197 callbacks = {
201 iopub : {
198 iopub : {
202 output : handle_output,
199 output : handle_output,
203 clear_output : handle_clear_output,
200 clear_output : handle_clear_output,
204
201
205 status : function (msg) {
202 status : function (msg) {
206 that._handle_status(msg, that.cell_callbacks(cell));
203 that._handle_status(msg, that.cell_callbacks(cell));
207 },
204 },
208
205
209 // Special function only registered by widget messages.
206 // Special function only registered by widget messages.
210 // Allows us to get the cell for a message so we know
207 // Allows us to get the cell for a message so we know
211 // where to add widgets if the code requires it.
208 // where to add widgets if the code requires it.
212 get_cell : function () {
209 get_cell : function () {
213 return cell;
210 return cell;
214 },
211 },
215 },
212 },
216 };
213 };
217 }
214 }
218 console.log('constructed callbacks for',cell);
219 return callbacks;
215 return callbacks;
220 },
216 },
221 });
217 });
222
218
223
219
224 //--------------------------------------------------------------------
220 //--------------------------------------------------------------------
225 // WidgetView class
221 // WidgetView class
226 //--------------------------------------------------------------------
222 //--------------------------------------------------------------------
227 var BaseWidgetView = Backbone.View.extend({
223 var BaseWidgetView = Backbone.View.extend({
228 initialize: function(options) {
224 initialize: function(options) {
229 this.model.on('change',this.update,this);
225 this.model.on('change',this.update,this);
230 this.widget_manager = options.widget_manager;
226 this.widget_manager = options.widget_manager;
231 this.comm_manager = options.widget_manager.comm_manager;
227 this.comm_manager = options.widget_manager.comm_manager;
232 this.cell = options.cell
228 this.cell = options.cell
233 },
229 },
234
230
235 update: function(){
231 update: function(){
236 // update thyself to be consistent with this.model
232 // update thyself to be consistent with this.model
237 },
233 },
238
234
239 child_view: function(comm_id, view_name) {
235 child_view: function(comm_id, view_name) {
240 var child_model = this.comm_manager.comms[comm_id].model;
236 var child_model = this.comm_manager.comms[comm_id].model;
241 var child_view = this.widget_manager.create_view(child_model, view_name, this.cell);
237 var child_view = this.widget_manager.create_view(child_model, view_name, this.cell);
242 return child_view;
238 return child_view;
243 },
239 },
244
240
245 render: function(){
241 render: function(){
246 // render thyself
242 // render thyself
247 },
243 },
248 send: function (content) {
244 send: function (content) {
249 this.model.send(content, this.model.cell_callbacks(this.cell));
245 this.model.send(content, this.model.cell_callbacks(this.cell));
250 },
246 },
251
247
252 touch: function () {
248 touch: function () {
253 this.model.last_modified_view = this;
249 this.model.last_modified_view = this;
254 this.model.save(this.model.changedAttributes(), {patch: true});
250 this.model.save(this.model.changedAttributes(), {patch: true});
255 },
251 },
256 });
252 });
257
253
258 var WidgetView = BaseWidgetView.extend({
254 var WidgetView = BaseWidgetView.extend({
259 initialize: function (options) {
255 initialize: function (options) {
260 this.visible = true;
256 this.visible = true;
261 BaseWidgetView.prototype.initialize.apply(this, arguments);
257 BaseWidgetView.prototype.initialize.apply(this, arguments);
262 },
258 },
263
259
264 add_class: function (selector, class_list) {
260 add_class: function (selector, class_list) {
265 var elements = this._get_selector_element(selector);
261 var elements = this._get_selector_element(selector);
266 if (elements.length > 0) {
262 if (elements.length > 0) {
267 elements.addClass(class_list);
263 elements.addClass(class_list);
268 }
264 }
269 },
265 },
270
266
271 remove_class: function (selector, class_list) {
267 remove_class: function (selector, class_list) {
272 var elements = this._get_selector_element(selector);
268 var elements = this._get_selector_element(selector);
273 if (elements.length > 0) {
269 if (elements.length > 0) {
274 elements.removeClass(class_list);
270 elements.removeClass(class_list);
275 }
271 }
276 },
272 },
277
273
278 update: function () {
274 update: function () {
279 // jng: hook into change:visible trigger
275 // jng: hook into change:visible trigger
280 var visible = this.model.get('visible');
276 var visible = this.model.get('visible');
281 if (visible !== undefined && this.visible !== visible) {
277 if (visible !== undefined && this.visible !== visible) {
282 this.visible = visible;
278 this.visible = visible;
283 this.$el.toggle(visible)
279 this.$el.toggle(visible)
284 }
280 }
285
281
286 if (this.model.css !== undefined) {
282 if (this.model.css !== undefined) {
287 for (var selector in this.model.css) {
283 for (var selector in this.model.css) {
288 if (this.model.css.hasOwnProperty(selector)) {
284 if (this.model.css.hasOwnProperty(selector)) {
289
285
290 // Apply the css traits to all elements that match the selector.
286 // Apply the css traits to all elements that match the selector.
291 var elements = this._get_selector_element(selector);
287 var elements = this._get_selector_element(selector);
292 if (elements.length > 0) {
288 if (elements.length > 0) {
293 var css_traits = this.model.css[selector];
289 var css_traits = this.model.css[selector];
294 for (var css_key in css_traits) {
290 for (var css_key in css_traits) {
295 if (css_traits.hasOwnProperty(css_key)) {
291 if (css_traits.hasOwnProperty(css_key)) {
296 elements.css(css_key, css_traits[css_key]);
292 elements.css(css_key, css_traits[css_key]);
297 }
293 }
298 }
294 }
299 }
295 }
300 }
296 }
301 }
297 }
302 }
298 }
303 },
299 },
304
300
305 _get_selector_element: function (selector) {
301 _get_selector_element: function (selector) {
306 // Get the elements via the css selector. If the selector is
302 // Get the elements via the css selector. If the selector is
307 // blank, apply the style to the $el_to_style element. If
303 // blank, apply the style to the $el_to_style element. If
308 // the $el_to_style element is not defined, use apply the
304 // the $el_to_style element is not defined, use apply the
309 // style to the view's element.
305 // style to the view's element.
310 var elements = this.$el.find(selector);
306 var elements = this.$el.find(selector);
311 if (selector === undefined || selector === null || selector === '') {
307 if (selector === undefined || selector === null || selector === '') {
312 if (this.$el_to_style === undefined) {
308 if (this.$el_to_style === undefined) {
313 elements = this.$el;
309 elements = this.$el;
314 } else {
310 } else {
315 elements = this.$el_to_style;
311 elements = this.$el_to_style;
316 }
312 }
317 }
313 }
318 return elements;
314 return elements;
319 },
315 },
320 });
316 });
321
317
322 IPython.WidgetModel = WidgetModel;
318 IPython.WidgetModel = WidgetModel;
323 IPython.WidgetView = WidgetView;
319 IPython.WidgetView = WidgetView;
324
320
325 return widget_manager;
321 return widget_manager;
326 });
322 });
@@ -1,263 +1,257 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // ContainerWidget
9 // ContainerWidget
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 **/
15 **/
16
16
17 define(["notebook/js/widgets/base"], function(widget_manager) {
17 define(["notebook/js/widgets/base"], function(widget_manager) {
18
18
19 var set_flex_property = function(element, property_name, enabled) {
19 var set_flex_property = function(element, property_name, enabled) {
20 if (enabled) {
20 if (enabled) {
21 element.addClass(property_name);
21 element.addClass(property_name);
22 } else {
22 } else {
23 element.removeClass(property_name);
23 element.removeClass(property_name);
24 }
24 }
25 };
25 };
26
26
27 var set_flex_properties = function(context, element) {
27 var set_flex_properties = function(context, element) {
28
28
29 // Apply flexible box model properties by adding and removing
29 // Apply flexible box model properties by adding and removing
30 // corrosponding CSS classes.
30 // corrosponding CSS classes.
31 // Defined in IPython/html/static/base/less/flexbox.less
31 // Defined in IPython/html/static/base/less/flexbox.less
32 set_flex_property(element, 'vbox', context.model.get('_vbox'));
32 set_flex_property(element, 'vbox', context.model.get('_vbox'));
33 set_flex_property(element, 'hbox', context.model.get('_hbox'));
33 set_flex_property(element, 'hbox', context.model.get('_hbox'));
34 set_flex_property(element, 'start', context.model.get('_pack_start'));
34 set_flex_property(element, 'start', context.model.get('_pack_start'));
35 set_flex_property(element, 'center', context.model.get('_pack_center'));
35 set_flex_property(element, 'center', context.model.get('_pack_center'));
36 set_flex_property(element, 'end', context.model.get('_pack_end'));
36 set_flex_property(element, 'end', context.model.get('_pack_end'));
37 set_flex_property(element, 'align-start', context.model.get('_align_start'));
37 set_flex_property(element, 'align-start', context.model.get('_align_start'));
38 set_flex_property(element, 'align-center', context.model.get('_align_center'));
38 set_flex_property(element, 'align-center', context.model.get('_align_center'));
39 set_flex_property(element, 'align-end', context.model.get('_align_end'));
39 set_flex_property(element, 'align-end', context.model.get('_align_end'));
40 set_flex_property(element, 'box-flex0', context.model.get('_flex0'));
40 set_flex_property(element, 'box-flex0', context.model.get('_flex0'));
41 set_flex_property(element, 'box-flex1', context.model.get('_flex1'));
41 set_flex_property(element, 'box-flex1', context.model.get('_flex1'));
42 set_flex_property(element, 'box-flex2', context.model.get('_flex2'));
42 set_flex_property(element, 'box-flex2', context.model.get('_flex2'));
43 };
43 };
44
44
45
45
46
46
47 var ContainerModel = IPython.WidgetModel.extend({});
47 var ContainerModel = IPython.WidgetModel.extend({});
48 widget_manager.register_widget_model('ContainerWidgetModel', ContainerModel);
48 widget_manager.register_widget_model('ContainerWidgetModel', ContainerModel);
49
49
50 var ContainerView = IPython.WidgetView.extend({
50 var ContainerView = IPython.WidgetView.extend({
51
51
52 render: function(){
52 render: function(){
53 this.$el
53 this.$el
54 .addClass('widget-container');
54 .addClass('widget-container');
55 var children = this.model.get('children');
55 var children = this.model.get('children');
56 for(var i in children) {
56 for(var i in children) {
57 var view = this.child_view(children[i]);
57 var view = this.child_view(children[i]);
58 this.$el.append(view.$el);
58 this.$el.append(view.$el);
59 }
59 }
60 this.update()
60 this.update()
61 },
61 },
62
62
63 update: function(){
63 update: function(){
64 set_flex_properties(this, this.$el);
64 set_flex_properties(this, this.$el);
65 return IPython.WidgetView.prototype.update.call(this);
65 return IPython.WidgetView.prototype.update.call(this);
66 },
66 },
67 });
67 });
68
68
69 widget_manager.register_widget_view('ContainerView', ContainerView);
69 widget_manager.register_widget_view('ContainerView', ContainerView);
70
70
71
71
72 var ModalView = IPython.WidgetView.extend({
72 var ModalView = IPython.WidgetView.extend({
73
73
74 render: function(){
74 render: function(){
75 var that = this;
75 var that = this;
76 this.$el
76 this.$el
77 .html('')
77 .html('')
78 .on("remove", function(){
78 .on("remove", function(){
79 that.$window.remove();
79 that.$window.remove();
80 });
80 });
81 this.$window = $('<div />')
81 this.$window = $('<div />')
82 .addClass('modal widget-modal')
82 .addClass('modal widget-modal')
83 .appendTo($('#notebook-container'))
83 .appendTo($('#notebook-container'))
84 .mousedown(function(){
84 .mousedown(function(){
85 that.bring_to_front();
85 that.bring_to_front();
86 });
86 });
87 this.$title_bar = $('<div />')
87 this.$title_bar = $('<div />')
88 .addClass('popover-title')
88 .addClass('popover-title')
89 .appendTo(this.$window)
89 .appendTo(this.$window)
90 .mousedown(function(){
90 .mousedown(function(){
91 that.bring_to_front();
91 that.bring_to_front();
92 });
92 });
93 this.$close = $('<button />')
93 this.$close = $('<button />')
94 .addClass('close icon-remove')
94 .addClass('close icon-remove')
95 .css('margin-left', '5px')
95 .css('margin-left', '5px')
96 .appendTo(this.$title_bar)
96 .appendTo(this.$title_bar)
97 .click(function(){
97 .click(function(){
98 that.hide();
98 that.hide();
99 event.stopPropagation();
99 event.stopPropagation();
100 });
100 });
101 this.$minimize = $('<button />')
101 this.$minimize = $('<button />')
102 .addClass('close icon-arrow-down')
102 .addClass('close icon-arrow-down')
103 .appendTo(this.$title_bar)
103 .appendTo(this.$title_bar)
104 .click(function(){
104 .click(function(){
105 that.popped_out = !that.popped_out;
105 that.popped_out = !that.popped_out;
106 if (!that.popped_out) {
106 if (!that.popped_out) {
107 that.$minimize
107 that.$minimize
108 .removeClass('icon-arrow-down')
108 .removeClass('icon-arrow-down')
109 .addClass('icon-arrow-up');
109 .addClass('icon-arrow-up');
110
110
111 that.$window
111 that.$window
112 .draggable('destroy')
112 .draggable('destroy')
113 .resizable('destroy')
113 .resizable('destroy')
114 .removeClass('widget-modal modal')
114 .removeClass('widget-modal modal')
115 .addClass('docked-widget-modal')
115 .addClass('docked-widget-modal')
116 .detach()
116 .detach()
117 .insertBefore(that.$show_button);
117 .insertBefore(that.$show_button);
118 that.$show_button.hide();
118 that.$show_button.hide();
119 that.$close.hide();
119 that.$close.hide();
120 } else {
120 } else {
121 that.$minimize
121 that.$minimize
122 .addClass('icon-arrow-down')
122 .addClass('icon-arrow-down')
123 .removeClass('icon-arrow-up');
123 .removeClass('icon-arrow-up');
124
124
125 that.$window
125 that.$window
126 .removeClass('docked-widget-modal')
126 .removeClass('docked-widget-modal')
127 .addClass('widget-modal modal')
127 .addClass('widget-modal modal')
128 .detach()
128 .detach()
129 .appendTo($('#notebook-container'))
129 .appendTo($('#notebook-container'))
130 .draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'})
130 .draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'})
131 .resizable()
131 .resizable()
132 .children('.ui-resizable-handle').show();
132 .children('.ui-resizable-handle').show();
133 that.show();
133 that.show();
134 that.$show_button.show();
134 that.$show_button.show();
135 that.$close.show();
135 that.$close.show();
136 }
136 }
137 event.stopPropagation();
137 event.stopPropagation();
138 });
138 });
139 this.$title = $('<div />')
139 this.$title = $('<div />')
140 .addClass('widget-modal-title')
140 .addClass('widget-modal-title')
141 .html('&nbsp;')
141 .html('&nbsp;')
142 .appendTo(this.$title_bar);
142 .appendTo(this.$title_bar);
143 this.$body = $('<div />')
143 this.$body = $('<div />')
144 .addClass('modal-body')
144 .addClass('modal-body')
145 .addClass('widget-modal-body')
145 .addClass('widget-modal-body')
146 .addClass('widget-container')
146 .addClass('widget-container')
147 .appendTo(this.$window);
147 .appendTo(this.$window);
148
148
149 this.$show_button = $('<button />')
149 this.$show_button = $('<button />')
150 .html('&nbsp;')
150 .html('&nbsp;')
151 .addClass('btn btn-info widget-modal-show')
151 .addClass('btn btn-info widget-modal-show')
152 .appendTo(this.$el)
152 .appendTo(this.$el)
153 .click(function(){
153 .click(function(){
154 that.show();
154 that.show();
155 });
155 });
156
156
157 this.$window.draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'});
157 this.$window.draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'});
158 this.$window.resizable();
158 this.$window.resizable();
159 this.$window.on('resize', function(){
159 this.$window.on('resize', function(){
160 that.$body.outerHeight(that.$window.innerHeight() - that.$title_bar.outerHeight());
160 that.$body.outerHeight(that.$window.innerHeight() - that.$title_bar.outerHeight());
161 });
161 });
162
162
163 this.$el_to_style = this.$body;
163 this.$el_to_style = this.$body;
164 this._shown_once = false;
164 this._shown_once = false;
165 this.popped_out = true;
165 this.popped_out = true;
166 },
166 },
167
167
168 hide: function() {
168 hide: function() {
169 this.$window.hide();
169 this.$window.hide();
170 this.$show_button.removeClass('btn-info');
170 this.$show_button.removeClass('btn-info');
171 },
171 },
172
172
173 show: function() {
173 show: function() {
174 this.$show_button.addClass('btn-info');
174 this.$show_button.addClass('btn-info');
175
175
176 this.$window.show();
176 this.$window.show();
177 if (this.popped_out) {
177 if (this.popped_out) {
178 this.$window.css("positon", "absolute");
178 this.$window.css("positon", "absolute");
179 this.$window.css("top", "0px");
179 this.$window.css("top", "0px");
180 this.$window.css("left", Math.max(0, (($('body').outerWidth() - this.$window.outerWidth()) / 2) +
180 this.$window.css("left", Math.max(0, (($('body').outerWidth() - this.$window.outerWidth()) / 2) +
181 $(window).scrollLeft()) + "px");
181 $(window).scrollLeft()) + "px");
182 this.bring_to_front();
182 this.bring_to_front();
183 }
183 }
184 },
184 },
185
185
186 bring_to_front: function() {
186 bring_to_front: function() {
187 var $widget_modals = $(".widget-modal");
187 var $widget_modals = $(".widget-modal");
188 var max_zindex = 0;
188 var max_zindex = 0;
189 $widget_modals.each(function (index, el){
189 $widget_modals.each(function (index, el){
190 max_zindex = Math.max(max_zindex, parseInt($(el).css('z-index')));
190 max_zindex = Math.max(max_zindex, parseInt($(el).css('z-index')));
191 });
191 });
192
192
193 // Start z-index of widget modals at 2000
193 // Start z-index of widget modals at 2000
194 max_zindex = Math.max(max_zindex, 2000);
194 max_zindex = Math.max(max_zindex, 2000);
195
195
196 $widget_modals.each(function (index, el){
196 $widget_modals.each(function (index, el){
197 $el = $(el);
197 $el = $(el);
198 if (max_zindex == parseInt($el.css('z-index'))) {
198 if (max_zindex == parseInt($el.css('z-index'))) {
199 $el.css('z-index', max_zindex - 1);
199 $el.css('z-index', max_zindex - 1);
200 }
200 }
201 });
201 });
202 this.$window.css('z-index', max_zindex);
202 this.$window.css('z-index', max_zindex);
203 },
203 },
204
204
205 update: function(){
205 update: function(){
206 set_flex_properties(this, this.$body);
206 set_flex_properties(this, this.$body);
207
207
208 var description = this.model.get('description');
208 var description = this.model.get('description');
209 description = description.replace(/ /g, '&nbsp;', 'm');
209 description = description.replace(/ /g, '&nbsp;', 'm');
210 description = description.replace(/\n/g, '<br>\n', 'm');
210 description = description.replace(/\n/g, '<br>\n', 'm');
211 if (description.length === 0) {
211 if (description.length === 0) {
212 this.$title.html('&nbsp;'); // Preserve title height
212 this.$title.html('&nbsp;'); // Preserve title height
213 } else {
213 } else {
214 this.$title.html(description);
214 this.$title.html(description);
215 }
215 }
216
216
217 var button_text = this.model.get('button_text');
217 var button_text = this.model.get('button_text');
218 button_text = button_text.replace(/ /g, '&nbsp;', 'm');
218 button_text = button_text.replace(/ /g, '&nbsp;', 'm');
219 button_text = button_text.replace(/\n/g, '<br>\n', 'm');
219 button_text = button_text.replace(/\n/g, '<br>\n', 'm');
220 if (button_text.length === 0) {
220 if (button_text.length === 0) {
221 this.$show_button.html('&nbsp;'); // Preserve button height
221 this.$show_button.html('&nbsp;'); // Preserve button height
222 } else {
222 } else {
223 this.$show_button.html(button_text);
223 this.$show_button.html(button_text);
224 }
224 }
225
225
226 if (!this._shown_once) {
226 if (!this._shown_once) {
227 this._shown_once = true;
227 this._shown_once = true;
228 this.show();
228 this.show();
229 }
229 }
230
230
231 return IPython.WidgetView.prototype.update.call(this);
231 return IPython.WidgetView.prototype.update.call(this);
232 },
232 },
233
234 add_child_view: function(attr, view) {
235 if (attr==='children') {
236 this.$body.append(view.$el);
237 }
238 },
239
233
240 _get_selector_element: function(selector) {
234 _get_selector_element: function(selector) {
241
235
242 // Since the modal actually isn't within the $el in the DOM, we need to extend
236 // Since the modal actually isn't within the $el in the DOM, we need to extend
243 // the selector logic to allow the user to set css on the modal if need be.
237 // the selector logic to allow the user to set css on the modal if need be.
244 // The convention used is:
238 // The convention used is:
245 // "modal" - select the modal div
239 // "modal" - select the modal div
246 // "modal [selector]" - select element(s) within the modal div.
240 // "modal [selector]" - select element(s) within the modal div.
247 // "[selector]" - select elements within $el
241 // "[selector]" - select elements within $el
248 // "" - select the $el_to_style
242 // "" - select the $el_to_style
249 if (selector.substring(0, 5) == 'modal') {
243 if (selector.substring(0, 5) == 'modal') {
250 if (selector == 'modal') {
244 if (selector == 'modal') {
251 return this.$window;
245 return this.$window;
252 } else {
246 } else {
253 return this.$window.find(selector.substring(6));
247 return this.$window.find(selector.substring(6));
254 }
248 }
255 } else {
249 } else {
256 return IPython.WidgetView.prototype._get_selector_element.call(this, selector);
250 return IPython.WidgetView.prototype._get_selector_element.call(this, selector);
257 }
251 }
258 },
252 },
259
253
260 });
254 });
261
255
262 widget_manager.register_widget_view('ModalView', ModalView);
256 widget_manager.register_widget_view('ModalView', ModalView);
263 });
257 });
@@ -1,257 +1,255 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // FloatRangeWidget
9 // FloatRangeWidget
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 **/
15 **/
16
16
17 define(["notebook/js/widgets/base"], function(widget_manager){
17 define(["notebook/js/widgets/base"], function(widget_manager){
18 var FloatRangeWidgetModel = IPython.WidgetModel.extend({});
18 var FloatRangeWidgetModel = IPython.WidgetModel.extend({});
19 widget_manager.register_widget_model('FloatRangeWidgetModel', FloatRangeWidgetModel);
19 widget_manager.register_widget_model('FloatRangeWidgetModel', FloatRangeWidgetModel);
20
20
21 var FloatSliderView = IPython.WidgetView.extend({
21 var FloatSliderView = IPython.WidgetView.extend({
22
22
23 // Called when view is rendered.
23 // Called when view is rendered.
24 render : function(){
24 render : function(){
25 this.$el
25 this.$el
26 .addClass('widget-hbox-single')
26 .addClass('widget-hbox-single')
27 .html('');
27 .html('');
28 this.$label = $('<div />')
28 this.$label = $('<div />')
29 .appendTo(this.$el)
29 .appendTo(this.$el)
30 .addClass('widget-hlabel')
30 .addClass('widget-hlabel')
31 .hide();
31 .hide();
32 this.$slider = $('<div />')
32 this.$slider = $('<div />')
33 .slider({})
33 .slider({})
34 .addClass('slider');
34 .addClass('slider');
35
35
36 // Put the slider in a container
36 // Put the slider in a container
37 this.$slider_container = $('<div />')
37 this.$slider_container = $('<div />')
38 .addClass('widget-hslider')
38 .addClass('widget-hslider')
39 .append(this.$slider);
39 .append(this.$slider);
40 this.$el_to_style = this.$slider_container; // Set default element to style
40 this.$el_to_style = this.$slider_container; // Set default element to style
41 this.$el.append(this.$slider_container);
41 this.$el.append(this.$slider_container);
42
42
43 // Set defaults.
43 // Set defaults.
44 this.update();
44 this.update();
45 },
45 },
46
46
47 // Handles: Backend -> Frontend Sync
47 // Handles: Backend -> Frontend Sync
48 // Frontent -> Frontend Sync
48 // Frontent -> Frontend Sync
49 update : function(){
49 update : function(){
50 // Slider related keys.
50 // Slider related keys.
51 var _keys = ['step', 'max', 'min', 'disabled'];
51 var _keys = ['step', 'max', 'min', 'disabled'];
52 for (var index in _keys) {
52 for (var index in _keys) {
53 var key = _keys[index];
53 var key = _keys[index];
54 if (this.model.get(key) !== undefined) {
54 if (this.model.get(key) !== undefined) {
55 this.$slider.slider("option", key, this.model.get(key));
55 this.$slider.slider("option", key, this.model.get(key));
56 }
56 }
57 }
57 }
58
58
59 // WORKAROUND FOR JQUERY SLIDER BUG.
59 // WORKAROUND FOR JQUERY SLIDER BUG.
60 // The horizontal position of the slider handle
60 // The horizontal position of the slider handle
61 // depends on the value of the slider at the time
61 // depends on the value of the slider at the time
62 // of orientation change. Before applying the new
62 // of orientation change. Before applying the new
63 // workaround, we set the value to the minimum to
63 // workaround, we set the value to the minimum to
64 // make sure that the horizontal placement of the
64 // make sure that the horizontal placement of the
65 // handle in the vertical slider is always
65 // handle in the vertical slider is always
66 // consistent.
66 // consistent.
67 var orientation = this.model.get('orientation');
67 var orientation = this.model.get('orientation');
68 var value = this.model.get('min');
68 var value = this.model.get('min');
69 this.$slider.slider('option', 'value', value);
69 this.$slider.slider('option', 'value', value);
70 this.$slider.slider('option', 'orientation', orientation);
70 this.$slider.slider('option', 'orientation', orientation);
71 value = this.model.get('value');
71 value = this.model.get('value');
72 this.$slider.slider('option', 'value', value);
72 this.$slider.slider('option', 'value', value);
73 console.log('updating value',value)
74
73
75 // Use the right CSS classes for vertical & horizontal sliders
74 // Use the right CSS classes for vertical & horizontal sliders
76 if (orientation=='vertical') {
75 if (orientation=='vertical') {
77 this.$slider_container
76 this.$slider_container
78 .removeClass('widget-hslider')
77 .removeClass('widget-hslider')
79 .addClass('widget-vslider');
78 .addClass('widget-vslider');
80 this.$el
79 this.$el
81 .removeClass('widget-hbox-single')
80 .removeClass('widget-hbox-single')
82 .addClass('widget-vbox-single');
81 .addClass('widget-vbox-single');
83 this.$label
82 this.$label
84 .removeClass('widget-hlabel')
83 .removeClass('widget-hlabel')
85 .addClass('widget-vlabel');
84 .addClass('widget-vlabel');
86
85
87 } else {
86 } else {
88 this.$slider_container
87 this.$slider_container
89 .removeClass('widget-vslider')
88 .removeClass('widget-vslider')
90 .addClass('widget-hslider');
89 .addClass('widget-hslider');
91 this.$el
90 this.$el
92 .removeClass('widget-vbox-single')
91 .removeClass('widget-vbox-single')
93 .addClass('widget-hbox-single');
92 .addClass('widget-hbox-single');
94 this.$label
93 this.$label
95 .removeClass('widget-vlabel')
94 .removeClass('widget-vlabel')
96 .addClass('widget-hlabel');
95 .addClass('widget-hlabel');
97 }
96 }
98
97
99 var description = this.model.get('description');
98 var description = this.model.get('description');
100 if (description.length === 0) {
99 if (description.length === 0) {
101 this.$label.hide();
100 this.$label.hide();
102 } else {
101 } else {
103 this.$label.html(description);
102 this.$label.html(description);
104 this.$label.show();
103 this.$label.show();
105 }
104 }
106 return IPython.WidgetView.prototype.update.call(this);
105 return IPython.WidgetView.prototype.update.call(this);
107 },
106 },
108
107
109 // Handles: User input
108 // Handles: User input
110 events: { "slide" : "handleSliderChange" },
109 events: { "slide" : "handleSliderChange" },
111 handleSliderChange: function(e, ui) {
110 handleSliderChange: function(e, ui) {
112 this.model.set('value', ui.value);
111 this.model.set('value', ui.value);
113 console.log('triggered value change', ui.value, this.model);
114 this.touch();
112 this.touch();
115 },
113 },
116 });
114 });
117
115
118 widget_manager.register_widget_view('FloatSliderView', FloatSliderView);
116 widget_manager.register_widget_view('FloatSliderView', FloatSliderView);
119
117
120
118
121 var FloatTextView = IPython.WidgetView.extend({
119 var FloatTextView = IPython.WidgetView.extend({
122
120
123 // Called when view is rendered.
121 // Called when view is rendered.
124 render : function(){
122 render : function(){
125 this.$el
123 this.$el
126 .addClass('widget-hbox-single')
124 .addClass('widget-hbox-single')
127 .html('');
125 .html('');
128 this.$label = $('<div />')
126 this.$label = $('<div />')
129 .appendTo(this.$el)
127 .appendTo(this.$el)
130 .addClass('widget-hlabel')
128 .addClass('widget-hlabel')
131 .hide();
129 .hide();
132 this.$textbox = $('<input type="text" />')
130 this.$textbox = $('<input type="text" />')
133 .addClass('input')
131 .addClass('input')
134 .addClass('widget-numeric-text')
132 .addClass('widget-numeric-text')
135 .appendTo(this.$el);
133 .appendTo(this.$el);
136 this.$el_to_style = this.$textbox; // Set default element to style
134 this.$el_to_style = this.$textbox; // Set default element to style
137 this.update(); // Set defaults.
135 this.update(); // Set defaults.
138 },
136 },
139
137
140 // Handles: Backend -> Frontend Sync
138 // Handles: Backend -> Frontend Sync
141 // Frontent -> Frontend Sync
139 // Frontent -> Frontend Sync
142 update : function(){
140 update : function(){
143 var value = this.model.get('value');
141 var value = this.model.get('value');
144 if (!this.changing && parseFloat(this.$textbox.val()) != value) {
142 if (!this.changing && parseFloat(this.$textbox.val()) != value) {
145 this.$textbox.val(value);
143 this.$textbox.val(value);
146 }
144 }
147
145
148 if (this.model.get('disabled')) {
146 if (this.model.get('disabled')) {
149 this.$textbox.attr('disabled','disabled');
147 this.$textbox.attr('disabled','disabled');
150 } else {
148 } else {
151 this.$textbox.removeAttr('disabled');
149 this.$textbox.removeAttr('disabled');
152 }
150 }
153
151
154 var description = this.model.get('description');
152 var description = this.model.get('description');
155 if (description.length === 0) {
153 if (description.length === 0) {
156 this.$label.hide();
154 this.$label.hide();
157 } else {
155 } else {
158 this.$label.html(description);
156 this.$label.html(description);
159 this.$label.show();
157 this.$label.show();
160 }
158 }
161 return IPython.WidgetView.prototype.update.call(this);
159 return IPython.WidgetView.prototype.update.call(this);
162 },
160 },
163
161
164
162
165 events: {"keyup input" : "handleChanging",
163 events: {"keyup input" : "handleChanging",
166 "paste input" : "handleChanging",
164 "paste input" : "handleChanging",
167 "cut input" : "handleChanging",
165 "cut input" : "handleChanging",
168 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
166 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
169
167
170 // Handles and validates user input.
168 // Handles and validates user input.
171 handleChanging: function(e) {
169 handleChanging: function(e) {
172
170
173 // Try to parse value as a float.
171 // Try to parse value as a float.
174 var numericalValue = 0.0;
172 var numericalValue = 0.0;
175 if (e.target.value !== '') {
173 if (e.target.value !== '') {
176 numericalValue = parseFloat(e.target.value);
174 numericalValue = parseFloat(e.target.value);
177 }
175 }
178
176
179 // If parse failed, reset value to value stored in model.
177 // If parse failed, reset value to value stored in model.
180 if (isNaN(numericalValue)) {
178 if (isNaN(numericalValue)) {
181 e.target.value = this.model.get('value');
179 e.target.value = this.model.get('value');
182 } else if (!isNaN(numericalValue)) {
180 } else if (!isNaN(numericalValue)) {
183 if (this.model.get('max') !== undefined) {
181 if (this.model.get('max') !== undefined) {
184 numericalValue = Math.min(this.model.get('max'), numericalValue);
182 numericalValue = Math.min(this.model.get('max'), numericalValue);
185 }
183 }
186 if (this.model.get('min') !== undefined) {
184 if (this.model.get('min') !== undefined) {
187 numericalValue = Math.max(this.model.get('min'), numericalValue);
185 numericalValue = Math.max(this.model.get('min'), numericalValue);
188 }
186 }
189
187
190 // Apply the value if it has changed.
188 // Apply the value if it has changed.
191 if (numericalValue != this.model.get('value')) {
189 if (numericalValue != this.model.get('value')) {
192 this.changing = true;
190 this.changing = true;
193 this.model.set('value', numericalValue);
191 this.model.set('value', numericalValue);
194 this.touch();
192 this.touch();
195 this.changing = false;
193 this.changing = false;
196 }
194 }
197 }
195 }
198 },
196 },
199
197
200 // Applies validated input.
198 // Applies validated input.
201 handleChanged: function(e) {
199 handleChanged: function(e) {
202 // Update the textbox
200 // Update the textbox
203 if (this.model.get('value') != e.target.value) {
201 if (this.model.get('value') != e.target.value) {
204 e.target.value = this.model.get('value');
202 e.target.value = this.model.get('value');
205 }
203 }
206 }
204 }
207 });
205 });
208
206
209 widget_manager.register_widget_view('FloatTextView', FloatTextView);
207 widget_manager.register_widget_view('FloatTextView', FloatTextView);
210
208
211
209
212 var ProgressView = IPython.WidgetView.extend({
210 var ProgressView = IPython.WidgetView.extend({
213
211
214 // Called when view is rendered.
212 // Called when view is rendered.
215 render : function(){
213 render : function(){
216 this.$el
214 this.$el
217 .addClass('widget-hbox-single')
215 .addClass('widget-hbox-single')
218 .html('');
216 .html('');
219 this.$label = $('<div />')
217 this.$label = $('<div />')
220 .appendTo(this.$el)
218 .appendTo(this.$el)
221 .addClass('widget-hlabel')
219 .addClass('widget-hlabel')
222 .hide();
220 .hide();
223 this.$progress = $('<div />')
221 this.$progress = $('<div />')
224 .addClass('progress')
222 .addClass('progress')
225 .addClass('widget-progress')
223 .addClass('widget-progress')
226 .appendTo(this.$el);
224 .appendTo(this.$el);
227 this.$el_to_style = this.$progress; // Set default element to style
225 this.$el_to_style = this.$progress; // Set default element to style
228 this.$bar = $('<div />')
226 this.$bar = $('<div />')
229 .addClass('bar')
227 .addClass('bar')
230 .css('width', '50%')
228 .css('width', '50%')
231 .appendTo(this.$progress);
229 .appendTo(this.$progress);
232 this.update(); // Set defaults.
230 this.update(); // Set defaults.
233 },
231 },
234
232
235 // Handles: Backend -> Frontend Sync
233 // Handles: Backend -> Frontend Sync
236 // Frontent -> Frontend Sync
234 // Frontent -> Frontend Sync
237 update : function(){
235 update : function(){
238 var value = this.model.get('value');
236 var value = this.model.get('value');
239 var max = this.model.get('max');
237 var max = this.model.get('max');
240 var min = this.model.get('min');
238 var min = this.model.get('min');
241 var percent = 100.0 * (value - min) / (max - min);
239 var percent = 100.0 * (value - min) / (max - min);
242 this.$bar.css('width', percent + '%');
240 this.$bar.css('width', percent + '%');
243
241
244 var description = this.model.get('description');
242 var description = this.model.get('description');
245 if (description.length === 0) {
243 if (description.length === 0) {
246 this.$label.hide();
244 this.$label.hide();
247 } else {
245 } else {
248 this.$label.html(description);
246 this.$label.html(description);
249 this.$label.show();
247 this.$label.show();
250 }
248 }
251 return IPython.WidgetView.prototype.update.call(this);
249 return IPython.WidgetView.prototype.update.call(this);
252 },
250 },
253
251
254 });
252 });
255
253
256 widget_manager.register_widget_view('ProgressView', ProgressView);
254 widget_manager.register_widget_view('ProgressView', ProgressView);
257 });
255 });
@@ -1,181 +1,180 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // MultiContainerWidget
9 // MultiContainerWidget
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 **/
15 **/
16
16
17 define(["notebook/js/widgets/base"], function(widget_manager){
17 define(["notebook/js/widgets/base"], function(widget_manager){
18 var MulticontainerModel = IPython.WidgetModel.extend({});
18 var MulticontainerModel = IPython.WidgetModel.extend({});
19 widget_manager.register_widget_model('MulticontainerWidgetModel', MulticontainerModel);
19 widget_manager.register_widget_model('MulticontainerWidgetModel', MulticontainerModel);
20
20
21 var AccordionView = IPython.WidgetView.extend({
21 var AccordionView = IPython.WidgetView.extend({
22
22
23 render: function(){
23 render: function(){
24 var guid = 'accordion' + IPython.utils.uuid();
24 var guid = 'accordion' + IPython.utils.uuid();
25 this.$el
25 this.$el
26 .attr('id', guid)
26 .attr('id', guid)
27 .addClass('accordion');
27 .addClass('accordion');
28 this.containers = [];
28 this.containers = [];
29 },
29 },
30 update: function() {
30 update: function() {
31 // Set tab titles
31 // Set tab titles
32 var titles = this.model.get('_titles');
32 var titles = this.model.get('_titles');
33 for (var page_index in titles) {
33 for (var page_index in titles) {
34
34
35 var accordian = this.containers[page_index];
35 var accordian = this.containers[page_index];
36 if (accordian !== undefined) {
36 if (accordian !== undefined) {
37 accordian
37 accordian
38 .find('.accordion-heading')
38 .find('.accordion-heading')
39 .find('.accordion-toggle')
39 .find('.accordion-toggle')
40 .html(titles[page_index]);
40 .html(titles[page_index]);
41 }
41 }
42 }
42 }
43
43
44 // Set selected page
44 // Set selected page
45 var selected_index = this.model.get("selected_index");
45 var selected_index = this.model.get("selected_index");
46 if (0 <= selected_index && selected_index < this.containers.length) {
46 if (0 <= selected_index && selected_index < this.containers.length) {
47 for (var index in this.containers) {
47 for (var index in this.containers) {
48 if (index==selected_index) {
48 if (index==selected_index) {
49 this.containers[index].find('.accordion-body').collapse('show');
49 this.containers[index].find('.accordion-body').collapse('show');
50 } else {
50 } else {
51 this.containers[index].find('.accordion-body').collapse('hide');
51 this.containers[index].find('.accordion-body').collapse('hide');
52 }
52 }
53
53
54 }
54 }
55 }
55 }
56
56
57 return IPython.WidgetView.prototype.update.call(this);
57 return IPython.WidgetView.prototype.update.call(this);
58 },
58 },
59
59
60 add_child_view: function(attr, view) {
60 add_child_view: function(attr, view) {
61
61
62 var index = this.containers.length;
62 var index = this.containers.length;
63 var uuid = IPython.utils.uuid();
63 var uuid = IPython.utils.uuid();
64 var accordion_group = $('<div />')
64 var accordion_group = $('<div />')
65 .addClass('accordion-group')
65 .addClass('accordion-group')
66 .appendTo(this.$el);
66 .appendTo(this.$el);
67 var accordion_heading = $('<div />')
67 var accordion_heading = $('<div />')
68 .addClass('accordion-heading')
68 .addClass('accordion-heading')
69 .appendTo(accordion_group);
69 .appendTo(accordion_group);
70 var that = this;
70 var that = this;
71 var accordion_toggle = $('<a />')
71 var accordion_toggle = $('<a />')
72 .addClass('accordion-toggle')
72 .addClass('accordion-toggle')
73 .attr('data-toggle', 'collapse')
73 .attr('data-toggle', 'collapse')
74 .attr('data-parent', '#' + this.$el.attr('id'))
74 .attr('data-parent', '#' + this.$el.attr('id'))
75 .attr('href', '#' + uuid)
75 .attr('href', '#' + uuid)
76 .click(function(evt){
76 .click(function(evt){
77 that.model.set("selected_index", index);
77 that.model.set("selected_index", index);
78 that.touch();
78 that.touch();
79 })
79 })
80 .html('Page ' + index)
80 .html('Page ' + index)
81 .appendTo(accordion_heading);
81 .appendTo(accordion_heading);
82 var accordion_body = $('<div />', {id: uuid})
82 var accordion_body = $('<div />', {id: uuid})
83 .addClass('accordion-body collapse')
83 .addClass('accordion-body collapse')
84 .appendTo(accordion_group);
84 .appendTo(accordion_group);
85 var accordion_inner = $('<div />')
85 var accordion_inner = $('<div />')
86 .addClass('accordion-inner')
86 .addClass('accordion-inner')
87 .appendTo(accordion_body);
87 .appendTo(accordion_body);
88 this.containers.push(accordion_group);
88 this.containers.push(accordion_group);
89 accordion_inner.append(view.$el);
89 accordion_inner.append(view.$el);
90
90
91 this.update();
91 this.update();
92
92
93 // Stupid workaround to close the bootstrap accordion tabs which
93 // Stupid workaround to close the bootstrap accordion tabs which
94 // open by default even though they don't have the `in` class
94 // open by default even though they don't have the `in` class
95 // attached to them. For some reason a delay is required.
95 // attached to them. For some reason a delay is required.
96 // TODO: Better fix.
96 // TODO: Better fix.
97 setTimeout(function(){ that.update(); }, 500);
97 setTimeout(function(){ that.update(); }, 500);
98 },
98 },
99 });
99 });
100
100
101 widget_manager.register_widget_view('AccordionView', AccordionView);
101 widget_manager.register_widget_view('AccordionView', AccordionView);
102
102
103 var TabView = IPython.WidgetView.extend({
103 var TabView = IPython.WidgetView.extend({
104
104
105 initialize: function() {
105 initialize: function() {
106 this.containers = [];
106 this.containers = [];
107 IPython.WidgetView.prototype.initialize.apply(this, arguments);
107 IPython.WidgetView.prototype.initialize.apply(this, arguments);
108 },
108 },
109
109
110 render: function(){
110 render: function(){
111 console.log('rendering tabs', this);
112 var uuid = 'tabs'+IPython.utils.uuid();
111 var uuid = 'tabs'+IPython.utils.uuid();
113 var that = this;
112 var that = this;
114 this.$tabs = $('<div />', {id: uuid})
113 this.$tabs = $('<div />', {id: uuid})
115 .addClass('nav')
114 .addClass('nav')
116 .addClass('nav-tabs')
115 .addClass('nav-tabs')
117 .appendTo(this.$el);
116 .appendTo(this.$el);
118 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
117 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
119 .addClass('tab-content')
118 .addClass('tab-content')
120 .appendTo(this.$el);
119 .appendTo(this.$el);
121 var children = this.model.get('children');
120 var children = this.model.get('children');
122 for (var i in children) {
121 for (var i in children) {
123 this.add_child_view(this.child_view(children[i]))
122 this.add_child_view(this.child_view(children[i]))
124 }
123 }
125 this.update();
124 this.update();
126 },
125 },
127
126
128 update: function() {
127 update: function() {
129 // Set tab titles
128 // Set tab titles
130 var titles = this.model.get('_titles');
129 var titles = this.model.get('_titles');
131 for (var page_index in titles) {
130 for (var page_index in titles) {
132 var tab_text = this.containers[page_index];
131 var tab_text = this.containers[page_index];
133 if (tab_text !== undefined) {
132 if (tab_text !== undefined) {
134 tab_text.html(titles[page_index]);
133 tab_text.html(titles[page_index]);
135 }
134 }
136 }
135 }
137
136
138 var selected_index = this.model.get('selected_index');
137 var selected_index = this.model.get('selected_index');
139 if (0 <= selected_index && selected_index < this.containers.length) {
138 if (0 <= selected_index && selected_index < this.containers.length) {
140 this.select_page(selected_index);
139 this.select_page(selected_index);
141 }
140 }
142
141
143 return IPython.WidgetView.prototype.update.call(this);
142 return IPython.WidgetView.prototype.update.call(this);
144 },
143 },
145
144
146 add_child_view: function(view) {
145 add_child_view: function(view) {
147 var index = this.containers.length;
146 var index = this.containers.length;
148 var uuid = IPython.utils.uuid();
147 var uuid = IPython.utils.uuid();
149
148
150 var that = this;
149 var that = this;
151 var tab = $('<li />')
150 var tab = $('<li />')
152 .css('list-style-type', 'none')
151 .css('list-style-type', 'none')
153 .appendTo(this.$tabs);
152 .appendTo(this.$tabs);
154 var tab_text = $('<a />')
153 var tab_text = $('<a />')
155 .attr('href', '#' + uuid)
154 .attr('href', '#' + uuid)
156 .attr('data-toggle', 'tab')
155 .attr('data-toggle', 'tab')
157 .html('Page ' + index)
156 .html('Page ' + index)
158 .appendTo(tab)
157 .appendTo(tab)
159 .click(function (e) {
158 .click(function (e) {
160 that.model.set("selected_index", index);
159 that.model.set("selected_index", index);
161 that.touch();
160 that.touch();
162 that.select_page(index);
161 that.select_page(index);
163 });
162 });
164 this.containers.push(tab_text);
163 this.containers.push(tab_text);
165
164
166 var contents_div = $('<div />', {id: uuid})
165 var contents_div = $('<div />', {id: uuid})
167 .addClass('tab-pane')
166 .addClass('tab-pane')
168 .addClass('fade')
167 .addClass('fade')
169 .append(view.$el)
168 .append(view.$el)
170 .appendTo(this.$tab_contents);
169 .appendTo(this.$tab_contents);
171 },
170 },
172
171
173 select_page: function(index) {
172 select_page: function(index) {
174 this.$tabs.find('li')
173 this.$tabs.find('li')
175 .removeClass('active');
174 .removeClass('active');
176 this.containers[index].tab('show');
175 this.containers[index].tab('show');
177 },
176 },
178 });
177 });
179
178
180 widget_manager.register_widget_view('TabView', TabView);
179 widget_manager.register_widget_view('TabView', TabView);
181 });
180 });
@@ -1,441 +1,438 b''
1 """Base Widget class. Allows user to create widgets in the backend that render
1 """Base Widget class. Allows user to create widgets in the backend that render
2 in the IPython notebook frontend.
2 in the IPython notebook frontend.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
5 # Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 from copy import copy
15 from copy import copy
16 from glob import glob
16 from glob import glob
17 import uuid
17 import uuid
18 import sys
18 import sys
19 import os
19 import os
20 import inspect
20 import inspect
21 import types
21 import types
22
22
23 import IPython
23 import IPython
24 from IPython.kernel.comm import Comm
24 from IPython.kernel.comm import Comm
25 from IPython.config import LoggingConfigurable
25 from IPython.config import LoggingConfigurable
26 from IPython.utils.traitlets import Unicode, Dict, List, Instance, Bool
26 from IPython.utils.traitlets import Unicode, Dict, List, Instance, Bool
27 from IPython.display import Javascript, display
27 from IPython.display import Javascript, display
28 from IPython.utils.py3compat import string_types
28 from IPython.utils.py3compat import string_types
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Classes
31 # Classes
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 class BaseWidget(LoggingConfigurable):
34 class BaseWidget(LoggingConfigurable):
35
35
36 # Shared declarations (Class level)
36 # Shared declarations (Class level)
37 _keys = List(Unicode, default_value = [],
37 _keys = List(Unicode, default_value = [],
38 help="List of keys comprising the state of the model.", allow_none=False)
38 help="List of keys comprising the state of the model.", allow_none=False)
39 widget_construction_callback = None
39 widget_construction_callback = None
40
40
41 def on_widget_constructed(callback):
41 def on_widget_constructed(callback):
42 """Class method, registers a callback to be called when a widget is
42 """Class method, registers a callback to be called when a widget is
43 constructed. The callback must have the following signature:
43 constructed. The callback must have the following signature:
44 callback(widget)"""
44 callback(widget)"""
45 Widget.widget_construction_callback = callback
45 Widget.widget_construction_callback = callback
46
46
47 def _handle_widget_constructed(widget):
47 def _handle_widget_constructed(widget):
48 """Class method, called when a widget is constructed."""
48 """Class method, called when a widget is constructed."""
49 if Widget.widget_construction_callback is not None and callable(Widget.widget_construction_callback):
49 if Widget.widget_construction_callback is not None and callable(Widget.widget_construction_callback):
50 Widget.widget_construction_callback(widget)
50 Widget.widget_construction_callback(widget)
51
51
52
52
53
53
54 # Public declarations (Instance level)
54 # Public declarations (Instance level)
55 target_name = Unicode('widget', help="""Name of the backbone model
55 target_name = Unicode('widget', help="""Name of the backbone model
56 registered in the frontend to create and sync this widget with.""")
56 registered in the frontend to create and sync this widget with.""")
57 default_view_name = Unicode(help="""Default view registered in the frontend
57 default_view_name = Unicode(help="""Default view registered in the frontend
58 to use to represent the widget.""")
58 to use to represent the widget.""")
59
59
60 # Private/protected declarations
60 # Private/protected declarations
61 # todo: change this to a context manager
61 # todo: change this to a context manager
62 _property_lock = (None, None) # Last updated (key, value) from the front-end. Prevents echo.
62 _property_lock = (None, None) # Last updated (key, value) from the front-end. Prevents echo.
63 _displayed = False
63 _displayed = False
64 _comm = Instance('IPython.kernel.comm.Comm')
64 _comm = Instance('IPython.kernel.comm.Comm')
65
65
66 def __init__(self, **kwargs):
66 def __init__(self, **kwargs):
67 """Public constructor
67 """Public constructor
68 """
68 """
69 self._display_callbacks = []
69 self._display_callbacks = []
70 self._msg_callbacks = []
70 self._msg_callbacks = []
71 super(BaseWidget, self).__init__(**kwargs)
71 super(BaseWidget, self).__init__(**kwargs)
72
72
73 # Register after init to allow default values to be specified
74 # TODO: register three different handlers, one for each list, and abstract out the common parts
75 #print self.keys, self._children_attr, self._children_lists_attr
76 self.on_trait_change(self._handle_property_changed, self.keys)
73 self.on_trait_change(self._handle_property_changed, self.keys)
77 Widget._handle_widget_constructed(self)
74 Widget._handle_widget_constructed(self)
78
75
79 def __del__(self):
76 def __del__(self):
80 """Object disposal"""
77 """Object disposal"""
81 self.close()
78 self.close()
82
79
83
80
84 def close(self):
81 def close(self):
85 """Close method. Closes the widget which closes the underlying comm.
82 """Close method. Closes the widget which closes the underlying comm.
86 When the comm is closed, all of the widget views are automatically
83 When the comm is closed, all of the widget views are automatically
87 removed from the frontend."""
84 removed from the frontend."""
88 self._close_communication()
85 self._close_communication()
89
86
90
87
91 # Properties
88 # Properties
92 @property
89 @property
93 def keys(self):
90 def keys(self):
94 keys = ['default_view_name']
91 keys = ['default_view_name']
95 keys.extend(self._keys)
92 keys.extend(self._keys)
96 return keys
93 return keys
97
94
98 @property
95 @property
99 def comm(self):
96 def comm(self):
100 if self._comm is None:
97 if self._comm is None:
101 self._open_communication()
98 self._open_communication()
102 return self._comm
99 return self._comm
103
100
104 # Event handlers
101 # Event handlers
105 def _handle_msg(self, msg):
102 def _handle_msg(self, msg):
106 """Called when a msg is recieved from the frontend"""
103 """Called when a msg is recieved from the frontend"""
107 data = msg['content']['data']
104 data = msg['content']['data']
108 method = data['method']
105 method = data['method']
109
106
110 # Handle backbone sync methods CREATE, PATCH, and UPDATE
107 # Handle backbone sync methods CREATE, PATCH, and UPDATE
111 if method == 'backbone':
108 if method == 'backbone':
112 if 'sync_method' in data and 'sync_data' in data:
109 if 'sync_method' in data and 'sync_data' in data:
113 sync_method = data['sync_method']
110 sync_method = data['sync_method']
114 sync_data = data['sync_data']
111 sync_data = data['sync_data']
115 self._handle_recieve_state(sync_data) # handles all methods
112 self._handle_recieve_state(sync_data) # handles all methods
116
113
117 # Handle a custom msg from the front-end
114 # Handle a custom msg from the front-end
118 elif method == 'custom':
115 elif method == 'custom':
119 if 'custom_content' in data:
116 if 'custom_content' in data:
120 self._handle_custom_msg(data['custom_content'])
117 self._handle_custom_msg(data['custom_content'])
121
118
122 def _handle_custom_msg(self, content):
119 def _handle_custom_msg(self, content):
123 """Called when a custom msg is recieved."""
120 """Called when a custom msg is recieved."""
124 for handler in self._msg_callbacks:
121 for handler in self._msg_callbacks:
125 if callable(handler):
122 if callable(handler):
126 argspec = inspect.getargspec(handler)
123 argspec = inspect.getargspec(handler)
127 nargs = len(argspec[0])
124 nargs = len(argspec[0])
128
125
129 # Bound methods have an additional 'self' argument
126 # Bound methods have an additional 'self' argument
130 if isinstance(handler, types.MethodType):
127 if isinstance(handler, types.MethodType):
131 nargs -= 1
128 nargs -= 1
132
129
133 # Call the callback
130 # Call the callback
134 if nargs == 1:
131 if nargs == 1:
135 handler(content)
132 handler(content)
136 elif nargs == 2:
133 elif nargs == 2:
137 handler(self, content)
134 handler(self, content)
138 else:
135 else:
139 raise TypeError('Widget msg callback must ' \
136 raise TypeError('Widget msg callback must ' \
140 'accept 1 or 2 arguments, not %d.' % nargs)
137 'accept 1 or 2 arguments, not %d.' % nargs)
141
138
142
139
143 def _handle_recieve_state(self, sync_data):
140 def _handle_recieve_state(self, sync_data):
144 """Called when a state is recieved from the frontend."""
141 """Called when a state is recieved from the frontend."""
145 # Use _keys instead of keys - Don't get retrieve the css from the client side.
142 # Use _keys instead of keys - Don't get retrieve the css from the client side.
146 for name in self._keys:
143 for name in self._keys:
147 if name in sync_data:
144 if name in sync_data:
148 try:
145 try:
149 self._property_lock = (name, sync_data[name])
146 self._property_lock = (name, sync_data[name])
150 setattr(self, name, sync_data[name])
147 setattr(self, name, sync_data[name])
151 finally:
148 finally:
152 self._property_lock = (None, None)
149 self._property_lock = (None, None)
153
150
154
151
155 def _handle_property_changed(self, name, old, new):
152 def _handle_property_changed(self, name, old, new):
156 """Called when a property has been changed."""
153 """Called when a property has been changed."""
157 # Make sure this isn't information that the front-end just sent us.
154 # Make sure this isn't information that the front-end just sent us.
158 if self._property_lock[0] != name and self._property_lock[1] != new:
155 if self._property_lock[0] != name and self._property_lock[1] != new:
159 # Send new state to frontend
156 # Send new state to frontend
160 self.send_state(key=name)
157 self.send_state(key=name)
161
158
162 def _handle_displayed(self, **kwargs):
159 def _handle_displayed(self, **kwargs):
163 """Called when a view has been displayed for this widget instance
160 """Called when a view has been displayed for this widget instance
164
161
165 Parameters
162 Parameters
166 ----------
163 ----------
167 [view_name]: unicode (optional kwarg)
164 [view_name]: unicode (optional kwarg)
168 Name of the view that was displayed."""
165 Name of the view that was displayed."""
169 for handler in self._display_callbacks:
166 for handler in self._display_callbacks:
170 if callable(handler):
167 if callable(handler):
171 argspec = inspect.getargspec(handler)
168 argspec = inspect.getargspec(handler)
172 nargs = len(argspec[0])
169 nargs = len(argspec[0])
173
170
174 # Bound methods have an additional 'self' argument
171 # Bound methods have an additional 'self' argument
175 if isinstance(handler, types.MethodType):
172 if isinstance(handler, types.MethodType):
176 nargs -= 1
173 nargs -= 1
177
174
178 # Call the callback
175 # Call the callback
179 if nargs == 0:
176 if nargs == 0:
180 handler()
177 handler()
181 elif nargs == 1:
178 elif nargs == 1:
182 handler(self)
179 handler(self)
183 elif nargs == 2:
180 elif nargs == 2:
184 handler(self, kwargs.get('view_name', None))
181 handler(self, kwargs.get('view_name', None))
185 else:
182 else:
186 handler(self, **kwargs)
183 handler(self, **kwargs)
187
184
188 # Public methods
185 # Public methods
189 def send_state(self, key=None):
186 def send_state(self, key=None):
190 """Sends the widget state, or a piece of it, to the frontend.
187 """Sends the widget state, or a piece of it, to the frontend.
191
188
192 Parameters
189 Parameters
193 ----------
190 ----------
194 key : unicode (optional)
191 key : unicode (optional)
195 A single property's name to sync with the frontend.
192 A single property's name to sync with the frontend.
196 """
193 """
197 self._send({"method": "update",
194 self._send({"method": "update",
198 "state": self.get_state()})
195 "state": self.get_state()})
199
196
200 def get_state(self, key=None):
197 def get_state(self, key=None):
201 """Gets the widget state, or a piece of it.
198 """Gets the widget state, or a piece of it.
202
199
203 Parameters
200 Parameters
204 ----------
201 ----------
205 key : unicode (optional)
202 key : unicode (optional)
206 A single property's name to get.
203 A single property's name to get.
207 """
204 """
208 state = {}
205 state = {}
209
206
210 # If a key is provided, just send the state of that key.
207 # If a key is provided, just send the state of that key.
211 if key is None:
208 if key is None:
212 keys = self.keys[:]
209 keys = self.keys[:]
213 else:
210 else:
214 keys = [key]
211 keys = [key]
215 for k in keys:
212 for k in keys:
216 value = getattr(self, k)
213 value = getattr(self, k)
217
214
218 # a more elegant solution to encoding BaseWidgets would be
215 # a more elegant solution to encoding BaseWidgets would be
219 # to tap into the JSON encoder and teach it how to deal
216 # to tap into the JSON encoder and teach it how to deal
220 # with BaseWidget objects, or maybe just teach the JSON
217 # with BaseWidget objects, or maybe just teach the JSON
221 # encoder to look for a _repr_json property before giving
218 # encoder to look for a _repr_json property before giving
222 # up encoding
219 # up encoding
223 if isinstance(value, BaseWidget):
220 if isinstance(value, BaseWidget):
224 value = value.comm.comm_id
221 value = value.comm.comm_id
225 elif isinstance(value, list) and isinstance(value[0], BaseWidget):
222 elif isinstance(value, list) and isinstance(value[0], BaseWidget):
226 # assume all elements of the list are widgets
223 # assume all elements of the list are widgets
227 value = [i.comm.comm_id for i in value]
224 value = [i.comm.comm_id for i in value]
228 state[k] = value
225 state[k] = value
229 return state
226 return state
230
227
231
228
232 def send(self, content):
229 def send(self, content):
233 """Sends a custom msg to the widget model in the front-end.
230 """Sends a custom msg to the widget model in the front-end.
234
231
235 Parameters
232 Parameters
236 ----------
233 ----------
237 content : dict
234 content : dict
238 Content of the message to send.
235 Content of the message to send.
239 """
236 """
240 self._send({"method": "custom",
237 self._send({"method": "custom",
241 "custom_content": content})
238 "custom_content": content})
242
239
243
240
244 def on_msg(self, callback, remove=False):
241 def on_msg(self, callback, remove=False):
245 """Register a callback for when a custom msg is recieved from the front-end
242 """Register a callback for when a custom msg is recieved from the front-end
246
243
247 Parameters
244 Parameters
248 ----------
245 ----------
249 callback: method handler
246 callback: method handler
250 Can have a signature of:
247 Can have a signature of:
251 - callback(content)
248 - callback(content)
252 - callback(sender, content)
249 - callback(sender, content)
253 remove: bool
250 remove: bool
254 True if the callback should be unregistered."""
251 True if the callback should be unregistered."""
255 if remove and callback in self._msg_callbacks:
252 if remove and callback in self._msg_callbacks:
256 self._msg_callbacks.remove(callback)
253 self._msg_callbacks.remove(callback)
257 elif not remove and not callback in self._msg_callbacks:
254 elif not remove and not callback in self._msg_callbacks:
258 self._msg_callbacks.append(callback)
255 self._msg_callbacks.append(callback)
259
256
260
257
261 def on_displayed(self, callback, remove=False):
258 def on_displayed(self, callback, remove=False):
262 """Register a callback to be called when the widget has been displayed
259 """Register a callback to be called when the widget has been displayed
263
260
264 Parameters
261 Parameters
265 ----------
262 ----------
266 callback: method handler
263 callback: method handler
267 Can have a signature of:
264 Can have a signature of:
268 - callback()
265 - callback()
269 - callback(sender)
266 - callback(sender)
270 - callback(sender, view_name)
267 - callback(sender, view_name)
271 - callback(sender, **kwargs)
268 - callback(sender, **kwargs)
272 kwargs from display call passed through without modification.
269 kwargs from display call passed through without modification.
273 remove: bool
270 remove: bool
274 True if the callback should be unregistered."""
271 True if the callback should be unregistered."""
275 if remove and callback in self._display_callbacks:
272 if remove and callback in self._display_callbacks:
276 self._display_callbacks.remove(callback)
273 self._display_callbacks.remove(callback)
277 elif not remove and not callback in self._display_callbacks:
274 elif not remove and not callback in self._display_callbacks:
278 self._display_callbacks.append(callback)
275 self._display_callbacks.append(callback)
279
276
280
277
281 # Support methods
278 # Support methods
282 def _repr_widget_(self, **kwargs):
279 def _repr_widget_(self, **kwargs):
283 """Function that is called when `IPython.display.display` is called on
280 """Function that is called when `IPython.display.display` is called on
284 the widget.
281 the widget.
285
282
286 Parameters
283 Parameters
287 ----------
284 ----------
288 view_name: unicode (optional)
285 view_name: unicode (optional)
289 View to display in the frontend. Overrides default_view_name."""
286 View to display in the frontend. Overrides default_view_name."""
290 view_name = kwargs.get('view_name', self.default_view_name)
287 view_name = kwargs.get('view_name', self.default_view_name)
291
288
292 # Create a communication.
289 # Create a communication.
293 self._open_communication()
290 self._open_communication()
294
291
295 # Make sure model is syncronized
292 # Make sure model is syncronized
296 self.send_state()
293 self.send_state()
297
294
298 # Show view.
295 # Show view.
299 self._send({"method": "display", "view_name": view_name})
296 self._send({"method": "display", "view_name": view_name})
300 self._displayed = True
297 self._displayed = True
301 self._handle_displayed(**kwargs)
298 self._handle_displayed(**kwargs)
302
299
303
300
304 def _open_communication(self):
301 def _open_communication(self):
305 """Opens a communication with the front-end."""
302 """Opens a communication with the front-end."""
306 # Create a comm.
303 # Create a comm.
307 if self._comm is None:
304 if self._comm is None:
308 self._comm = Comm(target_name=self.target_name)
305 self._comm = Comm(target_name=self.target_name)
309 self._comm.on_msg(self._handle_msg)
306 self._comm.on_msg(self._handle_msg)
310 self._comm.on_close(self._close_communication)
307 self._comm.on_close(self._close_communication)
311
308
312 # first update
309 # first update
313 self.send_state()
310 self.send_state()
314
311
315
312
316 def _close_communication(self):
313 def _close_communication(self):
317 """Closes a communication with the front-end."""
314 """Closes a communication with the front-end."""
318 if self._comm is not None:
315 if self._comm is not None:
319 try:
316 try:
320 self._comm.close()
317 self._comm.close()
321 finally:
318 finally:
322 self._comm = None
319 self._comm = None
323
320
324
321
325 def _send(self, msg):
322 def _send(self, msg):
326 """Sends a message to the model in the front-end"""
323 """Sends a message to the model in the front-end"""
327 if self._comm is not None:
324 if self._comm is not None:
328 self._comm.send(msg)
325 self._comm.send(msg)
329 return True
326 return True
330 else:
327 else:
331 return False
328 return False
332
329
333 class Widget(BaseWidget):
330 class Widget(BaseWidget):
334 visible = Bool(True, help="Whether or not the widget is visible.")
331 visible = Bool(True, help="Whether or not the widget is visible.")
335
332
336 # Private/protected declarations
333 # Private/protected declarations
337 _css = Dict() # Internal CSS property dict
334 _css = Dict() # Internal CSS property dict
338
335
339 # Properties
336 # Properties
340 @property
337 @property
341 def keys(self):
338 def keys(self):
342 keys = ['visible', '_css']
339 keys = ['visible', '_css']
343 keys.extend(super(Widget, self).keys)
340 keys.extend(super(Widget, self).keys)
344 return keys
341 return keys
345
342
346 def get_css(self, key, selector=""):
343 def get_css(self, key, selector=""):
347 """Get a CSS property of the widget. Note, this function does not
344 """Get a CSS property of the widget. Note, this function does not
348 actually request the CSS from the front-end; Only properties that have
345 actually request the CSS from the front-end; Only properties that have
349 been set with set_css can be read.
346 been set with set_css can be read.
350
347
351 Parameters
348 Parameters
352 ----------
349 ----------
353 key: unicode
350 key: unicode
354 CSS key
351 CSS key
355 selector: unicode (optional)
352 selector: unicode (optional)
356 JQuery selector used when the CSS key/value was set.
353 JQuery selector used when the CSS key/value was set.
357 """
354 """
358 if selector in self._css and key in self._css[selector]:
355 if selector in self._css and key in self._css[selector]:
359 return self._css[selector][key]
356 return self._css[selector][key]
360 else:
357 else:
361 return None
358 return None
362
359
363
360
364 def set_css(self, *args, **kwargs):
361 def set_css(self, *args, **kwargs):
365 """Set one or more CSS properties of the widget (shared among all of the
362 """Set one or more CSS properties of the widget (shared among all of the
366 views). This function has two signatures:
363 views). This function has two signatures:
367 - set_css(css_dict, [selector=''])
364 - set_css(css_dict, [selector=''])
368 - set_css(key, value, [selector=''])
365 - set_css(key, value, [selector=''])
369
366
370 Parameters
367 Parameters
371 ----------
368 ----------
372 css_dict : dict
369 css_dict : dict
373 CSS key/value pairs to apply
370 CSS key/value pairs to apply
374 key: unicode
371 key: unicode
375 CSS key
372 CSS key
376 value
373 value
377 CSS value
374 CSS value
378 selector: unicode (optional)
375 selector: unicode (optional)
379 JQuery selector to use to apply the CSS key/value.
376 JQuery selector to use to apply the CSS key/value.
380 """
377 """
381 selector = kwargs.get('selector', '')
378 selector = kwargs.get('selector', '')
382
379
383 # Signature 1: set_css(css_dict, [selector=''])
380 # Signature 1: set_css(css_dict, [selector=''])
384 if len(args) == 1:
381 if len(args) == 1:
385 if isinstance(args[0], dict):
382 if isinstance(args[0], dict):
386 for (key, value) in args[0].items():
383 for (key, value) in args[0].items():
387 self.set_css(key, value, selector=selector)
384 self.set_css(key, value, selector=selector)
388 else:
385 else:
389 raise Exception('css_dict must be a dict.')
386 raise Exception('css_dict must be a dict.')
390
387
391 # Signature 2: set_css(key, value, [selector=''])
388 # Signature 2: set_css(key, value, [selector=''])
392 elif len(args) == 2 or len(args) == 3:
389 elif len(args) == 2 or len(args) == 3:
393
390
394 # Selector can be a positional arg if it's the 3rd value
391 # Selector can be a positional arg if it's the 3rd value
395 if len(args) == 3:
392 if len(args) == 3:
396 selector = args[2]
393 selector = args[2]
397 if selector not in self._css:
394 if selector not in self._css:
398 self._css[selector] = {}
395 self._css[selector] = {}
399
396
400 # Only update the property if it has changed.
397 # Only update the property if it has changed.
401 key = args[0]
398 key = args[0]
402 value = args[1]
399 value = args[1]
403 if not (key in self._css[selector] and value in self._css[selector][key]):
400 if not (key in self._css[selector] and value in self._css[selector][key]):
404 self._css[selector][key] = value
401 self._css[selector][key] = value
405 self.send_state('_css') # Send new state to client.
402 self.send_state('_css') # Send new state to client.
406 else:
403 else:
407 raise Exception('set_css only accepts 1-3 arguments')
404 raise Exception('set_css only accepts 1-3 arguments')
408
405
409
406
410 def add_class(self, class_name, selector=""):
407 def add_class(self, class_name, selector=""):
411 """Add class[es] to a DOM element
408 """Add class[es] to a DOM element
412
409
413 Parameters
410 Parameters
414 ----------
411 ----------
415 class_name: unicode
412 class_name: unicode
416 Class name(s) to add to the DOM element(s). Multiple class names
413 Class name(s) to add to the DOM element(s). Multiple class names
417 must be space separated.
414 must be space separated.
418 selector: unicode (optional)
415 selector: unicode (optional)
419 JQuery selector to select the DOM element(s) that the class(es) will
416 JQuery selector to select the DOM element(s) that the class(es) will
420 be added to.
417 be added to.
421 """
418 """
422 self._send({"method": "add_class",
419 self._send({"method": "add_class",
423 "class_list": class_name,
420 "class_list": class_name,
424 "selector": selector})
421 "selector": selector})
425
422
426
423
427 def remove_class(self, class_name, selector=""):
424 def remove_class(self, class_name, selector=""):
428 """Remove class[es] from a DOM element
425 """Remove class[es] from a DOM element
429
426
430 Parameters
427 Parameters
431 ----------
428 ----------
432 class_name: unicode
429 class_name: unicode
433 Class name(s) to remove from the DOM element(s). Multiple class
430 Class name(s) to remove from the DOM element(s). Multiple class
434 names must be space separated.
431 names must be space separated.
435 selector: unicode (optional)
432 selector: unicode (optional)
436 JQuery selector to select the DOM element(s) that the class(es) will
433 JQuery selector to select the DOM element(s) that the class(es) will
437 be removed from.
434 be removed from.
438 """
435 """
439 self._send({"method": "remove_class",
436 self._send({"method": "remove_class",
440 "class_list": class_name,
437 "class_list": class_name,
441 "selector": selector})
438 "selector": selector})
@@ -1,203 +1,202 b''
1 """ContainerWidget class.
1 """ContainerWidget class.
2
2
3 Represents a container that can be used to group other widgets.
3 Represents a container that can be used to group other widgets.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from .widget import Widget
16 from .widget import Widget
17 from IPython.utils.traitlets import Unicode, Bool, List, Instance
17 from IPython.utils.traitlets import Unicode, Bool, List, Instance
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Classes
20 # Classes
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 class ContainerWidget(Widget):
22 class ContainerWidget(Widget):
23 target_name = Unicode('ContainerWidgetModel')
23 target_name = Unicode('ContainerWidgetModel')
24 default_view_name = Unicode('ContainerView')
24 default_view_name = Unicode('ContainerView')
25
25
26 children = []#List(Instance('IPython.html.widgets.widget.Widget'))
26 children = [] #List(Instance('IPython.html.widgets.widget.Widget'))
27 _children_lists_attr = List(Unicode, ['children'])
28
27
29 # Keys, all private and managed by helper methods. Flexible box model
28 # Keys, all private and managed by helper methods. Flexible box model
30 # classes...
29 # classes...
31 _keys = ['_vbox', '_hbox', '_align_start', '_align_end', '_align_center',
30 _keys = ['_vbox', '_hbox', '_align_start', '_align_end', '_align_center',
32 '_pack_start', '_pack_end', '_pack_center', '_flex0', '_flex1',
31 '_pack_start', '_pack_end', '_pack_center', '_flex0', '_flex1',
33 '_flex2', 'description', 'button_text']
32 '_flex2', 'description', 'button_text']
34 description = Unicode()
33 description = Unicode()
35 button_text = Unicode()
34 button_text = Unicode()
36 _hbox = Bool(False)
35 _hbox = Bool(False)
37 _vbox = Bool(False)
36 _vbox = Bool(False)
38 _align_start = Bool(False)
37 _align_start = Bool(False)
39 _align_end = Bool(False)
38 _align_end = Bool(False)
40 _align_center = Bool(False)
39 _align_center = Bool(False)
41 _pack_start = Bool(False)
40 _pack_start = Bool(False)
42 _pack_end = Bool(False)
41 _pack_end = Bool(False)
43 _pack_center = Bool(False)
42 _pack_center = Bool(False)
44 _flex0 = Bool(False)
43 _flex0 = Bool(False)
45 _flex1 = Bool(False)
44 _flex1 = Bool(False)
46 _flex2 = Bool(False)
45 _flex2 = Bool(False)
47
46
48 def hbox(self, enabled=True):
47 def hbox(self, enabled=True):
49 """Make this container an hbox. Automatically disables conflicting
48 """Make this container an hbox. Automatically disables conflicting
50 features.
49 features.
51
50
52 Parameters
51 Parameters
53 ----------
52 ----------
54 enabled: bool (optional)
53 enabled: bool (optional)
55 Enabled or disable the hbox feature of the container, defaults to
54 Enabled or disable the hbox feature of the container, defaults to
56 True."""
55 True."""
57 self._hbox = enabled
56 self._hbox = enabled
58 if enabled:
57 if enabled:
59 self._vbox = False
58 self._vbox = False
60
59
61 def vbox(self, enabled=True):
60 def vbox(self, enabled=True):
62 """Make this container an vbox. Automatically disables conflicting
61 """Make this container an vbox. Automatically disables conflicting
63 features.
62 features.
64
63
65 Parameters
64 Parameters
66 ----------
65 ----------
67 enabled: bool (optional)
66 enabled: bool (optional)
68 Enabled or disable the vbox feature of the container, defaults to
67 Enabled or disable the vbox feature of the container, defaults to
69 True."""
68 True."""
70 self._vbox = enabled
69 self._vbox = enabled
71 if enabled:
70 if enabled:
72 self._hbox = False
71 self._hbox = False
73
72
74 def align_start(self, enabled=True):
73 def align_start(self, enabled=True):
75 """Make the contents of this container align to the start of the axis.
74 """Make the contents of this container align to the start of the axis.
76 Automatically disables conflicting alignments.
75 Automatically disables conflicting alignments.
77
76
78 Parameters
77 Parameters
79 ----------
78 ----------
80 enabled: bool (optional)
79 enabled: bool (optional)
81 Enabled or disable the start alignment of the container, defaults to
80 Enabled or disable the start alignment of the container, defaults to
82 True."""
81 True."""
83 self._align_start = enabled
82 self._align_start = enabled
84 if enabled:
83 if enabled:
85 self._align_end = False
84 self._align_end = False
86 self._align_center = False
85 self._align_center = False
87
86
88 def align_end(self, enabled=True):
87 def align_end(self, enabled=True):
89 """Make the contents of this container align to the end of the axis.
88 """Make the contents of this container align to the end of the axis.
90 Automatically disables conflicting alignments.
89 Automatically disables conflicting alignments.
91
90
92 Parameters
91 Parameters
93 ----------
92 ----------
94 enabled: bool (optional)
93 enabled: bool (optional)
95 Enabled or disable the end alignment of the container, defaults to
94 Enabled or disable the end alignment of the container, defaults to
96 True."""
95 True."""
97 self._align_end = enabled
96 self._align_end = enabled
98 if enabled:
97 if enabled:
99 self._align_start = False
98 self._align_start = False
100 self._align_center = False
99 self._align_center = False
101
100
102 def align_center(self, enabled=True):
101 def align_center(self, enabled=True):
103 """Make the contents of this container align to the center of the axis.
102 """Make the contents of this container align to the center of the axis.
104 Automatically disables conflicting alignments.
103 Automatically disables conflicting alignments.
105
104
106 Parameters
105 Parameters
107 ----------
106 ----------
108 enabled: bool (optional)
107 enabled: bool (optional)
109 Enabled or disable the center alignment of the container, defaults to
108 Enabled or disable the center alignment of the container, defaults to
110 True."""
109 True."""
111 self._align_center = enabled
110 self._align_center = enabled
112 if enabled:
111 if enabled:
113 self._align_start = False
112 self._align_start = False
114 self._align_end = False
113 self._align_end = False
115
114
116
115
117 def pack_start(self, enabled=True):
116 def pack_start(self, enabled=True):
118 """Make the contents of this container pack to the start of the axis.
117 """Make the contents of this container pack to the start of the axis.
119 Automatically disables conflicting packings.
118 Automatically disables conflicting packings.
120
119
121 Parameters
120 Parameters
122 ----------
121 ----------
123 enabled: bool (optional)
122 enabled: bool (optional)
124 Enabled or disable the start packing of the container, defaults to
123 Enabled or disable the start packing of the container, defaults to
125 True."""
124 True."""
126 self._pack_start = enabled
125 self._pack_start = enabled
127 if enabled:
126 if enabled:
128 self._pack_end = False
127 self._pack_end = False
129 self._pack_center = False
128 self._pack_center = False
130
129
131 def pack_end(self, enabled=True):
130 def pack_end(self, enabled=True):
132 """Make the contents of this container pack to the end of the axis.
131 """Make the contents of this container pack to the end of the axis.
133 Automatically disables conflicting packings.
132 Automatically disables conflicting packings.
134
133
135 Parameters
134 Parameters
136 ----------
135 ----------
137 enabled: bool (optional)
136 enabled: bool (optional)
138 Enabled or disable the end packing of the container, defaults to
137 Enabled or disable the end packing of the container, defaults to
139 True."""
138 True."""
140 self._pack_end = enabled
139 self._pack_end = enabled
141 if enabled:
140 if enabled:
142 self._pack_start = False
141 self._pack_start = False
143 self._pack_center = False
142 self._pack_center = False
144
143
145 def pack_center(self, enabled=True):
144 def pack_center(self, enabled=True):
146 """Make the contents of this container pack to the center of the axis.
145 """Make the contents of this container pack to the center of the axis.
147 Automatically disables conflicting packings.
146 Automatically disables conflicting packings.
148
147
149 Parameters
148 Parameters
150 ----------
149 ----------
151 enabled: bool (optional)
150 enabled: bool (optional)
152 Enabled or disable the center packing of the container, defaults to
151 Enabled or disable the center packing of the container, defaults to
153 True."""
152 True."""
154 self._pack_center = enabled
153 self._pack_center = enabled
155 if enabled:
154 if enabled:
156 self._pack_start = False
155 self._pack_start = False
157 self._pack_end = False
156 self._pack_end = False
158
157
159
158
160 def flex0(self, enabled=True):
159 def flex0(self, enabled=True):
161 """Put this container in flex0 mode. Automatically disables conflicting
160 """Put this container in flex0 mode. Automatically disables conflicting
162 flex modes. See the widget tutorial part 5 example notebook for more
161 flex modes. See the widget tutorial part 5 example notebook for more
163 information.
162 information.
164
163
165 Parameters
164 Parameters
166 ----------
165 ----------
167 enabled: bool (optional)
166 enabled: bool (optional)
168 Enabled or disable the flex0 attribute of the container, defaults to
167 Enabled or disable the flex0 attribute of the container, defaults to
169 True."""
168 True."""
170 self._flex0 = enabled
169 self._flex0 = enabled
171 if enabled:
170 if enabled:
172 self._flex1 = False
171 self._flex1 = False
173 self._flex2 = False
172 self._flex2 = False
174
173
175 def flex1(self, enabled=True):
174 def flex1(self, enabled=True):
176 """Put this container in flex1 mode. Automatically disables conflicting
175 """Put this container in flex1 mode. Automatically disables conflicting
177 flex modes. See the widget tutorial part 5 example notebook for more
176 flex modes. See the widget tutorial part 5 example notebook for more
178 information.
177 information.
179
178
180 Parameters
179 Parameters
181 ----------
180 ----------
182 enabled: bool (optional)
181 enabled: bool (optional)
183 Enabled or disable the flex1 attribute of the container, defaults to
182 Enabled or disable the flex1 attribute of the container, defaults to
184 True."""
183 True."""
185 self._flex1 = enabled
184 self._flex1 = enabled
186 if enabled:
185 if enabled:
187 self._flex0 = False
186 self._flex0 = False
188 self._flex2 = False
187 self._flex2 = False
189
188
190 def flex2(self, enabled=True):
189 def flex2(self, enabled=True):
191 """Put this container in flex2 mode. Automatically disables conflicting
190 """Put this container in flex2 mode. Automatically disables conflicting
192 flex modes. See the widget tutorial part 5 example notebook for more
191 flex modes. See the widget tutorial part 5 example notebook for more
193 information.
192 information.
194
193
195 Parameters
194 Parameters
196 ----------
195 ----------
197 enabled: bool (optional)
196 enabled: bool (optional)
198 Enabled or disable the flex2 attribute of the container, defaults to
197 Enabled or disable the flex2 attribute of the container, defaults to
199 True."""
198 True."""
200 self._flex2 = enabled
199 self._flex2 = enabled
201 if enabled:
200 if enabled:
202 self._flex0 = False
201 self._flex0 = False
203 self._flex1 = False
202 self._flex1 = False
@@ -1,59 +1,58 b''
1 """MulticontainerWidget class.
1 """MulticontainerWidget class.
2
2
3 Represents a multipage container that can be used to group other widgets into
3 Represents a multipage container that can be used to group other widgets into
4 pages.
4 pages.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (c) 2013, the IPython Development Team.
7 # Copyright (c) 2013, the IPython Development Team.
8 #
8 #
9 # Distributed under the terms of the Modified BSD License.
9 # Distributed under the terms of the Modified BSD License.
10 #
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 from .widget import Widget
17 from .widget import Widget
18 from IPython.utils.traitlets import Unicode, Dict, Int, List, Instance
18 from IPython.utils.traitlets import Unicode, Dict, Int, List, Instance
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 class MulticontainerWidget(Widget):
23 class MulticontainerWidget(Widget):
24 target_name = Unicode('MulticontainerWidgetModel')
24 target_name = Unicode('MulticontainerWidgetModel')
25 default_view_name = Unicode('TabView')
25 default_view_name = Unicode('TabView')
26
26
27 # Keys
27 # Keys
28 _keys = ['_titles', 'selected_index']
28 _keys = ['_titles', 'selected_index']
29 _titles = Dict(help="Titles of the pages")
29 _titles = Dict(help="Titles of the pages")
30 selected_index = Int(0)
30 selected_index = Int(0)
31
31
32 children = []#List(Instance('IPython.html.widgets.widget.Widget'))
32 children = [] #List(Instance('IPython.html.widgets.widget.Widget'))
33 _children_lists_attr = List(Unicode, ['children'])
34
33
35 # Public methods
34 # Public methods
36 def set_title(self, index, title):
35 def set_title(self, index, title):
37 """Sets the title of a container page
36 """Sets the title of a container page
38
37
39 Parameters
38 Parameters
40 ----------
39 ----------
41 index : int
40 index : int
42 Index of the container page
41 Index of the container page
43 title : unicode
42 title : unicode
44 New title"""
43 New title"""
45 self._titles[index] = title
44 self._titles[index] = title
46 self.send_state('_titles')
45 self.send_state('_titles')
47
46
48
47
49 def get_title(self, index):
48 def get_title(self, index):
50 """Gets the title of a container pages
49 """Gets the title of a container pages
51
50
52 Parameters
51 Parameters
53 ----------
52 ----------
54 index : int
53 index : int
55 Index of the container page"""
54 Index of the container page"""
56 if index in self._titles:
55 if index in self._titles:
57 return self._titles[index]
56 return self._titles[index]
58 else:
57 else:
59 return None
58 return None
General Comments 0
You need to be logged in to leave comments. Login now