##// END OF EJS Templates
Merge pull request #7725 from SylvainCorlay/fabutton...
Jonathan Frederic -
r20608:66d38c2d merge
parent child Browse files
Show More
@@ -1,158 +1,157 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 "widgets/js/widget",
5 "widgets/js/widget",
6 "jquery",
6 "jquery",
7 "bootstrap",
7 "bootstrap",
8 ], function(widget, $){
8 ], function(widget, $){
9
9
10 var CheckboxView = widget.DOMWidgetView.extend({
10 var CheckboxView = widget.DOMWidgetView.extend({
11 render : function(){
11 render : function(){
12 /**
12 /**
13 * Called when view is rendered.
13 * Called when view is rendered.
14 */
14 */
15 this.$el
15 this.$el
16 .addClass('widget-hbox widget-checkbox');
16 .addClass('widget-hbox widget-checkbox');
17 this.$label = $('<div />')
17 this.$label = $('<div />')
18 .addClass('widget-label')
18 .addClass('widget-label')
19 .appendTo(this.$el)
19 .appendTo(this.$el)
20 .hide();
20 .hide();
21 this.$checkbox = $('<input />')
21 this.$checkbox = $('<input />')
22 .attr('type', 'checkbox')
22 .attr('type', 'checkbox')
23 .appendTo(this.$el)
23 .appendTo(this.$el)
24 .click($.proxy(this.handle_click, this));
24 .click($.proxy(this.handle_click, this));
25
25
26 this.update(); // Set defaults.
26 this.update(); // Set defaults.
27 },
27 },
28
28
29 update_attr: function(name, value) {
29 update_attr: function(name, value) {
30 /**
30 /**
31 * Set a css attr of the widget view.
31 * Set a css attr of the widget view.
32 */
32 */
33 if (name == 'padding' || name == 'margin') {
33 if (name == 'padding' || name == 'margin') {
34 this.$el.css(name, value);
34 this.$el.css(name, value);
35 } else {
35 } else {
36 this.$checkbox.css(name, value);
36 this.$checkbox.css(name, value);
37 }
37 }
38 },
38 },
39
39
40 handle_click: function() {
40 handle_click: function() {
41 /**
41 /**
42 * Handles when the checkbox is clicked.
42 * Handles when the checkbox is clicked.
43 *
43 *
44 * Calling model.set will trigger all of the other views of the
44 * Calling model.set will trigger all of the other views of the
45 * model to update.
45 * model to update.
46 */
46 */
47 var value = this.model.get('value');
47 var value = this.model.get('value');
48 this.model.set('value', ! value, {updated_view: this});
48 this.model.set('value', ! value, {updated_view: this});
49 this.touch();
49 this.touch();
50 },
50 },
51
51
52 update : function(options){
52 update : function(options){
53 /**
53 /**
54 * Update the contents of this view
54 * Update the contents of this view
55 *
55 *
56 * Called when the model is changed. The model may have been
56 * Called when the model is changed. The model may have been
57 * changed by another view or by a state update from the back-end.
57 * changed by another view or by a state update from the back-end.
58 */
58 */
59 this.$checkbox.prop('checked', this.model.get('value'));
59 this.$checkbox.prop('checked', this.model.get('value'));
60
60
61 if (options === undefined || options.updated_view != this) {
61 if (options === undefined || options.updated_view != this) {
62 var disabled = this.model.get('disabled');
62 this.$checkbox.prop("disabled", this.model.get("disabled"));
63 this.$checkbox.prop('disabled', disabled);
64
63
65 var description = this.model.get('description');
64 var description = this.model.get("description");
66 if (description.trim().length === 0) {
65 if (description.trim().length === 0) {
67 this.$label.hide();
66 this.$label.hide();
68 } else {
67 } else {
69 this.typeset(this.$label, description);
68 this.typeset(this.$label, description);
70 this.$label.show();
69 this.$label.show();
71 }
70 }
72 }
71 }
73 return CheckboxView.__super__.update.apply(this);
72 return CheckboxView.__super__.update.apply(this);
74 },
73 },
75
74
76 });
75 });
77
76
78
77
79 var ToggleButtonView = widget.DOMWidgetView.extend({
78 var ToggleButtonView = widget.DOMWidgetView.extend({
80 render : function() {
79 render : function() {
81 /**
80 /**
82 * Called when view is rendered.
81 * Called when view is rendered.
83 */
82 */
84 var that = this;
83 var that = this;
85 this.setElement($('<button />')
84 this.setElement($('<button />')
86 .addClass('btn btn-default')
85 .addClass('btn btn-default')
87 .attr('type', 'button')
86 .attr('type', 'button')
88 .on('click', function (e) {
87 .on('click', function (e) {
89 e.preventDefault();
88 e.preventDefault();
90 that.handle_click();
89 that.handle_click();
91 }));
90 }));
92 this.$el.attr("data-toggle", "tooltip");
91 this.$el.attr("data-toggle", "tooltip");
93 this.model.on('change:button_style', function(model, value) {
92 this.model.on('change:button_style', function(model, value) {
94 this.update_button_style();
93 this.update_button_style();
95 }, this);
94 }, this);
96 this.update_button_style('');
95 this.update_button_style('');
97
96
98 this.update(); // Set defaults.
97 this.update(); // Set defaults.
99 },
98 },
100
99
101 update_button_style: function(previous_trait_value) {
100 update_button_style: function(previous_trait_value) {
102 var class_map = {
101 var class_map = {
103 primary: ['btn-primary'],
102 primary: ['btn-primary'],
104 success: ['btn-success'],
103 success: ['btn-success'],
105 info: ['btn-info'],
104 info: ['btn-info'],
106 warning: ['btn-warning'],
105 warning: ['btn-warning'],
107 danger: ['btn-danger']
106 danger: ['btn-danger']
108 };
107 };
109 this.update_mapped_classes(class_map, 'button_style', previous_trait_value);
108 this.update_mapped_classes(class_map, 'button_style', previous_trait_value);
110 },
109 },
111
110
112 update : function(options){
111 update : function(options){
113 /**
112 /**
114 * Update the contents of this view
113 * Update the contents of this view
115 *
114 *
116 * Called when the model is changed. The model may have been
115 * Called when the model is changed. The model may have been
117 * changed by another view or by a state update from the back-end.
116 * changed by another view or by a state update from the back-end.
118 */
117 */
119 if (this.model.get('value')) {
118 if (this.model.get('value')) {
120 this.$el.addClass('active');
119 this.$el.addClass('active');
121 } else {
120 } else {
122 this.$el.removeClass('active');
121 this.$el.removeClass('active');
123 }
122 }
124
123
125 if (options === undefined || options.updated_view != this) {
124 if (options === undefined || options.updated_view != this) {
126
125 this.$el.prop("disabled", this.model.get("disabled"));
127 var disabled = this.model.get('disabled');
128 this.$el.prop('disabled', disabled);
129
130 var description = this.model.get('description');
131 this.$el.attr("title", this.model.get("tooltip"));
126 this.$el.attr("title", this.model.get("tooltip"));
132 if (description.trim().length === 0) {
127
128 var description = this.model.get("description");
129 var icon = this.model.get("icon");
130 if (description.trim().length === 0 && icon.trim().length ===0) {
133 this.$el.html("&nbsp;"); // Preserve button height
131 this.$el.html("&nbsp;"); // Preserve button height
134 } else {
132 } else {
135 this.$el.text(description);
133 this.$el.text(description);
134 $('<i class="fa"></i>').prependTo(this.$el).addClass(icon);
136 }
135 }
137 }
136 }
138 return ToggleButtonView.__super__.update.apply(this);
137 return ToggleButtonView.__super__.update.apply(this);
139 },
138 },
140
139
141 handle_click: function(e) {
140 handle_click: function(e) {
142 /**
141 /**
143 * Handles and validates user input.
142 * Handles and validates user input.
144 *
143 *
145 * Calling model.set will trigger all of the other views of the
144 * Calling model.set will trigger all of the other views of the
146 * model to update.
145 * model to update.
147 */
146 */
148 var value = this.model.get('value');
147 var value = this.model.get('value');
149 this.model.set('value', ! value, {updated_view: this});
148 this.model.set('value', ! value, {updated_view: this});
150 this.touch();
149 this.touch();
151 },
150 },
152 });
151 });
153
152
154 return {
153 return {
155 'CheckboxView': CheckboxView,
154 'CheckboxView': CheckboxView,
156 'ToggleButtonView': ToggleButtonView,
155 'ToggleButtonView': ToggleButtonView,
157 };
156 };
158 });
157 });
@@ -1,77 +1,75 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 "widgets/js/widget",
5 "widgets/js/widget",
6 "jquery",
6 "jquery",
7 "bootstrap",
7 "bootstrap",
8 ], function(widget, $){
8 ], function(widget, $){
9
9
10 var ButtonView = widget.DOMWidgetView.extend({
10 var ButtonView = widget.DOMWidgetView.extend({
11 render : function(){
11 render : function(){
12 /**
12 /**
13 * Called when view is rendered.
13 * Called when view is rendered.
14 */
14 */
15 this.setElement($("<button />")
15 this.setElement($("<button />")
16 .addClass('btn btn-default'));
16 .addClass('btn btn-default'));
17 this.$el.attr("data-toggle", "tooltip");
17 this.$el.attr("data-toggle", "tooltip");
18 this.model.on('change:button_style', function(model, value) {
18 this.model.on('change:button_style', function(model, value) {
19 this.update_button_style();
19 this.update_button_style();
20 }, this);
20 }, this);
21 this.update_button_style('');
21 this.update_button_style('');
22
22
23 this.update(); // Set defaults.
23 this.update(); // Set defaults.
24 },
24 },
25
25
26 update : function(){
26 update : function(){
27 /**
27 /**
28 * Update the contents of this view
28 * Update the contents of this view
29 *
29 *
30 * Called when the model is changed. The model may have been
30 * Called when the model is changed. The model may have been
31 * changed by another view or by a state update from the back-end.
31 * changed by another view or by a state update from the back-end.
32 */
32 */
33 var description = this.model.get('description');
33 this.$el.prop("disabled", this.model.get("disabled"));
34 this.$el.attr("title", this.model.get("tooltip"));
34 this.$el.attr("title", this.model.get("tooltip"));
35 if (description.length === 0) {
35
36 var description = this.model.get("description");
37 var icon = this.model.get("icon");
38 if (description.trim().length === 0 && icon.trim().length ===0) {
36 this.$el.html("&nbsp;"); // Preserve button height
39 this.$el.html("&nbsp;"); // Preserve button height
37 } else {
40 } else {
38 this.$el.text(description);
41 this.$el.text(description);
39 }
42 $('<i class="fa"></i>').prependTo(this.$el).addClass(icon);
40
41 if (this.model.get('disabled')) {
42 this.$el.attr('disabled','disabled');
43 } else {
44 this.$el.removeAttr('disabled');
45 }
43 }
46
44
47 return ButtonView.__super__.update.apply(this);
45 return ButtonView.__super__.update.apply(this);
48 },
46 },
49
47
50 update_button_style: function(previous_trait_value) {
48 update_button_style: function(previous_trait_value) {
51 var class_map = {
49 var class_map = {
52 primary: ['btn-primary'],
50 primary: ['btn-primary'],
53 success: ['btn-success'],
51 success: ['btn-success'],
54 info: ['btn-info'],
52 info: ['btn-info'],
55 warning: ['btn-warning'],
53 warning: ['btn-warning'],
56 danger: ['btn-danger']
54 danger: ['btn-danger']
57 };
55 };
58 this.update_mapped_classes(class_map, 'button_style', previous_trait_value);
56 this.update_mapped_classes(class_map, 'button_style', previous_trait_value);
59 },
57 },
60
58
61 events: {
59 events: {
62 // Dictionary of events and their handlers.
60 // Dictionary of events and their handlers.
63 'click': '_handle_click',
61 'click': '_handle_click',
64 },
62 },
65
63
66 _handle_click: function(){
64 _handle_click: function(){
67 /**
65 /**
68 * Handles when the button is clicked.
66 * Handles when the button is clicked.
69 */
67 */
70 this.send({event: 'click'});
68 this.send({event: 'click'});
71 },
69 },
72 });
70 });
73
71
74 return {
72 return {
75 'ButtonView': ButtonView,
73 'ButtonView': ButtonView,
76 };
74 };
77 });
75 });
@@ -1,573 +1,583 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 "widgets/js/widget",
5 "widgets/js/widget",
6 "base/js/utils",
6 "base/js/utils",
7 "jquery",
7 "jquery",
8 "underscore",
8 "underscore",
9 "bootstrap",
9 "bootstrap",
10 ], function(widget, utils, $, _){
10 ], function(widget, utils, $, _){
11
11
12 var DropdownView = widget.DOMWidgetView.extend({
12 var DropdownView = widget.DOMWidgetView.extend({
13 render : function(){
13 render : function(){
14 /**
14 /**
15 * Called when view is rendered.
15 * Called when view is rendered.
16 */
16 */
17 this.$el
17 this.$el
18 .addClass('widget-hbox widget-dropdown');
18 .addClass('widget-hbox widget-dropdown');
19 this.$label = $('<div />')
19 this.$label = $('<div />')
20 .appendTo(this.$el)
20 .appendTo(this.$el)
21 .addClass('widget-label')
21 .addClass('widget-label')
22 .hide();
22 .hide();
23 this.$buttongroup = $('<div />')
23 this.$buttongroup = $('<div />')
24 .addClass('widget_item')
24 .addClass('widget_item')
25 .addClass('btn-group')
25 .addClass('btn-group')
26 .appendTo(this.$el);
26 .appendTo(this.$el);
27 this.$droplabel = $('<button />')
27 this.$droplabel = $('<button />')
28 .addClass('btn btn-default')
28 .addClass('btn btn-default')
29 .addClass('widget-combo-btn')
29 .addClass('widget-combo-btn')
30 .html("&nbsp;")
30 .html("&nbsp;")
31 .appendTo(this.$buttongroup);
31 .appendTo(this.$buttongroup);
32 this.$dropbutton = $('<button />')
32 this.$dropbutton = $('<button />')
33 .addClass('btn btn-default')
33 .addClass('btn btn-default')
34 .addClass('dropdown-toggle')
34 .addClass('dropdown-toggle')
35 .addClass('widget-combo-carrot-btn')
35 .addClass('widget-combo-carrot-btn')
36 .attr('data-toggle', 'dropdown')
36 .attr('data-toggle', 'dropdown')
37 .append($('<span />').addClass("caret"))
37 .append($('<span />').addClass("caret"))
38 .appendTo(this.$buttongroup);
38 .appendTo(this.$buttongroup);
39 this.$droplist = $('<ul />')
39 this.$droplist = $('<ul />')
40 .addClass('dropdown-menu')
40 .addClass('dropdown-menu')
41 .appendTo(this.$buttongroup);
41 .appendTo(this.$buttongroup);
42
42
43 this.model.on('change:button_style', function(model, value) {
43 this.model.on('change:button_style', function(model, value) {
44 this.update_button_style();
44 this.update_button_style();
45 }, this);
45 }, this);
46 this.update_button_style('');
46 this.update_button_style('');
47
47
48 // Set defaults.
48 // Set defaults.
49 this.update();
49 this.update();
50 },
50 },
51
51
52 update : function(options){
52 update : function(options){
53 /**
53 /**
54 * Update the contents of this view
54 * Update the contents of this view
55 *
55 *
56 * Called when the model is changed. The model may have been
56 * Called when the model is changed. The model may have been
57 * changed by another view or by a state update from the back-end.
57 * changed by another view or by a state update from the back-end.
58 */
58 */
59
59
60 if (options === undefined || options.updated_view != this) {
60 if (options === undefined || options.updated_view != this) {
61 var selected_item_text = this.model.get('selected_label');
61 var selected_item_text = this.model.get('selected_label');
62 if (selected_item_text.trim().length === 0) {
62 if (selected_item_text.trim().length === 0) {
63 this.$droplabel.html("&nbsp;");
63 this.$droplabel.html("&nbsp;");
64 } else {
64 } else {
65 this.$droplabel.text(selected_item_text);
65 this.$droplabel.text(selected_item_text);
66 }
66 }
67
67
68 var items = this.model.get('_options_labels');
68 var items = this.model.get('_options_labels');
69 var $replace_droplist = $('<ul />')
69 var $replace_droplist = $('<ul />')
70 .addClass('dropdown-menu');
70 .addClass('dropdown-menu');
71 // Copy the style
71 // Copy the style
72 $replace_droplist.attr('style', this.$droplist.attr('style'));
72 $replace_droplist.attr('style', this.$droplist.attr('style'));
73 var that = this;
73 var that = this;
74 _.each(items, function(item, i) {
74 _.each(items, function(item, i) {
75 var item_button = $('<a href="#"/>')
75 var item_button = $('<a href="#"/>')
76 .text(item)
76 .text(item)
77 .on('click', $.proxy(that.handle_click, that));
77 .on('click', $.proxy(that.handle_click, that));
78 $replace_droplist.append($('<li />').append(item_button));
78 $replace_droplist.append($('<li />').append(item_button));
79 });
79 });
80
80
81 this.$droplist.replaceWith($replace_droplist);
81 this.$droplist.replaceWith($replace_droplist);
82 this.$droplist.remove();
82 this.$droplist.remove();
83 this.$droplist = $replace_droplist;
83 this.$droplist = $replace_droplist;
84
84
85 if (this.model.get('disabled')) {
85 if (this.model.get('disabled')) {
86 this.$buttongroup.attr('disabled','disabled');
86 this.$buttongroup.attr('disabled','disabled');
87 this.$droplabel.attr('disabled','disabled');
87 this.$droplabel.attr('disabled','disabled');
88 this.$dropbutton.attr('disabled','disabled');
88 this.$dropbutton.attr('disabled','disabled');
89 this.$droplist.attr('disabled','disabled');
89 this.$droplist.attr('disabled','disabled');
90 } else {
90 } else {
91 this.$buttongroup.removeAttr('disabled');
91 this.$buttongroup.removeAttr('disabled');
92 this.$droplabel.removeAttr('disabled');
92 this.$droplabel.removeAttr('disabled');
93 this.$dropbutton.removeAttr('disabled');
93 this.$dropbutton.removeAttr('disabled');
94 this.$droplist.removeAttr('disabled');
94 this.$droplist.removeAttr('disabled');
95 }
95 }
96
96
97 var description = this.model.get('description');
97 var description = this.model.get('description');
98 if (description.length === 0) {
98 if (description.length === 0) {
99 this.$label.hide();
99 this.$label.hide();
100 } else {
100 } else {
101 this.typeset(this.$label, description);
101 this.typeset(this.$label, description);
102 this.$label.show();
102 this.$label.show();
103 }
103 }
104 }
104 }
105 return DropdownView.__super__.update.apply(this);
105 return DropdownView.__super__.update.apply(this);
106 },
106 },
107
107
108 update_button_style: function(previous_trait_value) {
108 update_button_style: function(previous_trait_value) {
109 var class_map = {
109 var class_map = {
110 primary: ['btn-primary'],
110 primary: ['btn-primary'],
111 success: ['btn-success'],
111 success: ['btn-success'],
112 info: ['btn-info'],
112 info: ['btn-info'],
113 warning: ['btn-warning'],
113 warning: ['btn-warning'],
114 danger: ['btn-danger']
114 danger: ['btn-danger']
115 };
115 };
116 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$droplabel);
116 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$droplabel);
117 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$dropbutton);
117 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$dropbutton);
118 },
118 },
119
119
120 update_attr: function(name, value) {
120 update_attr: function(name, value) {
121 /**
121 /**
122 * Set a css attr of the widget view.
122 * Set a css attr of the widget view.
123 */
123 */
124 if (name.substring(0, 6) == 'border' || name == 'background' || name == 'color') {
124 if (name.substring(0, 6) == 'border' || name == 'background' || name == 'color') {
125 this.$droplabel.css(name, value);
125 this.$droplabel.css(name, value);
126 this.$dropbutton.css(name, value);
126 this.$dropbutton.css(name, value);
127 this.$droplist.css(name, value);
127 this.$droplist.css(name, value);
128 } else if (name == 'width') {
128 } else if (name == 'width') {
129 this.$droplist.css(name, value);
129 this.$droplist.css(name, value);
130 this.$droplabel.css(name, value);
130 this.$droplabel.css(name, value);
131 } else if (name == 'padding') {
131 } else if (name == 'padding') {
132 this.$droplist.css(name, value);
132 this.$droplist.css(name, value);
133 this.$buttongroup.css(name, value);
133 this.$buttongroup.css(name, value);
134 } else if (name == 'margin') {
134 } else if (name == 'margin') {
135 this.$buttongroup.css(name, value);
135 this.$buttongroup.css(name, value);
136 } else if (name == 'height') {
136 } else if (name == 'height') {
137 this.$droplabel.css(name, value);
137 this.$droplabel.css(name, value);
138 this.$dropbutton.css(name, value);
138 this.$dropbutton.css(name, value);
139 } else if (name == 'padding' || name == 'margin') {
139 } else if (name == 'padding' || name == 'margin') {
140 this.$el.css(name, value);
140 this.$el.css(name, value);
141 } else {
141 } else {
142 this.$droplist.css(name, value);
142 this.$droplist.css(name, value);
143 this.$droplabel.css(name, value);
143 this.$droplabel.css(name, value);
144 }
144 }
145 },
145 },
146
146
147 handle_click: function (e) {
147 handle_click: function (e) {
148 /**
148 /**
149 * Handle when a value is clicked.
149 * Handle when a value is clicked.
150 *
150 *
151 * Calling model.set will trigger all of the other views of the
151 * Calling model.set will trigger all of the other views of the
152 * model to update.
152 * model to update.
153 */
153 */
154 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
154 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
155 this.touch();
155 this.touch();
156
156
157 // Manually hide the droplist.
157 // Manually hide the droplist.
158 e.stopPropagation();
158 e.stopPropagation();
159 e.preventDefault();
159 e.preventDefault();
160 this.$buttongroup.removeClass('open');
160 this.$buttongroup.removeClass('open');
161 },
161 },
162
162
163 });
163 });
164
164
165
165
166 var RadioButtonsView = widget.DOMWidgetView.extend({
166 var RadioButtonsView = widget.DOMWidgetView.extend({
167 render : function(){
167 render : function(){
168 /**
168 /**
169 * Called when view is rendered.
169 * Called when view is rendered.
170 */
170 */
171 this.$el
171 this.$el
172 .addClass('widget-hbox widget-radio');
172 .addClass('widget-hbox widget-radio');
173 this.$label = $('<div />')
173 this.$label = $('<div />')
174 .appendTo(this.$el)
174 .appendTo(this.$el)
175 .addClass('widget-label')
175 .addClass('widget-label')
176 .hide();
176 .hide();
177 this.$container = $('<div />')
177 this.$container = $('<div />')
178 .appendTo(this.$el)
178 .appendTo(this.$el)
179 .addClass('widget-radio-box');
179 .addClass('widget-radio-box');
180 this.update();
180 this.update();
181 },
181 },
182
182
183 update : function(options){
183 update : function(options){
184 /**
184 /**
185 * Update the contents of this view
185 * Update the contents of this view
186 *
186 *
187 * Called when the model is changed. The model may have been
187 * Called when the model is changed. The model may have been
188 * changed by another view or by a state update from the back-end.
188 * changed by another view or by a state update from the back-end.
189 */
189 */
190 if (options === undefined || options.updated_view != this) {
190 if (options === undefined || options.updated_view != this) {
191 // Add missing items to the DOM.
191 // Add missing items to the DOM.
192 var items = this.model.get('_options_labels');
192 var items = this.model.get('_options_labels');
193 var disabled = this.model.get('disabled');
193 var disabled = this.model.get('disabled');
194 var that = this;
194 var that = this;
195 _.each(items, function(item, index) {
195 _.each(items, function(item, index) {
196 var item_query = ' :input[data-value="' + encodeURIComponent(item) + '"]';
196 var item_query = ' :input[data-value="' + encodeURIComponent(item) + '"]';
197 if (that.$el.find(item_query).length === 0) {
197 if (that.$el.find(item_query).length === 0) {
198 var $label = $('<label />')
198 var $label = $('<label />')
199 .addClass('radio')
199 .addClass('radio')
200 .text(item)
200 .text(item)
201 .appendTo(that.$container);
201 .appendTo(that.$container);
202
202
203 $('<input />')
203 $('<input />')
204 .attr('type', 'radio')
204 .attr('type', 'radio')
205 .addClass(that.model)
205 .addClass(that.model)
206 .val(item)
206 .val(item)
207 .attr('data-value', encodeURIComponent(item))
207 .attr('data-value', encodeURIComponent(item))
208 .prependTo($label)
208 .prependTo($label)
209 .on('click', $.proxy(that.handle_click, that));
209 .on('click', $.proxy(that.handle_click, that));
210 }
210 }
211
211
212 var $item_element = that.$container.find(item_query);
212 var $item_element = that.$container.find(item_query);
213 if (that.model.get('selected_label') == item) {
213 if (that.model.get('selected_label') == item) {
214 $item_element.prop('checked', true);
214 $item_element.prop('checked', true);
215 } else {
215 } else {
216 $item_element.prop('checked', false);
216 $item_element.prop('checked', false);
217 }
217 }
218 $item_element.prop('disabled', disabled);
218 $item_element.prop('disabled', disabled);
219 });
219 });
220
220
221 // Remove items that no longer exist.
221 // Remove items that no longer exist.
222 this.$container.find('input').each(function(i, obj) {
222 this.$container.find('input').each(function(i, obj) {
223 var value = $(obj).val();
223 var value = $(obj).val();
224 var found = false;
224 var found = false;
225 _.each(items, function(item, index) {
225 _.each(items, function(item, index) {
226 if (item == value) {
226 if (item == value) {
227 found = true;
227 found = true;
228 return false;
228 return false;
229 }
229 }
230 });
230 });
231
231
232 if (!found) {
232 if (!found) {
233 $(obj).parent().remove();
233 $(obj).parent().remove();
234 }
234 }
235 });
235 });
236
236
237 var description = this.model.get('description');
237 var description = this.model.get('description');
238 if (description.length === 0) {
238 if (description.length === 0) {
239 this.$label.hide();
239 this.$label.hide();
240 } else {
240 } else {
241 this.$label.text(description);
241 this.$label.text(description);
242 this.typeset(this.$label, description);
242 this.typeset(this.$label, description);
243 this.$label.show();
243 this.$label.show();
244 }
244 }
245 }
245 }
246 return RadioButtonsView.__super__.update.apply(this);
246 return RadioButtonsView.__super__.update.apply(this);
247 },
247 },
248
248
249 update_attr: function(name, value) {
249 update_attr: function(name, value) {
250 /**
250 /**
251 * Set a css attr of the widget view.
251 * Set a css attr of the widget view.
252 */
252 */
253 if (name == 'padding' || name == 'margin') {
253 if (name == 'padding' || name == 'margin') {
254 this.$el.css(name, value);
254 this.$el.css(name, value);
255 } else {
255 } else {
256 this.$container.css(name, value);
256 this.$container.css(name, value);
257 }
257 }
258 },
258 },
259
259
260 handle_click: function (e) {
260 handle_click: function (e) {
261 /**
261 /**
262 * Handle when a value is clicked.
262 * Handle when a value is clicked.
263 *
263 *
264 * Calling model.set will trigger all of the other views of the
264 * Calling model.set will trigger all of the other views of the
265 * model to update.
265 * model to update.
266 */
266 */
267 this.model.set('selected_label', $(e.target).val(), {updated_view: this});
267 this.model.set('selected_label', $(e.target).val(), {updated_view: this});
268 this.touch();
268 this.touch();
269 },
269 },
270 });
270 });
271
271
272
272
273 var ToggleButtonsView = widget.DOMWidgetView.extend({
273 var ToggleButtonsView = widget.DOMWidgetView.extend({
274 initialize: function() {
274 initialize: function() {
275 this._css_state = {};
275 this._css_state = {};
276 ToggleButtonsView.__super__.initialize.apply(this, arguments);
276 ToggleButtonsView.__super__.initialize.apply(this, arguments);
277 },
277 },
278
278
279 render: function() {
279 render: function() {
280 /**
280 /**
281 * Called when view is rendered.
281 * Called when view is rendered.
282 */
282 */
283 this.$el
283 this.$el
284 .addClass('widget-hbox widget-toggle-buttons');
284 .addClass('widget-hbox widget-toggle-buttons');
285 this.$label = $('<div />')
285 this.$label = $('<div />')
286 .appendTo(this.$el)
286 .appendTo(this.$el)
287 .addClass('widget-label')
287 .addClass('widget-label')
288 .hide();
288 .hide();
289 this.$buttongroup = $('<div />')
289 this.$buttongroup = $('<div />')
290 .addClass('btn-group')
290 .addClass('btn-group')
291 .appendTo(this.$el);
291 .appendTo(this.$el);
292
292
293 this.model.on('change:button_style', function(model, value) {
293 this.model.on('change:button_style', function(model, value) {
294 this.update_button_style();
294 this.update_button_style();
295 }, this);
295 }, this);
296 this.update_button_style('');
296 this.update_button_style('');
297 this.update();
297 this.update();
298 },
298 },
299
299
300 update : function(options){
300 update : function(options){
301 /**
301 /**
302 * Update the contents of this view
302 * Update the contents of this view
303 *
303 *
304 * Called when the model is changed. The model may have been
304 * Called when the model is changed. The model may have been
305 * changed by another view or by a state update from the back-end.
305 * changed by another view or by a state update from the back-end.
306 */
306 */
307 if (options === undefined || options.updated_view != this) {
307 if (options === undefined || options.updated_view != this) {
308 // Add missing items to the DOM.
308 // Add missing items to the DOM.
309 var items = this.model.get('_options_labels');
309 var items = this.model.get('_options_labels');
310 var icons = this.model.get('icons');
311 var previous_icons = this.model.previous('icons') || [];
310 var disabled = this.model.get('disabled');
312 var disabled = this.model.get('disabled');
311 var that = this;
313 var that = this;
312 var item_html;
314 var item_html;
313 _.each(items, function(item, index) {
315 _.each(items, function(item, index) {
314 if (item.trim().length === 0) {
316 if (item.trim().length === 0 && (!icons[index] ||
317 icons[index].trim().length === 0)) {
315 item_html = "&nbsp;";
318 item_html = "&nbsp;";
316 } else {
319 } else {
317 item_html = utils.escape_html(item);
320 item_html = utils.escape_html(item);
318 }
321 }
319 var item_query = '[data-value="' + encodeURIComponent(item) + '"]';
322 var item_query = '[data-value="' + encodeURIComponent(item) + '"]';
320 var $item_element = that.$buttongroup.find(item_query);
323 var $item_element = that.$buttongroup.find(item_query);
324 var $icon_element = $item_element.find('.fa');
321 if (!$item_element.length) {
325 if (!$item_element.length) {
322 $item_element = $('<button/>')
326 $item_element = $('<button/>')
323 .attr('type', 'button')
327 .attr('type', 'button')
324 .addClass('btn btn-default')
328 .addClass('btn btn-default')
325 .html(item_html)
329 .html(item_html)
326 .appendTo(that.$buttongroup)
330 .appendTo(that.$buttongroup)
327 .attr('data-value', encodeURIComponent(item))
331 .attr('data-value', encodeURIComponent(item))
332 .attr('data-toggle', 'tooltip')
328 .attr('value', item)
333 .attr('value', item)
329 .on('click', $.proxy(that.handle_click, that));
334 .on('click', $.proxy(that.handle_click, that));
330 that.update_style_traits($item_element);
335 that.update_style_traits($item_element);
336 $icon_element = $('<i class="fa"></i>').prependTo($item_element);
331 }
337 }
332 if (that.model.get('selected_label') == item) {
338 if (that.model.get('selected_label') == item) {
333 $item_element.addClass('active');
339 $item_element.addClass('active');
334 } else {
340 } else {
335 $item_element.removeClass('active');
341 $item_element.removeClass('active');
336 }
342 }
337 $item_element.prop('disabled', disabled);
343 $item_element.prop('disabled', disabled);
344 $item_element.attr('title', that.model.get('tooltips')[index]);
345 $icon_element
346 .removeClass(previous_icons[index])
347 .addClass(icons[index]);
338 });
348 });
339
349
340 // Remove items that no longer exist.
350 // Remove items that no longer exist.
341 this.$buttongroup.find('button').each(function(i, obj) {
351 this.$buttongroup.find('button').each(function(i, obj) {
342 var value = $(obj).attr('value');
352 var value = $(obj).attr('value');
343 var found = false;
353 var found = false;
344 _.each(items, function(item, index) {
354 _.each(items, function(item, index) {
345 if (item == value) {
355 if (item == value) {
346 found = true;
356 found = true;
347 return false;
357 return false;
348 }
358 }
349 });
359 });
350
360
351 if (!found) {
361 if (!found) {
352 $(obj).remove();
362 $(obj).remove();
353 }
363 }
354 });
364 });
355
365
356 var description = this.model.get('description');
366 var description = this.model.get('description');
357 if (description.length === 0) {
367 if (description.length === 0) {
358 this.$label.hide();
368 this.$label.hide();
359 } else {
369 } else {
360 this.$label.text();
370 this.$label.text();
361 this.typeset(this.$label, description);
371 this.typeset(this.$label, description);
362 this.$label.show();
372 this.$label.show();
363 }
373 }
364 }
374 }
365 return ToggleButtonsView.__super__.update.apply(this);
375 return ToggleButtonsView.__super__.update.apply(this);
366 },
376 },
367
377
368 update_attr: function(name, value) {
378 update_attr: function(name, value) {
369 /**
379 /**
370 * Set a css attr of the widget view.
380 * Set a css attr of the widget view.
371 */
381 */
372 if (name == 'padding' || name == 'margin') {
382 if (name == 'padding' || name == 'margin') {
373 this.$el.css(name, value);
383 this.$el.css(name, value);
374 } else {
384 } else {
375 this._css_state[name] = value;
385 this._css_state[name] = value;
376 this.update_style_traits();
386 this.update_style_traits();
377 }
387 }
378 },
388 },
379
389
380 update_style_traits: function(button) {
390 update_style_traits: function(button) {
381 for (var name in this._css_state) {
391 for (var name in this._css_state) {
382 if (this._css_state.hasOwnProperty(name)) {
392 if (this._css_state.hasOwnProperty(name)) {
383 if (name == 'margin') {
393 if (name == 'margin') {
384 this.$buttongroup.css(name, this._css_state[name]);
394 this.$buttongroup.css(name, this._css_state[name]);
385 } else if (name != 'width') {
395 } else if (name != 'width') {
386 if (button) {
396 if (button) {
387 button.css(name, this._css_state[name]);
397 button.css(name, this._css_state[name]);
388 } else {
398 } else {
389 this.$buttongroup.find('button').css(name, this._css_state[name]);
399 this.$buttongroup.find('button').css(name, this._css_state[name]);
390 }
400 }
391 }
401 }
392 }
402 }
393 }
403 }
394 },
404 },
395
405
396 update_button_style: function(previous_trait_value) {
406 update_button_style: function(previous_trait_value) {
397 var class_map = {
407 var class_map = {
398 primary: ['btn-primary'],
408 primary: ['btn-primary'],
399 success: ['btn-success'],
409 success: ['btn-success'],
400 info: ['btn-info'],
410 info: ['btn-info'],
401 warning: ['btn-warning'],
411 warning: ['btn-warning'],
402 danger: ['btn-danger']
412 danger: ['btn-danger']
403 };
413 };
404 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$buttongroup.find('button'));
414 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$buttongroup.find('button'));
405 },
415 },
406
416
407 handle_click: function (e) {
417 handle_click: function (e) {
408 /**
418 /**
409 * Handle when a value is clicked.
419 * Handle when a value is clicked.
410 *
420 *
411 * Calling model.set will trigger all of the other views of the
421 * Calling model.set will trigger all of the other views of the
412 * model to update.
422 * model to update.
413 */
423 */
414 this.model.set('selected_label', $(e.target).attr('value'), {updated_view: this});
424 this.model.set('selected_label', $(e.target).attr('value'), {updated_view: this});
415 this.touch();
425 this.touch();
416 },
426 },
417 });
427 });
418
428
419
429
420 var SelectView = widget.DOMWidgetView.extend({
430 var SelectView = widget.DOMWidgetView.extend({
421 render : function(){
431 render : function(){
422 /**
432 /**
423 * Called when view is rendered.
433 * Called when view is rendered.
424 */
434 */
425 this.$el
435 this.$el
426 .addClass('widget-hbox widget-select');
436 .addClass('widget-hbox widget-select');
427 this.$label = $('<div />')
437 this.$label = $('<div />')
428 .appendTo(this.$el)
438 .appendTo(this.$el)
429 .addClass('widget-label')
439 .addClass('widget-label')
430 .hide();
440 .hide();
431 this.$listbox = $('<select />')
441 this.$listbox = $('<select />')
432 .addClass('widget-listbox form-control')
442 .addClass('widget-listbox form-control')
433 .attr('size', 6)
443 .attr('size', 6)
434 .appendTo(this.$el)
444 .appendTo(this.$el)
435 .on('change', $.proxy(this.handle_change, this));
445 .on('change', $.proxy(this.handle_change, this));
436 this.update();
446 this.update();
437 },
447 },
438
448
439 update : function(options){
449 update : function(options){
440 /**
450 /**
441 * Update the contents of this view
451 * Update the contents of this view
442 *
452 *
443 * Called when the model is changed. The model may have been
453 * Called when the model is changed. The model may have been
444 * changed by another view or by a state update from the back-end.
454 * changed by another view or by a state update from the back-end.
445 */
455 */
446 if (options === undefined || options.updated_view != this) {
456 if (options === undefined || options.updated_view != this) {
447 // Add missing items to the DOM.
457 // Add missing items to the DOM.
448 var items = this.model.get('_options_labels');
458 var items = this.model.get('_options_labels');
449 var that = this;
459 var that = this;
450 _.each(items, function(item, index) {
460 _.each(items, function(item, index) {
451 var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
461 var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
452 if (that.$listbox.find(item_query).length === 0) {
462 if (that.$listbox.find(item_query).length === 0) {
453 $('<option />')
463 $('<option />')
454 .text(item)
464 .text(item)
455 .attr('data-value', encodeURIComponent(item))
465 .attr('data-value', encodeURIComponent(item))
456 .attr('selected_label', item)
466 .attr('selected_label', item)
457 .on("click", $.proxy(that.handle_click, that))
467 .on("click", $.proxy(that.handle_click, that))
458 .appendTo(that.$listbox);
468 .appendTo(that.$listbox);
459 }
469 }
460 });
470 });
461
471
462 // Select the correct element
472 // Select the correct element
463 this.$listbox.val(this.model.get('selected_label'));
473 this.$listbox.val(this.model.get('selected_label'));
464
474
465 // Disable listbox if needed
475 // Disable listbox if needed
466 var disabled = this.model.get('disabled');
476 var disabled = this.model.get('disabled');
467 this.$listbox.prop('disabled', disabled);
477 this.$listbox.prop('disabled', disabled);
468
478
469 // Remove items that no longer exist.
479 // Remove items that no longer exist.
470 this.$listbox.find('option').each(function(i, obj) {
480 this.$listbox.find('option').each(function(i, obj) {
471 var value = $(obj).text();
481 var value = $(obj).text();
472 var found = false;
482 var found = false;
473 _.each(items, function(item, index) {
483 _.each(items, function(item, index) {
474 if (item == value) {
484 if (item == value) {
475 found = true;
485 found = true;
476 return false;
486 return false;
477 }
487 }
478 });
488 });
479
489
480 if (!found) {
490 if (!found) {
481 $(obj).remove();
491 $(obj).remove();
482 }
492 }
483 });
493 });
484
494
485 var description = this.model.get('description');
495 var description = this.model.get('description');
486 if (description.length === 0) {
496 if (description.length === 0) {
487 this.$label.hide();
497 this.$label.hide();
488 } else {
498 } else {
489 this.typeset(this.$label, description);
499 this.typeset(this.$label, description);
490 this.$label.show();
500 this.$label.show();
491 }
501 }
492 }
502 }
493 return SelectView.__super__.update.apply(this);
503 return SelectView.__super__.update.apply(this);
494 },
504 },
495
505
496 update_attr: function(name, value) {
506 update_attr: function(name, value) {
497 /**
507 /**
498 * Set a css attr of the widget view.
508 * Set a css attr of the widget view.
499 */
509 */
500 if (name == 'padding' || name == 'margin') {
510 if (name == 'padding' || name == 'margin') {
501 this.$el.css(name, value);
511 this.$el.css(name, value);
502 } else {
512 } else {
503 this.$listbox.css(name, value);
513 this.$listbox.css(name, value);
504 }
514 }
505 },
515 },
506
516
507 handle_click: function (e) {
517 handle_click: function (e) {
508 /**
518 /**
509 * Handle when a new value is clicked.
519 * Handle when a new value is clicked.
510 */
520 */
511 this.$listbox.val($(e.target).val()).change();
521 this.$listbox.val($(e.target).val()).change();
512 },
522 },
513
523
514 handle_change: function (e) {
524 handle_change: function (e) {
515 /**
525 /**
516 * Handle when a new value is selected.
526 * Handle when a new value is selected.
517 *
527 *
518 * Calling model.set will trigger all of the other views of the
528 * Calling model.set will trigger all of the other views of the
519 * model to update.
529 * model to update.
520 */
530 */
521 this.model.set('selected_label', this.$listbox.val(), {updated_view: this});
531 this.model.set('selected_label', this.$listbox.val(), {updated_view: this});
522 this.touch();
532 this.touch();
523 },
533 },
524 });
534 });
525
535
526
536
527 var SelectMultipleView = SelectView.extend({
537 var SelectMultipleView = SelectView.extend({
528 render: function(){
538 render: function(){
529 /**
539 /**
530 * Called when view is rendered.
540 * Called when view is rendered.
531 */
541 */
532 SelectMultipleView.__super__.render.apply(this);
542 SelectMultipleView.__super__.render.apply(this);
533 this.$el.removeClass('widget-select')
543 this.$el.removeClass('widget-select')
534 .addClass('widget-select-multiple');
544 .addClass('widget-select-multiple');
535 this.$listbox.attr('multiple', true)
545 this.$listbox.attr('multiple', true)
536 .on('change', $.proxy(this.handle_change, this));
546 .on('change', $.proxy(this.handle_change, this));
537 return this;
547 return this;
538 },
548 },
539
549
540 update: function(){
550 update: function(){
541 /**
551 /**
542 * Update the contents of this view
552 * Update the contents of this view
543 *
553 *
544 * Called when the model is changed. The model may have been
554 * Called when the model is changed. The model may have been
545 * changed by another view or by a state update from the back-end.
555 * changed by another view or by a state update from the back-end.
546 */
556 */
547 SelectMultipleView.__super__.update.apply(this, arguments);
557 SelectMultipleView.__super__.update.apply(this, arguments);
548 this.$listbox.val(this.model.get('selected_labels'));
558 this.$listbox.val(this.model.get('selected_labels'));
549 },
559 },
550
560
551 handle_change: function (e) {
561 handle_change: function (e) {
552 /**
562 /**
553 * Handle when a new value is selected.
563 * Handle when a new value is selected.
554 *
564 *
555 * Calling model.set will trigger all of the other views of the
565 * Calling model.set will trigger all of the other views of the
556 * model to update.
566 * model to update.
557 */
567 */
558 this.model.set('selected_labels',
568 this.model.set('selected_labels',
559 (this.$listbox.val() || []).slice(),
569 (this.$listbox.val() || []).slice(),
560 {updated_view: this});
570 {updated_view: this});
561 this.touch();
571 this.touch();
562 },
572 },
563 });
573 });
564
574
565
575
566 return {
576 return {
567 'DropdownView': DropdownView,
577 'DropdownView': DropdownView,
568 'RadioButtonsView': RadioButtonsView,
578 'RadioButtonsView': RadioButtonsView,
569 'ToggleButtonsView': ToggleButtonsView,
579 'ToggleButtonsView': ToggleButtonsView,
570 'SelectView': SelectView,
580 'SelectView': SelectView,
571 'SelectMultipleView': SelectMultipleView,
581 'SelectMultipleView': SelectMultipleView,
572 };
582 };
573 });
583 });
@@ -1,92 +1,92 b''
1 // Test widget bool class
1 // Test widget bool class
2 casper.notebook_test(function () {
2 casper.notebook_test(function () {
3 "use strict";
3 "use strict";
4
4
5 // Create a checkbox and togglebutton.
5 // Create a checkbox and togglebutton.
6 var bool_index = this.append_cell(
6 var bool_index = this.append_cell(
7 'from IPython.html import widgets\n' +
7 'from IPython.html import widgets\n' +
8 'from IPython.display import display, clear_output\n' +
8 'from IPython.display import display, clear_output\n' +
9 'bool_widgets = [widgets.Checkbox(description="Title", value=True),\n' +
9 'bool_widgets = [widgets.Checkbox(description="Title", value=True),\n' +
10 ' widgets.ToggleButton(description="Title", value=True)]\n' +
10 ' widgets.ToggleButton(description="Title", value=True)]\n' +
11 'display(bool_widgets[0])\n' +
11 'display(bool_widgets[0])\n' +
12 'display(bool_widgets[1])\n' +
12 'display(bool_widgets[1])\n' +
13 'print("Success")');
13 'print("Success")');
14 this.execute_cell_then(bool_index, function(index){
14 this.execute_cell_then(bool_index, function(index){
15 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
15 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
16 'Create bool widget cell executed with correct output.');
16 'Create bool widget cell executed with correct output.');
17 });
17 });
18
18
19 // Wait for the widgets to actually display.
19 // Wait for the widgets to actually display.
20 var widget_checkbox_selector = '.widget-area .widget-subarea .widget-hbox input';
20 var widget_checkbox_selector = '.widget-area .widget-subarea .widget-hbox input';
21 var widget_togglebutton_selector = '.widget-area .widget-subarea button';
21 var widget_togglebutton_selector = '.widget-area .widget-subarea button';
22 this.wait_for_element(bool_index, widget_checkbox_selector);
22 this.wait_for_element(bool_index, widget_checkbox_selector);
23 this.wait_for_element(bool_index, widget_togglebutton_selector);
23 this.wait_for_element(bool_index, widget_togglebutton_selector);
24
24
25 // Continue the tests.
25 // Continue the tests.
26 this.then(function() {
26 this.then(function() {
27 this.test.assert(this.cell_element_exists(bool_index,
27 this.test.assert(this.cell_element_exists(bool_index,
28 '.widget-area .widget-subarea'),
28 '.widget-area .widget-subarea'),
29 'Widget subarea exists.');
29 'Widget subarea exists.');
30
30
31 this.test.assert(this.cell_element_exists(bool_index,
31 this.test.assert(this.cell_element_exists(bool_index,
32 widget_checkbox_selector),
32 widget_checkbox_selector),
33 'Checkbox exists.');
33 'Checkbox exists.');
34
34
35 this.test.assert(this.cell_element_function(bool_index,
35 this.test.assert(this.cell_element_function(bool_index,
36 widget_checkbox_selector, 'prop', ['checked']),
36 widget_checkbox_selector, 'prop', ['checked']),
37 'Checkbox is checked.');
37 'Checkbox is checked.');
38
38
39 this.test.assert(this.cell_element_exists(bool_index,
39 this.test.assert(this.cell_element_exists(bool_index,
40 '.widget-area .widget-subarea .widget-hbox .widget-label'),
40 '.widget-area .widget-subarea .widget-hbox .widget-label'),
41 'Checkbox label exists.');
41 'Checkbox label exists.');
42
42
43 this.test.assert(this.cell_element_function(bool_index,
43 this.test.assert(this.cell_element_function(bool_index,
44 '.widget-area .widget-subarea .widget-hbox .widget-label', 'html')=="Title",
44 '.widget-area .widget-subarea .widget-hbox .widget-label', 'html')=="Title",
45 'Checkbox labeled correctly.');
45 'Checkbox labeled correctly.');
46
46
47 this.test.assert(this.cell_element_exists(bool_index,
47 this.test.assert(this.cell_element_exists(bool_index,
48 widget_togglebutton_selector),
48 widget_togglebutton_selector),
49 'Toggle button exists.');
49 'Toggle button exists.');
50
50
51 this.test.assert(this.cell_element_function(bool_index,
51 this.test.assert(this.cell_element_function(bool_index,
52 widget_togglebutton_selector, 'html')=="Title",
52 widget_togglebutton_selector, 'html')=='<i class="fa"></i>Title',
53 'Toggle button labeled correctly.');
53 'Toggle button labeled correctly.');
54
54
55 this.test.assert(this.cell_element_function(bool_index,
55 this.test.assert(this.cell_element_function(bool_index,
56 widget_togglebutton_selector, 'hasClass', ['active']),
56 widget_togglebutton_selector, 'hasClass', ['active']),
57 'Toggle button is toggled.');
57 'Toggle button is toggled.');
58 });
58 });
59
59
60 // Try changing the state of the widgets programatically.
60 // Try changing the state of the widgets programatically.
61 var index = this.append_cell(
61 var index = this.append_cell(
62 'bool_widgets[0].value = False\n' +
62 'bool_widgets[0].value = False\n' +
63 'bool_widgets[1].value = False\n' +
63 'bool_widgets[1].value = False\n' +
64 'print("Success")');
64 'print("Success")');
65 this.execute_cell_then(index, function(index){
65 this.execute_cell_then(index, function(index){
66 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
66 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
67 'Change bool widget value cell executed with correct output.');
67 'Change bool widget value cell executed with correct output.');
68
68
69 this.test.assert(! this.cell_element_function(bool_index,
69 this.test.assert(! this.cell_element_function(bool_index,
70 widget_checkbox_selector, 'prop', ['checked']),
70 widget_checkbox_selector, 'prop', ['checked']),
71 'Checkbox is not checked. (1)');
71 'Checkbox is not checked. (1)');
72
72
73 this.test.assert(! this.cell_element_function(bool_index,
73 this.test.assert(! this.cell_element_function(bool_index,
74 widget_togglebutton_selector, 'hasClass', ['active']),
74 widget_togglebutton_selector, 'hasClass', ['active']),
75 'Toggle button is not toggled. (1)');
75 'Toggle button is not toggled. (1)');
76
76
77 // Try toggling the bool by clicking on the checkbox.
77 // Try toggling the bool by clicking on the checkbox.
78 this.cell_element_function(bool_index, widget_checkbox_selector, 'click');
78 this.cell_element_function(bool_index, widget_checkbox_selector, 'click');
79
79
80 this.test.assert(this.cell_element_function(bool_index,
80 this.test.assert(this.cell_element_function(bool_index,
81 widget_checkbox_selector, 'prop', ['checked']),
81 widget_checkbox_selector, 'prop', ['checked']),
82 'Checkbox is checked. (2)');
82 'Checkbox is checked. (2)');
83
83
84 // Try toggling the bool by clicking on the toggle button.
84 // Try toggling the bool by clicking on the toggle button.
85 this.cell_element_function(bool_index, widget_togglebutton_selector, 'click');
85 this.cell_element_function(bool_index, widget_togglebutton_selector, 'click');
86
86
87 this.test.assert(this.cell_element_function(bool_index,
87 this.test.assert(this.cell_element_function(bool_index,
88 widget_togglebutton_selector, 'hasClass', ['active']),
88 widget_togglebutton_selector, 'hasClass', ['active']),
89 'Toggle button is toggled. (3)');
89 'Toggle button is toggled. (3)');
90
90
91 });
91 });
92 });
92 });
@@ -1,48 +1,48 b''
1 // Test widget button class
1 // Test widget button class
2 casper.notebook_test(function () {
2 casper.notebook_test(function () {
3 var button_index = this.append_cell(
3 var button_index = this.append_cell(
4 'from IPython.html import widgets\n' +
4 'from IPython.html import widgets\n' +
5 'from IPython.display import display, clear_output\n' +
5 'from IPython.display import display, clear_output\n' +
6 'button = widgets.Button(description="Title")\n' +
6 'button = widgets.Button(description="Title")\n' +
7 'display(button)\n' +
7 'display(button)\n' +
8 'print("Success")\n' +
8 'print("Success")\n' +
9 'def handle_click(sender):\n' +
9 'def handle_click(sender):\n' +
10 ' display("Clicked")\n' +
10 ' display("Clicked")\n' +
11 'button.on_click(handle_click)');
11 'button.on_click(handle_click)');
12 this.execute_cell_then(button_index, function(index){
12 this.execute_cell_then(button_index, function(index){
13 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
13 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
14 'Create button cell executed with correct output.');
14 'Create button cell executed with correct output.');
15 });
15 });
16
16
17 // Wait for the widgets to actually display.
17 // Wait for the widgets to actually display.
18 var widget_button_selector = '.widget-area .widget-subarea button';
18 var widget_button_selector = '.widget-area .widget-subarea button';
19 this.wait_for_element(button_index, widget_button_selector);
19 this.wait_for_element(button_index, widget_button_selector);
20
20
21 // Continue with the tests.
21 // Continue with the tests.
22 this.then(function() {
22 this.then(function() {
23 this.test.assert(this.cell_element_exists(button_index,
23 this.test.assert(this.cell_element_exists(button_index,
24 '.widget-area .widget-subarea'),
24 '.widget-area .widget-subarea'),
25 'Widget subarea exists.');
25 'Widget subarea exists.');
26
26
27 this.test.assert(this.cell_element_exists(button_index,
27 this.test.assert(this.cell_element_exists(button_index,
28 widget_button_selector),
28 widget_button_selector),
29 'Widget button exists.');
29 'Widget button exists.');
30
30
31 this.test.assert(this.cell_element_function(button_index,
31 this.test.assert(this.cell_element_function(button_index,
32 widget_button_selector, 'html')=='Title',
32 widget_button_selector, 'html')=='<i class="fa"></i>Title',
33 'Set button description.');
33 'Set button description.');
34
34
35 this.cell_element_function(button_index,
35 this.cell_element_function(button_index,
36 widget_button_selector, 'click');
36 widget_button_selector, 'click');
37 });
37 });
38
38
39 this.wait_for_output(button_index, 1);
39 this.wait_for_output(button_index, 1);
40
40
41 this.then(function () {
41 this.then(function () {
42 var warning_text = this.get_output_cell(button_index, 1).text;
42 var warning_text = this.get_output_cell(button_index, 1).text;
43 this.test.assertNotEquals(warning_text.indexOf('Warning'), -1,
43 this.test.assertNotEquals(warning_text.indexOf('Warning'), -1,
44 'Importing widgets show a warning');
44 'Importing widgets show a warning');
45 this.test.assertEquals(this.get_output_cell(button_index, 2).data['text/plain'], "'Clicked'",
45 this.test.assertEquals(this.get_output_cell(button_index, 2).data['text/plain'], "'Clicked'",
46 'Button click event fires.');
46 'Button click event fires.');
47 });
47 });
48 });
48 });
@@ -1,71 +1,76 b''
1 """Bool class.
1 """Bool class.
2
2
3 Represents a boolean using a widget.
3 Represents a boolean using a widget.
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 DOMWidget, register
16 from .widget import DOMWidget, register
17 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
17 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
18 from IPython.utils.warn import DeprecatedClass
18 from IPython.utils.warn import DeprecatedClass
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 class _Bool(DOMWidget):
23 class _Bool(DOMWidget):
24 """A base class for creating widgets that represent booleans."""
24 """A base class for creating widgets that represent booleans."""
25 value = Bool(False, help="Bool value", sync=True)
25 value = Bool(False, help="Bool value", sync=True)
26 description = Unicode('', help="Description of the boolean (label).", sync=True)
26 description = Unicode('', help="Description of the boolean (label).", sync=True)
27 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
27 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
28
28
29 def __init__(self, value=None, **kwargs):
29 def __init__(self, value=None, **kwargs):
30 if value is not None:
30 if value is not None:
31 kwargs['value'] = value
31 kwargs['value'] = value
32 super(_Bool, self).__init__(**kwargs)
32 super(_Bool, self).__init__(**kwargs)
33
33
34 @register('IPython.Checkbox')
34 @register('IPython.Checkbox')
35 class Checkbox(_Bool):
35 class Checkbox(_Bool):
36 """Displays a boolean `value` in the form of a checkbox.
36 """Displays a boolean `value` in the form of a checkbox.
37
37
38 Parameters
38 Parameters
39 ----------
39 ----------
40 value : {True,False}
40 value : {True,False}
41 value of the checkbox: True-checked, False-unchecked
41 value of the checkbox: True-checked, False-unchecked
42 description : str
42 description : str
43 description displayed next to the checkbox
43 description displayed next to the checkbox
44 """
44 """
45 _view_name = Unicode('CheckboxView', sync=True)
45 _view_name = Unicode('CheckboxView', sync=True)
46
46
47
47
48 @register('IPython.ToggleButton')
48 @register('IPython.ToggleButton')
49 class ToggleButton(_Bool):
49 class ToggleButton(_Bool):
50 """Displays a boolean `value` in the form of a toggle button.
50 """Displays a boolean `value` in the form of a toggle button.
51
51
52 Parameters
52 Parameters
53 ----------
53 ----------
54 value : {True,False}
54 value : {True,False}
55 value of the toggle button: True-pressed, False-unpressed
55 value of the toggle button: True-pressed, False-unpressed
56 description : str
56 description : str
57 description displayed next to the button
57 description displayed next to the button
58 tooltip: str
59 tooltip caption of the toggle button
60 icon: str
61 font-awesome icon name
58 """
62 """
59
63
60 _view_name = Unicode('ToggleButtonView', sync=True)
64 _view_name = Unicode('ToggleButtonView', sync=True)
61 tooltip = Unicode(help="Tooltip caption of the toggle button.", sync=True)
65 tooltip = Unicode(help="Tooltip caption of the toggle button.", sync=True)
66 icon = Unicode('', help= "Font-awesome icon.", sync=True)
62
67
63 button_style = CaselessStrEnum(
68 button_style = CaselessStrEnum(
64 values=['primary', 'success', 'info', 'warning', 'danger', ''],
69 values=['primary', 'success', 'info', 'warning', 'danger', ''],
65 default_value='', allow_none=True, sync=True, help="""Use a
70 default_value='', allow_none=True, sync=True, help="""Use a
66 predefined styling for the button.""")
71 predefined styling for the button.""")
67
72
68
73
69 # Remove in IPython 4.0
74 # Remove in IPython 4.0
70 CheckboxWidget = DeprecatedClass(Checkbox, 'CheckboxWidget')
75 CheckboxWidget = DeprecatedClass(Checkbox, 'CheckboxWidget')
71 ToggleButtonWidget = DeprecatedClass(ToggleButton, 'ToggleButtonWidget')
76 ToggleButtonWidget = DeprecatedClass(ToggleButton, 'ToggleButtonWidget')
@@ -1,72 +1,82 b''
1 """Button class.
1 """Button class.
2
2
3 Represents a button in the frontend using a widget. Allows user to listen for
3 Represents a button in the frontend using a widget. Allows user to listen for
4 click events on the button and trigger backend code when the clicks are fired.
4 click events on the button and trigger backend code when the clicks are fired.
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 DOMWidget, CallbackDispatcher, register
17 from .widget import DOMWidget, CallbackDispatcher, register
18 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
18 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
19 from IPython.utils.warn import DeprecatedClass
19 from IPython.utils.warn import DeprecatedClass
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Classes
22 # Classes
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 @register('IPython.Button')
24 @register('IPython.Button')
25 class Button(DOMWidget):
25 class Button(DOMWidget):
26 """Button widget.
26 """Button widget.
27 This widget has an `on_click` method that allows you to listen for the
28 user clicking on the button. The click event itself is stateless.
27
29
28 This widget has an `on_click` method that allows you to listen for the
30 Parameters
29 user clicking on the button. The click event itself is stateless."""
31 ----------
32 description : str
33 description displayed next to the button
34 tooltip: str
35 tooltip caption of the toggle button
36 icon: str
37 font-awesome icon name
38 """
30 _view_name = Unicode('ButtonView', sync=True)
39 _view_name = Unicode('ButtonView', sync=True)
31
40
32 # Keys
41 # Keys
33 description = Unicode('', help="Button label.", sync=True)
42 description = Unicode('', help="Button label.", sync=True)
34 tooltip = Unicode(help="Tooltip caption of the button.", sync=True)
43 tooltip = Unicode(help="Tooltip caption of the button.", sync=True)
35 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
44 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
45 icon = Unicode('', help= "Font-awesome icon.", sync=True)
36
46
37 button_style = CaselessStrEnum(
47 button_style = CaselessStrEnum(
38 values=['primary', 'success', 'info', 'warning', 'danger', ''],
48 values=['primary', 'success', 'info', 'warning', 'danger', ''],
39 default_value='', allow_none=True, sync=True, help="""Use a
49 default_value='', allow_none=True, sync=True, help="""Use a
40 predefined styling for the button.""")
50 predefined styling for the button.""")
41
51
42 def __init__(self, **kwargs):
52 def __init__(self, **kwargs):
43 """Constructor"""
53 """Constructor"""
44 super(Button, self).__init__(**kwargs)
54 super(Button, self).__init__(**kwargs)
45 self._click_handlers = CallbackDispatcher()
55 self._click_handlers = CallbackDispatcher()
46 self.on_msg(self._handle_button_msg)
56 self.on_msg(self._handle_button_msg)
47
57
48 def on_click(self, callback, remove=False):
58 def on_click(self, callback, remove=False):
49 """Register a callback to execute when the button is clicked.
59 """Register a callback to execute when the button is clicked.
50
60
51 The callback will be called with one argument,
61 The callback will be called with one argument,
52 the clicked button widget instance.
62 the clicked button widget instance.
53
63
54 Parameters
64 Parameters
55 ----------
65 ----------
56 remove : bool (optional)
66 remove : bool (optional)
57 Set to true to remove the callback from the list of callbacks."""
67 Set to true to remove the callback from the list of callbacks."""
58 self._click_handlers.register_callback(callback, remove=remove)
68 self._click_handlers.register_callback(callback, remove=remove)
59
69
60 def _handle_button_msg(self, _, content):
70 def _handle_button_msg(self, _, content):
61 """Handle a msg from the front-end.
71 """Handle a msg from the front-end.
62
72
63 Parameters
73 Parameters
64 ----------
74 ----------
65 content: dict
75 content: dict
66 Content of the msg."""
76 Content of the msg."""
67 if content.get('event', '') == 'click':
77 if content.get('event', '') == 'click':
68 self._click_handlers(self)
78 self._click_handlers(self)
69
79
70
80
71 # Remove in IPython 4.0
81 # Remove in IPython 4.0
72 ButtonWidget = DeprecatedClass(Button, 'ButtonWidget')
82 ButtonWidget = DeprecatedClass(Button, 'ButtonWidget')
@@ -1,240 +1,248 b''
1 """Selection classes.
1 """Selection classes.
2
2
3 Represents an enumeration using a widget.
3 Represents an enumeration using a widget.
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
16
17 from collections import OrderedDict
17 from collections import OrderedDict
18 from threading import Lock
18 from threading import Lock
19
19
20 from .widget import DOMWidget, register
20 from .widget import DOMWidget, register
21 from IPython.utils.traitlets import (
21 from IPython.utils.traitlets import (
22 Unicode, Bool, Any, Dict, TraitError, CaselessStrEnum, Tuple
22 Unicode, Bool, Any, Dict, TraitError, CaselessStrEnum, Tuple, List
23 )
23 )
24 from IPython.utils.py3compat import unicode_type
24 from IPython.utils.py3compat import unicode_type
25 from IPython.utils.warn import DeprecatedClass
25 from IPython.utils.warn import DeprecatedClass
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # SelectionWidget
28 # SelectionWidget
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 class _Selection(DOMWidget):
30 class _Selection(DOMWidget):
31 """Base class for Selection widgets
31 """Base class for Selection widgets
32
32
33 ``options`` can be specified as a list or dict. If given as a list,
33 ``options`` can be specified as a list or dict. If given as a list,
34 it will be transformed to a dict of the form ``{str(value):value}``.
34 it will be transformed to a dict of the form ``{str(value):value}``.
35
36 When programmatically setting the value, a reverse lookup is performed
37 among the options to set the value of ``selected_label`` accordingly. The
38 reverse lookup uses the equality operator by default, but an other
39 predicate may be provided via the ``equals`` argument. For example, when
40 dealing with numpy arrays, one may set equals=np.array_equal.
35 """
41 """
36
42
37 value = Any(help="Selected value")
43 value = Any(help="Selected value")
38 selected_label = Unicode(help="The label of the selected value", sync=True)
44 selected_label = Unicode(help="The label of the selected value", sync=True)
39 options = Any(help="""List of (key, value) tuples or dict of values that the
45 options = Any(help="""List of (key, value) tuples or dict of values that the
40 user can select.
46 user can select.
41
47
42 The keys of this list are the strings that will be displayed in the UI,
48 The keys of this list are the strings that will be displayed in the UI,
43 representing the actual Python choices.
49 representing the actual Python choices.
44
50
45 The keys of this list are also available as _options_labels.
51 The keys of this list are also available as _options_labels.
46 """)
52 """)
47
53
48 _options_dict = Dict()
54 _options_dict = Dict()
49 _options_labels = Tuple(sync=True)
55 _options_labels = Tuple(sync=True)
50 _options_values = Tuple()
56 _options_values = Tuple()
51
57
52 disabled = Bool(False, help="Enable or disable user changes", sync=True)
58 disabled = Bool(False, help="Enable or disable user changes", sync=True)
53 description = Unicode(help="Description of the value this widget represents", sync=True)
59 description = Unicode(help="Description of the value this widget represents", sync=True)
54
60
55 def __init__(self, *args, **kwargs):
61 def __init__(self, *args, **kwargs):
56 self.value_lock = Lock()
62 self.value_lock = Lock()
57 self.options_lock = Lock()
63 self.options_lock = Lock()
58 self.equals = kwargs.pop('equals', lambda x, y: x == y)
64 self.equals = kwargs.pop('equals', lambda x, y: x == y)
59 self.on_trait_change(self._options_readonly_changed, ['_options_dict', '_options_labels', '_options_values', '_options'])
65 self.on_trait_change(self._options_readonly_changed, ['_options_dict', '_options_labels', '_options_values', '_options'])
60 if 'options' in kwargs:
66 if 'options' in kwargs:
61 self.options = kwargs.pop('options')
67 self.options = kwargs.pop('options')
62 DOMWidget.__init__(self, *args, **kwargs)
68 DOMWidget.__init__(self, *args, **kwargs)
63 self._value_in_options()
69 self._value_in_options()
64
70
65 def _make_options(self, x):
71 def _make_options(self, x):
66 # If x is a dict, convert it to list format.
72 # If x is a dict, convert it to list format.
67 if isinstance(x, (OrderedDict, dict)):
73 if isinstance(x, (OrderedDict, dict)):
68 return [(k, v) for k, v in x.items()]
74 return [(k, v) for k, v in x.items()]
69
75
70 # Make sure x is a list or tuple.
76 # Make sure x is a list or tuple.
71 if not isinstance(x, (list, tuple)):
77 if not isinstance(x, (list, tuple)):
72 raise ValueError('x')
78 raise ValueError('x')
73
79
74 # If x is an ordinary list, use the option values as names.
80 # If x is an ordinary list, use the option values as names.
75 for y in x:
81 for y in x:
76 if not isinstance(y, (list, tuple)) or len(y) < 2:
82 if not isinstance(y, (list, tuple)) or len(y) < 2:
77 return [(i, i) for i in x]
83 return [(i, i) for i in x]
78
84
79 # Value is already in the correct format.
85 # Value is already in the correct format.
80 return x
86 return x
81
87
82 def _options_changed(self, name, old, new):
88 def _options_changed(self, name, old, new):
83 """Handles when the options tuple has been changed.
89 """Handles when the options tuple has been changed.
84
90
85 Setting options implies setting option labels from the keys of the dict.
91 Setting options implies setting option labels from the keys of the dict.
86 """
92 """
87 if self.options_lock.acquire(False):
93 if self.options_lock.acquire(False):
88 try:
94 try:
89 self.options = new
95 self.options = new
90
96
91 options = self._make_options(new)
97 options = self._make_options(new)
92 self._options_dict = {i[0]: i[1] for i in options}
98 self._options_dict = {i[0]: i[1] for i in options}
93 self._options_labels = [i[0] for i in options]
99 self._options_labels = [i[0] for i in options]
94 self._options_values = [i[1] for i in options]
100 self._options_values = [i[1] for i in options]
95 self._value_in_options()
101 self._value_in_options()
96 finally:
102 finally:
97 self.options_lock.release()
103 self.options_lock.release()
98
104
99 def _value_in_options(self):
105 def _value_in_options(self):
100 # ensure that the chosen value is one of the choices
106 # ensure that the chosen value is one of the choices
101
107
102 if self._options_values:
108 if self._options_values:
103 if self.value not in self._options_values:
109 if self.value not in self._options_values:
104 self.value = next(iter(self._options_values))
110 self.value = next(iter(self._options_values))
105
111
106 def _options_readonly_changed(self, name, old, new):
112 def _options_readonly_changed(self, name, old, new):
107 if not self.options_lock.locked():
113 if not self.options_lock.locked():
108 raise TraitError("`.%s` is a read-only trait. Use the `.options` tuple instead." % name)
114 raise TraitError("`.%s` is a read-only trait. Use the `.options` tuple instead." % name)
109
115
110 def _value_changed(self, name, old, new):
116 def _value_changed(self, name, old, new):
111 """Called when value has been changed"""
117 """Called when value has been changed"""
112 if self.value_lock.acquire(False):
118 if self.value_lock.acquire(False):
113 try:
119 try:
114 # Reverse dictionary lookup for the value name
120 # Reverse dictionary lookup for the value name
115 for k, v in self._options_dict.items():
121 for k, v in self._options_dict.items():
116 if self.equals(new, v):
122 if self.equals(new, v):
117 # set the selected value name
123 # set the selected value name
118 self.selected_label = k
124 self.selected_label = k
119 return
125 return
120 # undo the change, and raise KeyError
126 # undo the change, and raise KeyError
121 self.value = old
127 self.value = old
122 raise KeyError(new)
128 raise KeyError(new)
123 finally:
129 finally:
124 self.value_lock.release()
130 self.value_lock.release()
125
131
126 def _selected_label_changed(self, name, old, new):
132 def _selected_label_changed(self, name, old, new):
127 """Called when the value name has been changed (typically by the frontend)."""
133 """Called when the value name has been changed (typically by the frontend)."""
128 if self.value_lock.acquire(False):
134 if self.value_lock.acquire(False):
129 try:
135 try:
130 self.value = self._options_dict[new]
136 self.value = self._options_dict[new]
131 finally:
137 finally:
132 self.value_lock.release()
138 self.value_lock.release()
133
139
134
140
135 class _MultipleSelection(_Selection):
141 class _MultipleSelection(_Selection):
136 """Base class for MultipleSelection widgets.
142 """Base class for MultipleSelection widgets.
137
143
138 As with ``_Selection``, ``options`` can be specified as a list or dict. If
144 As with ``_Selection``, ``options`` can be specified as a list or dict. If
139 given as a list, it will be transformed to a dict of the form
145 given as a list, it will be transformed to a dict of the form
140 ``{str(value): value}``.
146 ``{str(value): value}``.
141
147
142 Despite their names, ``value`` (and ``selected_label``) will be tuples, even
148 Despite their names, ``value`` (and ``selected_label``) will be tuples, even
143 if only a single option is selected.
149 if only a single option is selected.
144 """
150 """
145
151
146 value = Tuple(help="Selected values")
152 value = Tuple(help="Selected values")
147 selected_labels = Tuple(help="The labels of the selected options",
153 selected_labels = Tuple(help="The labels of the selected options",
148 sync=True)
154 sync=True)
149
155
150 @property
156 @property
151 def selected_label(self):
157 def selected_label(self):
152 raise AttributeError(
158 raise AttributeError(
153 "Does not support selected_label, use selected_labels")
159 "Does not support selected_label, use selected_labels")
154
160
155 def _value_in_options(self):
161 def _value_in_options(self):
156 # ensure that the chosen value is one of the choices
162 # ensure that the chosen value is one of the choices
157 if self.options:
163 if self.options:
158 old_value = self.value or []
164 old_value = self.value or []
159 new_value = []
165 new_value = []
160 for value in old_value:
166 for value in old_value:
161 if value in self._options_dict.values():
167 if value in self._options_dict.values():
162 new_value.append(value)
168 new_value.append(value)
163 if new_value:
169 if new_value:
164 self.value = new_value
170 self.value = new_value
165 else:
171 else:
166 self.value = [next(iter(self._options_dict.values()))]
172 self.value = [next(iter(self._options_dict.values()))]
167
173
168 def _value_changed(self, name, old, new):
174 def _value_changed(self, name, old, new):
169 """Called when value has been changed"""
175 """Called when value has been changed"""
170 if self.value_lock.acquire(False):
176 if self.value_lock.acquire(False):
171 try:
177 try:
172 self.selected_labels = [
178 self.selected_labels = [
173 self._options_labels[self._options_values.index(v)]
179 self._options_labels[self._options_values.index(v)]
174 for v in new
180 for v in new
175 ]
181 ]
176 except:
182 except:
177 self.value = old
183 self.value = old
178 raise KeyError(new)
184 raise KeyError(new)
179 finally:
185 finally:
180 self.value_lock.release()
186 self.value_lock.release()
181
187
182 def _selected_labels_changed(self, name, old, new):
188 def _selected_labels_changed(self, name, old, new):
183 """Called when the selected label has been changed (typically by the
189 """Called when the selected label has been changed (typically by the
184 frontend)."""
190 frontend)."""
185 if self.value_lock.acquire(False):
191 if self.value_lock.acquire(False):
186 try:
192 try:
187 self.value = [self._options_dict[name] for name in new]
193 self.value = [self._options_dict[name] for name in new]
188 finally:
194 finally:
189 self.value_lock.release()
195 self.value_lock.release()
190
196
191
197
192 @register('IPython.ToggleButtons')
198 @register('IPython.ToggleButtons')
193 class ToggleButtons(_Selection):
199 class ToggleButtons(_Selection):
194 """Group of toggle buttons that represent an enumeration. Only one toggle
200 """Group of toggle buttons that represent an enumeration. Only one toggle
195 button can be toggled at any point in time."""
201 button can be toggled at any point in time."""
196 _view_name = Unicode('ToggleButtonsView', sync=True)
202 _view_name = Unicode('ToggleButtonsView', sync=True)
203 tooltips = List(Unicode(), sync=True)
204 icons = List(Unicode(), sync=True)
197
205
198 button_style = CaselessStrEnum(
206 button_style = CaselessStrEnum(
199 values=['primary', 'success', 'info', 'warning', 'danger', ''],
207 values=['primary', 'success', 'info', 'warning', 'danger', ''],
200 default_value='', allow_none=True, sync=True, help="""Use a
208 default_value='', allow_none=True, sync=True, help="""Use a
201 predefined styling for the buttons.""")
209 predefined styling for the buttons.""")
202
210
203 @register('IPython.Dropdown')
211 @register('IPython.Dropdown')
204 class Dropdown(_Selection):
212 class Dropdown(_Selection):
205 """Allows you to select a single item from a dropdown."""
213 """Allows you to select a single item from a dropdown."""
206 _view_name = Unicode('DropdownView', sync=True)
214 _view_name = Unicode('DropdownView', sync=True)
207
215
208 button_style = CaselessStrEnum(
216 button_style = CaselessStrEnum(
209 values=['primary', 'success', 'info', 'warning', 'danger', ''],
217 values=['primary', 'success', 'info', 'warning', 'danger', ''],
210 default_value='', allow_none=True, sync=True, help="""Use a
218 default_value='', allow_none=True, sync=True, help="""Use a
211 predefined styling for the buttons.""")
219 predefined styling for the buttons.""")
212
220
213 @register('IPython.RadioButtons')
221 @register('IPython.RadioButtons')
214 class RadioButtons(_Selection):
222 class RadioButtons(_Selection):
215 """Group of radio buttons that represent an enumeration. Only one radio
223 """Group of radio buttons that represent an enumeration. Only one radio
216 button can be toggled at any point in time."""
224 button can be toggled at any point in time."""
217 _view_name = Unicode('RadioButtonsView', sync=True)
225 _view_name = Unicode('RadioButtonsView', sync=True)
218
226
219
227
220
228
221 @register('IPython.Select')
229 @register('IPython.Select')
222 class Select(_Selection):
230 class Select(_Selection):
223 """Listbox that only allows one item to be selected at any given time."""
231 """Listbox that only allows one item to be selected at any given time."""
224 _view_name = Unicode('SelectView', sync=True)
232 _view_name = Unicode('SelectView', sync=True)
225
233
226
234
227 @register('IPython.SelectMultiple')
235 @register('IPython.SelectMultiple')
228 class SelectMultiple(_MultipleSelection):
236 class SelectMultiple(_MultipleSelection):
229 """Listbox that allows many items to be selected at any given time.
237 """Listbox that allows many items to be selected at any given time.
230 Despite their names, inherited from ``_Selection``, the currently chosen
238 Despite their names, inherited from ``_Selection``, the currently chosen
231 option values, ``value``, or their labels, ``selected_labels`` must both be
239 option values, ``value``, or their labels, ``selected_labels`` must both be
232 updated with a list-like object."""
240 updated with a list-like object."""
233 _view_name = Unicode('SelectMultipleView', sync=True)
241 _view_name = Unicode('SelectMultipleView', sync=True)
234
242
235
243
236 # Remove in IPython 4.0
244 # Remove in IPython 4.0
237 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
245 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
238 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
246 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
239 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
247 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
240 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
248 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
General Comments 0
You need to be logged in to leave comments. Login now