Upgrade to ExtJS 3.3.0 - Released 10/06/2010
[extjs.git] / docs / source / ToolbarLayout.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.0
11  * Copyright(c) 2006-2010 Ext JS, Inc.
12  * licensing@extjs.com
13  * http://www.extjs.com/license
14  */
15 <div id="cls-Ext.layout.ToolbarLayout"></div>/**
16  * @class Ext.layout.ToolbarLayout
17  * @extends Ext.layout.ContainerLayout
18  * Layout manager used by Ext.Toolbar. This is highly specialised for use by Toolbars and would not
19  * usually be used by any other class.
20  */
21 Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {
22     monitorResize : true,
23
24     type: 'toolbar',
25
26     <div id="prop-Ext.layout.ToolbarLayout-triggerWidth"></div>/**
27      * @property triggerWidth
28      * @type Number
29      * The width allocated for the menu trigger at the extreme right end of the Toolbar
30      */
31     triggerWidth: 18,
32
33     <div id="prop-Ext.layout.ToolbarLayout-noItemsMenuText"></div>/**
34      * @property noItemsMenuText
35      * @type String
36      * HTML fragment to render into the toolbar overflow menu if there are no items to display
37      */
38     noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',
39
40     /**
41      * @private
42      * @property lastOverflow
43      * @type Boolean
44      * Used internally to record whether the last layout caused an overflow or not
45      */
46     lastOverflow: false,
47
48     /**
49      * @private
50      * @property tableHTML
51      * @type String
52      * String used to build the HTML injected to support the Toolbar's layout. The align property is
53      * injected into this string inside the td.x-toolbar-left element during onLayout.
54      */
55     tableHTML: [
56         '<table cellspacing="0" class="x-toolbar-ct">',
57             '<tbody>',
58                 '<tr>',
59                     '<td class="x-toolbar-left" align="{0}">',
60                         '<table cellspacing="0">',
61                             '<tbody>',
62                                 '<tr class="x-toolbar-left-row"></tr>',
63                             '</tbody>',
64                         '</table>',
65                     '</td>',
66                     '<td class="x-toolbar-right" align="right">',
67                         '<table cellspacing="0" class="x-toolbar-right-ct">',
68                             '<tbody>',
69                                 '<tr>',
70                                     '<td>',
71                                         '<table cellspacing="0">',
72                                             '<tbody>',
73                                                 '<tr class="x-toolbar-right-row"></tr>',
74                                             '</tbody>',
75                                         '</table>',
76                                     '</td>',
77                                     '<td>',
78                                         '<table cellspacing="0">',
79                                             '<tbody>',
80                                                 '<tr class="x-toolbar-extras-row"></tr>',
81                                             '</tbody>',
82                                         '</table>',
83                                     '</td>',
84                                 '</tr>',
85                             '</tbody>',
86                         '</table>',
87                     '</td>',
88                 '</tr>',
89             '</tbody>',
90         '</table>'
91     ].join(""),
92
93     /**
94      * @private
95      * Create the wrapping Toolbar HTML and render/move all the items into the correct places
96      */
97     onLayout : function(ct, target) {
98         //render the Toolbar <table> HTML if it's not already present
99         if (!this.leftTr) {
100             var align = ct.buttonAlign == 'center' ? 'center' : 'left';
101
102             target.addClass('x-toolbar-layout-ct');
103             target.insertHtml('beforeEnd', String.format(this.tableHTML, align));
104
105             this.leftTr   = target.child('tr.x-toolbar-left-row', true);
106             this.rightTr  = target.child('tr.x-toolbar-right-row', true);
107             this.extrasTr = target.child('tr.x-toolbar-extras-row', true);
108
109             if (this.hiddenItem == undefined) {
110                 <div id="prop-Ext.layout.ToolbarLayout-hiddenItems"></div>/**
111                  * @property hiddenItems
112                  * @type Array
113                  * Holds all items that are currently hidden due to there not being enough space to render them
114                  * These items will appear on the expand menu.
115                  */
116                 this.hiddenItems = [];
117             }
118         }
119
120         var side     = ct.buttonAlign == 'right' ? this.rightTr : this.leftTr,
121             items    = ct.items.items,
122             position = 0;
123
124         //render each item if not already rendered, place it into the correct (left or right) target
125         for (var i = 0, len = items.length, c; i < len; i++, position++) {
126             c = items[i];
127
128             if (c.isFill) {
129                 side   = this.rightTr;
130                 position = -1;
131             } else if (!c.rendered) {
132                 c.render(this.insertCell(c, side, position));
133                 this.configureItem(c);
134             } else {
135                 if (!c.xtbHidden && !this.isValidParent(c, side.childNodes[position])) {
136                     var td = this.insertCell(c, side, position);
137                     td.appendChild(c.getPositionEl().dom);
138                     c.container = Ext.get(td);
139                 }
140             }
141         }
142
143         //strip extra empty cells
144         this.cleanup(this.leftTr);
145         this.cleanup(this.rightTr);
146         this.cleanup(this.extrasTr);
147         this.fitToSize(target);
148     },
149
150     /**
151      * @private
152      * Removes any empty nodes from the given element
153      * @param {Ext.Element} el The element to clean up
154      */
155     cleanup : function(el) {
156         var cn = el.childNodes, i, c;
157
158         for (i = cn.length-1; i >= 0 && (c = cn[i]); i--) {
159             if (!c.firstChild) {
160                 el.removeChild(c);
161             }
162         }
163     },
164
165     /**
166      * @private
167      * Inserts the given Toolbar item into the given element
168      * @param {Ext.Component} c The component to add
169      * @param {Ext.Element} target The target to add the component to
170      * @param {Number} position The position to add the component at
171      */
172     insertCell : function(c, target, position) {
173         var td = document.createElement('td');
174         td.className = 'x-toolbar-cell';
175
176         target.insertBefore(td, target.childNodes[position] || null);
177
178         return td;
179     },
180
181     /**
182      * @private
183      * Hides an item because it will not fit in the available width. The item will be unhidden again
184      * if the Toolbar is resized to be large enough to show it
185      * @param {Ext.Component} item The item to hide
186      */
187     hideItem : function(item) {
188         this.hiddenItems.push(item);
189
190         item.xtbHidden = true;
191         item.xtbWidth = item.getPositionEl().dom.parentNode.offsetWidth;
192         item.hide();
193     },
194
195     /**
196      * @private
197      * Unhides an item that was previously hidden due to there not being enough space left on the Toolbar
198      * @param {Ext.Component} item The item to show
199      */
200     unhideItem : function(item) {
201         item.show();
202         item.xtbHidden = false;
203         this.hiddenItems.remove(item);
204     },
205
206     /**
207      * @private
208      * Returns the width of the given toolbar item. If the item is currently hidden because there
209      * is not enough room to render it, its previous width is returned
210      * @param {Ext.Component} c The component to measure
211      * @return {Number} The width of the item
212      */
213     getItemWidth : function(c) {
214         return c.hidden ? (c.xtbWidth || 0) : c.getPositionEl().dom.parentNode.offsetWidth;
215     },
216
217     /**
218      * @private
219      * Called at the end of onLayout. At this point the Toolbar has already been resized, so we need
220      * to fit the items into the available width. We add up the width required by all of the items in
221      * the toolbar - if we don't have enough space we hide the extra items and render the expand menu
222      * trigger.
223      * @param {Ext.Element} target The Element the Toolbar is currently laid out within
224      */
225     fitToSize : function(target) {
226         if (this.container.enableOverflow === false) {
227             return;
228         }
229
230         var width       = target.dom.clientWidth,
231             tableWidth  = target.dom.firstChild.offsetWidth,
232             clipWidth   = width - this.triggerWidth,
233             lastWidth   = this.lastWidth || 0,
234
235             hiddenItems = this.hiddenItems,
236             hasHiddens  = hiddenItems.length != 0,
237             isLarger    = width >= lastWidth;
238
239         this.lastWidth  = width;
240
241         if (tableWidth > width || (hasHiddens && isLarger)) {
242             var items     = this.container.items.items,
243                 len       = items.length,
244                 loopWidth = 0,
245                 item;
246
247             for (var i = 0; i < len; i++) {
248                 item = items[i];
249
250                 if (!item.isFill) {
251                     loopWidth += this.getItemWidth(item);
252                     if (loopWidth > clipWidth) {
253                         if (!(item.hidden || item.xtbHidden)) {
254                             this.hideItem(item);
255                         }
256                     } else if (item.xtbHidden) {
257                         this.unhideItem(item);
258                     }
259                 }
260             }
261         }
262
263         //test for number of hidden items again here because they may have changed above
264         hasHiddens = hiddenItems.length != 0;
265
266         if (hasHiddens) {
267             this.initMore();
268
269             if (!this.lastOverflow) {
270                 this.container.fireEvent('overflowchange', this.container, true);
271                 this.lastOverflow = true;
272             }
273         } else if (this.more) {
274             this.clearMenu();
275             this.more.destroy();
276             delete this.more;
277
278             if (this.lastOverflow) {
279                 this.container.fireEvent('overflowchange', this.container, false);
280                 this.lastOverflow = false;
281             }
282         }
283     },
284
285     /**
286      * @private
287      * Returns a menu config for a given component. This config is used to create a menu item
288      * to be added to the expander menu
289      * @param {Ext.Component} component The component to create the config for
290      * @param {Boolean} hideOnClick Passed through to the menu item
291      */
292     createMenuConfig : function(component, hideOnClick){
293         var config = Ext.apply({}, component.initialConfig),
294             group  = component.toggleGroup;
295
296         Ext.copyTo(config, component, [
297             'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
298         ]);
299
300         Ext.apply(config, {
301             text       : component.overflowText || component.text,
302             hideOnClick: hideOnClick
303         });
304
305         if (group || component.enableToggle) {
306             Ext.apply(config, {
307                 group  : group,
308                 checked: component.pressed,
309                 listeners: {
310                     checkchange: function(item, checked){
311                         component.toggle(checked);
312                     }
313                 }
314             });
315         }
316
317         delete config.ownerCt;
318         delete config.xtype;
319         delete config.id;
320
321         return config;
322     },
323
324     /**
325      * @private
326      * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
327      * @param {Ext.menu.Menu} menu The menu to add to
328      * @param {Ext.Component} component The component to add
329      */
330     addComponentToMenu : function(menu, component) {
331         if (component instanceof Ext.Toolbar.Separator) {
332             menu.add('-');
333
334         } else if (Ext.isFunction(component.isXType)) {
335             if (component.isXType('splitbutton')) {
336                 menu.add(this.createMenuConfig(component, true));
337
338             } else if (component.isXType('button')) {
339                 menu.add(this.createMenuConfig(component, !component.menu));
340
341             } else if (component.isXType('buttongroup')) {
342                 component.items.each(function(item){
343                      this.addComponentToMenu(menu, item);
344                 }, this);
345             }
346         }
347     },
348
349     /**
350      * @private
351      * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
352      * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
353      */
354     clearMenu : function(){
355         var menu = this.moreMenu;
356         if (menu && menu.items) {
357             menu.items.each(function(item){
358                 delete item.menu;
359             });
360         }
361     },
362
363     /**
364      * @private
365      * Called before the expand menu is shown, this rebuilds the menu since it was last shown because
366      * it is possible that the items hidden due to space limitations on the Toolbar have changed since.
367      * @param {Ext.menu.Menu} m The menu
368      */
369     beforeMoreShow : function(menu) {
370         var items = this.container.items.items,
371             len   = items.length,
372             item,
373             prev;
374
375         var needsSep = function(group, item){
376             return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
377         };
378
379         this.clearMenu();
380         menu.removeAll();
381         for (var i = 0; i < len; i++) {
382             item = items[i];
383             if (item.xtbHidden) {
384                 if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
385                     menu.add('-');
386                 }
387                 this.addComponentToMenu(menu, item);
388                 prev = item;
389             }
390         }
391
392         // put something so the menu isn't empty if no compatible items found
393         if (menu.items.length < 1) {
394             menu.add(this.noItemsMenuText);
395         }
396     },
397
398     /**
399      * @private
400      * Creates the expand trigger and menu, adding them to the <tr> at the extreme right of the
401      * Toolbar table
402      */
403     initMore : function(){
404         if (!this.more) {
405             /**
406              * @private
407              * @property moreMenu
408              * @type Ext.menu.Menu
409              * The expand menu - holds items for every Toolbar item that cannot be shown
410              * because the Toolbar is currently not wide enough.
411              */
412             this.moreMenu = new Ext.menu.Menu({
413                 ownerCt : this.container,
414                 listeners: {
415                     beforeshow: this.beforeMoreShow,
416                     scope: this
417                 }
418             });
419
420             /**
421              * @private
422              * @property more
423              * @type Ext.Button
424              * The expand button which triggers the overflow menu to be shown
425              */
426             this.more = new Ext.Button({
427                 iconCls: 'x-toolbar-more-icon',
428                 cls    : 'x-toolbar-more',
429                 menu   : this.moreMenu,
430                 ownerCt: this.container
431             });
432
433             var td = this.insertCell(this.more, this.extrasTr, 100);
434             this.more.render(td);
435         }
436     },
437
438     destroy : function(){
439         Ext.destroy(this.more, this.moreMenu);
440         delete this.leftTr;
441         delete this.rightTr;
442         delete this.extrasTr;
443         Ext.layout.ToolbarLayout.superclass.destroy.call(this);
444     }
445 });
446
447 Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;
448 </pre>    
449 </body>
450 </html>