Upgrade to ExtJS 3.2.1 - Released 04/27/2010
[extjs.git] / src / widgets / layout / BoxLayout.js
index 2034c12..3408de1 100644 (file)
@@ -1,6 +1,6 @@
 /*!
- * Ext JS Library 3.1.1
- * Copyright(c) 2006-2010 Ext JS, LLC
+ * Ext JS Library 3.2.1
+ * Copyright(c) 2006-2010 Ext JS, Inc.
  * licensing@extjs.com
  * http://www.extjs.com/license
  */
@@ -71,16 +71,130 @@ 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);
         }
     },
 
+    /**
+     * @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);
+
+        var items = this.getVisibleItems(container),
+            tSize = this.getLayoutTargetSize();
+
+        /**
+         * @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 = this.calculateChildBoxes(items, tSize);
+
+        this.updateInnerCtSize(tSize, this.childBoxCache);
+        this.updateChildBoxes(this.childBoxCache.boxes);
+
+        // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
+        this.handleTargetOverflow(tSize, container, target);
+    },
+
+    /**
+     * 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);
+        }
+    },
+
+    /**
+     * @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: Ext.emptyFn,
+
+    /**
+     * @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');
+
+        if (overflow && overflow != 'hidden' &&!this.adjustmentPass) {
+            var newTargetSize = this.getLayoutTargetSize();
+            if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height){
+                this.adjustmentPass = true;
+                this.onLayout(container, target);
+            }
+        }
+
+        delete this.adjustmentPass;
+    },
+
     // private
     isValidParent : function(c, target){
         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
     },
 
+    /**
+     * @private
+     * Returns all items that are both rendered and visible
+     * @return {Array} All matching items
+     */
+    getVisibleItems: function(ct) {
+        var ct  = ct || this.container,
+            t   = ct.getLayoutTarget(),
+            cti = ct.items.items,
+            len = cti.length,
+
+            i, c, items = [];
+
+        for (i = 0; i < len; i++) {
+            if((c = cti[i]).rendered && this.isValidParent(c, t) && c.hidden !== true  && c.collapsed !== true){
+                items.push(c);
+            }
+        }
+
+        return items;
+    },
+
     // private
     renderAll : function(ct, target){
         if(!this.innerCt){
@@ -92,14 +206,18 @@ Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
         Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt);
     },
 
