Upgrade to ExtJS 3.2.1 - Released 04/27/2010
[extjs.git] / src / widgets / layout / BoxLayout.js
1 /*!
2  * Ext JS Library 3.2.1
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.layout.BoxLayout
9  * @extends Ext.layout.ContainerLayout
10  * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>
11  */
12 Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
13     /**
14      * @cfg {Object} defaultMargins
15      * <p>If the individual contained items do not have a <tt>margins</tt>
16      * property specified, the default margins from this property will be
17      * applied to each item.</p>
18      * <br><p>This property may be specified as an object containing margins
19      * to apply in the format:</p><pre><code>
20 {
21     top: (top margin),
22     right: (right margin),
23     bottom: (bottom margin),
24     left: (left margin)
25 }</code></pre>
26      * <p>This property may also be specified as a string containing
27      * space-separated, numeric margin values. The order of the sides associated
28      * with each value matches the way CSS processes margin values:</p>
29      * <div class="mdetail-params"><ul>
30      * <li>If there is only one value, it applies to all sides.</li>
31      * <li>If there are two values, the top and bottom borders are set to the
32      * first value and the right and left are set to the second.</li>
33      * <li>If there are three values, the top is set to the first value, the left
34      * and right are set to the second, and the bottom is set to the third.</li>
35      * <li>If there are four values, they apply to the top, right, bottom, and
36      * left, respectively.</li>
37      * </ul></div>
38      * <p>Defaults to:</p><pre><code>
39      * {top:0, right:0, bottom:0, left:0}
40      * </code></pre>
41      */
42     defaultMargins : {left:0,top:0,right:0,bottom:0},
43     /**
44      * @cfg {String} padding
45      * <p>Sets the padding to be applied to all child items managed by this layout.</p>
46      * <p>This property must be specified as a string containing
47      * space-separated, numeric padding values. The order of the sides associated
48      * with each value matches the way CSS processes padding values:</p>
49      * <div class="mdetail-params"><ul>
50      * <li>If there is only one value, it applies to all sides.</li>
51      * <li>If there are two values, the top and bottom borders are set to the
52      * first value and the right and left are set to the second.</li>
53      * <li>If there are three values, the top is set to the first value, the left
54      * and right are set to the second, and the bottom is set to the third.</li>
55      * <li>If there are four values, they apply to the top, right, bottom, and
56      * left, respectively.</li>
57      * </ul></div>
58      * <p>Defaults to: <code>"0"</code></p>
59      */
60     padding : '0',
61     // documented in subclasses
62     pack : 'start',
63
64     // private
65     monitorResize : true,
66     type: 'box',
67     scrollOffset : 0,
68     extraCls : 'x-box-item',
69     targetCls : 'x-box-layout-ct',
70     innerCls : 'x-box-inner',
71
72     constructor : function(config){
73         Ext.layout.BoxLayout.superclass.constructor.call(this, config);
74
75         if (Ext.isString(this.defaultMargins)) {
76             this.defaultMargins = this.parseMargins(this.defaultMargins);
77         }
78     },
79
80     /**
81      * @private
82      * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values
83      * when laying out
84      */
85     onLayout: function(container, target) {
86         Ext.layout.BoxLayout.superclass.onLayout.call(this, container, target);
87
88         var items = this.getVisibleItems(container),
89             tSize = this.getLayoutTargetSize();
90
91         /**
92          * @private
93          * @property layoutTargetLastSize
94          * @type Object
95          * Private cache of the last measured size of the layout target. This should never be used except by
96          * BoxLayout subclasses during their onLayout run.
97          */
98         this.layoutTargetLastSize = tSize;
99
100         /**
101          * @private
102          * @property childBoxCache
103          * @type Array
104          * Array of the last calculated height, width, top and left positions of each visible rendered component
105          * within the Box layout.
106          */
107         this.childBoxCache = this.calculateChildBoxes(items, tSize);
108
109         this.updateInnerCtSize(tSize, this.childBoxCache);
110         this.updateChildBoxes(this.childBoxCache.boxes);
111
112         // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
113         this.handleTargetOverflow(tSize, container, target);
114     },
115
116     /**
117      * Resizes and repositions each child component
118      * @param {Array} boxes The box measurements
119      */
120     updateChildBoxes: function(boxes) {
121         for (var i = 0, length = boxes.length; i < length; i++) {
122             var box  = boxes[i],
123                 comp = box.component;
124
125             if (box.dirtySize) {
126                 comp.setSize(box.width, box.height);
127             }
128             // Don't set positions to NaN
129             if (isNaN(box.left) || isNaN(box.top)) {
130                 continue;
131             }
132             comp.setPosition(box.left, box.top);
133         }
134     },
135
136     /**
137      * @private
138      * Called by onRender just before the child components are sized and positioned. This resizes the innerCt
139      * to make sure all child items fit within it. We call this before sizing the children because if our child
140      * items are larger than the previous innerCt size the browser will insert scrollbars and then remove them
141      * again immediately afterwards, giving a performance hit.
142      * Subclasses should provide an implementation.
143      * @param {Object} currentSize The current height and width of the innerCt
144      * @param {Array} calculations The new box calculations of all items to be laid out
145      */
146     updateInnerCtSize: Ext.emptyFn,
147
148     /**
149      * @private
150      * This should be called after onLayout of any BoxLayout subclass. If the target's overflow is not set to 'hidden',
151      * we need to lay out a second time because the scrollbars may have modified the height and width of the layout
152      * target. Having a Box layout inside such a target is therefore not recommended.
153      * @param {Object} previousTargetSize The size and height of the layout target before we just laid out
154      * @param {Ext.Container} container The container
155      * @param {Ext.Element} target The target element
156      */
157     handleTargetOverflow: function(previousTargetSize, container, target) {
158         var overflow = target.getStyle('overflow');
159
160         if (overflow && overflow != 'hidden' &&!this.adjustmentPass) {
161             var newTargetSize = this.getLayoutTargetSize();
162             if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height){
163                 this.adjustmentPass = true;
164                 this.onLayout(container, target);
165             }
166         }
167
168         delete this.adjustmentPass;
169     },
170
171     // private
172     isValidParent : function(c, target){
173         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
174     },
175
176     /**
177      * @private
178      * Returns all items that are both rendered and visible
179      * @return {Array} All matching items
180      */
181     getVisibleItems: function(ct) {
182         var ct  = ct || this.container,
183             t   = ct.getLayoutTarget(),
184             cti = ct.items.items,
185             len = cti.length,
186
187             i, c, items = [];
188
189         for (i = 0; i < len; i++) {
190             if((c = cti[i]).rendered && this.isValidParent(c, t) && c.hidden !== true  && c.collapsed !== true){
191                 items.push(c);
192             }
193         }
194
195         return items;
196     },
197
198     // private
199     renderAll : function(ct, target){
200         if(!this.innerCt){
201             // the innerCt prevents wrapping and shuffling while
202             // the container is resizing
203             this.innerCt = target.createChild({cls:this.innerCls});
204             this.padding = this.parseMargins(this.padding);
205         }
206         Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt);
207     },
208
209     getLayoutTargetSize : function(){
210         var target = this.container.getLayoutTarget(), ret;
211         if (target) {
212             ret = target.getViewSize();
213
214             // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
215             // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
216             // with getViewSize
217             if (Ext.isIE && Ext.isStrict && ret.width == 0){
218                 ret =  target.getStyleSize();
219             }
220
221             ret.width -= target.getPadding('lr');
222             ret.height -= target.getPadding('tb');
223         }
224         return ret;
225     },
226
227     // private
228     renderItem : function(c){
229         if(Ext.isString(c.margins)){
230             c.margins = this.parseMargins(c.margins);
231         }else if(!c.margins){
232             c.margins = this.defaultMargins;
233         }
234         Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments);
235     }
236 });
237
238 /**
239  * @class Ext.layout.VBoxLayout
240  * @extends Ext.layout.BoxLayout
241  * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical
242  * space between child items containing a numeric <code>flex</code> configuration.</p>
243  * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option.
244  */
245 Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
246     /**
247      * @cfg {String} align
248      * Controls how the child items of the container are aligned. Acceptable configuration values for this
249      * property are:
250      * <div class="mdetail-params"><ul>
251      * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally
252      * at the <b>left</b> side of the container</div></li>
253      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the
254      * <b>mid-width</b> of the container</div></li>
255      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill
256      * the width of the container</div></li>
257      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to
258      * the size of the largest item.</div></li>
259      * </ul></div>
260      */
261     align : 'left', // left, center, stretch, strechmax
262     type: 'vbox',
263
264     /**
265      * @cfg {String} pack
266      * Controls how the child items of the container are packed together. Acceptable configuration values
267      * for this property are:
268      * <div class="mdetail-params"><ul>
269      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
270      * <b>top</b> side of container</div></li>
271      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
272      * <b>mid-height</b> of container</div></li>
273      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>bottom</b>
274      * side of container</div></li>
275      * </ul></div>
276      */
277
278     /**
279      * @cfg {Number} flex
280      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
281      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>vertically</b>
282      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
283      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
284      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
285      */
286
287     /**
288      * @private
289      * See parent documentation
290      */
291     updateInnerCtSize: function(tSize, calcs) {
292         var innerCtHeight = tSize.height,
293             innerCtWidth  = calcs.meta.maxWidth + this.padding.left + this.padding.right;
294
295         if (this.align == 'stretch') {
296             innerCtWidth = tSize.width;
297         } else if (this.align == 'center') {
298             innerCtWidth = Math.max(tSize.width, innerCtWidth);
299         }
300
301         //we set the innerCt size first because if our child items are larger than the previous innerCt size
302         //the browser will insert scrollbars and then remove them again immediately afterwards
303         this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
304     },
305
306     /**
307      * @private
308      * Calculates the size and positioning of each item in the VBox. This iterates over all of the rendered,
309      * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
310      * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
311      * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
312      * @param {Object} targetSize Object containing target size and height
313      * @return {Object} Object containing box measurements for each child, plus meta data
314      */
315     calculateChildBoxes: function(visibleItems, targetSize) {
316         var visibleCount = visibleItems.length,
317
318             padding      = this.padding,
319             topOffset    = padding.top,
320             leftOffset   = padding.left,
321             paddingVert  = topOffset  + padding.bottom,
322             paddingHoriz = leftOffset + padding.right,
323
324             width        = targetSize.width - this.scrollOffset,
325             height       = targetSize.height,
326             availWidth   = Math.max(0, width - paddingHoriz),
327
328             isStart      = this.pack == 'start',
329             isCenter     = this.pack == 'center',
330             isEnd        = this.pack == 'end',
331
332             nonFlexHeight= 0,
333             maxWidth     = 0,
334             totalFlex    = 0,
335
336             //used to cache the calculated size and position values for each child item
337             boxes        = [],
338
339             //used in the for loops below, just declared here for brevity
340             child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedHeight, horizMargins, stretchWidth;
341
342             //gather the total flex of all flexed items and the width taken up by fixed width items
343             for (i = 0; i < visibleCount; i++) {
344                 child = visibleItems[i];
345                 childHeight = child.height;
346                 childWidth  = child.width;
347                 canLayout   = !child.hasLayout && Ext.isFunction(child.doLayout);
348
349
350                 // Static height (numeric) requires no calcs
351                 if (!Ext.isNumber(childHeight)) {
352
353                     // flex and not 'auto' height
354                     if (child.flex && !childHeight) {
355                         totalFlex += child.flex;
356
357                     // Not flexed or 'auto' height or undefined height
358                     } else {
359                         //Render and layout sub-containers without a flex or width defined, as otherwise we
360                         //don't know how wide the sub-container should be and cannot calculate flexed widths
361                         if (!childHeight && canLayout) {
362                             child.doLayout();
363                         }
364
365                         childSize = child.getSize();
366                         childWidth = childSize.width;
367                         childHeight = childSize.height;
368                     }
369                 }
370
371                 childMargins = child.margins;
372
373                 nonFlexHeight += (childHeight || 0) + childMargins.top + childMargins.bottom;
374
375                 // Max width for align - force layout of non-layed out subcontainers without a numeric width
376                 if (!Ext.isNumber(childWidth)) {
377                     if (canLayout) {
378                         child.doLayout();
379                     }
380                     childWidth = child.getWidth();
381                 }
382
383                 maxWidth = Math.max(maxWidth, childWidth + childMargins.left + childMargins.right);
384
385                 //cache the size of each child component
386                 boxes.push({
387                     component: child,
388                     height   : childHeight || undefined,
389                     width    : childWidth || undefined
390                 });
391             }
392
393             //the height available to the flexed items
394             var availableHeight = Math.max(0, (height - nonFlexHeight - paddingVert));
395
396             if (isCenter) {
397                 topOffset += availableHeight / 2;
398             } else if (isEnd) {
399                 topOffset += availableHeight;
400             }
401
402             //temporary variables used in the flex height calculations below
403             var remainingHeight = availableHeight,
404                 remainingFlex   = totalFlex;
405
406             //calculate the height of each flexed item, and the left + top positions of every item
407             for (i = 0; i < visibleCount; i++) {
408                 child = visibleItems[i];
409                 calcs = boxes[i];
410
411                 childMargins = child.margins;
412                 horizMargins = childMargins.left + childMargins.right;
413
414                 topOffset   += childMargins.top;
415
416                 if (isStart && child.flex && !child.height) {
417                     flexedHeight     = Math.ceil((child.flex / remainingFlex) * remainingHeight);
418                     remainingHeight -= flexedHeight;
419                     remainingFlex   -= child.flex;
420
421                     calcs.height = flexedHeight;
422                     calcs.dirtySize = true;
423                 }
424
425                 calcs.left = leftOffset + childMargins.left;
426                 calcs.top  = topOffset;
427
428                 switch (this.align) {
429                     case 'stretch':
430                         stretchWidth = availWidth - horizMargins;
431                         calcs.width  = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
432                         calcs.dirtySize = true;
433                         break;
434                     case 'stretchmax':
435                         stretchWidth = maxWidth - horizMargins;
436                         calcs.width  = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
437                         calcs.dirtySize = true;
438                         break;
439                     case 'center':
440                         var diff = availWidth - calcs.width - horizMargins;
441                         if (diff > 0) {
442                             calcs.left = leftOffset + horizMargins + (diff / 2);
443                         }
444                 }
445
446                 topOffset += calcs.height + childMargins.bottom;
447             }
448
449         return {
450             boxes: boxes,
451             meta : {
452                 maxWidth: maxWidth
453             }
454         };
455     }
456 });
457
458 Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout;
459
460 /**
461  * @class Ext.layout.HBoxLayout
462  * @extends Ext.layout.BoxLayout
463  * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
464  * space between child items containing a numeric <code>flex</code> configuration.</p>
465  * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option.
466  */
467 Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
468     /**
469      * @cfg {String} align
470      * Controls how the child items of the container are aligned. Acceptable configuration values for this
471      * property are:
472      * <div class="mdetail-params"><ul>
473      * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically
474      * at the <b>top</b> of the container</div></li>
475      * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically in the
476      * <b>middle</b> of the container</div></li>
477      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill
478      * the height of the container</div></li>
479      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to
480      * the height of the largest item.</div></li>
481      */
482     align: 'top', // top, middle, stretch, strechmax
483
484     type : 'hbox',
485
486     /**
487      * @private
488      * See parent documentation
489      */
490     updateInnerCtSize: function(tSize, calcs) {
491         var innerCtWidth  = tSize.width,
492             innerCtHeight = calcs.meta.maxHeight + this.padding.top + this.padding.bottom;
493
494         if (this.align == 'stretch') {
495             innerCtHeight = tSize.height;
496         } else if (this.align == 'middle') {
497             innerCtHeight = Math.max(tSize.height, innerCtHeight);
498         }
499
500         this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
501     },
502
503     /**
504      * @cfg {String} pack
505      * Controls how the child items of the container are packed together. Acceptable configuration values
506      * for this property are:
507      * <div class="mdetail-params"><ul>
508      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
509      * <b>left</b> side of container</div></li>
510      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
511      * <b>mid-width</b> of container</div></li>
512      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>
513      * side of container</div></li>
514      * </ul></div>
515      */
516     /**
517      * @cfg {Number} flex
518      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
519      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>
520      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
521      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
522      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
523      */
524
525     /**
526      * @private
527      * Calculates the size and positioning of each item in the HBox. This iterates over all of the rendered,
528      * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
529      * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
530      * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
531      * @param {Object} targetSize Object containing target size and height
532      * @return {Object} Object containing box measurements for each child, plus meta data
533      */
534     calculateChildBoxes: function(visibleItems, targetSize) {
535         var visibleCount = visibleItems.length,
536
537             padding      = this.padding,
538             topOffset    = padding.top,
539             leftOffset   = padding.left,
540             paddingVert  = topOffset  + padding.bottom,
541             paddingHoriz = leftOffset + padding.right,
542
543             width        = targetSize.width - this.scrollOffset,
544             height       = targetSize.height,
545             availHeight  = Math.max(0, height - paddingVert),
546
547             isStart      = this.pack == 'start',
548             isCenter     = this.pack == 'center',
549             isEnd        = this.pack == 'end',
550             // isRestore    = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
551
552             nonFlexWidth = 0,
553             maxHeight    = 0,
554             totalFlex    = 0,
555
556             //used to cache the calculated size and position values for each child item
557             boxes        = [],
558
559             //used in the for loops below, just declared here for brevity
560             child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedWidth, vertMargins, stretchHeight;
561
562             //gather the total flex of all flexed items and the width taken up by fixed width items
563             for (i = 0; i < visibleCount; i++) {
564                 child       = visibleItems[i];
565                 childHeight = child.height;
566                 childWidth  = child.width;
567                 canLayout   = !child.hasLayout && Ext.isFunction(child.doLayout);
568
569                 // Static width (numeric) requires no calcs
570                 if (!Ext.isNumber(childWidth)) {
571
572                     // flex and not 'auto' width
573                     if (child.flex && !childWidth) {
574                         totalFlex += child.flex;
575
576                     // Not flexed or 'auto' width or undefined width
577                     } else {
578                         //Render and layout sub-containers without a flex or width defined, as otherwise we
579                         //don't know how wide the sub-container should be and cannot calculate flexed widths
580                         if (!childWidth && canLayout) {
581                             child.doLayout();
582                         }
583
584                         childSize   = child.getSize();
585                         childWidth  = childSize.width;
586                         childHeight = childSize.height;
587                     }
588                 }
589
590                 childMargins = child.margins;
591
592                 nonFlexWidth += (childWidth || 0) + childMargins.left + childMargins.right;
593
594                 // Max height for align - force layout of non-layed out subcontainers without a numeric height
595                 if (!Ext.isNumber(childHeight)) {
596                     if (canLayout) {
597                         child.doLayout();
598                     }
599                     childHeight = child.getHeight();
600                 }
601
602                 maxHeight = Math.max(maxHeight, childHeight + childMargins.top + childMargins.bottom);
603
604                 //cache the size of each child component
605                 boxes.push({
606                     component: child,
607                     height   : childHeight || undefined,
608                     width    : childWidth || undefined
609                 });
610             }
611
612             //the width available to the flexed items
613             var availableWidth = Math.max(0, (width - nonFlexWidth - paddingHoriz));
614
615             if (isCenter) {
616                 leftOffset += availableWidth / 2;
617             } else if (isEnd) {
618                 leftOffset += availableWidth;
619             }
620
621             //temporary variables used in the flex width calculations below
622             var remainingWidth = availableWidth,
623                 remainingFlex  = totalFlex;
624
625             //calculate the widths of each flexed item, and the left + top positions of every item
626             for (i = 0; i < visibleCount; i++) {
627                 child = visibleItems[i];
628                 calcs = boxes[i];
629
630                 childMargins = child.margins;
631                 vertMargins  = childMargins.top + childMargins.bottom;
632
633                 leftOffset  += childMargins.left;
634
635                 if (isStart && child.flex && !child.width) {
636                     flexedWidth     = Math.ceil((child.flex / remainingFlex) * remainingWidth);
637                     remainingWidth -= flexedWidth;
638                     remainingFlex  -= child.flex;
639
640                     calcs.width = flexedWidth;
641                     calcs.dirtySize = true;
642                 }
643
644                 calcs.left = leftOffset;
645                 calcs.top  = topOffset + childMargins.top;
646
647                 switch (this.align) {
648                     case 'stretch':
649                         stretchHeight = availHeight - vertMargins;
650                         calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
651                         calcs.dirtySize = true;
652                         break;
653                     case 'stretchmax':
654                         stretchHeight = maxHeight - vertMargins;
655                         calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
656                         calcs.dirtySize = true;
657                         break;
658                     case 'middle':
659                         var diff = availHeight - calcs.height - vertMargins;
660                         if (diff > 0) {
661                             calcs.top = topOffset + vertMargins + (diff / 2);
662                         }
663                 }
664                 leftOffset += calcs.width + childMargins.right;
665             }
666
667         return {
668             boxes: boxes,
669             meta : {
670                 maxHeight: maxHeight
671             }
672         };
673     }
674 });
675
676 Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;