##// END OF EJS Templates
Changed selection widget API to use labels list...
Jonathan Frederic -
Show More
@@ -1,376 +1,376 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // SelectionWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(WidgetManager){
18 18
19 19 var DropdownView = IPython.DOMWidgetView.extend({
20 20 render : function(){
21 21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .appendTo(this.$el)
26 26 .addClass('widget-hlabel')
27 27 .hide();
28 28 this.$buttongroup = $('<div />')
29 29 .addClass('widget_item')
30 30 .addClass('btn-group')
31 31 .appendTo(this.$el);
32 32 this.$el_to_style = this.$buttongroup; // Set default element to style
33 33 this.$droplabel = $('<button />')
34 34 .addClass('btn')
35 35 .addClass('widget-combo-btn')
36 36 .text(' ')
37 37 .appendTo(this.$buttongroup);
38 38 this.$dropbutton = $('<button />')
39 39 .addClass('btn')
40 40 .addClass('dropdown-toggle')
41 41 .addClass('widget-combo-carrot-btn')
42 42 .attr('data-toggle', 'dropdown')
43 43 .append($('<span />').addClass("caret"))
44 44 .appendTo(this.$buttongroup);
45 45 this.$droplist = $('<ul />')
46 46 .addClass('dropdown-menu')
47 47 .appendTo(this.$buttongroup);
48 48
49 49 // Set defaults.
50 50 this.update();
51 51 },
52 52
53 53 update : function(options){
54 54 // Update the contents of this view
55 55 //
56 56 // Called when the model is changed. The model may have been
57 57 // changed by another view or by a state update from the back-end.
58 58
59 59 if (options === undefined || options.updated_view != this) {
60 60 var selected_item_text = this.model.get('_value');
61 61 if (selected_item_text.length === 0) {
62 62 this.$droplabel.text(' ');
63 63 } else {
64 64 this.$droplabel.text(selected_item_text);
65 65 }
66 66
67 var items = this.model.get('_values');
67 var items = this.model.get('labels');
68 68 var $replace_droplist = $('<ul />')
69 69 .addClass('dropdown-menu');
70 70 var that = this;
71 71 _.each(items, function(item, i) {
72 72 var item_button = $('<a href="#"/>')
73 73 .text(item)
74 74 .on('click', $.proxy(that.handle_click, that));
75 75 $replace_droplist.append($('<li />').append(item_button));
76 76 });
77 77
78 78 this.$droplist.replaceWith($replace_droplist);
79 79 this.$droplist.remove();
80 80 this.$droplist = $replace_droplist;
81 81
82 82 if (this.model.get('disabled')) {
83 83 this.$buttongroup.attr('disabled','disabled');
84 84 this.$droplabel.attr('disabled','disabled');
85 85 this.$dropbutton.attr('disabled','disabled');
86 86 this.$droplist.attr('disabled','disabled');
87 87 } else {
88 88 this.$buttongroup.removeAttr('disabled');
89 89 this.$droplabel.removeAttr('disabled');
90 90 this.$dropbutton.removeAttr('disabled');
91 91 this.$droplist.removeAttr('disabled');
92 92 }
93 93
94 94 var description = this.model.get('description');
95 95 if (description.length === 0) {
96 96 this.$label.hide();
97 97 } else {
98 98 this.$label.text(description);
99 99 this.$label.show();
100 100 }
101 101 }
102 102 return DropdownView.__super__.update.apply(this);
103 103 },
104 104
105 105 handle_click: function (e) {
106 106 // Handle when a value is clicked.
107 107
108 108 // Calling model.set will trigger all of the other views of the
109 109 // model to update.
110 110 this.model.set('_value', $(e.target).text(), {updated_view: this});
111 111 this.touch();
112 112 },
113 113
114 114 });
115 115 WidgetManager.register_widget_view('DropdownView', DropdownView);
116 116
117 117
118 118 var RadioButtonsView = IPython.DOMWidgetView.extend({
119 119 render : function(){
120 120 // Called when view is rendered.
121 121 this.$el
122 122 .addClass('widget-hbox');
123 123 this.$label = $('<div />')
124 124 .appendTo(this.$el)
125 125 .addClass('widget-hlabel')
126 126 .hide();
127 127 this.$container = $('<div />')
128 128 .appendTo(this.$el)
129 129 .addClass('widget-container')
130 130 .addClass('vbox');
131 131 this.$el_to_style = this.$container; // Set default element to style
132 132 this.update();
133 133 },
134 134
135 135 update : function(options){
136 136 // Update the contents of this view
137 137 //
138 138 // Called when the model is changed. The model may have been
139 139 // changed by another view or by a state update from the back-end.
140 140 if (options === undefined || options.updated_view != this) {
141 141 // Add missing items to the DOM.
142 var items = this.model.get('_values');
142 var items = this.model.get('labels');
143 143 var disabled = this.model.get('disabled');
144 144 var that = this;
145 145 _.each(items, function(item, index) {
146 146 var item_query = ' :input[value="' + item + '"]';
147 147 if (that.$el.find(item_query).length === 0) {
148 148 var $label = $('<label />')
149 149 .addClass('radio')
150 150 .text(item)
151 151 .appendTo(that.$container);
152 152
153 153 $('<input />')
154 154 .attr('type', 'radio')
155 155 .addClass(that.model)
156 156 .val(item)
157 157 .prependTo($label)
158 158 .on('click', $.proxy(that.handle_click, that));
159 159 }
160 160
161 161 var $item_element = that.$container.find(item_query);
162 162 if (that.model.get('_value') == item) {
163 163 $item_element.prop('checked', true);
164 164 } else {
165 165 $item_element.prop('checked', false);
166 166 }
167 167 $item_element.prop('disabled', disabled);
168 168 });
169 169
170 170 // Remove items that no longer exist.
171 171 this.$container.find('input').each(function(i, obj) {
172 172 var value = $(obj).val();
173 173 var found = false;
174 174 _.each(items, function(item, index) {
175 175 if (item == value) {
176 176 found = true;
177 177 return false;
178 178 }
179 179 });
180 180
181 181 if (!found) {
182 182 $(obj).parent().remove();
183 183 }
184 184 });
185 185
186 186 var description = this.model.get('description');
187 187 if (description.length === 0) {
188 188 this.$label.hide();
189 189 } else {
190 190 this.$label.text(description);
191 191 this.$label.show();
192 192 }
193 193 }
194 194 return RadioButtonsView.__super__.update.apply(this);
195 195 },
196 196
197 197 handle_click: function (e) {
198 198 // Handle when a value is clicked.
199 199
200 200 // Calling model.set will trigger all of the other views of the
201 201 // model to update.
202 202 this.model.set('_value', $(e.target).val(), {updated_view: this});
203 203 this.touch();
204 204 },
205 205 });
206 206 WidgetManager.register_widget_view('RadioButtonsView', RadioButtonsView);
207 207
208 208
209 209 var ToggleButtonsView = IPython.DOMWidgetView.extend({
210 210 render : function(){
211 211 // Called when view is rendered.
212 212 this.$el
213 213 .addClass('widget-hbox-single');
214 214 this.$label = $('<div />')
215 215 .appendTo(this.$el)
216 216 .addClass('widget-hlabel')
217 217 .hide();
218 218 this.$buttongroup = $('<div />')
219 219 .addClass('btn-group')
220 220 .attr('data-toggle', 'buttons-radio')
221 221 .appendTo(this.$el);
222 222 this.$el_to_style = this.$buttongroup; // Set default element to style
223 223 this.update();
224 224 },
225 225
226 226 update : function(options){
227 227 // Update the contents of this view
228 228 //
229 229 // Called when the model is changed. The model may have been
230 230 // changed by another view or by a state update from the back-end.
231 231 if (options === undefined || options.updated_view != this) {
232 232 // Add missing items to the DOM.
233 var items = this.model.get('_values');
233 var items = this.model.get('labels');
234 234 var disabled = this.model.get('disabled');
235 235 var that = this;
236 236 _.each(items, function(item, index) {
237 237 var item_query = ' :contains("' + item + '")';
238 238 if (that.$buttongroup.find(item_query).length === 0) {
239 239 $('<button />')
240 240 .attr('type', 'button')
241 241 .addClass('btn')
242 242 .text(item)
243 243 .appendTo(that.$buttongroup)
244 244 .on('click', $.proxy(that.handle_click, that));
245 245 }
246 246
247 247 var $item_element = that.$buttongroup.find(item_query);
248 248 if (that.model.get('_value') == item) {
249 249 $item_element.addClass('active');
250 250 } else {
251 251 $item_element.removeClass('active');
252 252 }
253 253 $item_element.prop('disabled', disabled);
254 254 });
255 255
256 256 // Remove items that no longer exist.
257 257 this.$buttongroup.find('button').each(function(i, obj) {
258 258 var value = $(obj).text();
259 259 var found = false;
260 260 _.each(items, function(item, index) {
261 261 if (item == value) {
262 262 found = true;
263 263 return false;
264 264 }
265 265 });
266 266
267 267 if (!found) {
268 268 $(obj).remove();
269 269 }
270 270 });
271 271
272 272 var description = this.model.get('description');
273 273 if (description.length === 0) {
274 274 this.$label.hide();
275 275 } else {
276 276 this.$label.text(description);
277 277 this.$label.show();
278 278 }
279 279 }
280 280 return ToggleButtonsView.__super__.update.apply(this);
281 281 },
282 282
283 283 handle_click: function (e) {
284 284 // Handle when a value is clicked.
285 285
286 286 // Calling model.set will trigger all of the other views of the
287 287 // model to update.
288 288 this.model.set('_value', $(e.target).text(), {updated_view: this});
289 289 this.touch();
290 290 },
291 291 });
292 292 WidgetManager.register_widget_view('ToggleButtonsView', ToggleButtonsView);
293 293
294 294
295 295 var ListBoxView = IPython.DOMWidgetView.extend({
296 296 render : function(){
297 297 // Called when view is rendered.
298 298 this.$el
299 299 .addClass('widget-hbox');
300 300 this.$label = $('<div />')
301 301 .appendTo(this.$el)
302 302 .addClass('widget-hlabel')
303 303 .hide();
304 304 this.$listbox = $('<select />')
305 305 .addClass('widget-listbox')
306 306 .attr('size', 6)
307 307 .appendTo(this.$el);
308 308 this.$el_to_style = this.$listbox; // Set default element to style
309 309 this.update();
310 310 },
311 311
312 312 update : function(options){
313 313 // Update the contents of this view
314 314 //
315 315 // Called when the model is changed. The model may have been
316 316 // changed by another view or by a state update from the back-end.
317 317 if (options === undefined || options.updated_view != this) {
318 318 // Add missing items to the DOM.
319 var items = this.model.get('_values');
319 var items = this.model.get('labels');
320 320 var that = this;
321 321 _.each(items, function(item, index) {
322 322 var item_query = ' :contains("' + item + '")';
323 323 if (that.$listbox.find(item_query).length === 0) {
324 324 $('<option />')
325 325 .text(item)
326 326 .attr('_value', item)
327 327 .appendTo(that.$listbox)
328 328 .on('click', $.proxy(that.handle_click, that));
329 329 }
330 330 });
331 331
332 332 // Select the correct element
333 333 this.$listbox.val(this.model.get('_value'));
334 334
335 335 // Disable listbox if needed
336 336 var disabled = this.model.get('disabled');
337 337 this.$listbox.prop('disabled', disabled);
338 338
339 339 // Remove items that no longer exist.
340 340 this.$listbox.find('option').each(function(i, obj) {
341 341 var value = $(obj).text();
342 342 var found = false;
343 343 _.each(items, function(item, index) {
344 344 if (item == value) {
345 345 found = true;
346 346 return false;
347 347 }
348 348 });
349 349
350 350 if (!found) {
351 351 $(obj).remove();
352 352 }
353 353 });
354 354
355 355 var description = this.model.get('description');
356 356 if (description.length === 0) {
357 357 this.$label.hide();
358 358 } else {
359 359 this.$label.text(description);
360 360 this.$label.show();
361 361 }
362 362 }
363 363 return ListBoxView.__super__.update.apply(this);
364 364 },
365 365
366 366 handle_click: function (e) {
367 367 // Handle when a value is clicked.
368 368
369 369 // Calling model.set will trigger all of the other views of the
370 370 // model to update.
371 371 this.model.set('_value', $(e.target).text(), {updated_view: this});
372 372 this.touch();
373 373 },
374 374 });
375 375 WidgetManager.register_widget_view('ListBoxView', ListBoxView);
376 376 });
@@ -1,100 +1,95 b''
1 1 """SelectionWidget class.
2 2
3 3 Represents an enumeration using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from threading import Lock
17 17
18 18 from .widget import DOMWidget
19 19 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # SelectionWidget
23 23 #-----------------------------------------------------------------------------
24 24 class _SelectionWidget(DOMWidget):
25 25 value = Any(help="Selected value")
26 26 values = List(help="List of values the user can select")
27 value_names = Dict(help="""List of string representations for each value.
27 labels = List(help="""List of string representations for each value.
28 28 These string representations are used to display the values in the
29 front-end.""")
29 front-end.""", sync=True) # Only synced to the back-end.
30 30 disabled = Bool(False, help="Enable or disable user changes", sync=True)
31 31 description = Unicode(help="Description of the value this widget represents", sync=True)
32 32
33 33 _value = Unicode(sync=True) # Bi-directionally synced.
34 _values = List(sync=True) # Only back-end to front-end synced.
35 _reverse_value_names = Dict()
36 34
37 35 def __init__(self, *pargs, **kwargs):
38 36 """Constructor"""
39 DOMWidget.__init__(self, *pargs, **kwargs)
40 37 self.value_lock = Lock()
41 38 self.on_trait_change(self._string_value_set, ['_value'])
39 DOMWidget.__init__(self, *pargs, **kwargs)
42 40
43 def _value_names_changed(self, name=None, old=None, new=None):
41 def _labels_changed(self, name=None, old=None, new=None):
44 42 """Handles when the value_names Dict has been changed.
45 43
46 44 This method sets the _reverse_value_names Dict to the inverse of the new
47 45 value for the value_names Dict."""
48 self._reverse_value_names = {v:k for k, v in self.value_names.items()}
49 self._values_changed()
46 if len(new) != len(self.values):
47 raise TypeError('Labels list must be the same size as the values list.')
50 48
51 49 def _values_changed(self, name=None, old=None, new=None):
52 """Called when values has been changed"""
53 self._values = [self._get_string_repr(v) for v in self.values]
50 """Handles when the value_names Dict has been changed.
51
52 This method sets the _reverse_value_names Dict to the inverse of the new
53 value for the value_names Dict."""
54 if len(new) != len(self.labels):
55 self.labels = [(self.labels[i] if i < len(self.labels) else str(v)) for i, v in enumerate(new)]
54 56
55 57 def _value_changed(self, name, old, new):
56 58 """Called when value has been changed"""
57 59 if self.value_lock.acquire(False):
58 60 try:
59 61 # Make sure the value is in the list of values.
60 62 if new in self.values:
61 63 # Set the string version of the value.
62 self._value = self._get_string_repr(new)
64 self._value = self.labels[self.values.index(new)]
63 65 else:
64 66 raise TypeError('Value must be a value in the values list.')
65 67 finally:
66 68 self.value_lock.release()
67 69
68 def _get_string_repr(self, value):
69 """Get the string repr of a value"""
70 if value not in self.value_names:
71 self.value_names[value] = str(value)
72 self._value_names_changed()
73 return self.value_names[value]
74
75 70 def _string_value_set(self, name, old, new):
76 71 """Called when _value has been changed."""
77 72 if self.value_lock.acquire(False):
78 73 try:
79 if new in self._reverse_value_names:
80 self.value = self._reverse_value_names[new]
74 if new in self.labels:
75 self.value = self.values[self.labels.index(new)]
81 76 else:
82 77 self.value = None
83 78 finally:
84 79 self.value_lock.release()
85 80
86 81
87 82 class ToggleButtonsWidget(_SelectionWidget):
88 83 _view_name = Unicode('ToggleButtonsView', sync=True)
89 84
90 85
91 86 class DropdownWidget(_SelectionWidget):
92 87 _view_name = Unicode('DropdownView', sync=True)
93 88
94 89
95 90 class RadioButtonsWidget(_SelectionWidget):
96 91 _view_name = Unicode('RadioButtonsView', sync=True)
97 92
98 93
99 94 class ListBoxWidget(_SelectionWidget):
100 95 _view_name = Unicode('ListBoxView', sync=True)
General Comments 0
You need to be logged in to leave comments. Login now