3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
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.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.layout.container.boxOverflow.Menu
17 * @extends Ext.layout.container.boxOverflow.None
20 Ext.define('Ext.layout.container.boxOverflow.Menu', {
22 /* Begin Definitions */
24 extend: 'Ext.layout.container.boxOverflow.None',
25 requires: ['Ext.toolbar.Separator', 'Ext.button.Button'],
26 alternateClassName: 'Ext.layout.boxOverflow.Menu',
31 * @cfg {String} afterCtCls
32 * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
33 * which must always be present at the rightmost edge of the Container
37 * @property noItemsMenuText
39 * HTML fragment to render into the toolbar overflow menu if there are no items to display
41 noItemsMenuText : '<div class="' + Ext.baseCSSPrefix + 'toolbar-no-items">(None)</div>',
43 constructor: function(layout) {
46 me.callParent(arguments);
48 // Before layout, we need to re-show all items which we may have hidden due to a previous overflow.
49 layout.beforeLayout = Ext.Function.createInterceptor(layout.beforeLayout, this.clearOverflow, this);
51 me.afterCtCls = me.afterCtCls || Ext.baseCSSPrefix + 'box-menu-' + layout.parallelAfter;
55 * Array of all items that are currently hidden and should go into the dropdown menu
60 onRemove: function(comp){
61 Ext.Array.remove(this.menuItems, comp);
64 handleOverflow: function(calculations, targetSize) {
67 methodName = 'get' + layout.parallelPrefixCap,
69 posArgs = [null, null];
71 me.callParent(arguments);
72 this.createMenu(calculations, targetSize);
73 newSize[layout.perpendicularPrefix] = targetSize[layout.perpendicularPrefix];
74 newSize[layout.parallelPrefix] = targetSize[layout.parallelPrefix] - me.afterCt[methodName]();
76 // Center the menuTrigger button.
77 // TODO: Should we emulate align: 'middle' like this, or should we 'stretchmax' the menuTrigger?
78 posArgs[layout.perpendicularSizeIndex] = (calculations.meta.maxSize - me.menuTrigger['get' + layout.perpendicularPrefixCap]()) / 2;
79 me.menuTrigger.setPosition.apply(me.menuTrigger, posArgs);
81 return { targetSize: newSize };
86 * Called by the layout, when it determines that there is no overflow.
87 * Also called as an interceptor to the layout's onLayout method to reshow
88 * previously hidden overflowing items.
90 clearOverflow: function(calculations, targetSize) {
92 newWidth = targetSize ? targetSize.width + (me.afterCt ? me.afterCt.getWidth() : 0) : 0,
95 length = items.length,
99 for (; i < length; i++) {
104 return targetSize ? {
106 height: targetSize.height,
115 showTrigger: function() {
116 this.menuTrigger.show();
122 hideTrigger: function() {
123 if (this.menuTrigger !== undefined) {
124 this.menuTrigger.hide();
130 * Called before the overflow menu is shown. This constructs the menu's items, caching them for as long as it can.
132 beforeMenuShow: function(menu) {
134 items = me.menuItems,
140 var needsSep = function(group, prev){
141 return group.isXType('buttongroup') && !(prev instanceof Ext.toolbar.Separator);
147 for (; i < len; i++) {
150 // Do not show a separator as a first item
151 if (!i && (item instanceof Ext.toolbar.Separator)) {
154 if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
158 me.addComponentToMenu(menu, item);
162 // put something so the menu isn't empty if no compatible items found
163 if (menu.items.length < 1) {
164 menu.add(me.noItemsMenuText);
170 * Returns a menu config for a given component. This config is used to create a menu item
171 * to be added to the expander menu
172 * @param {Ext.Component} component The component to create the config for
173 * @param {Boolean} hideOnClick Passed through to the menu item
175 createMenuConfig : function(component, hideOnClick) {
176 var config = Ext.apply({}, component.initialConfig),
177 group = component.toggleGroup;
179 Ext.copyTo(config, component, [
180 'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
184 text : component.overflowText || component.text,
185 hideOnClick: hideOnClick,
189 if (group || component.enableToggle) {
192 checked: component.pressed,
194 checkchange: function(item, checked){
195 component.toggle(checked);
201 delete config.ownerCt;
209 * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
210 * @param {Ext.menu.Menu} menu The menu to add to
211 * @param {Ext.Component} component The component to add
213 addComponentToMenu : function(menu, component) {
215 if (component instanceof Ext.toolbar.Separator) {
217 } else if (component.isComponent) {
218 if (component.isXType('splitbutton')) {
219 menu.add(me.createMenuConfig(component, true));
221 } else if (component.isXType('button')) {
222 menu.add(me.createMenuConfig(component, !component.menu));
224 } else if (component.isXType('buttongroup')) {
225 component.items.each(function(item){
226 me.addComponentToMenu(menu, item);
229 menu.add(Ext.create(Ext.getClassName(component), me.createMenuConfig(component)));
236 * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
237 * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
239 clearMenu : function() {
240 var menu = this.moreMenu;
241 if (menu && menu.items) {
242 menu.items.each(function(item) {
252 * Creates the overflow trigger and menu used when enableOverflow is set to true and the items
253 * in the layout are too wide to fit in the space available
255 createMenu: function(calculations, targetSize) {
258 startProp = layout.parallelBefore,
259 sizeProp = layout.parallelPrefix,
260 available = targetSize[sizeProp],
261 boxes = calculations.boxes,
266 if (!me.menuTrigger) {
267 me.createInnerElements();
272 * @type Ext.menu.Menu
273 * The expand menu - holds items for every item that cannot be shown
274 * because the container is currently not large enough.
276 me.menu = Ext.create('Ext.menu.Menu', {
279 beforeshow: me.beforeMenuShow
285 * @property menuTrigger
286 * @type Ext.button.Button
287 * The expand button which triggers the overflow menu to be shown
289 me.menuTrigger = Ext.create('Ext.button.Button', {
290 ownerCt : me.layout.owner, // To enable the Menu to ascertain a valid zIndexManager owner in the same tree
291 iconCls : me.layout.owner.menuTriggerCls,
292 ui : layout.owner instanceof Ext.toolbar.Toolbar ? 'default-toolbar' : 'default',
294 getSplitCls: function() { return '';},
299 available -= me.afterCt.getWidth();
301 // Hide all items which are off the end, and store them to allow them to be restored
302 // before each layout operation.
303 me.menuItems.length = 0;
304 for (; i < len; i++) {
306 if (box[startProp] + box[sizeProp] > available) {
307 me.menuItems.push(box.component);
308 box.component.hide();
315 * Creates the beforeCt, innerCt and afterCt elements if they have not already been created
316 * @param {Ext.container.Container} container The Container attached to this Layout instance
317 * @param {Ext.Element} target The target Element
319 createInnerElements: function() {
321 target = me.layout.getRenderTarget();
324 target.addCls(Ext.baseCSSPrefix + me.layout.direction + '-box-overflow-body');
325 this.afterCt = target.insertSibling({cls: Ext.layout.container.Box.prototype.innerCls + ' ' + this.afterCtCls}, 'before');
332 destroy: function() {
333 Ext.destroy(this.menu, this.menuTrigger);