blob: 3fbf0ac70e1e1111bd232c245c717abd92cb2692 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.containers.utilityClasses
{
import mx.containers.BoxDirection;
import mx.controls.scrollClasses.ScrollBar;
import mx.core.Container;
import mx.core.EdgeMetrics;
import mx.core.IUIComponent;
import mx.core.mx_internal;
import mx.core.ScrollPolicy;
use namespace mx_internal;
[ExcludeClass]
/**
* @private
* The BoxLayout class is for internal use only.
*/
public class BoxLayout extends Layout
{
include "../../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function BoxLayout()
{
super();
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// direction
//----------------------------------
/**
* @private
*/
public var direction:String = BoxDirection.VERTICAL;
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
* Measure container as per Box layout rules.
*/
override public function measure():void
{
var target:Container = super.target;
var isVertical:Boolean = this.isVertical();
var minWidth:Number = 0;
var minHeight:Number = 0;
var preferredWidth:Number = 0;
var preferredHeight:Number = 0;
var n:int = target.numChildren;
var numChildrenWithOwnSpace:int = n;
for (var i:int = 0; i < n; i++)
{
var child:IUIComponent = target.getLayoutChildAt(i);
if (!child.includeInLayout)
{
numChildrenWithOwnSpace--;
continue;
}
var wPref:Number = child.getExplicitOrMeasuredWidth();
var hPref:Number = child.getExplicitOrMeasuredHeight();
if (isVertical)
{
minWidth = Math.max(!isNaN(child.percentWidth) ?
child.minWidth : wPref, minWidth);
preferredWidth = Math.max(wPref, preferredWidth);
minHeight += !isNaN(child.percentHeight) ?
child.minHeight : hPref;
preferredHeight += hPref;
}
else
{
minWidth += !isNaN(child.percentWidth) ?
child.minWidth : wPref;
preferredWidth += wPref;
minHeight = Math.max(!isNaN(child.percentHeight) ?
child.minHeight : hPref, minHeight);
preferredHeight = Math.max(hPref, preferredHeight);
}
}
var wPadding:Number = widthPadding(numChildrenWithOwnSpace);
var hPadding:Number = heightPadding(numChildrenWithOwnSpace);
target.measuredMinWidth = minWidth + wPadding;
target.measuredMinHeight = minHeight + hPadding;
target.measuredWidth = preferredWidth + wPadding;
target.measuredHeight = preferredHeight + hPadding;
}
/**
* @private
* Lay out children as per Box layout rules.
*/
override public function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
var target:Container = super.target;
var n:int = target.numChildren;
if (n == 0)
return;
var vm:EdgeMetrics = target.viewMetricsAndPadding;
var paddingLeft:Number = target.getStyle("paddingLeft");
var paddingTop:Number = target.getStyle("paddingTop");
var horizontalAlign:Number = getHorizontalAlignValue();
var verticalAlign:Number = getVerticalAlignValue();
var mw:Number = target.scaleX > 0 && target.scaleX != 1 ?
target.minWidth / Math.abs(target.scaleX) :
target.minWidth;
var mh:Number = target.scaleY > 0 && target.scaleY != 1 ?
target.minHeight / Math.abs(target.scaleY) :
target.minHeight;
var w:Number = Math.max(unscaledWidth, mw) - vm.right - vm.left;
var h:Number = Math.max(unscaledHeight, mh) - vm.bottom - vm.top;
var horizontalScrollBar:ScrollBar = target.horizontalScrollBar;
var verticalScrollBar:ScrollBar = target.verticalScrollBar;
var gap:Number;
var numChildrenWithOwnSpace:int;
var excessSpace:Number;
var top:Number;
var left:Number;
var i:int;
var obj:IUIComponent;
if (n == 1)
{
// This is an optimized code path for the case where there's one
// child. This case is especially common when the Box is really
// a GridItem. This code path works for both horizontal and
// vertical layout.
var child:IUIComponent = target.getLayoutChildAt(0);
var percentWidth:Number = child.percentWidth;
var percentHeight:Number = child.percentHeight;
var width:Number;
if (percentWidth)
{
width = Math.max(child.minWidth,
Math.min(child.maxWidth,
((percentWidth >= 100) ? w : (w * percentWidth / 100))));
}
else
{
width = child.getExplicitOrMeasuredWidth();
}
var height:Number
if (percentHeight)
{
height = Math.max(child.minHeight,
Math.min(child.maxHeight,
((percentHeight >= 100) ? h : (h * percentHeight / 100))));
}
else
{
height = child.getExplicitOrMeasuredHeight();
}
// if scaled and zoom is playing, best to let the sizes be non-integer
if (child.scaleX == 1 && child.scaleY == 1)
child.setActualSize(Math.floor(width), Math.floor(height));
else
child.setActualSize(width, height);
// Ignore scrollbar sizes for child alignment purpose.
if (verticalScrollBar != null &&
target.verticalScrollPolicy == ScrollPolicy.AUTO)
{
w += verticalScrollBar.minWidth;
}
if (horizontalScrollBar != null &&
target.horizontalScrollPolicy == ScrollPolicy.AUTO)
{
h += horizontalScrollBar.minHeight;
}
// Use the child's width and height because a Resize effect might
// have changed the child's dimensions. Bug 146158.
left = (w - child.width) * horizontalAlign + paddingLeft;
top = (h - child.height) * verticalAlign + paddingTop;
child.move(Math.floor(left), Math.floor(top));
}
else if (isVertical()) // VBOX
{
gap = target.getStyle("verticalGap");
numChildrenWithOwnSpace = n;
for (i = 0; i < n; i++)
{
if (!IUIComponent(target.getChildAt(i)).includeInLayout)
numChildrenWithOwnSpace--;
}
// Stretch everything as needed, including widths.
excessSpace = Flex.flexChildHeightsProportionally(
target, h - (numChildrenWithOwnSpace - 1) * gap, w);
// Ignore scrollbar sizes for child alignment purpose.
if (horizontalScrollBar != null &&
target.horizontalScrollPolicy == ScrollPolicy.AUTO)
{
excessSpace += horizontalScrollBar.minHeight;
}
if (verticalScrollBar != null &&
target.verticalScrollPolicy == ScrollPolicy.AUTO)
{
w += verticalScrollBar.minWidth;
}
top = paddingTop + excessSpace * verticalAlign;
for (i = 0; i < n; i++)
{
obj = target.getLayoutChildAt(i);
left = (w - obj.width) * horizontalAlign + paddingLeft;
obj.move(Math.floor(left), Math.floor(top));
if (obj.includeInLayout)
top += obj.height + gap;
}
}
else // HBOX
{
gap = target.getStyle("horizontalGap");
numChildrenWithOwnSpace = n;
for (i = 0; i < n; i++)
{
if (!IUIComponent(target.getChildAt(i)).includeInLayout)
numChildrenWithOwnSpace--;
}
// stretch everything as needed including heights
excessSpace = Flex.flexChildWidthsProportionally(
target, w - (numChildrenWithOwnSpace - 1) * gap, h);
// Ignore scrollbar sizes for child alignment purpose.
if (horizontalScrollBar != null &&
target.horizontalScrollPolicy == ScrollPolicy.AUTO)
{
h += horizontalScrollBar.minHeight;
}
if (verticalScrollBar != null &&
target.verticalScrollPolicy == ScrollPolicy.AUTO)
{
excessSpace += verticalScrollBar.minWidth;
}
left = paddingLeft + excessSpace * horizontalAlign;
for (i = 0; i < n; i++)
{
obj = target.getLayoutChildAt(i);
top = (h - obj.height) * verticalAlign + paddingTop;
obj.move(Math.floor(left), Math.floor(top));
if (obj.includeInLayout)
left += obj.width + gap;
}
}
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function isVertical():Boolean
{
return direction != BoxDirection.HORIZONTAL;
}
/**
* @private
*/
mx_internal function widthPadding(numChildren:Number):Number
{
var vm:EdgeMetrics = target.viewMetricsAndPadding;
var padding:Number = vm.left + vm.right;
if (numChildren > 1 && isVertical() == false)
{
padding += target.getStyle("horizontalGap") *
(numChildren - 1);
}
return padding;
}
/**
* @private
*/
mx_internal function heightPadding(numChildren:Number):Number
{
var vm:EdgeMetrics = target.viewMetricsAndPadding;
var padding:Number = vm.top + vm.bottom;
if (numChildren > 1 && isVertical())
{
padding += target.getStyle("verticalGap") *
(numChildren - 1);
}
return padding;
}
/**
* @private
* Returns a numeric value for the align setting.
* 0 = left/top, 0.5 = center, 1 = right/bottom
*/
mx_internal function getHorizontalAlignValue():Number
{
var horizontalAlign:String = target.getStyle("horizontalAlign");
if (horizontalAlign == "center")
return 0.5;
else if (horizontalAlign == "right")
return 1;
// default = left
return 0;
}
/**
* @private
* Returns a numeric value for the align setting.
* 0 = left/top, 0.5 = center, 1 = right/bottom
*/
mx_internal function getVerticalAlignValue():Number
{
var verticalAlign:String = target.getStyle("verticalAlign");
if (verticalAlign == "middle")
return 0.5;
else if (verticalAlign == "bottom")
return 1;
// default = top
return 0;
}
}
}