-    onLayout : function(ct, target){
-        this.renderAll(ct, target);
-    },
-
     getLayoutTargetSize : function(){
         var target = this.container.getLayoutTarget(), ret;
         if (target) {
             ret = target.getViewSize();
+
+            // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
+            // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
+            // with getViewSize
+            if (Ext.isIE && Ext.isStrict && ret.width == 0){
+                ret =  target.getStyleSize();
+            }
+
             ret.width -= target.getPadding('lr');
             ret.height -= target.getPadding('tb');
         }
@@ -142,6 +260,7 @@ Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
      */
     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
@@ -155,6 +274,7 @@ Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
      * side of container</div></li>
      * </ul></div>
      */
+
     /**
      * @cfg {Number} flex
      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
@@ -164,138 +284,174 @@ Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
      */
 
-    // 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);
-        }
+    /**
+     * @private
+     * See parent documentation
+     */
+    updateInnerCtSize: function(tSize, calcs) {
+        var innerCtHeight = tSize.height,
+            innerCtWidth  = calcs.meta.maxWidth + this.padding.left + this.padding.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;
+        if (this.align == 'stretch') {
+            innerCtWidth = tSize.width;
+        } else if (this.align == 'center') {
+            innerCtWidth = Math.max(tSize.width, innerCtWidth);
         }
 
-        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();
-            }
+        //we set the innerCt size first because if our child items are larger than the previous innerCt size
+        //the browser will insert scrollbars and then remove them again immediately afterwards
+        this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
+    },
+
+    /**
+     * @private
+     * Calculates the size and positioning of each item in the VBox. 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 maxHeight 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 visibleCount = visibleItems.length,
+
+            padding      = this.padding,
+            topOffset    = padding.top,
+            leftOffset   = padding.left,
+            paddingVert  = topOffset  + padding.bottom,
+            paddingHoriz = leftOffset + padding.right,
+
+            width        = targetSize.width - this.scrollOffset,
+            height       = targetSize.height,
+            availWidth   = Math.max(0, width - paddingHoriz),
+
+            isStart      = this.pack == 'start',
+            isCenter     = this.pack == 'center',
+            isEnd        = this.pack == 'end',
+
+            nonFlexHeight= 0,
+            maxWidth     = 0,
+            totalFlex    = 0,
+
+            //used to cache the calculated size and position values for each child item
+            boxes        = [],
+
+            //used in the for loops below, just declared here for brevity
+            child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedHeight, horizMargins, stretchWidth;
+
+            //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];
+                childHeight = child.height;
+                childWidth  = child.width;
+                canLayout   = !child.hasLayout && Ext.isFunction(child.doLayout);
 
-        }
 
-        // 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();
+                // Static height (numeric) requires no calcs
+                if (!Ext.isNumber(childHeight)) {
+
+                    // flex and not 'auto' height
+                    if (child.flex && !childHeight) {
+                        totalFlex += child.flex;
+
+                    // Not flexed or 'auto' height or undefined height
+                    } else {
+                        //Render and layout sub-containers without a flex or width defined, as otherwise we
+                        //don't know how wide the sub-container should be and cannot calculate flexed widths
+                        if (!childHeight && canLayout) {
+                            child.doLayout();
+                        }
+
+                        childSize = child.getSize();
+                        childWidth = childSize.width;
+                        childHeight = childSize.height;
+                    }
+                }
+
+                childMargins = child.margins;
+
+                nonFlexHeight += (childHeight || 0) + childMargins.top + childMargins.bottom;
+
+                // Max width for align - force layout of non-layed out subcontainers without a numeric width
+                if (!Ext.isNumber(childWidth)) {
+                    if (canLayout) {
+                        child.doLayout();
+                    }
+                    childWidth = child.getWidth();
                 }
-                ch = c.getHeight();
-            } else {
-                ch = 0;
+
+                maxWidth = Math.max(maxWidth, childWidth + childMargins.left + childMargins.right);
+
+                //cache the size of each child component
+                boxes.push({
+                    component: child,
+                    height   : childHeight || undefined,
+                    width    : childWidth || undefined
+                });
             }
 
-            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);
+            //the height available to the flexed items
+            var availableHeight = Math.max(0, (height - nonFlexHeight - paddingVert));
+
+            if (isCenter) {
+                topOffset += availableHeight / 2;
+            } else if (isEnd) {
+                topOffset += availableHeight;
             }
-        }
-        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
-
-            // Adjust left pos for centering
-            if(this.align == 'center'){
-                if((diff = availableWidth - (c.getWidth() + cm.left + cm.right)) > 0){
-                    cl += (diff/2);
-                    aw -= diff;
+
+            //temporary variables used in the flex height calculations below
+            var remainingHeight = availableHeight,
+                remainingFlex   = totalFlex;
+
+            //calculate the height of each flexed item, and the left + top positions of every item
+            for (i = 0; i < visibleCount; i++) {
+                child = visibleItems[i];
+                calcs = boxes[i];
+
+                childMargins = child.margins;
+                horizMargins = childMargins.left + childMargins.right;
+
+                topOffset   += childMargins.top;
+
+                if (isStart && child.flex && !child.height) {
+                    flexedHeight     = Math.ceil((child.flex / remainingFlex) * remainingHeight);
+                    remainingHeight -= flexedHeight;
+                    remainingFlex   -= child.flex;
+
+                    calcs.height = flexedHeight;
+                    calcs.dirtySize = true;
                 }
-            }
 
-            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();
+                calcs.left = leftOffset + childMargins.left;
+                calcs.top  = topOffset;
+
+                switch (this.align) {
+                    case 'stretch':
+                        stretchWidth = availWidth - horizMargins;
+                        calcs.width  = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
+                        calcs.dirtySize = true;
+                        break;
+                    case 'stretchmax':
+                        stretchWidth = maxWidth - horizMargins;
+                        calcs.width  = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
+                        calcs.dirtySize = true;
+                        break;
+                    case 'center':
+                        var diff = availWidth - calcs.width - horizMargins;
+                        if (diff > 0) {
+                            calcs.left = leftOffset + horizMargins + (diff / 2);
+                        }
+                }
+
+                topOffset += calcs.height + childMargins.bottom;
             }
-            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){
-                this.adjustmentPass = true;
-                this.onLayout(ct, target);
+
+        return {
+            boxes: boxes,
+            meta : {
+                maxWidth: maxWidth
             }
-        }
-        delete this.adjustmentPass;
+        };
     }
 });
 
@@ -323,8 +479,27 @@ Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to
      * the height of the largest item.</div></li>
      */
