X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/2e847cf21b8ab9d15fa167b315ca5b2fa92638fc..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/docs/source/Box.html diff --git a/docs/source/Box.html b/docs/source/Box.html new file mode 100644 index 00000000..2ccdc6b6 --- /dev/null +++ b/docs/source/Box.html @@ -0,0 +1,838 @@ +
\ No newline at end of file/** + * @class Ext.layout.container.Box + * @extends Ext.layout.container.Container + * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p> + */ + +Ext.define('Ext.layout.container.Box', { + + /* Begin Definitions */ + + alias: ['layout.box'], + extend: 'Ext.layout.container.Container', + alternateClassName: 'Ext.layout.BoxLayout', + + requires: [ + 'Ext.layout.container.boxOverflow.None', + 'Ext.layout.container.boxOverflow.Menu', + 'Ext.layout.container.boxOverflow.Scroller', + 'Ext.util.Format', + 'Ext.dd.DragDropManager' + ], + + /* End Definitions */ + + /** + * @cfg {Mixed} animate + * <p>If truthy, child Component are <i>animated</i> into position whenever the Container + * is layed out. If this option is numeric, it is used as the animation duration in milliseconds.</p> + * <p>May be set as a property at any time.</p> + */ + + /** + * @cfg {Object} defaultMargins + * <p>If the individual contained items do not have a <tt>margins</tt> + * property specified or margin specified via CSS, the default margins from this property will be + * applied to each item.</p> + * <br><p>This property may be specified as an object containing margins + * to apply in the format:</p><pre><code> +{ + top: (top margin), + right: (right margin), + bottom: (bottom margin), + left: (left margin) +}</code></pre> + * <p>This property may also be specified as a string containing + * space-separated, numeric margin values. The order of the sides associated + * with each value matches the way CSS processes margin values:</p> + * <div class="mdetail-params"><ul> + * <li>If there is only one value, it applies to all sides.</li> + * <li>If there are two values, the top and bottom borders are set to the + * first value and the right and left are set to the second.</li> + * <li>If there are three values, the top is set to the first value, the left + * and right are set to the second, and the bottom is set to the third.</li> + * <li>If there are four values, they apply to the top, right, bottom, and + * left, respectively.</li> + * </ul></div> + * <p>Defaults to:</p><pre><code> + * {top:0, right:0, bottom:0, left:0} + * </code></pre> + */ + defaultMargins: { + top: 0, + right: 0, + bottom: 0, + left: 0 + }, + + /** + * @cfg {String} padding + * <p>Sets the padding to be applied to all child items managed by this layout.</p> + * <p>This property must be specified as a string containing + * space-separated, numeric padding values. The order of the sides associated + * with each value matches the way CSS processes padding values:</p> + * <div class="mdetail-params"><ul> + * <li>If there is only one value, it applies to all sides.</li> + * <li>If there are two values, the top and bottom borders are set to the + * first value and the right and left are set to the second.</li> + * <li>If there are three values, the top is set to the first value, the left + * and right are set to the second, and the bottom is set to the third.</li> + * <li>If there are four values, they apply to the top, right, bottom, and + * left, respectively.</li> + * </ul></div> + * <p>Defaults to: <code>"0"</code></p> + */ + padding: '0', + // documented in subclasses + pack: 'start', + + /** + * @cfg {String} pack + * Controls how the child items of the container are packed together. Acceptable configuration values + * for this property are: + * <div class="mdetail-params"><ul> + * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at + * <b>left</b> side of container</div></li> + * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at + * <b>mid-width</b> of container</div></li> + * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b> + * side of container</div></li> + * </ul></div> + */ + /** + * @cfg {Number} flex + * This configuration option is to be applied to <b>child <tt>items</tt></b> of the container managed + * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b> + * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with + * a <tt>flex</tt> value specified. Any child items that have either a <tt>flex = 0</tt> or + * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed). + */ + + type: 'box', + scrollOffset: 0, + itemCls: Ext.baseCSSPrefix + 'box-item', + targetCls: Ext.baseCSSPrefix + 'box-layout-ct', + innerCls: Ext.baseCSSPrefix + 'box-inner', + + bindToOwnerCtContainer: true, + + fixedLayout: false, + + // availableSpaceOffset is used to adjust the availableWidth, typically used + // to reserve space for a scrollbar + availableSpaceOffset: 0, + + // whether or not to reserve the availableSpaceOffset in layout calculations + reserveOffset: true, + + /** + * @cfg {Boolean} clearInnerCtOnLayout + */ + clearInnerCtOnLayout: false, + + flexSortFn: function (a, b) { + var maxParallelPrefix = 'max' + this.parallelPrefixCap, + infiniteValue = Infinity; + a = a.component[maxParallelPrefix] || infiniteValue; + b = b.component[maxParallelPrefix] || infiniteValue; + // IE 6/7 Don't like Infinity - Infinity... + if (!isFinite(a) && !isFinite(b)) { + return false; + } + return a - b; + }, + + // Sort into *descending* order. + minSizeSortFn: function(a, b) { + return b.available - a.available; + }, + + constructor: function(config) { + var me = this; + + me.callParent(arguments); + + // The sort function needs access to properties in this, so must be bound. + me.flexSortFn = Ext.Function.bind(me.flexSortFn, me); + + me.initOverflowHandler(); + }, + + /** + * @private + * Returns the current size and positioning of the passed child item. + * @param {Component} child The child Component to calculate the box for + * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height. + */ + getChildBox: function(child) { + child = child.el || this.owner.getComponent(child).el; + return { + left: child.getLeft(true), + top: child.getTop(true), + width: child.getWidth(), + height: child.getHeight() + }; + }, + + /** + * @private + * Calculates the size and positioning of the passed child item. + * @param {Component} child The child Component to calculate the box for + * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height. + */ + calculateChildBox: function(child) { + var me = this, + boxes = me.calculateChildBoxes(me.getVisibleItems(), me.getLayoutTargetSize()).boxes, + ln = boxes.length, + i = 0; + + child = me.owner.getComponent(child); + for (; i < ln; i++) { + if (boxes[i].component === child) { + return boxes[i]; + } + } + }, + + /** + * @private + * Calculates the size and positioning of each item in the box. This iterates over all of the rendered, + * visible items and returns a height, width, top and left for each, as well as a reference to each. Also + * returns meta data such as maxSize which are useful when resizing layout wrappers such as this.innerCt. + * @param {Array} visibleItems The array of all rendered, visible items to be calculated for + * @param {Object} targetSize Object containing target size and height + * @return {Object} Object containing box measurements for each child, plus meta data + */ + calculateChildBoxes: function(visibleItems, targetSize) { + var me = this, + math = Math, + mmax = math.max, + infiniteValue = Infinity, + undefinedValue, + + parallelPrefix = me.parallelPrefix, + parallelPrefixCap = me.parallelPrefixCap, + perpendicularPrefix = me.perpendicularPrefix, + perpendicularPrefixCap = me.perpendicularPrefixCap, + parallelMinString = 'min' + parallelPrefixCap, + perpendicularMinString = 'min' + perpendicularPrefixCap, + perpendicularMaxString = 'max' + perpendicularPrefixCap, + + parallelSize = targetSize[parallelPrefix] - me.scrollOffset, + perpendicularSize = targetSize[perpendicularPrefix], + padding = me.padding, + parallelOffset = padding[me.parallelBefore], + paddingParallel = parallelOffset + padding[me.parallelAfter], + perpendicularOffset = padding[me.perpendicularLeftTop], + paddingPerpendicular = perpendicularOffset + padding[me.perpendicularRightBottom], + availPerpendicularSize = mmax(0, perpendicularSize - paddingPerpendicular), + + isStart = me.pack == 'start', + isCenter = me.pack == 'center', + isEnd = me.pack == 'end', + + constrain = Ext.Number.constrain, + visibleCount = visibleItems.length, + nonFlexSize = 0, + totalFlex = 0, + desiredSize = 0, + minimumSize = 0, + maxSize = 0, + boxes = [], + minSizes = [], + calculatedWidth, + + i, child, childParallel, childPerpendicular, childMargins, childSize, minParallel, tmpObj, shortfall, + tooNarrow, availableSpace, minSize, item, length, itemIndex, box, oldSize, newSize, reduction, diff, + flexedBoxes, remainingSpace, remainingFlex, flexedSize, parallelMargins, calcs, offset, + perpendicularMargins, stretchSize; + + //gather the total flex of all flexed items and the width taken up by fixed width items + for (i = 0; i < visibleCount; i++) { + child = visibleItems[i]; + childPerpendicular = child[perpendicularPrefix]; + me.layoutItem(child); + childMargins = child.margins; + parallelMargins = childMargins[me.parallelBefore] + childMargins[me.parallelAfter]; + + // Create the box description object for this child item. + tmpObj = { + component: child, + margins: childMargins + }; + + // flex and not 'auto' width + if (child.flex) { + totalFlex += child.flex; + childParallel = undefinedValue; + } + // Not flexed or 'auto' width or undefined width + else { + if (!(child[parallelPrefix] && childPerpendicular)) { + childSize = child.getSize(); + } + childParallel = child[parallelPrefix] || childSize[parallelPrefix]; + childPerpendicular = childPerpendicular || childSize[perpendicularPrefix]; + } + + nonFlexSize += parallelMargins + (childParallel || 0); + desiredSize += parallelMargins + (child.flex ? child[parallelMinString] || 0 : childParallel); + minimumSize += parallelMargins + (child[parallelMinString] || childParallel || 0); + + // Max height for align - force layout of non-laid out subcontainers without a numeric height + if (typeof childPerpendicular != 'number') { + // Clear any static sizing and revert to flow so we can get a proper measurement + // child['set' + perpendicularPrefixCap](null); + childPerpendicular = child['get' + perpendicularPrefixCap](); + } + + // Track the maximum perpendicular size for use by the stretch and stretchmax align config values. + maxSize = mmax(maxSize, childPerpendicular + childMargins[me.perpendicularLeftTop] + childMargins[me.perpendicularRightBottom]); + + tmpObj[parallelPrefix] = childParallel || undefinedValue; + tmpObj[perpendicularPrefix] = childPerpendicular || undefinedValue; + boxes.push(tmpObj); + } + shortfall = desiredSize - parallelSize; + tooNarrow = minimumSize > parallelSize; + + //the space available to the flexed items + availableSpace = mmax(0, parallelSize - nonFlexSize - paddingParallel - (me.reserveOffset ? me.availableSpaceOffset : 0)); + + if (tooNarrow) { + for (i = 0; i < visibleCount; i++) { + box = boxes[i]; + minSize = visibleItems[i][parallelMinString] || visibleItems[i][parallelPrefix] || box[parallelPrefix]; + box.dirtySize = box.dirtySize || box[parallelPrefix] != minSize; + box[parallelPrefix] = minSize; + } + } + else { + //all flexed items should be sized to their minimum size, other items should be shrunk down until + //the shortfall has been accounted for + if (shortfall > 0) { + /* + * When we have a shortfall but are not tooNarrow, we need to shrink the width of each non-flexed item. + * Flexed items are immediately reduced to their minWidth and anything already at minWidth is ignored. + * The remaining items are collected into the minWidths array, which is later used to distribute the shortfall. + */ + for (i = 0; i < visibleCount; i++) { + item = visibleItems[i]; + minSize = item[parallelMinString] || 0; + + //shrink each non-flex tab by an equal amount to make them all fit. Flexed items are all + //shrunk to their minSize because they're flexible and should be the first to lose size + if (item.flex) { + box = boxes[i]; + box.dirtySize = box.dirtySize || box[parallelPrefix] != minSize; + box[parallelPrefix] = minSize; + } + else { + minSizes.push({ + minSize: minSize, + available: boxes[i][parallelPrefix] - minSize, + index: i + }); + } + } + + //sort by descending amount of width remaining before minWidth is reached + Ext.Array.sort(minSizes, me.minSizeSortFn); + + /* + * Distribute the shortfall (difference between total desired size of all items and actual size available) + * between the non-flexed items. We try to distribute the shortfall evenly, but apply it to items with the + * smallest difference between their size and minSize first, so that if reducing the size by the average + * amount would make that item less than its minSize, we carry the remainder over to the next item. + */ + for (i = 0, length = minSizes.length; i < length; i++) { + itemIndex = minSizes[i].index; + + if (itemIndex == undefinedValue) { + continue; + } + item = visibleItems[itemIndex]; + minSize = minSizes[i].minSize; + + box = boxes[itemIndex]; + oldSize = box[parallelPrefix]; + newSize = mmax(minSize, oldSize - math.ceil(shortfall / (length - i))); + reduction = oldSize - newSize; + + box.dirtySize = box.dirtySize || box[parallelPrefix] != newSize; + box[parallelPrefix] = newSize; + shortfall -= reduction; + } + } + else { + remainingSpace = availableSpace; + remainingFlex = totalFlex; + flexedBoxes = []; + + // Create an array containing *just the flexed boxes* for allocation of remainingSpace + for (i = 0; i < visibleCount; i++) { + child = visibleItems[i]; + if (isStart && child.flex) { + flexedBoxes.push(boxes[Ext.Array.indexOf(visibleItems, child)]); + } + } + // The flexed boxes need to be sorted in ascending order of maxSize to work properly + // so that unallocated space caused by maxWidth being less than flexed width + // can be reallocated to subsequent flexed boxes. + Ext.Array.sort(flexedBoxes, me.flexSortFn); + + // Calculate the size of each flexed item, and attempt to set it. + for (i = 0; i < flexedBoxes.length; i++) { + calcs = flexedBoxes[i]; + child = calcs.component; + childMargins = calcs.margins; + + flexedSize = math.ceil((child.flex / remainingFlex) * remainingSpace); + + // Implement maxSize and minSize check + flexedSize = Math.max(child['min' + parallelPrefixCap] || 0, math.min(child['max' + parallelPrefixCap] || infiniteValue, flexedSize)); + + // Remaining space has already had all parallel margins subtracted from it, so just subtract consumed size + remainingSpace -= flexedSize; + remainingFlex -= child.flex; + + calcs.dirtySize = calcs.dirtySize || calcs[parallelPrefix] != flexedSize; + calcs[parallelPrefix] = flexedSize; + } + } + } + + if (isCenter) { + parallelOffset += availableSpace / 2; + } + else if (isEnd) { + parallelOffset += availableSpace; + } + + // Fix for left and right docked Components in a dock component layout. This is for docked Headers and docked Toolbars. + // Older Microsoft browsers do not size a position:absolute element's width to match its content. + // So in this case, in the updateInnerCtSize method we may need to adjust the size of the owning Container's element explicitly based upon + // the discovered max width. So here we put a calculatedWidth property in the metadata to facilitate this. + if (me.owner.dock && (Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks) && !me.owner.width && me.direction == 'vertical') { + + calculatedWidth = maxSize + me.owner.el.getPadding('lr') + me.owner.el.getBorderWidth('lr'); + if (me.owner.frameSize) { + calculatedWidth += me.owner.frameSize.left + me.owner.frameSize.right; + } + // If the owning element is not sized, calculate the available width to center or stretch in based upon maxSize + availPerpendicularSize = Math.min(availPerpendicularSize, targetSize.width = maxSize + padding.left + padding.right); + } + + //finally, calculate the left and top position of each item + for (i = 0; i < visibleCount; i++) { + child = visibleItems[i]; + calcs = boxes[i]; + + childMargins = calcs.margins; + + perpendicularMargins = childMargins[me.perpendicularLeftTop] + childMargins[me.perpendicularRightBottom]; + + // Advance past the "before" margin + parallelOffset += childMargins[me.parallelBefore]; + + calcs[me.parallelBefore] = parallelOffset; + calcs[me.perpendicularLeftTop] = perpendicularOffset + childMargins[me.perpendicularLeftTop]; + + if (me.align == 'stretch') { + stretchSize = constrain(availPerpendicularSize - perpendicularMargins, child[perpendicularMinString] || 0, child[perpendicularMaxString] || infiniteValue); + calcs.dirtySize = calcs.dirtySize || calcs[perpendicularPrefix] != stretchSize; + calcs[perpendicularPrefix] = stretchSize; + } + else if (me.align == 'stretchmax') { + stretchSize = constrain(maxSize - perpendicularMargins, child[perpendicularMinString] || 0, child[perpendicularMaxString] || infiniteValue); + calcs.dirtySize = calcs.dirtySize || calcs[perpendicularPrefix] != stretchSize; + calcs[perpendicularPrefix] = stretchSize; + } + else if (me.align == me.alignCenteringString) { + // When calculating a centered position within the content box of the innerCt, the width of the borders must be subtracted from + // the size to yield the space available to center within. + // The updateInnerCtSize method explicitly adds the border widths to the set size of the innerCt. + diff = mmax(availPerpendicularSize, maxSize) - me.innerCt.getBorderWidth(me.perpendicularLT + me.perpendicularRB) - calcs[perpendicularPrefix]; + if (diff > 0) { + calcs[me.perpendicularLeftTop] = perpendicularOffset + Math.round(diff / 2); + } + } + + // Advance past the box size and the "after" margin + parallelOffset += (calcs[parallelPrefix] || 0) + childMargins[me.parallelAfter]; + } + + return { + boxes: boxes, + meta : { + calculatedWidth: calculatedWidth, + maxSize: maxSize, + nonFlexSize: nonFlexSize, + desiredSize: desiredSize, + minimumSize: minimumSize, + shortfall: shortfall, + tooNarrow: tooNarrow + } + }; + }, + + /** + * @private + */ + initOverflowHandler: function() { + 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.container.boxOverflow[handlerType]; + if (constructor[this.type]) { + constructor = constructor[this.type]; + } + + this.overflowHandler = Ext.create('Ext.layout.container.boxOverflow.' + handlerType, this, handler); + }, + + /** + * @private + * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values + * when laying out + */ + onLayout: function() { + this.callParent(); + // Clear the innerCt size so it doesn't influence the child items. + if (this.clearInnerCtOnLayout === true && this.adjustmentPass !== true) { + this.innerCt.setSize(null, null); + } + + var me = this, + targetSize = me.getLayoutTargetSize(), + items = me.getVisibleItems(), + calcs = me.calculateChildBoxes(items, targetSize), + boxes = calcs.boxes, + meta = calcs.meta, + handler, method, results; + + if (me.autoSize && calcs.meta.desiredSize) { + targetSize[me.parallelPrefix] = calcs.meta.desiredSize; + } + + //invoke the overflow handler, if one is configured + if (meta.shortfall > 0) { + handler = me.overflowHandler; + method = meta.tooNarrow ? 'handleOverflow': 'clearOverflow'; + + results = handler[method](calcs, targetSize); + + if (results) { + if (results.targetSize) { + targetSize = results.targetSize; + } + + if (results.recalculate) { + items = me.getVisibleItems(owner); + calcs = me.calculateChildBoxes(items, targetSize); + boxes = calcs.boxes; + } + } + } else { + me.overflowHandler.clearOverflow(); + } + + /** + * @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. + */ + me.layoutTargetLastSize = targetSize; + + /** + * @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. + */ + me.childBoxCache = calcs; + + me.updateInnerCtSize(targetSize, calcs); + me.updateChildBoxes(boxes); + me.handleTargetOverflow(targetSize); + }, + + /** + * Resizes and repositions each child component + * @param {Array} boxes The box measurements + */ + updateChildBoxes: function(boxes) { + var me = this, + i = 0, + length = boxes.length, + animQueue = [], + dd = Ext.dd.DDM.getDDById(me.innerCt.id), // Any DD active on this layout's element (The BoxReorderer plugin does this.) + oldBox, newBox, changed, comp, boxAnim, animCallback; + + for (; i < length; i++) { + newBox = boxes[i]; + comp = newBox.component; + + // If a Component is being drag/dropped, skip positioning it. + // Accomodate the BoxReorderer plugin: Its current dragEl must not be positioned by the layout + if (dd && (dd.getDragEl() === comp.el.dom)) { + continue; + } + + changed = false; + + oldBox = me.getChildBox(comp); + + // If we are animating, we build up an array of Anim config objects, one for each + // child Component which has any changed box properties. Those with unchanged + // properties are not animated. + if (me.animate) { + // Animate may be a config object containing callback. + animCallback = me.animate.callback || me.animate; + boxAnim = { + layoutAnimation: true, // Component Target handler must use set*Calculated*Size + target: comp, + from: {}, + to: {}, + listeners: {} + }; + // Only set from and to properties when there's a change. + // Perform as few Component setter methods as possible. + // Temporarily set the property values that we are not animating + // so that doComponentLayout does not auto-size them. + if (!isNaN(newBox.width) && (newBox.width != oldBox.width)) { + changed = true; + // boxAnim.from.width = oldBox.width; + boxAnim.to.width = newBox.width; + } + if (!isNaN(newBox.height) && (newBox.height != oldBox.height)) { + changed = true; + // boxAnim.from.height = oldBox.height; + boxAnim.to.height = newBox.height; + } + if (!isNaN(newBox.left) && (newBox.left != oldBox.left)) { + changed = true; + // boxAnim.from.left = oldBox.left; + boxAnim.to.left = newBox.left; + } + if (!isNaN(newBox.top) && (newBox.top != oldBox.top)) { + changed = true; + // boxAnim.from.top = oldBox.top; + boxAnim.to.top = newBox.top; + } + if (changed) { + animQueue.push(boxAnim); + } + } else { + if (newBox.dirtySize) { + if (newBox.width !== oldBox.width || newBox.height !== oldBox.height) { + me.setItemSize(comp, newBox.width, newBox.height); + } + } + // Don't set positions to NaN + if (isNaN(newBox.left) || isNaN(newBox.top)) { + continue; + } + comp.setPosition(newBox.left, newBox.top); + } + } + + // Kick off any queued animations + length = animQueue.length; + if (length) { + + // A function which cleans up when a Component's animation is done. + // The last one to finish calls the callback. + var afterAnimate = function(anim) { + // When we've animated all changed boxes into position, clear our busy flag and call the callback. + length -= 1; + if (!length) { + me.layoutBusy = false; + if (Ext.isFunction(animCallback)) { + animCallback(); + } + } + }; + + var beforeAnimate = function() { + me.layoutBusy = true; + }; + + // Start each box animation off + for (i = 0, length = animQueue.length; i < length; i++) { + boxAnim = animQueue[i]; + + // Clean up the Component after. Clean up the *layout* after the last animation finishes + boxAnim.listeners.afteranimate = afterAnimate; + + // The layout is busy during animation, and may not be called, so set the flag when the first animation begins + if (!i) { + boxAnim.listeners.beforeanimate = beforeAnimate; + } + if (me.animate.duration) { + boxAnim.duration = me.animate.duration; + } + comp = boxAnim.target; + delete boxAnim.target; + // Stop any currently running animation + comp.stopAnimation(); + comp.animate(boxAnim); + } + } + }, + + /** + * @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 me = this, + mmax = Math.max, + align = me.align, + padding = me.padding, + width = tSize.width, + height = tSize.height, + meta = calcs.meta, + innerCtWidth, + innerCtHeight; + + if (me.direction == 'horizontal') { + innerCtWidth = width; + innerCtHeight = meta.maxSize + padding.top + padding.bottom + me.innerCt.getBorderWidth('tb'); + + if (align == 'stretch') { + innerCtHeight = height; + } + else if (align == 'middle') { + innerCtHeight = mmax(height, innerCtHeight); + } + } else { + innerCtHeight = height; + innerCtWidth = meta.maxSize + padding.left + padding.right + me.innerCt.getBorderWidth('lr'); + + if (align == 'stretch') { + innerCtWidth = width; + } + else if (align == 'center') { + innerCtWidth = mmax(width, innerCtWidth); + } + } + me.getRenderTarget().setSize(innerCtWidth || undefined, innerCtHeight || undefined); + + // If a calculated width has been found (and this only happens for auto-width vertical docked Components in old Microsoft browsers) + // then, if the Component has not assumed the size of its content, set it to do so. + if (meta.calculatedWidth && me.owner.el.getWidth() > meta.calculatedWidth) { + me.owner.el.setWidth(meta.calculatedWidth); + } + + if (me.innerCt.dom.scrollTop) { + me.innerCt.dom.scrollTop = 0; + } + }, + + /** + * @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} container The container + * @param {Ext.core.Element} target The target element + * @return True if the layout overflowed, and was reflowed in a secondary onLayout call. + */ + handleTargetOverflow: function(previousTargetSize) { + var target = this.getTarget(), + overflow = target.getStyle('overflow'), + newTargetSize; + + if (overflow && overflow != 'hidden' && !this.adjustmentPass) { + newTargetSize = this.getLayoutTargetSize(); + if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height) { + this.adjustmentPass = true; + this.onLayout(); + return true; + } + } + + delete this.adjustmentPass; + }, + + // private + isValidParent : function(item, target, position) { + // Note: Box layouts do not care about order within the innerCt element because it's an absolutely positioning layout + // We only care whether the item is a direct child of the innerCt element. + var itemEl = item.el ? item.el.dom : Ext.getDom(item); + return (itemEl && this.innerCt && itemEl.parentNode === this.innerCt.dom) || false; + }, + + // Overridden method from AbstractContainer. + // Used in the base AbstractLayout.beforeLayout method to render all items into. + getRenderTarget: function() { + if (!this.innerCt) { + // the innerCt prevents wrapping and shuffling while the container is resizing + this.innerCt = this.getTarget().createChild({ + cls: this.innerCls, + role: 'presentation' + }); + this.padding = Ext.util.Format.parseBox(this.padding); + } + return this.innerCt; + }, + + // private + renderItem: function(item, target) { + this.callParent(arguments); + var me = this, + itemEl = item.getEl(), + style = itemEl.dom.style, + margins = item.margins || item.margin; + + // Parse the item's margin/margins specification + if (margins) { + if (Ext.isString(margins) || Ext.isNumber(margins)) { + margins = Ext.util.Format.parseBox(margins); + } else { + Ext.applyIf(margins, {top: 0, right: 0, bottom: 0, left: 0}); + } + } else { + margins = Ext.apply({}, me.defaultMargins); + } + + // Add any before/after CSS margins to the configured margins, and zero the CSS margins + margins.top += itemEl.getMargin('t'); + margins.right += itemEl.getMargin('r'); + margins.bottom += itemEl.getMargin('b'); + margins.left += itemEl.getMargin('l'); + style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = '0'; + + // Item must reference calculated margins. + item.margins = margins; + }, + + /** + * @private + */ + destroy: function() { + Ext.destroy(this.overflowHandler); + this.callParent(arguments); + } +});