X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/2e847cf21b8ab9d15fa167b315ca5b2fa92638fc..0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6:/src/widgets/layout/BoxLayout.js diff --git a/src/widgets/layout/BoxLayout.js b/src/widgets/layout/BoxLayout.js index 2034c127..730cd6d3 100644 --- a/src/widgets/layout/BoxLayout.js +++ b/src/widgets/layout/BoxLayout.js @@ -1,8 +1,8 @@ /*! - * Ext JS Library 3.1.1 - * Copyright(c) 2006-2010 Ext JS, LLC - * licensing@extjs.com - * http://www.extjs.com/license + * Ext JS Library 3.3.1 + * Copyright(c) 2006-2010 Sencha Inc. + * licensing@sencha.com + * http://www.sencha.com/license */ /** * @class Ext.layout.BoxLayout @@ -71,411 +71,274 @@ Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, { constructor : function(config){ Ext.layout.BoxLayout.superclass.constructor.call(this, config); - if(Ext.isString(this.defaultMargins)){ + + if (Ext.isString(this.defaultMargins)) { this.defaultMargins = this.parseMargins(this.defaultMargins); } + + var handler = this.overflowHandler; + + if (typeof handler == 'string') { + handler = { + type: handler + }; + } + + var handlerType = 'none'; + if (handler && handler.type != undefined) { + handlerType = handler.type; + } + + var constructor = Ext.layout.boxOverflow[handlerType]; + if (constructor[this.type]) { + constructor = constructor[this.type]; + } + + this.overflowHandler = new constructor(this, handler); }, - // private - isValidParent : function(c, target){ - return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom; - }, + /** + * @private + * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values + * when laying out + */ + onLayout: function(container, target) { + Ext.layout.BoxLayout.superclass.onLayout.call(this, container, target); - // private - renderAll : function(ct, target){ - if(!this.innerCt){ - // the innerCt prevents wrapping and shuffling while - // the container is resizing - this.innerCt = target.createChild({cls:this.innerCls}); - this.padding = this.parseMargins(this.padding); + var tSize = this.getLayoutTargetSize(), + items = this.getVisibleItems(container), + calcs = this.calculateChildBoxes(items, tSize), + boxes = calcs.boxes, + meta = calcs.meta; + + //invoke the overflow handler, if one is configured + if (tSize.width > 0) { + var handler = this.overflowHandler, + method = meta.tooNarrow ? 'handleOverflow' : 'clearOverflow'; + + var results = handler[method](calcs, tSize); + + if (results) { + if (results.targetSize) { + tSize = results.targetSize; + } + + if (results.recalculate) { + items = this.getVisibleItems(container); + calcs = this.calculateChildBoxes(items, tSize); + boxes = calcs.boxes; + } + } } - Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt); - }, + + /** + * @private + * @property layoutTargetLastSize + * @type Object + * Private cache of the last measured size of the layout target. This should never be used except by + * BoxLayout subclasses during their onLayout run. + */ + this.layoutTargetLastSize = tSize; + + /** + * @private + * @property childBoxCache + * @type Array + * Array of the last calculated height, width, top and left positions of each visible rendered component + * within the Box layout. + */ + this.childBoxCache = calcs; + + this.updateInnerCtSize(tSize, calcs); + this.updateChildBoxes(boxes); - onLayout : function(ct, target){ - this.renderAll(ct, target); + // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary. + this.handleTargetOverflow(tSize, container, target); }, - getLayoutTargetSize : function(){ - var target = this.container.getLayoutTarget(), ret; - if (target) { - ret = target.getViewSize(); - ret.width -= target.getPadding('lr'); - ret.height -= target.getPadding('tb'); + /** + * Resizes and repositions each child component + * @param {Array} boxes The box measurements + */ + updateChildBoxes: function(boxes) { + for (var i = 0, length = boxes.length; i < length; i++) { + var box = boxes[i], + comp = box.component; + + if (box.dirtySize) { + comp.setSize(box.width, box.height); + } + // Don't set positions to NaN + if (isNaN(box.left) || isNaN(box.top)) { + continue; + } + + comp.setPosition(box.left, box.top); } - return ret; }, - // private - renderItem : function(c){ - if(Ext.isString(c.margins)){ - c.margins = this.parseMargins(c.margins); - }else if(!c.margins){ - c.margins = this.defaultMargins; - } - Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments); - } -}); - -/** - * @class Ext.layout.VBoxLayout - * @extends Ext.layout.BoxLayout - *

A layout that arranges items vertically down a Container. This layout optionally divides available vertical - * space between child items containing a numeric flex configuration.

- * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option. - */ -Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, { /** - * @cfg {String} align - * Controls how the child items of the container are aligned. Acceptable configuration values for this - * property are: - *
- */ - align : 'left', // left, center, stretch, strechmax - type: 'vbox', - /** - * @cfg {String} pack - * Controls how the child items of the container are packed together. Acceptable configuration values - * for this property are: - *
- */ - /** - * @cfg {Number} flex - * This configuation option is to be applied to child items of the container managed - * by this layout. Each child item with a flex property will be flexed vertically - * according to each item's relative flex value compared to the sum of all items with - * a flex value specified. Any child items that have either a flex = 0 or - * flex = undefined will not be 'flexed' (the initial size will not be changed). + * @private + * Called by onRender just before the child components are sized and positioned. This resizes the innerCt + * to make sure all child items fit within it. We call this before sizing the children because if our child + * items are larger than the previous innerCt size the browser will insert scrollbars and then remove them + * again immediately afterwards, giving a performance hit. + * Subclasses should provide an implementation. + * @param {Object} currentSize The current height and width of the innerCt + * @param {Array} calculations The new box calculations of all items to be laid out */ + updateInnerCtSize: function(tSize, calcs) { + var align = this.align, + padding = this.padding, + width = tSize.width, + height = tSize.height; + + if (this.type == 'hbox') { + var innerCtWidth = width, + innerCtHeight = calcs.meta.maxHeight + padding.top + padding.bottom; - // private - onLayout : function(ct, target){ - Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target); - - var cs = this.getRenderedItems(ct), csLen = cs.length, - c, i, cm, ch, margin, cl, diff, aw, availHeight, - size = this.getLayoutTargetSize(), - w = size.width, - h = size.height - this.scrollOffset, - l = this.padding.left, - t = this.padding.top, - isStart = this.pack == 'start', - extraHeight = 0, - maxWidth = 0, - totalFlex = 0, - usedHeight = 0, - idx = 0, - heights = [], - restore = []; - - // Do only width calculations and apply those first, as they can affect height - for (i = 0 ; i < csLen; i++) { - c = cs[i]; - cm = c.margins; - margin = cm.top + cm.bottom; - // Max height for align - maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right); - } - - var innerCtWidth = maxWidth + this.padding.left + this.padding.right; - switch(this.align){ - case 'stretch': - this.innerCt.setSize(w, h); - break; - case 'stretchmax': - case 'left': - this.innerCt.setSize(innerCtWidth, h); - break; - case 'center': - this.innerCt.setSize(w = Math.max(w, innerCtWidth), h); - break; - } - - var availableWidth = Math.max(0, w - this.padding.left - this.padding.right); - // Apply widths - for (i = 0 ; i < csLen; i++) { - c = cs[i]; - cm = c.margins; - if(this.align == 'stretch'){ - c.setWidth(((w - (this.padding.left + this.padding.right)) - (cm.left + cm.right)).constrain( - c.minWidth || 0, c.maxWidth || 1000000)); - }else if(this.align == 'stretchmax'){ - c.setWidth((maxWidth - (cm.left + cm.right)).constrain( - c.minWidth || 0, c.maxWidth || 1000000)); - }else if(isStart && c.flex){ - c.setWidth(); + if (align == 'stretch') { + innerCtHeight = height; + } else if (align == 'middle') { + innerCtHeight = Math.max(height, innerCtHeight); } + } else { + var innerCtHeight = height, + innerCtWidth = calcs.meta.maxWidth + padding.left + padding.right; - } - - // Height calculations - for (i = 0 ; i < csLen; i++) { - c = cs[i]; - // Total of all the flex values - totalFlex += c.flex || 0; - // Don't run height calculations on flexed items - if (!c.flex) { - // Render and layout sub-containers without a flex or height, once - if (!c.height && !c.hasLayout && c.doLayout) { - c.doLayout(); - } - ch = c.getHeight(); - } else { - ch = 0; + if (align == 'stretch') { + innerCtWidth = width; + } else if (align == 'center') { + innerCtWidth = Math.max(width, innerCtWidth); } - - cm = c.margins; - // Determine how much height is available to flex - extraHeight += ch + cm.top + cm.bottom; } - // Final avail height calc - availHeight = Math.max(0, (h - extraHeight - this.padding.top - this.padding.bottom)); - var leftOver = availHeight; - for (i = 0 ; i < csLen; i++) { - c = cs[i]; - if(isStart && c.flex){ - ch = Math.floor(availHeight * (c.flex / totalFlex)); - leftOver -= ch; - heights.push(ch); - } - } - if(this.pack == 'center'){ - t += availHeight ? availHeight / 2 : 0; - }else if(this.pack == 'end'){ - t += availHeight; - } - idx = 0; - // Apply heights - for (i = 0 ; i < csLen; i++) { - c = cs[i]; - cm = c.margins; - t += cm.top; - aw = availableWidth; - cl = l + cm.left // default left pos + this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined); + }, - // Adjust left pos for centering - if(this.align == 'center'){ - if((diff = availableWidth - (c.getWidth() + cm.left + cm.right)) > 0){ - cl += (diff/2); - aw -= diff; - } - } + /** + * @private + * This should be called after onLayout of any BoxLayout subclass. If the target's overflow is not set to 'hidden', + * we need to lay out a second time because the scrollbars may have modified the height and width of the layout + * target. Having a Box layout inside such a target is therefore not recommended. + * @param {Object} previousTargetSize The size and height of the layout target before we just laid out + * @param {Ext.Container} container The container + * @param {Ext.Element} target The target element + */ + handleTargetOverflow: function(previousTargetSize, container, target) { + var overflow = target.getStyle('overflow'); - c.setPosition(cl, t); - if(isStart && c.flex){ - ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0)); - c.setSize(aw, ch); - }else{ - ch = c.getHeight(); - } - t += ch + cm.bottom; - } - // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary. - if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) { - var ts = this.getLayoutTargetSize(); - if (ts.width != size.width || ts.height != size.height){ + if (overflow && overflow != 'hidden' &&!this.adjustmentPass) { + var newTargetSize = this.getLayoutTargetSize(); + if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height){ this.adjustmentPass = true; - this.onLayout(ct, target); + this.onLayout(container, target); } } + delete this.adjustmentPass; - } -}); + }, -Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout; + // private + isValidParent : function(c, target) { + return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom; + }, -/** - * @class Ext.layout.HBoxLayout - * @extends Ext.layout.BoxLayout - *

A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal - * space between child items containing a numeric flex configuration.

- * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option. - */ -Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, { - /** - * @cfg {String} align - * Controls how the child items of the container are aligned. Acceptable configuration values for this - * property are: - *