PDA

View Full Version : Multiline Toolbar Extension



dangreenfield
12 Sep 2010, 8:06 PM
http://www.sencha.com/forum/attachment.php?attachmentid=22345&d=1284350744

I've often wondered why Ext didn't handle multiline toolbars, especially in the HtmlEditor. I investigated the code to see if this could be done and realised that it only required a small change in order to handle it. I've added the code below incase someone else also requires the functionality, and propose that it be included in any future release of Ext.

I felt the best way to identify when a new toolbar line was required was through a new Toolbar Item, called a Break. This works similar to the Fill Item and means that you can include it in the toolbar at any point where you feel that a new toolbar line should be started.



Ext.Toolbar.Break = Ext.extend(Ext.Toolbar.Item, {
render: Ext.emptyFn,
isBreak: true
});

Ext.reg('tbbreak', Ext.Toolbar.Break);


Adding a Break to the toolbar is as simple as including '.' as a Toolbar Item. I overrode the Ext.Toolbar code to handle this functionality.



Ext.apply(Ext.Toolbar.prototype, {

// Override the lookupComponent code to cater for the Break Item
lookupComponent: function(c) {
if (Ext.isString(c)) {

// New code
if (c == '.') {
c = new Ext.Toolbar.Break();

// Existing code
} else if (c == '-') {
c = new Ext.Toolbar.Separator();
} else if (c == ' ') {
c = new Ext.Toolbar.Spacer();
} else if (c == '->') {
c = new Ext.Toolbar.Fill();
} else {
c = new Ext.Toolbar.TextItem(c);
}
this.applyDefaults(c);
} else {
if (c.isFormField || c.render) {
c = this.createComponent(c);
} else if (c.tag) {
c = new Ext.Toolbar.Item({autoEl: c});
} else if (c.tagName) {
c = new Ext.Toolbar.Item({el:c});
} else if (Ext.isObject(c)) {
c = c.xtype ? this.createComponent(c) : this.constructButton(c);
}
}
return c;
},

// Add a function for adding a Break item
addBreak: function() {
this.add(new Ext.Toolbar.Break());
}

});


I chose to override the Ext.Toolbar code rather than extend it so that the multiline functionality would be available to all Toolbars.

The next step was to override the Ext.layout.ToolbarLayout.onLayout function. This is where the multiline functionality has been included.



// Override existing Toolbar onLayout with enhanced layout functionality
// Overriding the function makes it available to all toolbars
Ext.apply(Ext.layout.ToolbarLayout.prototype, {

// onLayout is the function to override
onLayout: function(ct, target) {
var tableIndex = 0, targetTable;
var layout = this;

// Function to cleanup toolbar rows
// Was previously called once but is now called for each toolbar table
function cleanupRows() {
layout.cleanup(layout.leftTr);
layout.cleanup(layout.rightTr);
layout.cleanup(layout.extrasTr);
}

// Function to add a new toolbar table
// Is called for each toolbar row
function nextTable() {

// Create new table if not already created (could have been added after render)
if (!target.dom.childNodes[tableIndex]) {
var align = ct.buttonAlign == 'center' ? 'center' : 'left';
target.insertHtml('beforeEnd', String.format(layout.tableHTML, align));
}

// Focus on current table
targetTable = Ext.fly(target.dom.childNodes[tableIndex]);

// If second or greater table then clean up previous table
// and add a class that adds a spacer between tables
if (tableIndex) {
cleanupRows();
targetTable.addClass('x-toolbar-add-row');
}

// Increment table index
tableIndex++;

// Assign specific row handlers
layout.leftTr = targetTable.child('tr.x-toolbar-left-row', true);
layout.rightTr = targetTable.child('tr.x-toolbar-right-row', true);
layout.extrasTr = targetTable.child('tr.x-toolbar-extras-row', true);
layout.side = ct.buttonAlign == 'right' ? layout.rightTr : layout.leftTr;
}

// If running for the first time, perform necessary functionality
if (!this.leftTr) {
target.addClass('x-toolbar-layout-ct');
if (this.hiddenItem == undefined) {
this.hiddenItems = [];
}
}

// Create and/or select first toolbar table
nextTable();

// Loop though toolbar items
var items = ct.items.items, position = 0;
for (var i = 0, len = items.length, c; i < len; i++, position++) {
c = items[i];

// If item is the new toolbar break item then...
if (c.isBreak) {

// ...create and/or select additional toolbar table
nextTable();

// Existing code...
} else if (c.isFill) {
this.side = this.rightTr;
position = -1;
} else if (!c.rendered) {
c.render(this.insertCell(c, this.side, position));
} else {
if (!c.xtbHidden && !this.isValidParent(c, this.side.childNodes[position])) {
var td = this.insertCell(c, this.side, position);
td.appendChild(c.getPositionEl().dom);
c.container = Ext.get(td);
}
}
}

// Clean up last toolbar table
cleanupRows();

this.fitToSize(target);
}
});


