Merge pull request #6700 from Carreau/actions-2

Use notebook actions in toolbar
This commit is contained in:
Min RK 2014-12-12 12:39:46 -08:00
commit 982a742112
6 changed files with 262 additions and 178 deletions

View File

@ -38,6 +38,14 @@ define([
// for this particular combination // for this particular combination
this.element.addClass('notification_widget btn btn-xs navbar-btn'); this.element.addClass('notification_widget btn btn-xs navbar-btn');
}; };
/**
* hide the widget and empty the text
**/
NotificationWidget.prototype.hide = function () {
var that = this;
this.element.fadeOut(100, function(){that.inner.text('');});
};
/** /**
* Set the notification widget message to display for a certain * Set the notification widget message to display for a certain

View File

@ -2,11 +2,12 @@
// Distributed under the terms of the Modified BSD License. // Distributed under the terms of the Modified BSD License.
define([ define([
'require',
'base/js/namespace', 'base/js/namespace',
'jquery', 'jquery',
'notebook/js/toolbar', './toolbar',
'notebook/js/celltoolbar', './celltoolbar'
], function(IPython, $, toolbar, celltoolbar) { ], function(require, IPython, $, toolbar, celltoolbar) {
"use strict"; "use strict";
var MainToolBar = function (selector, options) { var MainToolBar = function (selector, options) {
@ -19,141 +20,97 @@ define([
* Dictionary of keyword arguments. * Dictionary of keyword arguments.
* events: $(Events) instance * events: $(Events) instance
* notebook: Notebook instance * notebook: Notebook instance
*/ **/
toolbar.ToolBar.apply(this, arguments); toolbar.ToolBar.apply(this, [selector, options] );
this.events = options.events; this.events = options.events;
this.notebook = options.notebook; this.notebook = options.notebook;
this.construct(); this._make();
this.add_celltype_list(); Object.seal(this);
this.add_celltoolbar_list();
this.bind_events();
}; };
MainToolBar.prototype = Object.create(toolbar.ToolBar.prototype); MainToolBar.prototype = Object.create(toolbar.ToolBar.prototype);
MainToolBar.prototype.construct = function () { MainToolBar.prototype._make = function () {
var that = this; var grps = [
this.add_buttons_group([ [
{ ['ipython.save-notebook'],
id : 'save_b', 'save-notbook'
label : 'Save and Checkpoint', ],
icon : 'fa-save', [
callback : function () { ['ipython.insert-cell-after'],
that.notebook.save_checkpoint(); 'insert_above_below'],
} [
} ['ipython.cut-selected-cell',
]); 'ipython.copy-selected-cell',
'ipython.paste-cell-after'
this.add_buttons_group([ ] ,
{ 'cut_copy_paste'],
id : 'insert_below_b', [
label : 'Insert Cell Below', ['ipython.move-selected-cell-up',
icon : 'fa-plus', 'ipython.move-selected-cell-down'
callback : function () { ],
that.notebook.insert_cell_below('code'); 'move_up_down'],
that.notebook.select_next(); [ ['ipython.run-select-next',
that.notebook.focus_cell(); 'ipython.interrupt-kernel',
} 'ipython.restart-kernel'
} ],
],'insert_above_below'); 'run_int'],
['<add_celltype_list>'],
this.add_buttons_group([ ['<add_celltoolbar_list>']
{ ];
id : 'cut_b', this.construct(grps);
label : 'Cut Cell',
icon : 'fa-cut',
callback : function () {
that.notebook.cut_cell();
}
},
{
id : 'copy_b',
label : 'Copy Cell',
icon : 'fa-copy',
callback : function () {
that.notebook.copy_cell();
}
},
{
id : 'paste_b',
label : 'Paste Cell Below',
icon : 'fa-paste',
callback : function () {
that.notebook.paste_cell_below();
}
}
],'cut_copy_paste');
this.add_buttons_group([
{
id : 'move_up_b',
label : 'Move Cell Up',
icon : 'fa-arrow-up',
callback : function () {
that.notebook.move_cell_up();
}
},
{
id : 'move_down_b',
label : 'Move Cell Down',
icon : 'fa-arrow-down',
callback : function () {
that.notebook.move_cell_down();
}
}
],'move_up_down');
this.add_buttons_group([
{
id : 'run_b',
label : 'Run Cell',
icon : 'fa-play',
callback : function () {
/**
* emulate default shift-enter behavior
*/
that.notebook.execute_cell_and_select_below();
}
},
{
id : 'interrupt_b',
label : 'Interrupt',
icon : 'fa-stop',
callback : function () {
that.notebook.kernel.interrupt();
}
},
{
id : 'repeat_b',
label : 'Restart Kernel',
icon : 'fa-repeat',
callback : function () {
that.notebook.restart_kernel();
}
}
],'run_int');
}; };
MainToolBar.prototype.add_celltype_list = function () { // add a cell type drop down to the maintoolbar.
this.element // triggered when the pseudo action `<add_celltype_list>` is
.append($('<select/>') // encountered when building a toolbar.
.attr('id','cell_type') MainToolBar.prototype._pseudo_actions.add_celltype_list = function () {
.addClass('form-control select-xs') var that = this;
.append($('<option/>').attr('value','code').text('Code')) var sel = $('<select/>')
.append($('<option/>').attr('value','markdown').text('Markdown')) .attr('id','cell_type')
.append($('<option/>').attr('value','raw').text('Raw NBConvert')) .addClass('form-control select-xs')
.append($('<option/>').attr('value','heading').text('Heading')) .append($('<option/>').attr('value','code').text('Code'))
); .append($('<option/>').attr('value','markdown').text('Markdown'))
.append($('<option/>').attr('value','raw').text('Raw NBConvert'))
.append($('<option/>').attr('value','heading').text('Heading'));
this.events.on('selected_cell_type_changed.Notebook', function (event, data) {
if (data.cell_type === 'heading') {
sel.val('Markdown');
} else {
sel.val(data.cell_type);
}
});
sel.change(function () {
var cell_type = $(this).val();
switch (cell_type) {
case 'code':
that.notebook.to_code();
break;
case 'markdown':
that.notebook.to_markdown();
break;
case 'raw':
that.notebook.to_raw();
break;
case 'heading':
that.notebook._warn_heading();
that.notebook.to_heading();
sel.val('markdown');
break;
default:
console.log("unrecognized cell type:", cell_type);
}
});
return sel;
}; };
MainToolBar.prototype.add_celltoolbar_list = function () { MainToolBar.prototype._pseudo_actions.add_celltoolbar_list = function () {
var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:'); var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:');
var select = $('<select/>') var select = $('<select/>')
.attr('id', 'ctb_select') .attr('id', 'ctb_select')
.addClass('form-control select-xs') .addClass('form-control select-xs')
.append($('<option/>').attr('value', '').text('None')); .append($('<option/>').attr('value', '').text('None'));
this.element.append(label).append(select);
var that = this; var that = this;
select.change(function() { select.change(function() {
var val = $(this).val(); var val = $(this).val();
@ -182,42 +139,13 @@ define([
if (select.val() !== data.name) if (select.val() !== data.name)
select.val(data.name); select.val(data.name);
}); });
var wrapper = $('<div/>').addClass('btn-group');
wrapper.append(label).append(select);
return wrapper;
}; };
MainToolBar.prototype.bind_events = function () { // Backwards compatibility.
var that = this;
this.element.find('#cell_type').change(function () {
var cell_type = $(this).val();
switch (cell_type) {
case 'code':
that.notebook.to_code();
break;
case 'markdown':
that.notebook.to_markdown();
break;
case 'raw':
that.notebook.to_raw();
break;
case 'heading':
that.notebook._warn_heading();
that.notebook.to_heading();
that.element.find('#cell_type').val("markdown");
break;
default:
console.log("unrecognized cell type:", cell_type);
}
});
this.events.on('selected_cell_type_changed.Notebook', function (event, data) {
if (data.cell_type === 'heading') {
that.element.find('#cell_type').val(data.cell_type+data.level);
} else {
that.element.find('#cell_type').val(data.cell_type);
}
});
};
// Backwards compatability.
IPython.MainToolBar = MainToolBar; IPython.MainToolBar = MainToolBar;
return {'MainToolBar': MainToolBar}; return {'MainToolBar': MainToolBar};

View File

@ -65,6 +65,86 @@ define([
{ shortcut: cmd_ctrl + "Shift-z", help:"redo" }, { shortcut: cmd_ctrl + "Shift-z", help:"redo" },
{ shortcut: cmd_ctrl + "y", help:"redo" }, { shortcut: cmd_ctrl + "y", help:"redo" },
].concat( platform_specific ); ].concat( platform_specific );
var mac_humanize_map = {
// all these are unicode, will probably display badly on anything except macs.
// these are the standard symbol that are used in MacOS native menus
// cf http://apple.stackexchange.com/questions/55727/
// for htmlentities and/or unicode value
'cmd':'⌘',
'shift':'⇧',
'alt':'⌥',
'up':'↑',
'down':'↓',
'left':'←',
'right':'→',
'eject':'⏏',
'tab':'⇥',
'backtab':'⇤',
'capslock':'⇪',
'esc':'⎋',
'ctrl':'⌃',
'enter':'↩',
'pageup':'⇞',
'pagedown':'⇟',
'home':'↖',
'end':'↘',
'altenter':'⌤',
'space':'␣',
'delete':'⌦',
'backspace':'⌫',
'apple':'',
};
var default_humanize_map = {
'shift':'Shift',
'alt':'Alt',
'up':'Up',
'down':'Down',
'left':'Left',
'right':'Right',
'tab':'Tab',
'capslock':'Caps Lock',
'esc':'Esc',
'ctrl':'Ctrl',
'enter':'Enter',
'pageup':'Page Up',
'pagedown':'Page Down',
'home':'Home',
'end':'End',
'space':'Space',
'backspace':'Backspace',
};
var humanize_map;
if (platform === 'MacOS'){
humanize_map = mac_humanize_map;
} else {
humanize_map = default_humanize_map;
}
function humanize_key(key){
if (key.length === 1){
key = key.toUpperCase();
}
return humanize_map[key.toLowerCase()]||key;
}
function humanize_sequence(sequence){
var joinchar = ',';
var hum = _.map(sequence.replace(/meta/g, 'cmd').split(','), humanize_shortcut).join(joinchar);
return hum;
}
function humanize_shortcut(shortcut){
var joinchar = '-';
if (platform === 'MacOS'){
joinchar = '';
}
var sh = _.map(shortcut.split('-'), humanize_key ).join(joinchar);
return sh;
}
QuickHelp.prototype.show_keyboard_shortcuts = function () { QuickHelp.prototype.show_keyboard_shortcuts = function () {
@ -157,7 +237,10 @@ define([
var build_one = function (s) { var build_one = function (s) {
var help = s.help; var help = s.help;
var shortcut = prettify(s.shortcut); var shortcut = '';
if(s.shortcut){
shortcut = prettify(humanize_sequence(s.shortcut));
}
return $('<div>').addClass('quickhelp'). return $('<div>').addClass('quickhelp').
append($('<span/>').addClass('shortcut_key').append($(shortcut))). append($('<span/>').addClass('shortcut_key').append($(shortcut))).
append($('<span/>').addClass('shortcut_descr').text(' : ' + help)); append($('<span/>').addClass('shortcut_descr').text(' : ' + help));

View File

@ -3,7 +3,7 @@
define([ define([
'base/js/namespace', 'base/js/namespace',
'jquery', 'jquery'
], function(IPython, $) { ], function(IPython, $) {
"use strict"; "use strict";
@ -11,22 +11,52 @@ define([
* A generic toolbar on which one can add button * A generic toolbar on which one can add button
* @class ToolBar * @class ToolBar
* @constructor * @constructor
* @param {Dom object} selector * @param {Dom_object} selector
*/ */
var ToolBar = function (selector) { var ToolBar = function (selector, options) {
this.selector = selector; this.selector = selector;
this.actions = (options||{}).actions;
if (this.selector !== undefined) { if (this.selector !== undefined) {
this.element = $(selector); this.element = $(selector);
this.style(); this.style();
} }
}; };
ToolBar.prototype._pseudo_actions={};
ToolBar.prototype.construct = function (config) {
for(var k in config){
this.add_buttons_group(config[k][0],k[1]);
}
};
/** /**
* add a group of button into the current toolbar. * Add a group of button into the current toolbar.
* *
* Use a [dict of [list of action name]] to trigger
* on click to the button
* *
* @example * @example
* *
* ... todo, maybe use a list of list to keep ordering.
*
* [
* [
* [
* action_name_1,
* action_name_2,
* action_name_3,
* ],
* optional_group_name
* ],
* ...
* ]
*
* For backward compatibility this also support the
* old methods of adding busson directly bound to callbacks:
* @example
* # deprecate, do not use
* IPython.toolbar.add_buttons_group([ * IPython.toolbar.add_buttons_group([
* { * {
* label:'my button', * label:'my button',
@ -52,27 +82,62 @@ define([
* @param [list.id] {String} id to give to the button * @param [list.id] {String} id to give to the button
* @param [group_id] {String} optionnal id to give to the group * @param [group_id] {String} optionnal id to give to the group
* *
*
* for private usage, the key can also be strings starting with '<' and ending with '>' to inject custom element that cannot
* be bound to an action.
*
*/ */
// TODO JUPYTER:
// get rid of legacy code that handle things that are not actions.
ToolBar.prototype.add_buttons_group = function (list, group_id) { ToolBar.prototype.add_buttons_group = function (list, group_id) {
// handle custom call of pseudoaction binding.
if(typeof(list) === 'string' && list.slice(0,1) === '<' && list.slice(-1) === '>'){
var _pseudo_action;
try{
_pseudo_action = list.slice(1,-1);
this.element.append(this._pseudo_actions[_pseudo_action].call(this));
} catch (e) {
console.warn('ouch, calling ', _pseudo_action, 'does not seem to work...:', e);
}
return ;
}
var that = this;
var btn_group = $('<div/>').addClass("btn-group"); var btn_group = $('<div/>').addClass("btn-group");
if( group_id !== undefined ) { if( group_id !== undefined ) {
btn_group.attr('id',group_id); btn_group.attr('id',group_id);
} }
var el;
for(var i=0; i < list.length; i++) { for(var i=0; i < list.length; i++) {
el = list[i];
var button = $('<button/>') // IIFE because javascript don't have loop scope so
.addClass('btn btn-default') // action_name would otherwise be the same on all iteration
.attr("title", el.label) // of the loop
.append( (function(i,list){
$("<i/>").addClass(el.icon).addClass('fa') var el = list[i];
); var action_name;
var id = el.id; var action;
if( id !== undefined ) if(typeof(el) === 'string'){
button.attr('id',id); action = that.actions.get(el);
var fun = el.callback; action_name = el;
button.click(fun);
btn_group.append(button); }
var button = $('<button/>')
.addClass('btn btn-default')
.attr("title", el.label||action.help)
.append(
$("<i/>").addClass(el.icon||action.icon).addClass('fa')
);
var id = el.id;
if( id !== undefined ){
button.attr('id',id);
}
button.attr('data-jupyter-action', action_name);
var fun = el.callback|| function(){
that.actions.call(action_name);
};
button.click(fun);
btn_group.append(button);
})(i,list);
// END IIFE
} }
$(this.selector).append(btn_group); $(this.selector).append(btn_group);
}; };

View File

@ -66,7 +66,7 @@ casper.notebook_test(function () {
IPython.notebook.select(0); IPython.notebook.select(0);
cell.clear_output(); cell.clear_output();
cell.set_text('a=13; print(a)'); cell.set_text('a=13; print(a)');
$('#run_b').click(); $("button[data-jupyter-action='ipython.run-select-next']")[0].click()
}); });
this.wait_for_output(0); this.wait_for_output(0);

View File

@ -29,7 +29,7 @@ casper.notebook_test(function () {
$('#cell_type').val('markdown').change(); $('#cell_type').val('markdown').change();
var cell = IPython.notebook.get_selected_cell(); var cell = IPython.notebook.get_selected_cell();
cell.set_text('*Baz*'); cell.set_text('*Baz*');
$('#run_b').click(); $("button[data-jupyter-action='ipython.run-select-next']")[0].click();
return cell.get_rendered(); return cell.get_rendered();
}); });
this.test.assertEquals(output.trim(), '<p><em>Baz</em></p>', 'Markdown toolbar items work.'); this.test.assertEquals(output.trim(), '<p><em>Baz</em></p>', 'Markdown toolbar items work.');