From 8022cff0419709b6d8b49d80a1b71903d9fb55d9 Mon Sep 17 00:00:00 2001 From: Matthias BUSSONNIER Date: Sun, 29 Jul 2012 21:43:17 +0200 Subject: [PATCH 01/26] Add a per cell toolbar. This add a per-cell globally toggleable toolbar the main purpouse is to easily edit metadata. this come with a few example like adding checkbox, dropdown list, simple button, button with icon... please see the js-doc of IPython/frontend/html/notebook/static/js/MetaUI.js for more info --- .../html/notebook/static/css/metaui.css | 54 +++ .../html/notebook/static/css/notebook.css | 2 + .../frontend/html/notebook/static/js/cell.js | 1 + .../html/notebook/static/js/codecell.js | 3 + .../html/notebook/static/js/maintoolbar.js | 10 + .../html/notebook/static/js/metaui.js | 359 ++++++++++++++++++ .../html/notebook/static/js/textcell.js | 4 +- .../html/notebook/static/js/toolbar.js | 2 +- .../html/notebook/templates/notebook.html | 2 + 9 files changed, 435 insertions(+), 2 deletions(-) create mode 100644 IPython/frontend/html/notebook/static/css/metaui.css create mode 100644 IPython/frontend/html/notebook/static/js/metaui.js diff --git a/IPython/frontend/html/notebook/static/css/metaui.css b/IPython/frontend/html/notebook/static/css/metaui.css new file mode 100644 index 000000000..1e1e6c01f --- /dev/null +++ b/IPython/frontend/html/notebook/static/css/metaui.css @@ -0,0 +1,54 @@ +/*Css for the metadata edit area*/ + +.metaedit{ + border:thin solid #DDD; + margin-left:81px; + border-bottom:none; + background : #EEE; + border-top-right-radius: 3px; + border-top-left-radius: 3px; + display:none; +} + +.code_cell .metaedit{ + margin-left:81px; +} + +.text_cell .metaedit{ + margin-left:0px; +} + +.editmetaon div.input_area , .editmetaon div.text_cell_input{ + border-top-right-radius: 0px; + border-top-left-radius: 0px; +} + +.editmetaon .metaedit { + display:block; +} + +.metaedit ui-button { + border :none; +} + +.button_container { + float:right; + /*width:60px;*/ +} + +.button_container .ui-state-default, .button_container .ui-state-hover, .button_container .ui-state-hover span{ + border-radius : 0 0 0 0; + border : none; +} + +.metaedit select { + margin:10px; + margin-top:0px; + margin-bottom:0px; +} + +.metaedit input[type=checkbox] { + margin-bottom:1px; + +} + diff --git a/IPython/frontend/html/notebook/static/css/notebook.css b/IPython/frontend/html/notebook/static/css/notebook.css index 83f55775f..2e50b0823 100644 --- a/IPython/frontend/html/notebook/static/css/notebook.css +++ b/IPython/frontend/html/notebook/static/css/notebook.css @@ -29,6 +29,7 @@ span#notebook_name { font-size: 146.5%; } + .ui-menubar-item .ui-button .ui-button-text { padding: 0.4em 1.0em; font-size: 100%; @@ -145,6 +146,7 @@ div.ui-widget-content { .cell { border: 1px solid transparent; + position: relative; } div.cell { diff --git a/IPython/frontend/html/notebook/static/js/cell.js b/IPython/frontend/html/notebook/static/js/cell.js index e33f221c1..1092b5b87 100644 --- a/IPython/frontend/html/notebook/static/js/cell.js +++ b/IPython/frontend/html/notebook/static/js/cell.js @@ -163,6 +163,7 @@ var IPython = (function (IPython) { if (data.metadata !== undefined) { this.metadata = data.metadata; } + this.metaui.rebuild(); }; diff --git a/IPython/frontend/html/notebook/static/js/codecell.js b/IPython/frontend/html/notebook/static/js/codecell.js index 9cf2ba9b2..0f80c0f0a 100644 --- a/IPython/frontend/html/notebook/static/js/codecell.js +++ b/IPython/frontend/html/notebook/static/js/codecell.js @@ -58,7 +58,10 @@ var IPython = (function (IPython) { /** @method create_element */ CodeCell.prototype.create_element = function () { + this.metaui = new IPython.MetaUI(this); + var cell = $('
').addClass('cell border-box-sizing code_cell vbox'); + cell.append(this.metaui.element); cell.attr('tabindex','2'); var input = $('
').addClass('input hbox'); input.append($('
').addClass('prompt input_prompt')); diff --git a/IPython/frontend/html/notebook/static/js/maintoolbar.js b/IPython/frontend/html/notebook/static/js/maintoolbar.js index c82a4667b..cbe5440e5 100644 --- a/IPython/frontend/html/notebook/static/js/maintoolbar.js +++ b/IPython/frontend/html/notebook/static/js/maintoolbar.js @@ -116,6 +116,16 @@ var IPython = (function (IPython) { } ],'run_int'); + this.add_buttons_group([ + { + id : 'show_meta', + label : 'Show Per Cell Toolbar', + icon : 'ui-icon-image', + callback : function () { + $('body').toggleClass('editmetaon'); + } + } + ],'meta_on'); }; diff --git a/IPython/frontend/html/notebook/static/js/metaui.js b/IPython/frontend/html/notebook/static/js/metaui.js new file mode 100644 index 000000000..8bb7b93ef --- /dev/null +++ b/IPython/frontend/html/notebook/static/js/metaui.js @@ -0,0 +1,359 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2012 The IPython Development Team +// +// Distributed under the terms of the BSD License. The full license is in +// the file COPYING, distributed as part of this software. +//---------------------------------------------------------------------------- + +//============================================================================ +// MetaUI +//============================================================================ + + +/** + * A Module to control the per-cell toolbar. + * @module IPython + * @namespace IPython + * @submodule MetaUI + */ +var IPython = (function (IPython) { + "use strict"; + + + /** + * @constructor + * @class MetaUI + * @param {The cell to attach the metadata UI to} cell + */ + var MetaUI = function (cell) { + MetaUI._instances.push(this); + this.metainner = $('
'); + this.cell = cell; + this.element = $('
').addClass('metaedit') + .append(this.metainner) + this.rebuild(); + return this; + }; + + /** + * Class variable that should contain a dict of all availlable callback + * we need to think of wether or not we allow nested namespace + * @property _callback_dict + * @private + */ + MetaUI._callback_dict = {}; + + /** + * Class variable that should contain the reverse order list of the button + * to add to the toolbar of each cell + * @property _button_list + * @private + */ + MetaUI._button_list = []; + + /** + * keep a list of all instances to + * be able to llop over them... + * but how to 'destroy' them ? + * have to think about it... + * or loop over the cells, and find their MetaUI instances. + * @private + * @property _instances + */ + MetaUI._instances =[] + + /** + * keep a list of all the availlabel presets for the toolbar + * @private + * @property _presets + */ + MetaUI._presets ={} + + // this is by design not a prototype. + /** + * Register a callback to create an UI element in a cell toolbar. + * @method register_callback + * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name + * for easier sorting and avoid collision + * @param callback {function(div, cell)} callback that will be called to generate the ui element + * + * + * The callback will receive the following element : + * + * * a div in which to add element. + * * the cell it is responsable from + * + * @example + * + * Example that create callback for a button that toggle between `true` and `false` label, + * with the metadata under the key 'foo' to reflect the status of the button. + * + * // first param reference to a DOM div + * // second param reference to the cell. + * var toggle = function(div, cell) { + * var button_container = $(div) + * + * // let's create a button that show the current value of the metadata + * var button = $('
').button({label:String(cell.metadata.foo)}); + * + * // On click, change the metadata value and update the button label + * button.click(function(){ + * var v = cell.metadata.foo; + * cell.metadata.foo = !v; + * button.button("option","label",String(!v)); + * }) + * + * // add the button to the DOM div. + * button_container.append(button); + * } + * + * // now we register the callback under the name `foo` to give the + * // user the ability to use it later + * MetaUI.register_callback('foo',toggle); + */ + MetaUI.register_callback = function(name, callback){ + // what do we do if name already exist ? + MetaUI._callback_dict[name] = callback; + }; + + /** + * Register a preset of UI element in a cell toolbar. + * Not supported Yet. + * @method register_preset + * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name + * for easier sorting and avoid collision + * @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list + * should correspond to a name of a registerd callback. + * + * @private + * @example + * + * MetaUI.register_callback('foo.c1',function(div,cell){...}); + * MetaUI.register_callback('foo.c2',function(div,cell){...}); + * MetaUI.register_callback('foo.c3',function(div,cell){...}); + * MetaUI.register_callback('foo.c4',function(div,cell){...}); + * MetaUI.register_callback('foo.c5',function(div,cell){...}); + * + * MetaUI.register_preset('foo.foo_preset1',['foo.c1','foo.c2','foo.c5']) + * MetaUI.register_preset('foo.foo_preset2',['foo.c4','foo.c5']) + */ + MetaUI.register_preset = function(name, preset_list){ + MetaUI._presets[name] = preset_list + } + /** + * set an UI preset from `register_preset` + * @method set_preset + * @param preset_name {String} string corresponding to the preset name + * + * @static + * @private + * @example + * + * MetaUI.set_preset('foo.foo_preset1'); + */ + MetaUI.set_preset= function(preset_name){ + var preset = MetaUI._presets[preset_name]; + + if(preset != undefined){ + MetaUI._button_list = preset; + MetaUI.rebuild_all(); + } + } + + // this is by design not a prototype. + /** + * This should be called on the class and not on a instance as it will trigger + * rebuild of all the instances. + * @method rebuild_all + * @static + * + */ + MetaUI.rebuild_all = function(){ + for(var i in MetaUI._instances){ + MetaUI._instances[i].rebuild(); + } + } + + /** + * Rebuild all the button on the toolbar to update it's state. + * @method rebuild + */ + MetaUI.prototype.rebuild = function(){ + // strip evrything from the div + // which is probabli metainner. + // or this.element. + this.metainner.empty(); + //this.add_raw_edit_button() + + + var cdict = MetaUI._callback_dict; + var preset = MetaUI._button_list; + // Yes we iterate on the class varaible, not the instance one. + for ( var index in MetaUI._button_list){ + var local_div = $('
').addClass('button_container'); + this.metainner.append(local_div) + cdict[preset[index]](local_div,this.cell) + } + + } + + var raw_edit = function(cell){ + + var md = cell.metadata + + var textarea = $('