This leaves two things to do. One is to add a 1px padding to the top of any additional Toolbar lines. To do this, add the following code to the ext-all.css file (or one of your own loaded css files).



.x-toolbar .x-toolbar-add-row {
padding-top: 1px;
}


The last step is to provide an HtmlEditor plugin that allows you to add a Break to the Toolbar.



Ext.ux.form.HtmlEditor.Break = function() {

// PRIVATE

// pointer to Ext.form.HtmlEditor
var editor;

// Render Toolbar Break
function onRender() {
editor.getToolbar().addBreak();
}

// PUBLIC

return {

// Ext.ux.form.HtmlEditor.Break.init
// called upon instantiation
init: function(htmlEditor) {
editor = htmlEditor;

// Call onRender when Toolbar rendered
editor.on('render', onRender, this);
}
}
};


Here is an example of an HtmlEditor using this plugin... (See the attached image to see the results of this)



...
items: [{
xtype: 'htmleditor',
plugins: [
new Ext.ux.form.HtmlEditor.Break(),
new Ext.ux.form.HtmlEditor.UndoRedo(),
new Ext.ux.form.HtmlEditor.Styles(),
new Ext.ux.form.HtmlEditor.FontSize(),
new Ext.ux.form.HtmlEditor.Separator(),
new Ext.ux.form.HtmlEditor.Word(),
new Ext.ux.form.HtmlEditor.Divider(),
new Ext.ux.form.HtmlEditor.Table(),
new Ext.ux.form.HtmlEditor.HR(),
new Ext.ux.form.HtmlEditor.IndentOutdent(),
new Ext.ux.form.HtmlEditor.SubSuperScript(),
new Ext.ux.form.HtmlEditor.RemoveFormat(),
new Ext.ux.form.HtmlEditor.SpecialCharacters(),
new Ext.ux.form.HtmlEditor.Break(),
new Ext.ux.form.HtmlEditor.TableFull()
]
}]
...


With this enhancement, you can build a TinyMCE looking toolbar to your HtmlEditor. As it's available to all Toolbars, you can use it elsewhere as well (such as at the top or bottom of a panel).

d1rty
22 Sep 2010, 5:30 AM
works great, thanks

dan_b
22 Sep 2010, 7:18 AM
Funnily enough I was just wondering about that... thanks!

d1rty
23 Sep 2010, 12:40 AM
One small issue, it's causing my the tbfill on a grid top toolbar to disappear.

without MultiLineToolbar.js
http://farm5.static.flickr.com/4154/5016644213_7ffe5066e8_o.png

with MultiLineToolbar.js
http://farm5.static.flickr.com/4124/5016644195_4b461f58dd_o.png

I've attached the file which contains all the Multiline toolbar changes.. but i can't see what might be killing the tbfill

Chris

dangreenfield
23 Sep 2010, 6:03 PM
One small issue, it's causing my the tbfill on a grid top toolbar to disappear.

Oops! My fault. I forgot to put 'this.' before 'side' on line 47.

I've updated my first post and the attached zip file.

Thanks for spotting this Chris!

d1rty
23 Sep 2010, 11:05 PM
perfect, that fixed it

tobiu
24 Sep 2010, 7:19 AM
i was already happy with the simple way:



tbar : {
height : 54
,layout : 'anchor'
,xtype : 'container'
,defaults : {
anchor : '100%'
,height : 27
}
,items : [
toolbar1
,toolbar2
]
}



kind regards ;)
tobiu