-    align : 'top', // top, middle, stretch, strechmax
-    type: 'hbox',
+    align: 'top', // top, middle, stretch, strechmax
+
+    type : 'hbox',
+
+    /**
+     * @private
+     * See parent documentation
+     */
+    updateInnerCtSize: function(tSize, calcs) {
+        var innerCtWidth  = tSize.width,
+            innerCtHeight = calcs.meta.maxHeight + this.padding.top + this.padding.bottom;
+
+        if (this.align == 'stretch') {
+            innerCtHeight = tSize.height;
+        } else if (this.align == 'middle') {
+            innerCtHeight = Math.max(tSize.height, innerCtHeight);
+        }
+
+        this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
+    },
+
     /**
      * @cfg {String} pack
      * Controls how the child items of the container are packed together. Acceptable configuration values
@@ -347,134 +522,154 @@ Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
      */
 
-    // private
-    onLayout : function(ct, target){
-        Ext.layout.HBoxLayout.superclass.onLayout.call(this, ct, target);
-
-        var cs = this.getRenderedItems(ct), csLen = cs.length,
-            c, i, cm, cw, ch, diff, availWidth,
-            size = this.getLayoutTargetSize(),
-            w = size.width - this.scrollOffset,
-            h = size.height,
-            l = this.padding.left,
-            t = this.padding.top,
-            isStart = this.pack == 'start',
-            isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
-            extraWidth = 0,
-            maxHeight = 0,
-            totalFlex = 0,
-            usedWidth = 0;
-
-        for (i = 0 ; i < csLen; i++) {
-            c = cs[i];
-            // Total of all the flex values
-            totalFlex += c.flex || 0;
-            // Don't run width calculations on flexed items
-            if (!c.flex) {
-                // Render and layout sub-containers without a flex or width, once
-                if (!c.width && !c.hasLayout && c.doLayout) {
-                    c.doLayout();
+    /**
+     * @private
+     * Calculates the size and positioning of each item in the HBox. 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 maxHeight 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 visibleCount = visibleItems.length,
+
+            padding      = this.padding,
+            topOffset    = padding.top,
+            leftOffset   = padding.left,
+            paddingVert  = topOffset  + padding.bottom,
+            paddingHoriz = leftOffset + padding.right,
+
+            width        = targetSize.width - this.scrollOffset,
+            height       = targetSize.height,
+            availHeight  = Math.max(0, height - paddingVert),
+
+            isStart      = this.pack == 'start',
+            isCenter     = this.pack == 'center',
+            isEnd        = this.pack == 'end',
+            // isRestore    = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
+
+            nonFlexWidth = 0,
+            maxHeight    = 0,
+            totalFlex    = 0,
+
+            //used to cache the calculated size and position values for each child item
+            boxes        = [],
+
+            //used in the for loops below, just declared here for brevity
+            child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedWidth, vertMargins, stretchHeight;
+
+            //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];
+                childHeight = child.height;
+                childWidth  = child.width;
+                canLayout   = !child.hasLayout && Ext.isFunction(child.doLayout);
+
+                // Static width (numeric) requires no calcs
+                if (!Ext.isNumber(childWidth)) {
+
+                    // flex and not 'auto' width
+                    if (child.flex && !childWidth) {
+                        totalFlex += child.flex;
+
+                    // Not flexed or 'auto' width or undefined width
+                    } else {
+                        //Render and layout sub-containers without a flex or width defined, as otherwise we
+                        //don't know how wide the sub-container should be and cannot calculate flexed widths
+                        if (!childWidth && canLayout) {
+                            child.doLayout();
+                        }
+
+                        childSize   = child.getSize();
+                        childWidth  = childSize.width;
+                        childHeight = childSize.height;
+                    }
                 }
-                cw = c.getWidth();
-            } else {
-                cw = 0;
-            }
-            cm = c.margins;
-            // Determine how much width is available to flex
-            extraWidth += cw + cm.left + cm.right;
-            // Max height for align
-            maxHeight = Math.max(maxHeight, c.getHeight() + cm.top + cm.bottom);
-        }
-        // Final avail width calc
-        availWidth = Math.max(0, (w - extraWidth - this.padding.left - this.padding.right));
-
-        var innerCtHeight = maxHeight + this.padding.top + this.padding.bottom;
-        switch(this.align){
-            case 'stretch':
-                this.innerCt.setSize(w, h);
-                break;
-            case 'stretchmax':
-            case 'top':
-                this.innerCt.setSize(w, innerCtHeight);
-                break;
-            case 'middle':
-                this.innerCt.setSize(w, h = Math.max(h, innerCtHeight));
-                break;
-        }
 
-        var leftOver = availWidth,
-            widths = [],
-            restore = [],
-            idx = 0,
-            availableHeight = Math.max(0, h - this.padding.top - this.padding.bottom);
-
-        for (i = 0 ; i < csLen; i++) {
-            c = cs[i];
-            if(isStart && c.flex){
-                cw = Math.floor(availWidth * (c.flex / totalFlex));
-                leftOver -= cw;
-                widths.push(cw);
-            }
-        }
+                childMargins = child.margins;
 
-        if(this.pack == 'center'){
-            l += availWidth ? availWidth / 2 : 0;
-        }else if(this.pack == 'end'){
-            l += availWidth;
-        }
-        for (i = 0 ; i < csLen; i++) {
-            c = cs[i];
-            cm = c.margins;
-            l += cm.left;
-            c.setPosition(l, t + cm.top);
-            if(isStart && c.flex){
-                cw = Math.max(0, widths[idx++] + (leftOver-- > 0 ? 1 : 0));
-                if(isRestore){
-                    restore.push(c.getHeight());
+                nonFlexWidth += (childWidth || 0) + childMargins.left + childMargins.right;
+
+                // Max height for align - force layout of non-layed out subcontainers without a numeric height
+                if (!Ext.isNumber(childHeight)) {
+                    if (canLayout) {
+                        child.doLayout();
+                    }
+                    childHeight = child.getHeight();
                 }
-                c.setSize(cw, availableHeight);
-            }else{
-                cw = c.getWidth();
+
+                maxHeight = Math.max(maxHeight, childHeight + childMargins.top + childMargins.bottom);
+
+                //cache the size of each child component
+                boxes.push({
+                    component: child,
+                    height   : childHeight || undefined,
+                    width    : childWidth || undefined
+                });
             }
-            l += cw + cm.right;
-        }
 
-        idx = 0;
-        for (i = 0 ; i < csLen; i++) {
-            c = cs[i];
-            cm = c.margins;
-            ch = c.getHeight();
-            if(isStart && c.flex){
-                ch = restore[idx++];
+            //the width available to the flexed items
+            var availableWidth = Math.max(0, (width - nonFlexWidth - paddingHoriz));
+
+            if (isCenter) {
+                leftOffset += availableWidth / 2;
+            } else if (isEnd) {
+                leftOffset += availableWidth;
             }
-            if(this.align == 'stretch'){
-                c.setHeight(((h - (this.padding.top + this.padding.bottom)) - (cm.top + cm.bottom)).constrain(
-                    c.minHeight || 0, c.maxHeight || 1000000));
-            }else if(this.align == 'stretchmax'){
-                c.setHeight((maxHeight - (cm.top + cm.bottom)).constrain(
-                    c.minHeight || 0, c.maxHeight || 1000000));
-            }else{
-                if(this.align == 'middle'){
-                    diff = availableHeight - (ch + cm.top + cm.bottom);
-                    ch = t + cm.top + (diff/2);
-                    if(diff > 0){
-                        c.setPosition(c.x, ch);
-                    }
+
+            //temporary variables used in the flex width calculations below
+            var remainingWidth = availableWidth,
+                remainingFlex  = totalFlex;
+
+            //calculate the widths of each flexed item, and the left + top positions of every item
+            for (i = 0; i < visibleCount; i++) {
+                child = visibleItems[i];
+                calcs = boxes[i];
+
+                childMargins = child.margins;
+                vertMargins  = childMargins.top + childMargins.bottom;
+
+                leftOffset  += childMargins.left;
+
+                if (isStart && child.flex && !child.width) {
+                    flexedWidth     = Math.ceil((child.flex / remainingFlex) * remainingWidth);
+                    remainingWidth -= flexedWidth;
+                    remainingFlex  -= child.flex;
+
+                    calcs.width = flexedWidth;
+                    calcs.dirtySize = true;
                 }
-                if(isStart && c.flex){
-                    c.setHeight(ch);
+
+                calcs.left = leftOffset;
+                calcs.top  = topOffset + childMargins.top;
+
+                switch (this.align) {
+                    case 'stretch':
+                        stretchHeight = availHeight - vertMargins;
+                        calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
+                        calcs.dirtySize = true;
+                        break;
+                    case 'stretchmax':
+                        stretchHeight = maxHeight - vertMargins;
+                        calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
+                        calcs.dirtySize = true;
+                        break;
+                    case 'middle':
+                        var diff = availHeight - calcs.height - vertMargins;
+                        if (diff > 0) {
+                            calcs.top = topOffset + vertMargins + (diff / 2);
+                        }
                 }
+                leftOffset += calcs.width + childMargins.right;
             }
-        }
-        // 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){
-                this.adjustmentPass = true;
-                this.onLayout(ct, target);
+
+        return {
+            boxes: boxes,
+            meta : {
+                maxHeight: maxHeight
             }
-        }
-        delete this.adjustmentPass;
+        };
     }
 });