Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / toolbar / Toolbar.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.toolbar.Toolbar
17  * @extends Ext.container.Container
18
19 Basic Toolbar class. Although the {@link Ext.container.Container#defaultType defaultType} for Toolbar is {@link Ext.button.Button button}, Toolbar 
20 elements (child items for the Toolbar container) may be virtually any type of Component. Toolbar elements can be created explicitly via their 
21 constructors, or implicitly via their xtypes, and can be {@link #add}ed dynamically.
22
23 __Some items have shortcut strings for creation:__
24
25 | Shortcut | xtype         | Class                         | Description                                        |
26 |:---------|:--------------|:------------------------------|:---------------------------------------------------|
27 | `->`     | `tbspacer`    | {@link Ext.toolbar.Fill}      | begin using the right-justified button container   |
28 | `-`      | `tbseparator` | {@link Ext.toolbar.Separator} | add a vertical separator bar between toolbar items |
29 | ` `      | `tbspacer`    | {@link Ext.toolbar.Spacer}    | add horiztonal space between elements              |
30
31 {@img Ext.toolbar.Toolbar/Ext.toolbar.Toolbar1.png Toolbar component}
32 Example usage:
33
34     Ext.create('Ext.toolbar.Toolbar', {
35         renderTo: document.body,
36         width   : 500,
37         items: [
38             {
39                 // xtype: 'button', // default for Toolbars
40                 text: 'Button'
41             },
42             {
43                 xtype: 'splitbutton',
44                 text : 'Split Button'
45             },
46             // begin using the right-justified button container
47             '->', // same as {xtype: 'tbfill'}, // Ext.toolbar.Fill
48             {
49                 xtype    : 'textfield',
50                 name     : 'field1',
51                 emptyText: 'enter search term'
52             },
53             // add a vertical separator bar between toolbar items
54             '-', // same as {xtype: 'tbseparator'} to create Ext.toolbar.Separator
55             'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.toolbar.TextItem
56             {xtype: 'tbspacer'},// same as ' ' to create Ext.toolbar.Spacer
57             'text 2',
58             {xtype: 'tbspacer', width: 50}, // add a 50px space
59             'text 3'
60         ]
61     });
62
63 Toolbars have {@link #enable} and {@link #disable} methods which when called, will enable/disable all items within your toolbar.
64
65 {@img Ext.toolbar.Toolbar/Ext.toolbar.Toolbar2.png Toolbar component}
66 Example usage:
67
68     Ext.create('Ext.toolbar.Toolbar', {
69         renderTo: document.body,
70         width   : 400,
71         items: [
72             {
73                 text: 'Button'
74             },
75             {
76                 xtype: 'splitbutton',
77                 text : 'Split Button'
78             },
79             '->',
80             {
81                 xtype    : 'textfield',
82                 name     : 'field1',
83                 emptyText: 'enter search term'
84             }
85         ]
86     });
87
88 {@img Ext.toolbar.Toolbar/Ext.toolbar.Toolbar3.png Toolbar component}
89 Example usage:
90     
91     var enableBtn = Ext.create('Ext.button.Button', {
92         text    : 'Enable All Items',
93         disabled: true,
94         scope   : this,
95         handler : function() {
96             //disable the enable button and enable the disable button
97             enableBtn.disable();
98             disableBtn.enable();
99             
100             //enable the toolbar
101             toolbar.enable();
102         }
103     });
104     
105     var disableBtn = Ext.create('Ext.button.Button', {
106         text    : 'Disable All Items',
107         scope   : this,
108         handler : function() {
109             //enable the enable button and disable button
110             disableBtn.disable();
111             enableBtn.enable();
112             
113             //disable the toolbar
114             toolbar.disable();
115         }
116     });
117     
118     var toolbar = Ext.create('Ext.toolbar.Toolbar', {
119         renderTo: document.body,
120         width   : 400,
121         margin  : '5 0 0 0',
122         items   : [enableBtn, disableBtn]
123     });
124
125 Adding items to and removing items from a toolbar is as simple as calling the {@link #add} and {@link #remove} methods. There is also a {@link #removeAll} method 
126 which remove all items within the toolbar.
127
128 {@img Ext.toolbar.Toolbar/Ext.toolbar.Toolbar4.png Toolbar component}
129 Example usage:
130
131     var toolbar = Ext.create('Ext.toolbar.Toolbar', {
132         renderTo: document.body,
133         width   : 700,
134         items: [
135             {
136                 text: 'Example Button'
137             }
138         ]
139     });
140     
141     var addedItems = [];
142     
143     Ext.create('Ext.toolbar.Toolbar', {
144         renderTo: document.body,
145         width   : 700,
146         margin  : '5 0 0 0',
147         items   : [
148             {
149                 text   : 'Add a button',
150                 scope  : this,
151                 handler: function() {
152                     var text = prompt('Please enter the text for your button:');
153                     addedItems.push(toolbar.add({
154                         text: text
155                     }));
156                 }
157             },
158             {
159                 text   : 'Add a text item',
160                 scope  : this,
161                 handler: function() {
162                     var text = prompt('Please enter the text for your item:');
163                     addedItems.push(toolbar.add(text));
164                 }
165             },
166             {
167                 text   : 'Add a toolbar seperator',
168                 scope  : this,
169                 handler: function() {
170                     addedItems.push(toolbar.add('-'));
171                 }
172             },
173             {
174                 text   : 'Add a toolbar spacer',
175                 scope  : this,
176                 handler: function() {
177                     addedItems.push(toolbar.add('->'));
178                 }
179             },
180             '->',
181             {
182                 text   : 'Remove last inserted item',
183                 scope  : this,
184                 handler: function() {
185                     if (addedItems.length) {
186                         toolbar.remove(addedItems.pop());
187                     } else if (toolbar.items.length) {
188                         toolbar.remove(toolbar.items.last());
189                     } else {
190                         alert('No items in the toolbar');
191                     }
192                 }
193             },
194             {
195                 text   : 'Remove all items',
196                 scope  : this,
197                 handler: function() {
198                     toolbar.removeAll();
199                 }
200             }
201         ]
202     });
203
204  * @constructor
205  * Creates a new Toolbar
206  * @param {Object/Array} config A config object or an array of buttons to <code>{@link #add}</code>
207  * @docauthor Robert Dougan <rob@sencha.com>
208  * @markdown
209  */
210 Ext.define('Ext.toolbar.Toolbar', {
211     extend: 'Ext.container.Container',
212     requires: [
213         'Ext.toolbar.Fill',
214         'Ext.layout.container.HBox',
215         'Ext.layout.container.VBox',
216         'Ext.FocusManager'
217     ],
218     uses: [
219         'Ext.toolbar.Separator'
220     ],
221     alias: 'widget.toolbar',
222     alternateClassName: 'Ext.Toolbar',
223     
224     isToolbar: true,
225     baseCls  : Ext.baseCSSPrefix + 'toolbar',
226     ariaRole : 'toolbar',
227     
228     defaultType: 'button',
229     
230     /**
231      * @cfg {Boolean} vertical
232      * Set to `true` to make the toolbar vertical. The layout will become a `vbox`.
233      * (defaults to `false`)
234      */
235     vertical: false,
236
237     /**
238      * @cfg {String/Object} layout
239      * This class assigns a default layout (<code>layout:'<b>hbox</b>'</code>).
240      * Developers <i>may</i> override this configuration option if another layout
241      * is required (the constructor must be passed a configuration object in this
242      * case instead of an array).
243      * See {@link Ext.container.Container#layout} for additional information.
244      */
245
246     /**
247      * @cfg {Boolean} enableOverflow
248      * Defaults to false. Configure <code>true</code> to make the toolbar provide a button
249      * which activates a dropdown Menu to show items which overflow the Toolbar's width.
250      */
251     enableOverflow: false,
252     
253     // private
254     trackMenus: true,
255     
256     itemCls: Ext.baseCSSPrefix + 'toolbar-item',
257     
258     initComponent: function() {
259         var me = this,
260             keys;
261
262         // check for simplified (old-style) overflow config:
263         if (!me.layout && me.enableOverflow) {
264             me.layout = { overflowHandler: 'Menu' };
265         }
266         
267         if (me.dock === 'right' || me.dock === 'left') {
268             me.vertical = true;
269         }
270
271         me.layout = Ext.applyIf(Ext.isString(me.layout) ? {
272             type: me.layout
273         } : me.layout || {}, {
274             type: me.vertical ? 'vbox' : 'hbox',
275             align: me.vertical ? 'stretchmax' : 'middle',
276             clearInnerCtOnLayout: true
277         });
278         
279         if (me.vertical) {
280             me.addClsWithUI('vertical');
281         }
282         
283         // @TODO: remove this hack and implement a more general solution
284         if (me.ui === 'footer') {
285             me.ignoreBorderManagement = true;
286         }
287         
288         me.callParent();
289
290         /**
291          * @event overflowchange
292          * Fires after the overflow state has changed.
293          * @param {Object} c The Container
294          * @param {Boolean} lastOverflow overflow state
295          */
296         me.addEvents('overflowchange');
297         
298         // Subscribe to Ext.FocusManager for key navigation
299         keys = me.vertical ? ['up', 'down'] : ['left', 'right'];
300         Ext.FocusManager.subscribe(me, {
301             keys: keys
302         });
303     },
304
305     /**
306      * <p>Adds element(s) to the toolbar -- this function takes a variable number of
307      * arguments of mixed type and adds them to the toolbar.</p>
308      * <br><p><b>Note</b>: See the notes within {@link Ext.container.Container#add}.</p>
309      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
310      * <ul>
311      * <li>{@link Ext.button.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
312      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
313      * <li>Field: Any form field (equivalent to {@link #addField})</li>
314      * <li>Item: Any subclass of {@link Ext.toolbar.Item} (equivalent to {@link #addItem})</li>
315      * <li>String: Any generic string (gets wrapped in a {@link Ext.toolbar.TextItem}, equivalent to {@link #addText}).
316      * Note that there are a few special strings that are treated differently as explained next.</li>
317      * <li>'-': Creates a separator element (equivalent to {@link #addSeparator})</li>
318      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
319      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
320      * </ul>
321      * @param {Mixed} arg2
322      * @param {Mixed} etc.
323      * @method add
324      */
325
326     // private
327     lookupComponent: function(c) {
328         if (Ext.isString(c)) {
329             var shortcut = Ext.toolbar.Toolbar.shortcuts[c];
330             if (shortcut) {
331                 c = {
332                     xtype: shortcut
333                 };
334             } else {
335                 c = {
336                     xtype: 'tbtext',
337                     text: c
338                 };
339             }
340             this.applyDefaults(c);
341         }
342         return this.callParent(arguments);
343     },
344
345     // private
346     applyDefaults: function(c) {
347         if (!Ext.isString(c)) {
348             c = this.callParent(arguments);
349             var d = this.internalDefaults;
350             if (c.events) {
351                 Ext.applyIf(c.initialConfig, d);
352                 Ext.apply(c, d);
353             } else {
354                 Ext.applyIf(c, d);
355             }
356         }
357         return c;
358     },
359
360     // private
361     trackMenu: function(item, remove) {
362         if (this.trackMenus && item.menu) {
363             var method = remove ? 'mun' : 'mon',
364                 me = this;
365
366             me[method](item, 'menutriggerover', me.onButtonTriggerOver, me);
367             me[method](item, 'menushow', me.onButtonMenuShow, me);
368             me[method](item, 'menuhide', me.onButtonMenuHide, me);
369         }
370     },
371
372     // private
373     constructButton: function(item) {
374         return item.events ? item : this.createComponent(item, item.split ? 'splitbutton' : this.defaultType);
375     },
376
377     // private
378     onBeforeAdd: function(component) {
379         if (component.is('field') || (component.is('button') && this.ui != 'footer')) {
380             component.ui = component.ui + '-toolbar';
381         }
382         
383         // Any separators needs to know if is vertical or not
384         if (component instanceof Ext.toolbar.Separator) {
385             component.setUI((this.vertical) ? 'vertical' : 'horizontal');
386         }
387         
388         this.callParent(arguments);
389     },
390
391     // private
392     onAdd: function(component) {
393         this.callParent(arguments);
394
395         this.trackMenu(component);
396         if (this.disabled) {
397             component.disable();
398         }
399     },
400
401     // private
402     onRemove: function(c) {
403         this.callParent(arguments);
404         this.trackMenu(c, true);
405     },
406
407     // private
408     onButtonTriggerOver: function(btn){
409         if (this.activeMenuBtn && this.activeMenuBtn != btn) {
410             this.activeMenuBtn.hideMenu();
411             btn.showMenu();
412             this.activeMenuBtn = btn;
413         }
414     },
415
416     // private
417     onButtonMenuShow: function(btn) {
418         this.activeMenuBtn = btn;
419     },
420
421     // private
422     onButtonMenuHide: function(btn) {
423         delete this.activeMenuBtn;
424     }
425 }, function() {
426     this.shortcuts = {
427         '-' : 'tbseparator',
428         ' ' : 'tbspacer',
429         '->': 'tbfill'
430     };
431 });