first commit

This commit is contained in:
2020-10-20 00:58:15 +02:00
commit 7f1b9bfca5
222 changed files with 56918 additions and 0 deletions

View File

@ -0,0 +1,267 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
import com.yahoo.astra.layout.events.LayoutEvent;
import flash.display.DisplayObject;
import flash.events.EventDispatcher;
import flash.geom.Rectangle;
/**
* Some basic properties shared by multiple ILayoutMode implementations.
* Should be treated as an abstract class that is meant to be subclassed.
*
* @author Josh Tynjala
* @see ILayoutMode
* @see com.yahoo.astra.layout.ILayoutContainer
*/
public class BaseLayoutMode extends EventDispatcher implements IAdvancedLayoutMode
{
//--------------------------------------
// Constructor
//--------------------------------------
/**
* Constructor.
*/
public function BaseLayoutMode()
{
}
//--------------------------------------
// Properties
//--------------------------------------
/**
* @private
* Storage for the paddingLeft property.
*/
private var _paddingLeft:Number = 0;
/**
* The number of pixels displayed at the left of the target's children.
*/
public function get paddingLeft():Number
{
return this._paddingLeft;
}
/**
* @private
*/
public function set paddingLeft(value:Number):void
{
if(this._paddingLeft != value)
{
this._paddingLeft = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
}
/**
* @private
* Storage for the paddingRight property.
*/
private var _paddingRight:Number = 0;
/**
* The number of pixels displayed at the right of the target's children.
*/
public function get paddingRight():Number
{
return this._paddingRight;
}
/**
* @private
*/
public function set paddingRight(value:Number):void
{
if(this._paddingRight != value)
{
this._paddingRight = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
}
/**
* @private
* Storage for the paddingTop property.
*/
private var _paddingTop:Number = 0;
/**
* The number of pixels displayed at the top of the target's children.
*/
public function get paddingTop():Number
{
return this._paddingTop;
}
/**
* @private
*/
public function set paddingTop(value:Number):void
{
if(this._paddingTop != value)
{
this._paddingTop = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
}
/**
* @private
* Storage for the paddingBottom property.
*/
private var _paddingBottom:Number = 0;
/**
* The number of pixels displayed at the bottom of the target's children.
*/
public function get paddingBottom():Number
{
return this._paddingBottom;
}
/**
* @private
*/
public function set paddingBottom(value:Number):void
{
if(this._paddingBottom != value)
{
this._paddingBottom = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
}
/**
* @private
* Storage for the clients with advanced configuration options.
*/
protected var clients:Array = [];
/**
* @private
* Storage for the advanced configuration options. Indicies
* correspond to the indices in the clients Array.
*/
protected var configurations:Array = [];
//--------------------------------------
// Public Methods
//--------------------------------------
/**
* @inheritDoc
*/
public function addClient(target:DisplayObject, configuration:Object = null):void
{
if(!target)
{
throw new ArgumentError("Target must be a DisplayObject. Received " + target + ".");
}
configuration = configuration ? configuration : {};
var generatedConfig:Object = this.newConfiguration();
for(var prop:String in configuration)
{
generatedConfig[prop] = configuration[prop];
}
//reuse an existing client if we're re-adding it
var index:int = this.clients.indexOf(target);
if(index < 0)
{
index = this.clients.length;
this.clients[index] = target;
}
this.configurations[index] = generatedConfig;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @inheritDoc
*/
public function removeClient(target:DisplayObject):void
{
var index:int = this.clients.indexOf(target);
if(index < 0)
{
throw new ArgumentError("Cannot call removeClient() with a DisplayObject that is not a client.");
}
this.clients.splice(index, 1);
this.configurations.splice(index, 1);
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @inheritDoc
*/
public function hasClient(target:DisplayObject):Boolean
{
return this.clients.indexOf(target) >= 0;
}
/**
* @inheritDoc
*/
public function layoutObjects(displayObjects:Array, bounds:Rectangle):Rectangle
{
//to be overridden
throw new Error("Method BaseLayoutMode.layoutChildren() must be overridden!");
return new Rectangle();
}
//--------------------------------------
// Protected Methods
//--------------------------------------
/**
* @private
* Ensures that every child of the target has a configuration and creates a list of children
* that are included in the layout.
*/
protected function configureChildren(targets:Array):Array
{
var childrenInLayout:Array = [];
var childCount:int = targets.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(targets[i]);
var index:int = this.clients.indexOf(child);
if(index < 0)
{
//set up a default configuration if the child hasn't been added as a client
index = this.clients.length;
this.clients.push(child);
this.configurations.push(this.newConfiguration());
}
//only return children that have includeInLayout specified
var config:Object = this.configurations[index];
if(config.includeInLayout)
{
childrenInLayout.push(child);
}
}
return childrenInLayout;
}
/**
* @private
* The default configuration properties for a client of the layout mode.
*/
protected function newConfiguration():Object
{
return { includeInLayout: true };
}
}
}

View File

@ -0,0 +1,96 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
/**
* Constraint values available in BorderLayout.
*
* @see BorderLayout
* @author Josh Tynjala
*/
public class BorderConstraints
{
//--------------------------------------
// Static Properties
//--------------------------------------
/**
* The target will be constrained to the top edge. Its width will be
* altered to fit to the width of the container. Its height will
* remain unchanged.
*
* <p>Consider the <code>TOP</code> constraint to work like a page's
* header. It appears above all other constrained children, with no other
* children appearing to the left or right. Multiple <code>TOP</code> constraints
* will be arranged vertically from the top down in the order that they
* were registered as clients of the BorderLayout algorithm.</p>
*/
public static const TOP:String = "top";
/**
* The target will be constrained to the bottom edge. Its width will be
* altered to fit to the width of the container. Its height will
* remain unchanged.
*
* <p>Consider the <code>BOTTOM</code> constraint to work like a page's
* footer. It appears below all other constrained children, with no other
* children appearing to the left or right. Multiple <code>BOTTOM</code> constraints
* will be arranged vertically from the bottom up in the order that they
* were registered as clients of the BorderLayout algorithm.</p>
*/
public static const BOTTOM:String = "bottom";
/**
* The target will be constrained to the left edge. It will appear
* below any items constrained to the top and above items constrained to
* the bottom. Its height will be altered to fill the remaining height
* of the container (after the TOP and BOTTOM constraints are measured),
* and its width will remain unchanged.
*
* <p>Consinder the <code>LEFT</code> constraint to work like a page's
* sidebar that is aligned to the left. It appears to the left of all other
* children, with only the top and bottom constraints taking precendence.
* Multiple <code>LEFT</code> constraints will be arranged horizontally
* from left to right in the order that they were registered as clients
* of the BorderLayout algorithm.</p>
*/
public static const LEFT:String = "left";
/**
* The target will be constrained to the right edge. It will appear
* below any items constrained to the top and above items constrained to
* the bottom. Its height will be altered to fill the remaining height
* of the container (after the TOP and BOTTOM constraints are measured),
* and its width will remain unchanged.
*
* <p>Consinder the <code>RIGHT</code> constraint to work like a page's
* sidebar that is aligned to the right. It appears to the right of all other
* children, with only the top and bottom constraints taking precendence.
* Multiple <code>RIGHT</code> constraints will be arranged horizontally
* from right to left in the order that they were registered as clients
* of the BorderLayout algorithm.</p>
*/
public static const RIGHT:String = "right";
/**
* The target will be constrained to the center of the container. It
* will appear between all other constrained children. Its height will be
* altered to fill the remaining height of the container (after the TOP
* and BOTTOM constraints are measured) and its width will be altered to
* fill the remaining width of the container (after the LEFT and RIGHT
* constraints are measured).
*
* <p>Consider the <code>CENTER</code> constraint to work like a page's
* primary content. It appears in between all other constraints and changes
* size to fill the remaining area (after all other constraints are
* measured). Multiple <code>CENTER</code> constraints will be arranged
* vertically from top down starting from the bottom edge of the
* <code>TOP</code> constraints to the top edge of any <code>BOTTOM</code>
* constraints.</p>
*/
public static const CENTER:String = "center";
}
}

View File

@ -0,0 +1,496 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
import com.yahoo.astra.layout.events.LayoutEvent;
import com.yahoo.astra.utils.DisplayObjectUtil;
import flash.display.DisplayObject;
import flash.geom.Rectangle;
/**
* Arranges a DisplayObjectContainer's children using a page-like border
* algorithm. Children with TOP and BOTTOM constraints will be positioned
* and sized like page headers and footers. LEFT and RIGHT constrained
* children will be positioned and sized like sidebars and CENTER
* constrained children will be positioned and stretched to fill the
* remaining space.
*
* <p><strong>Advanced Client Options:</strong></p>
* <p>Client configuration parameters allow a developer to specify
* behaviors for individual children of the target container. To set these
* advanced options, one must call <code>addClient()</code> on the BorderLayout
* instance and pass the child to configure along with an object specifying
* the configuration parameters.</p>
*
* @example The following code adds clients to a BorderLayout instance:
* <listing version="3.0">
* var border:BorderLayout = new BorderLayout();
* border.addClient( headerSprite, { constraint: BorderConstraints.TOP } );
* border.addClient( contentSprite,
* {
* constraint: BorderConstraints.CENTER,
* maintainAspectRatio: true,
* horizontalAlign: HorizontalAlignment.CENTER,
* verticalAlign: VerticalAlignment.MIDDLE
* });
* border.addClient( footerSprite, { constraint: BorderConstraints.BOTTOM } );
*
* var container:LayoutContainer = new LayoutContainer();
* container.layoutMode = border;
* this.addChild( container );
* </listing>
*
* <p>Several client parameters are available with the BorderLayout algorithm:</p>
* <dl>
* <dt><strong><code>constraint</code></strong> : String</dt>
* <dd>The BorderConstraints value to be used on the target by the layout algorithm. The default
* value is <code>BorderConstraints.CENTER</code>.</dd>
* <dt><strong><code>maintainAspectRatio</code></strong> : Boolean</dt>
* <dd>If true, the aspect ratio of the target will be maintained if it is resized.</dd>
* <dt><strong><code>horizontalAlign</code></strong> : String</dt>
* <dd>The horizontal alignment used when positioning the target. Used in combination with
* <code>maintainAspectRatio</code>.</dd>
* <dt><strong><code>verticalAlign</code></strong> : String</dt>
* <dd>The vertical alignment used when positioning the target. Used in combination with
* <code>maintainAspectRatio</code>.</dd>
* <dt><strong><code>aspectRatio</code></strong> : Number</dt>
* <dd>The desired aspect ratio to use with <code>maintainAspectRatio</code>. This value is optional.
* If no aspect ratio is provided, it will be determined based on the target's original width and height.</dd>
* <dt><strong><code>includeInLayout</code></strong> : Boolean</dt>
* <dd>If <code>false</code>, the target will not be included in layout calculations. The default value is <code>true</code>.</dd>
* </dl>
*
* @see BorderConstraints
* @see HorizontalAlignment
* @see VerticalAlignment
* @see com.yahoo.astra.layout.LayoutContainer
*
* @author Josh Tynjala
*/
public class BorderLayout extends BaseLayoutMode implements IAdvancedLayoutMode
{
//--------------------------------------
// Constructor
//--------------------------------------
/**
* Constructor.
*/
public function BorderLayout()
{
}
//--------------------------------------
// Properties
//--------------------------------------
/**
* @private
* Storage for the verticalGap property.
*/
private var _verticalGap:Number = 0;
/**
* The number of vertical pixels between each item displayed by this
* container.
*/
public function get verticalGap():Number
{
return this._verticalGap;
}
/**
* @private
*/
public function set verticalGap(value:Number):void
{
this._verticalGap = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the horizontalGap property.
*/
private var _horizontalGap:Number = 0;
/**
* The number of horizontal pixels between each item displayed by this
* container.
*/
public function get horizontalGap():Number
{
return this._horizontalGap;
}
/**
* @private
*/
public function set horizontalGap(value:Number):void
{
this._horizontalGap = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
//--------------------------------------
// Public Methods
//--------------------------------------
/**
* @private
*/
override public function addClient(target:DisplayObject, configuration:Object = null):void
{
//if horizontalAlign or verticalAlign is not specified, set some defaults
configuration.horizontalAlign = configuration.horizontalAlign ? configuration.horizontalAlign : "left";
configuration.verticalAlign = configuration.verticalAlign ? configuration.verticalAlign : "top";
//if no aspectRatio has been specified, use the aspect ratio
//calculated from the target's width and height
if(configuration.maintainAspectRatio && !configuration.aspectRatio)
{
configuration.aspectRatio = target.width / target.height;
}
super.addClient(target, configuration);
}
/**
* @inheritDoc
*/
override public function layoutObjects(displayObjects:Array, bounds:Rectangle):Rectangle
{
const START_X:Number = bounds.x + this.paddingLeft;
const START_Y:Number = bounds.y + this.paddingTop;
var width:Number = bounds.width;
if(bounds.width == Number.POSITIVE_INFINITY)
{
width = this.measureChildWidths();
}
width -= (this.paddingLeft + this.paddingRight);
var height:Number = bounds.height;
if(bounds.height == Number.POSITIVE_INFINITY)
{
height = this.measureChildHeights();
}
height -= (this.paddingTop + this.paddingBottom);
var remainingWidth:Number = width;
var remainingHeight:Number = height;
//position the top children
var topHeight:Number = 0;
var topChildren:Array = this.getChildrenByConstraint(BorderConstraints.TOP, true);
var topChildCount:int = topChildren.length;
for(var i:int = 0; i < topChildCount; i++)
{
var topChild:DisplayObject = DisplayObject(topChildren[i]);
var config:Object = this.configurations[this.clients.indexOf(topChild)];
var x:Number = START_X;
var y:Number = START_Y + topHeight;
if(config.maintainAspectRatio)
{
DisplayObjectUtil.resizeAndMaintainAspectRatio(topChild, width, topChild.height, config.aspectRatio);
}
else
{
topChild.width = width;
}
DisplayObjectUtil.align(topChild, new Rectangle(x, y, width, topChild.height), config.horizontalAlign, config.verticalAlign);
topHeight += topChild.height + this.verticalGap;
}
remainingHeight -= topHeight;
//position the bottom children
var bottomHeight:Number = 0;
var bottomChildren:Array = this.getChildrenByConstraint(BorderConstraints.BOTTOM, true);
var bottomChildCount:int = bottomChildren.length;
for(i = 0; i < bottomChildCount; i++)
{
var bottomChild:DisplayObject = DisplayObject(bottomChildren[i]);
config = this.configurations[this.clients.indexOf(bottomChild)];
bottomHeight += bottomChild.height;
x = START_X;
y = START_Y + height - bottomHeight;
if(config.maintainAspectRatio)
{
DisplayObjectUtil.resizeAndMaintainAspectRatio(bottomChild, width, bottomChild.height, config.aspectRatio);
}
else
{
bottomChild.width = width;
}
DisplayObjectUtil.align(bottomChild, new Rectangle(x, y, width, bottomChild.height), config.horizontalAlign, config.verticalAlign);
bottomHeight += this.verticalGap;
}
//if topHeight + bottomHeight < the total height, fix the overlap
var difference:Number = (START_Y + topHeight) - (START_Y + height - bottomHeight);
if(difference > 0)
{
for(i = 0; i < bottomChildCount; i++)
{
bottomChild = DisplayObject(bottomChildren[i]);
bottomChild.y += difference;
}
}
remainingHeight -= bottomHeight;
//the height of the center area affects the height of the left and right areas
var centerHeight:Number = Math.max(remainingHeight, 0);
//position the left children
var leftWidth:Number = 0;
var leftChildren:Array = this.getChildrenByConstraint(BorderConstraints.LEFT, true);
var leftChildCount:int = leftChildren.length;
for(i = 0; i < leftChildCount; i++)
{
var leftChild:DisplayObject = DisplayObject(leftChildren[i]);
config = this.configurations[this.clients.indexOf(leftChild)];
x = START_X + leftWidth;
y = START_Y + topHeight;
if(config.maintainAspectRatio)
{
DisplayObjectUtil.resizeAndMaintainAspectRatio(leftChild, leftChild.width, centerHeight, config.aspectRatio);
}
else
{
leftChild.height = centerHeight;
}
DisplayObjectUtil.align(leftChild, new Rectangle(x, y, leftChild.width, centerHeight), config.horizontalAlign, config.verticalAlign);
leftWidth += leftChild.width + this.horizontalGap;
}
remainingWidth -= leftWidth;
//position the right children
var rightWidth:Number = 0;
var rightChildren:Array = this.getChildrenByConstraint(BorderConstraints.RIGHT, true);
var rightChildCount:int = rightChildren.length;
for(i = 0; i < rightChildCount; i++)
{
var rightChild:DisplayObject = DisplayObject(rightChildren[i]);
config = this.configurations[this.clients.indexOf(rightChild)];
rightWidth += rightChild.width;
x = START_X + width - rightWidth;
y = START_Y + topHeight;
if(config.maintainAspectRatio)
{
DisplayObjectUtil.resizeAndMaintainAspectRatio(rightChild, rightChild.width, centerHeight, config.aspectRatio);
}
else
{
rightChild.height = centerHeight;
}
DisplayObjectUtil.align(rightChild, new Rectangle(x, y, rightChild.width, centerHeight), config.horizontalAlign, config.verticalAlign);
rightWidth += this.horizontalGap;
}
//if leftWidth + rightWidth < the total width, fix the overlap
difference = (START_X + leftWidth) - (START_X + width - rightWidth);
if(difference > 0)
{
for(i = 0; i < rightChildCount; i++)
{
rightChild = DisplayObject(rightChildren[i]);
rightChild.x += difference;
}
}
remainingWidth -= rightWidth;
//position the center children in the remaining width
var centerWidth:Number = Math.max(remainingWidth, 0);
var centerChildren:Array = this.getChildrenByConstraint(BorderConstraints.CENTER, true);
var centerChildCount:int = centerChildren.length;
var centerChildHeight:Number = centerHeight / centerChildCount;
for(i = 0; i < centerChildCount; i++)
{
var centerChild:DisplayObject = DisplayObject(centerChildren[i]);
config = this.configurations[this.clients.indexOf(centerChild)];
x = START_X + leftWidth;
y = START_Y + topHeight + (i * centerChildHeight);
if(config.maintainAspectRatio)
{
DisplayObjectUtil.resizeAndMaintainAspectRatio(centerChild, centerWidth, centerChildHeight, config.aspectRatio);
}
else
{
centerChild.width = centerWidth;
centerChild.height = centerChildHeight;
}
DisplayObjectUtil.align(centerChild, new Rectangle(x, y, centerWidth, centerChildHeight), config.horizontalAlign, config.verticalAlign);
}
if(remainingWidth < 0)
{
width -= remainingWidth;
}
if(remainingHeight < 0)
{
height -= remainingHeight;
}
bounds.width = width + this.paddingLeft + this.paddingRight;
bounds.height = height + this.paddingTop + this.paddingBottom;
return bounds;
}
/**
* @private
* Creates the default configuration for this layout mode.
*/
override protected function newConfiguration():Object
{
return {
includeInLayout: true,
constraint: BorderConstraints.CENTER
};
}
/**
* @private
* If no width is specified for the layout container, we need to
* measure the children to determine the best width.
*/
protected function measureChildWidths():Number
{
var totalWidth:Number = this.paddingLeft + this.paddingRight;
var leftChildren:Array = this.getChildrenByConstraint(BorderConstraints.LEFT, true);
var childCount:int = leftChildren.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(leftChildren[i]);
totalWidth += child.width + this.horizontalGap;
}
var rightChildren:Array = this.getChildrenByConstraint(BorderConstraints.RIGHT, true);
childCount = rightChildren.length;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(rightChildren[i]);
totalWidth += child.width + this.horizontalGap;
}
var maxWidth:Number = 0;
var centerChildren:Array = this.getChildrenByConstraint(BorderConstraints.CENTER, true);
childCount = centerChildren.length;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(centerChildren[i]);
maxWidth = Math.max(maxWidth, child.width);
}
totalWidth += maxWidth;
maxWidth = 0;
var topChildren:Array = this.getChildrenByConstraint(BorderConstraints.TOP, true);
childCount = topChildren.length;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(topChildren[i]);
maxWidth = Math.max(maxWidth, child.width);
}
var bottomChildren:Array = this.getChildrenByConstraint(BorderConstraints.BOTTOM, true);
childCount = bottomChildren.length;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(bottomChildren[i]);
maxWidth = Math.max(maxWidth, child.width);
}
totalWidth = Math.max(maxWidth, totalWidth);
return totalWidth;
}
/**
* @private
* If no height is specified for the layout container, we need to
* measure the children to determine the best height.
*/
protected function measureChildHeights():Number
{
var totalHeight:Number = this.paddingTop + this.paddingBottom;
var topChildren:Array = this.getChildrenByConstraint(BorderConstraints.TOP, true);
var childCount:int = topChildren.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(topChildren[i]);
totalHeight += child.height;
}
var bottomChildren:Array = this.getChildrenByConstraint(BorderConstraints.BOTTOM, true);
childCount = bottomChildren.length;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(bottomChildren[i]);
totalHeight += child.height;
}
var centerTotalHeight:Number = 0;
var centerChildren:Array = this.getChildrenByConstraint(BorderConstraints.CENTER, true);
childCount = centerChildren.length;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(centerChildren[i]);
centerTotalHeight += child.height;
}
var maxHeight:Number = centerTotalHeight;
var rightChildren:Array = this.getChildrenByConstraint(BorderConstraints.RIGHT, true);
childCount = rightChildren.length;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(rightChildren[i]);
maxHeight = Math.max(maxHeight, child.height);
}
var leftChildren:Array = this.getChildrenByConstraint(BorderConstraints.LEFT, true);
childCount = leftChildren.length;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(leftChildren[i]);
maxHeight = Math.max(maxHeight, child.height);
}
totalHeight += maxHeight;
return totalHeight;
}
/**
* @private
* A simple filter for getting all the clients with a specific constraint
* in their configuration.
*/
protected function getChildrenByConstraint(constraint:String, inLayoutOnly:Boolean = false):Array
{
return this.clients.filter(function(item:DisplayObject, index:int, source:Array):Boolean
{
var configuration:Object = this.configurations[index];
return configuration.constraint == constraint && (inLayoutOnly ? configuration.includeInLayout : true);
}, this);
}
}
}

View File

@ -0,0 +1,444 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
import com.yahoo.astra.layout.events.LayoutEvent;
import com.yahoo.astra.utils.DisplayObjectUtil;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.geom.Rectangle;
/**
* Arranges a DisplayObjectContainer's children in a single column or row.
*
* @example The following code configures a BoxLayout instance and passes it to a container:
* <listing version="3.0">
* var box:BoxLayout = new BoxLayout();
* box.direction = "vertical";
* box.verticalGap = 4;
* box.verticalAlign = VerticalAlignment.MIDDLE;
*
* var container:LayoutContainer = new LayoutContainer();
* container.layoutMode = box;
* this.addChild( container );
* </listing>
*
* <p><strong>Advanced Client Options:</strong></p>
* <p>Optional client configuration parameters allow a developer to specify
* behaviors for individual children of the target container. To set these
* advanced options, one must call <code>addClient()</code> on the BoxLayout
* instance and pass the child to configure along with an object specifying
* the configuration parameters. Several client parameters are available to
* the BoxLayout algorithm:</p>
*
* <dl>
* <dt><strong><code>percentWidth</code></strong> : Number</dt>
* <dd>The target's width will be updated based on a percentage of the width specified in the layout bounds.</dd>
* <dt><strong><code>percentHeight</code></strong> : Number</dt>
* <dd>The target's width will be updated based on a percentage of the width specified in the layout bounds.</dd>
* <dt><strong><code>minWidth</code></strong> : Number</dt>
* <dd>The minimum width value to allow when resizing. The default value is <code>0</code>.</dd>
* <dt><strong><code>minHeight</code></strong> : Number</dt>
* <dd>The minimum height value to allow when resizing. The default value is <code>0</code>.</dd>
* <dt><strong><code>maxWidth</code></strong> : Number</dt>
* <dd>The maximum width value to allow when resizing. The default value is <code>10000</code>.</dd>
* <dt><strong><code>maxHeight</code></strong> : Number</dt>
* <dd>The maximum height value to allow when resizing. The default value is <code>10000</code>.</dd>
* <dt><strong><code>includeInLayout</code></strong> : Boolean</dt>
* <dd>If <code>false</code>, the target will not be included in layout calculations. The default value is <code>true</code>.</dd>
* </dl>
*
* @example The following code adds multiple clients to a BoxLayout instance:
* <listing version="3.0">
* var box:BoxLayout = new BoxLayout();
* box.direction = "vertical";
* box.addClient( headerSprite, { percentWidth: 100 } );
* box.addClient( contentSprite,
* {
* percentWidth: 100,
* percentHeight: 100,
* minWidth: 640,
* minHeight: 480
* });
* box.addClient( footerSprite, { percentWidth: 100 } );
*
* var container:LayoutContainer = new LayoutContainer( box );
* container.width = 1024;
* container.height = 768;
* container.addChild( headerSprite );
* container.addChild( contentSprite );
* container.addChild( footerSprite );
* this.addChild( container );
* </listing>
*
* @author Josh Tynjala
* @see com.yahoo.astra.layout.LayoutContainer
*/
public class BoxLayout extends BaseLayoutMode implements IAdvancedLayoutMode
{
//--------------------------------------
// Static Properties
//--------------------------------------
/**
* @private
* The default maximum number of pixels to calculate width sizing.
*/
private static const DEFAULT_MAX_WIDTH:Number = 10000;
/**
* @private
* The default maximum number of pixels to calculate height sizing.
*/
private static const DEFAULT_MAX_HEIGHT:Number = 10000;
//--------------------------------------
// Constructor
//--------------------------------------
/**
* Constructor.
*/
public function BoxLayout()
{
}
//--------------------------------------
// Properties
//--------------------------------------
/**
* @private
* Storage for the direction property.
*/
private var _direction:String = "horizontal";
/**
* The direction in which children of the target are laid out. Valid
* direction values include <code>"vertical"</code> or <code>"horizontal"</code>.
*/
public function get direction():String
{
return this._direction;
}
/**
* @private
*/
public function set direction(value:String):void
{
this._direction = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the verticalGap property.
*/
private var _verticalGap:Number = 0;
/**
* The number of pixels appearing between the target's children
* vertically.
*/
public function get verticalGap():Number
{
return this._verticalGap;
}
/**
* @private
*/
public function set verticalGap(value:Number):void
{
this._verticalGap = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the horizontalGap property.
*/
private var _horizontalGap:Number = 0;
/**
* The number of pixels appearing between the target's children
* horizontally.
*/
public function get horizontalGap():Number
{
return this._horizontalGap;
}
/**
* @private
*/
public function set horizontalGap(value:Number):void
{
this._horizontalGap = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the verticalAlign property.
*/
private var _verticalAlign:String = "top";
/**
* The vertical alignment of children displayed in the target.
*
* @see VerticalAlignment
*/
public function get verticalAlign():String
{
return this._verticalAlign;
}
/**
* @private
*/
public function set verticalAlign(value:String):void
{
this._verticalAlign = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the horizontalAlign property.
*/
private var _horizontalAlign:String = "left";
/**
* The horizontal alignment of children displayed in the target.
*
* @see HorizontalAlignment
*/
public function get horizontalAlign():String
{
return this._horizontalAlign;
}
/**
* @private
*/
public function set horizontalAlign(value:String):void
{
this._horizontalAlign = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* The maximum width value from among the current target's children.
*/
protected var maxChildWidth:Number;
/**
* @private
* The maximum height value from among the current target's children.
*/
protected var maxChildHeight:Number;
//--------------------------------------
// Public Methods
//--------------------------------------
/**
* @inheritDoc
*/
override public function layoutObjects(displayObjects:Array, bounds:Rectangle):Rectangle
{
var childrenInLayout:Array = this.configureChildren(displayObjects);
//determine the available horizontal space
var hSpaceForChildren:Number = bounds.width - this.paddingLeft - this.paddingRight;
if((hSpaceForChildren == Infinity)||(hSpaceForChildren >9000))
{
hSpaceForChildren = DEFAULT_MAX_WIDTH;
}
//determine the available vertical space
var vSpaceForChildren:Number = bounds.height - this.paddingTop - this.paddingBottom;
if((vSpaceForChildren == Infinity)||(vSpaceForChildren >9000))
{
vSpaceForChildren = DEFAULT_MAX_HEIGHT;
}
//resize the children based on the available space and the specified percentage width and height values.
if(this.direction == "vertical")
{
vSpaceForChildren -= (this.verticalGap * (childrenInLayout.length - 1));
PercentageSizeUtil.flexChildHeightsProportionally(this.clients, this.configurations, hSpaceForChildren, vSpaceForChildren);
}
else
{
hSpaceForChildren -= (this.horizontalGap * (childrenInLayout.length - 1));
PercentageSizeUtil.flexChildWidthsProportionally(this.clients, this.configurations, hSpaceForChildren, vSpaceForChildren);
}
this.maxChildWidth = 0;
this.maxChildHeight = 0;
var childCount:int = childrenInLayout.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(childrenInLayout[i]);
//measure the child's width
this.maxChildWidth = Math.max(this.maxChildWidth, child.width);
this.maxChildHeight = Math.max(this.maxChildHeight, child.height);
}
if(this.direction == "vertical")
{
this.layoutChildrenVertically(childrenInLayout, bounds);
}
else
{
this.layoutChildrenHorizontally(childrenInLayout, bounds);
}
bounds = LayoutModeUtil.calculateChildBounds(childrenInLayout);
bounds.width += this.paddingRight;
bounds.height += this.paddingBottom;
return bounds;
}
//--------------------------------------
// Protected Methods
//--------------------------------------
/**
* @private
* Positions the children when direction is vertical.
*/
protected function layoutChildrenVertically(children:Array, bounds:Rectangle):void
{
var maxXPosition:Number = bounds.width;
if(maxXPosition == Number.POSITIVE_INFINITY)
{
maxXPosition = this.maxChildWidth;
}
maxXPosition -= (this.paddingLeft + this.paddingRight);
var xPosition:Number = bounds.x + this.paddingLeft;
var yPosition:Number = bounds.y + this.paddingTop;
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
child.x = xPosition;
child.y = yPosition;
DisplayObjectUtil.align(child, new Rectangle(child.x, child.y, maxXPosition, child.height), this.horizontalAlign, null);
yPosition += child.height + this.verticalGap;
}
//special case: if the combined height of the children
//is less than the total height specified in the bounds,
//then we can align vertically as well!
var totalHeight:Number = yPosition - this.verticalGap - bounds.y + this.paddingBottom;
if(totalHeight < bounds.height)
{
var middleStart:Number = (bounds.height - totalHeight) / 2;
var rightStart:Number = bounds.height - totalHeight - bounds.y;
rightStart = (rightStart == Infinity)?DEFAULT_MAX_HEIGHT:rightStart;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(children[i]);
switch(this.verticalAlign)
{
case "middle":
child.y += middleStart;
break;
case "bottom":
child.y += rightStart;
break;
}
}
}
}
/**
* @private
* Positions the children when direction is horizontal.
*/
protected function layoutChildrenHorizontally(children:Array, bounds:Rectangle):void
{
var maxYPosition:Number = bounds.height;
if(maxYPosition == Number.POSITIVE_INFINITY)
{
maxYPosition = this.maxChildHeight;
}
maxYPosition -= (this.paddingBottom + this.paddingTop);
var xPosition:Number = bounds.x + this.paddingLeft;
var yPosition:Number = bounds.y + this.paddingTop
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
child.x = xPosition;
child.y = yPosition;
DisplayObjectUtil.align(child, new Rectangle(child.x, child.y, child.width, maxYPosition), null, this.verticalAlign);
xPosition += child.width + this.horizontalGap;
}
//special case: if the combined width of the children
//is less than the total width specified in the bounds,
//then we can align horizontally as well!
var totalWidth:Number = xPosition - this.horizontalGap - bounds.x + this.paddingRight;
if(totalWidth < bounds.width)
{
var middleStart:Number = (bounds.width - totalWidth) / 2;
var rightStart:Number = bounds.width - totalWidth;
rightStart = (rightStart == Infinity)?DEFAULT_MAX_WIDTH:rightStart;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(children[i]);
switch(this.horizontalAlign)
{
case "center":
child.x += middleStart;
break;
case "right":
child.x += rightStart;
break;
}
}
}
}
/**
* @private
* Creates the default configuration for this layout mode.
*/
override protected function newConfiguration():Object
{
return {
includeInLayout: true,
minWidth: 0,
maxWidth: 10000,
minHeight: 0,
maxHeight: 10000,
percentWidth: NaN,
percentHeight: NaN
};
}
}
}

View File

@ -0,0 +1,366 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
import com.yahoo.astra.layout.events.LayoutEvent;
import com.yahoo.astra.utils.DisplayObjectUtil;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.geom.Rectangle;
/**
* Arranges a DisplayObjectContainer's children using a flow algorithm. When
* a child is too large for a row or column, a new row or column is created.
* Similar to the flow of text in a document.
*
* @example The following code configures a FlowLayout instance and passes it to a container:
* <listing version="3.0">
* var flow:FlowLayout = new FlowLayout();
* flow.direction = "horizontal";
* flow.horizontalGap = 1;
* flow.verticalGap = 4;
* flow.verticalAlign = VerticalAlignment.BOTTOM;
*
* var container:LayoutContainer = new LayoutContainer();
* container.layoutMode = flow;
* this.addChild( container );
* </listing>
*
* <p><strong>Advanced Client Options:</strong></p>
* <p>Optional client configuration parameters allow a developer to specify
* behaviors for individual children of the target container. To set these
* advanced options, one must call <code>addClient()</code> on the FlowLayout
* instance and pass the child to configure along with an object specifying
* the configuration parameters. The following client parameters are available to
* the FlowLayout algorithm:</p>
*
* <dl>
* <dt><strong><code>includeInLayout</code></strong> : Boolean</dt>
* <dd>If <code>false</code>, the target will not be included in layout calculations. The default value is <code>true</code>.</dd>
* </dl>
*
* @author Josh Tynjala
* @see com.yahoo.astra.layout.LayoutContainer
*/
public class FlowLayout extends BaseLayoutMode implements IAdvancedLayoutMode
{
//--------------------------------------
// Constructor
//--------------------------------------
/**
* Constructor.
*/
public function FlowLayout()
{
super();
}
//--------------------------------------
// Properties
//--------------------------------------
/**
* @private
* Storage for the direction property.
*/
private var _direction:String = "horizontal";
/**
* The direction in which children of the target are laid out. Once
* the edge of the container is reached, the children will begin
* appearing on the next row or column. Valid direction values include
* <code>"vertical"</code> or <code>"horizontal"</code>.
*/
public function get direction():String
{
return this._direction;
}
/**
* @private
*/
public function set direction(value:String):void
{
this._direction = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the verticalGap property.
*/
private var _verticalGap:Number = 0;
/**
* The number of pixels appearing between the target's children
* vertically.
*/
public function get verticalGap():Number
{
return this._verticalGap;
}
/**
* @private
*/
public function set verticalGap(value:Number):void
{
this._verticalGap = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the horizontalGap property.
*/
private var _horizontalGap:Number = 0;
/**
* The number of pixels appearing between the target's children
* horizontally.
*/
public function get horizontalGap():Number
{
return this._horizontalGap;
}
/**
* @private
*/
public function set horizontalGap(value:Number):void
{
this._horizontalGap = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the verticalAlign property.
*/
private var _verticalAlign:String = "top";
/**
* The vertical alignment of children displayed in the target.
*
* @see VerticalAlignment
*/
public function get verticalAlign():String
{
return this._verticalAlign;
}
/**
* @private
*/
public function set verticalAlign(value:String):void
{
this._verticalAlign = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the horizontalAlign property.
*/
private var _horizontalAlign:String = "left";
/**
* The horizontal alignment of children displayed in the target.
*
* @see HorizontalAlignment
*/
public function get horizontalAlign():String
{
return this._horizontalAlign;
}
/**
* @private
*/
public function set horizontalAlign(value:String):void
{
this._horizontalAlign = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
//--------------------------------------
// Public Methods
//--------------------------------------
/**
* @inheritDoc
*/
override public function layoutObjects(displayObjects:Array, bounds:Rectangle):Rectangle
{
var childrenInLayout:Array = this.configureChildren(displayObjects);
if(this.direction == "vertical")
{
this.layoutChildrenVertically(childrenInLayout, bounds);
}
else
{
this.layoutChildrenHorizontally(childrenInLayout, bounds);
}
bounds = LayoutModeUtil.calculateChildBounds(childrenInLayout);
bounds.width += this.paddingRight;
bounds.height += this.paddingBottom;
return bounds;
}
//--------------------------------------
// Protected Methods
//--------------------------------------
/**
* @private
* Positions the children when direction is vertical.
*/
protected function layoutChildrenVertically(children:Array, bounds:Rectangle):void
{
const START_Y:Number = bounds.y + this.paddingTop;
var xPosition:Number = bounds.x + this.paddingLeft;
var yPosition:Number = START_Y;
var maxChildWidth:Number = 0;
var column:Array = [];;
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
//next column if we're over the height, but not if we're at yposition == bounds.y
var endOfColumn:Number = yPosition + child.height + this.paddingBottom;
if(endOfColumn - bounds.y >= bounds.height && yPosition != START_Y)
{
//update alignment
this.alignColumn(column, maxChildWidth, bounds);
xPosition += maxChildWidth + this.horizontalGap;
yPosition = START_Y;
maxChildWidth = 0;
column = [];
}
child.x = xPosition;
child.y = yPosition;
column.push(child);
maxChildWidth = Math.max(maxChildWidth, child.width);
yPosition += child.height + this.verticalGap;
}
this.alignColumn(column, maxChildWidth, bounds);
}
/**
* @private
* Positions the children when direction is horizontal.
*/
protected function layoutChildrenHorizontally(children:Array, bounds:Rectangle):void
{
const START_X:Number = bounds.x + this.paddingLeft;
var xPosition:Number = START_X;
var yPosition:Number = bounds.y + this.paddingTop;
var maxChildHeight:Number = 0;
var row:Array = [];
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
var childWidth:Number = child.width;
var childHeight:Number = child.height;
//next row if we're over the width, but not if we're at xposition == bounds.x
var endOfRow:Number = xPosition + child.width + this.paddingRight;
if(endOfRow - bounds.x >= bounds.width && xPosition != START_X)
{
//update alignment
this.alignRow(row, maxChildHeight, bounds);
xPosition = START_X;
yPosition += maxChildHeight + this.verticalGap;
maxChildHeight = 0;
row = [];
}
child.x = xPosition;
child.y = yPosition;
row.push(child);
maxChildHeight = Math.max(maxChildHeight, childHeight);
xPosition += child.width + this.horizontalGap;
}
this.alignRow(row, maxChildHeight, bounds);
}
/**
* @private
* Repositions a column of children based on the alignment values.
*/
protected function alignColumn(column:Array, maxChildWidth:Number, bounds:Rectangle):void
{
if(column.length == 0)
{
return;
}
var lastChild:DisplayObject = DisplayObject(column[column.length - 1]);
var columnHeight:Number = (lastChild.y + lastChild.height) - bounds.y + this.paddingBottom;
var difference:Number = bounds.height - columnHeight;
var columnCount:int = column.length;
for(var i:int = 0; i < columnCount; i++)
{
var child:DisplayObject = DisplayObject(column[i]);
DisplayObjectUtil.align(child, new Rectangle(child.x, child.y, maxChildWidth, child.height), this.horizontalAlign, null);
switch(this.verticalAlign)
{
case "middle":
child.y += difference / 2;
break;
case "bottom":
child.y += difference;
break;
}
}
}
/**
* @private
* Repositions a row of children based on the alignment values.
*/
protected function alignRow(row:Array, maxChildHeight:Number, bounds:Rectangle):void
{
if(row.length == 0)
{
return;
}
var lastChild:DisplayObject = DisplayObject(row[row.length - 1]);
var rowWidth:Number = (lastChild.x + lastChild.width) - bounds.x + this.paddingRight;
var difference:Number = bounds.width - rowWidth;
var rowCount:int = row.length;
for(var i:int = 0; i < rowCount; i++)
{
var child:DisplayObject = DisplayObject(row[i]);
DisplayObjectUtil.align(child, new Rectangle(child.x, child.y, child.width, maxChildHeight), null, this.verticalAlign);
switch(this.horizontalAlign)
{
case "center":
child.x += difference / 2;
break;
case "right":
child.x += difference;
break;
}
}
}
}
}

View File

@ -0,0 +1,35 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
/**
* A set of constants for horizontal alignment values used by
* the layout engine.
*
* @author Josh Tynjala
*/
public class HorizontalAlignment
{
//--------------------------------------
// Static Properties
//--------------------------------------
/**
* Items will be aligned to the left of the available bounds.
*/
public static const LEFT:String = "left";
/**
* Items will be aligned to the center of the available bounds.
*/
public static const CENTER:String = "center";
/**
* Items will be aligned to the right of the available bounds.
*/
public static const RIGHT:String = "right";
}
}

View File

@ -0,0 +1,41 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
import flash.display.DisplayObject;
/**
* Defines the methods required for layout modes that have
* advanced configuration settings for individual children.
*
* @author Josh Tynjala
*/
public interface IAdvancedLayoutMode extends ILayoutMode
{
/**
* Registers a specific display object with the layout algorithm. If certain
* settings need to be specified for individual display objects, they
* should be passed to the layout algorithm here.
*
* @param target The client to add
* @param configuration An optional set of name-value pairs for the client's configuration.
*/
function addClient(target:DisplayObject, configuration:Object = null):void;
/**
* Unregisters a specific display object from the layout algorithm.
*
* @param target The client to remove
*/
function removeClient(target:DisplayObject):void;
/**
* Returns true if a display object has been registered as a client.
*
* @param The display object that may be registered
*/
function hasClient(target:DisplayObject):Boolean;
}
}

View File

@ -0,0 +1,40 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
import flash.events.IEventDispatcher;
import flash.geom.Rectangle;
/**
* Defines the properties and functions required
* for layout modes used by ILayoutContainer.
*
* @author Josh Tynjala
*/
public interface ILayoutMode extends IEventDispatcher
{
//--------------------------------------
// Methods
//--------------------------------------
/**
* The DisplayObjects in the input parameter will be positioned and sized
* based on a specified rectangle. There is no requirement that the
* display objects remain entirely within the rectangle.
*
* <p>Returns the actual rectangular region in which the laid out
* children will appear. This may be larger or smaller than the
* suggested rectangle. This returned value is expected to be used by
* container components to determine if scrollbars or other navigation
* controls are needed.</p>
*
* @param displayObjects An Array of DisplayObjects to be laid out.
* @param bounds The rectangular region in which the display objects should be placed.
* @return The actual region in which the display objects are contained.
*/
function layoutObjects(displayObjects:Array, bounds:Rectangle):Rectangle;
}
}

View File

@ -0,0 +1,50 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
import flash.display.DisplayObject;
import flash.geom.Rectangle;
/**
* Utility functions shared by implementations of ILayoutMode.
*
* @author Josh Tynjala
* @see ILayoutMode
*/
public class LayoutModeUtil
{
//--------------------------------------
// Static Methods
//--------------------------------------
/**
* Calculates the rectangular bounds occupied by the target's children.
*
* @param children The set of children to use to calculate the maximum bounds
*/
public static function calculateChildBounds(children:Array):Rectangle
{
var minX:Number = 0;
var maxX:Number = 0;
var minY:Number = 0;
var maxY:Number = 0;
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
var childMaxX:Number = child.x + child.width;
var childMaxY:Number = child.y + child.height;
minX = Math.min(minX, child.x);
minY = Math.min(minY, child.y);
maxX = Math.max(maxX, childMaxX);
maxY = Math.max(maxY, childMaxY);
}
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
}
}
}

View File

@ -0,0 +1,639 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
/**
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is part of the Open Source Flex 3 SDK Downloads
* (http://opensource.adobe.com/wiki/display/flexsdk/Downloads)
*
* The Initial Developer of the Original Code is Adobe Systems Incorporated
* (see original files for appropriate copyright notices)
*
* Contributor(s): Yahoo! Inc.
*
* Copyright (c) 2008 Yahoo! Inc. All Rights Reserved.
*/
package com.yahoo.astra.layout.modes
{
import flash.display.DisplayObject;
/**
* Utility functions used for determining pixel-based sizes from percentage-based sizes.
*
* @author Josh Tynjala and Adobe Systems Inc.
*/
public class PercentageSizeUtil
{
//--------------------------------------
// Static Methods
//--------------------------------------
/**
* This function sets the width of each child so that the widths add up
* to spaceForChildren. Each child is set to its preferred width if its
* percentWidth is zero. If it's percentWidth is a positive number the
* child grows depending on the size of its parent. The height of each
* child is set to its preferred height. The return value is any extra
* space that's left over after growing all children to their maxWidth.
*/
public static function flexChildWidthsProportionally(children:Array, configurations:Array, totalWidth:Number, totalHeight:Number):Number
{
var spaceToDistribute:Number = totalWidth;
var totalPercentWidth:Number = 0;
var childInfoArray:Array = [];
// If the child is flexible, store information about it in the
// childInfoArray. For non-flexible children, just set the child's
// width and height immediately.
//
// Also calculate the sum of all widthFlexes, and calculate the
// sum of the width of all non-flexible children.
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
var config:Object = configurations[i];
if(!config.includeInLayout)
{
//skip non-configuration children
continue;
}
var percentWidth:Number = config.percentWidth;
var percentHeight:Number = config.percentHeight;
var height:Number = NaN;
if(!isNaN(percentHeight))
{
height = Math.max(config.minHeight,
Math.min(config.maxHeight, ((percentHeight >= 100) ? totalHeight : totalHeight * percentHeight / 100)));
}
if(!isNaN(percentWidth))
{
totalPercentWidth += percentWidth;
var childInfo:ChildInfo = new ChildInfo();
childInfo.percent = percentWidth;
childInfo.min = config.minWidth;
childInfo.max = config.maxWidth;
childInfo.opposite = height;
childInfo.child = child;
childInfoArray.push(childInfo);
}
else
{
//var width:Number = child.width;
// if scaled and zoom is playing, best to let the sizes be non-integer
// otherwise the rounding creates an error that accumulates in some components like List
if(child.scaleX == 1 && child.scaleY == 1)
{
if(!isNaN(height))
{
child.height = Math.floor(height);
}
}
else
{
if(!isNaN(height))
{
child.height = height;
}
}
// Need to account for the actual child width since
// setActualSize may trigger a Resize effect, which
// could change the size of the component.
spaceToDistribute -= child.width;
}
}
// Distribute the extra space among the children.
if(totalPercentWidth)
{
spaceToDistribute = flexChildrenProportionally(totalWidth, spaceToDistribute, totalPercentWidth, childInfoArray);
// Set the widths and heights of the flexible children
childCount = childInfoArray.length;
for(i = 0; i < childCount; i++)
{
childInfo = ChildInfo(childInfoArray[i]);
child = childInfo.child;
// if scaled and zoom is playing, best to let the sizes be non-integer
// otherwise the rounding creates an error that accumulates in some components like List
if(child.scaleX == 1 && child.scaleY == 1)
{
child.width = Math.floor(childInfo.size);
if(!isNaN(childInfo.opposite))
{
child.height = Math.floor(childInfo.opposite);
}
}
else
{
child.width = childInfo.size;
if(!isNaN(childInfo.opposite))
{
child.height = childInfo.opposite;
}
}
}
distributeExtraWidth(children, configurations, totalWidth);
}
return spaceToDistribute;
}
/**
* This function sets the height of each child
* so that the heights add up to spaceForChildren.
* Each child is set to its preferred height
* if its percentHeight is zero.
* If its percentHeight is a positive number,
* the child grows (or shrinks) to consume its share of extra space.
* The width of each child is set to its preferred width.
* The return value is any extra space that's left over
* after growing all children to their maxHeight.
*/
public static function flexChildHeightsProportionally(children:Array, configurations:Array,
totalWidth:Number, totalHeight:Number):Number
{
var spaceToDistribute:Number = totalHeight;
var totalPercentHeight:Number = 0;
var childInfoArray:Array = [];
// If the child is flexible, store information about it in the
// childInfoArray. For non-flexible children, just set the child's
// width and height immediately.
//
// Also calculate the sum of all percentHeights, and calculate the
// sum of the height of all non-flexible children.
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
var config:Object = configurations[i];
if(!config.includeInLayout)
{
//skip children that aren't in the layout
continue;
}
var percentWidth:Number = config.percentWidth;
var percentHeight:Number = config.percentHeight;
var width:Number = NaN;
if(!isNaN(percentWidth))
{
width = Math.max(config.minWidth, Math.min(config.maxWidth,
((percentWidth >= 100) ? totalWidth : totalWidth * percentWidth / 100)));
}
if(!isNaN(percentHeight))
{
totalPercentHeight += percentHeight;
var childInfo:ChildInfo = new ChildInfo();
childInfo.percent = percentHeight;
childInfo.min = config.minHeight;
childInfo.max = config.maxHeight;
childInfo.opposite = width;
childInfo.child = child;
childInfoArray.push(childInfo);
}
else
{
if(child.scaleX == 1 && child.scaleY == 1)
{
if(!isNaN(width))
{
child.width = Math.floor(width);
}
}
else
{
if(!isNaN(width))
{
child.width = width;
}
}
// Need to account for the actual child height since
// setActualSize may trigger a Resize effect, which
// could change the size of the component.
spaceToDistribute -= child.height;
}
}
// Distribute the extra space among the children.
if(totalPercentHeight)
{
spaceToDistribute = flexChildrenProportionally(totalHeight, spaceToDistribute, totalPercentHeight, childInfoArray);
// Set the widths and heights of the flexible children
childCount = childInfoArray.length;
for(i = 0; i < childCount; i++)
{
childInfo = ChildInfo(childInfoArray[i]);
child = childInfo.child;
// if scaled and zoom is playing, best to let the sizes be non-integer
// otherwise the rounding creates an error that accumulates in some components like List
if(child.scaleX == 1 && child.scaleY == 1)
{
if(!isNaN(childInfo.opposite))
{
child.width = Math.floor(childInfo.opposite);
}
child.height = Math.floor(childInfo.size);
}
else
{
if(!isNaN(childInfo.opposite))
{
child.width = childInfo.opposite;
}
child.height = childInfo.size;
}
}
distributeExtraHeight(children, configurations, totalHeight);
}
return spaceToDistribute;
}
/**
* This function distributes excess space among the flexible children.
* It does so with a view to keep the children's overall size
* close the ratios specified by their percent.
*
* @param spaceForChildren The total space for all children
*
* @param spaceToDistribute The space that needs to be distributed
* among the flexible children.
*
* @param childInfoArray An array of Objects. When this function
* is called, each object should define the following properties:
* - percent: the percentWidth or percentHeight of the child (depending
* on whether we're growing in a horizontal or vertical direction)
* - min: the minimum width (or height) for that child
* - max: the maximum width (or height) for that child
*
* @return When this function finishes executing, a "size" property
* will be defined for each child object. The size property contains
* the portion of the spaceToDistribute to be distributed to the child.
* Ideally, the sum of all size properties is spaceToDistribute.
* If all the children hit their minWidth/maxWidth/minHeight/maxHeight
* before the space was distributed, then the remaining unused space
* is returned. Otherwise, the return value is zero.
*/
public static function flexChildrenProportionally(spaceForChildren:Number, spaceToDistribute:Number,
totalPercent:Number, childInfoArray:Array):Number
{
// The algorithm iterivately attempts to break down the space that
// is consumed by "flexible" containers into ratios that are related
// to the percentWidth/percentHeight of the participating containers.
var numChildren:int = childInfoArray.length;
var flexConsumed:Number; // space consumed by flexible compontents
var done:Boolean;
// We now do something a little tricky so that we can
// support partial filling of the space. If our total
// percent < 100% then we can trim off some space.
var unused:Number = spaceToDistribute - (spaceForChildren * totalPercent / 100);
if(unused > 0)
{
spaceToDistribute -= unused;
}
// Continue as long as there are some remaining flexible children.
// The "done" flag isn't strictly necessary, except that it catches
// cases where round-off error causes totalPercent to not exactly
// equal zero.
do
{
flexConsumed = 0; // space consumed by flexible compontents
done = true; // we are optimistic
// Space for flexible children is the total amount of space
// available minus the amount of space consumed by non-flexible
// components.Divide that space in proportion to the percent
// of the child
var spacePerPercent:Number = spaceToDistribute / totalPercent;
// Attempt to divide out the space using our percent amounts,
// if we hit its limit then that control becomes 'non-flexible'
// and we run the whole space to distribute calculation again.
for(var i:int = 0; i < numChildren; i++)
{
var childInfo:ChildInfo = ChildInfo(childInfoArray[i]);
// Set its size in proportion to its percent.
var size:Number = childInfo.percent * spacePerPercent;
// If our flexiblity calc say grow/shrink more than we are
// allowed, then we grow/shrink whatever we can, remove
// ourselves from the array for the next pass, and start
// the loop over again so that the space that we weren't
// able to consume / release can be re-used by others.
if(size < childInfo.min)
{
var min:Number = childInfo.min;
childInfo.size = min;
// Move this object to the end of the array
// and decrement the length of the array.
// This is slightly expensive, but we don't expect
// to hit these min/max limits very often.
childInfoArray[i] = childInfoArray[--numChildren];
childInfoArray[numChildren] = childInfo;
totalPercent -= childInfo.percent;
spaceToDistribute -= min;
done = false;
break;
}
else if(size > childInfo.max)
{
var max:Number = childInfo.max;
childInfo.size = max;
childInfoArray[i] = childInfoArray[--numChildren];
childInfoArray[numChildren] = childInfo;
totalPercent -= childInfo.percent;
spaceToDistribute -= max;
done = false;
break;
}
else
{
// All is well, let's carry on...
childInfo.size = size;
flexConsumed += size;
}
}
}
while(!done);
return Math.max(0, Math.floor(spaceToDistribute - flexConsumed))
}
/**
* This function distributes excess space among the flexible children
* because of rounding errors where we want to keep children's dimensions
* full pixel amounts. This only distributes the extra space
* if there was some rounding down and there are still
* flexible children.
*
* @param parent The parent container of the children.
*
* @param spaceForChildren The total space for all children
*/
public static function distributeExtraHeight(children:Array, configurations:Array, spaceForChildren:Number):void
{
// We should only get here after distributing the majority of the
// space already. This is done in flexChildHeightsProportionally.
// Strategy here is to keep adding 1 pixel at a time to each
// component that's flexible (percentHeight defined and hasn't
// reached maxHeight yet). We could use another approach where
// we add more than a pixel at a time, but we'd have to first
// calculate exactly how many flexible components we have first
// and see how much space we can add to them without hitting
// their maxHeight. Since we're just dealing with rounding
// issues, we should only make one pass here (if we hit maxHeight
// problems, we might make more than one, but not many more).
// We just distribute from the top-down and don't care about
// who was "rounded down the most"
// First check if we should distribute any extra space. To do
// this, we check to see if someone suffers from rounding error.
var wantToGrow:Boolean = false;
var percentHeight:Number;
var spaceToDistribute:Number = spaceForChildren;
var spaceUsed:Number = 0;
var childHeight:Number;
var wantSpace:Number;
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
var config:Object = configurations[i];
if(!config.includeInLayout)
{
continue;
}
childHeight = child.height;
percentHeight = config.percentHeight;
spaceUsed += childHeight;
if(!isNaN(percentHeight))
{
wantSpace = Math.ceil(percentHeight/100 * spaceForChildren);
if(wantSpace > childHeight)
{
wantToGrow = true;
}
}
}
// No need to distribute extra size
if(!wantToGrow)
{
return;
}
// Start distributing...
spaceToDistribute -= spaceUsed;
// If we still have components that will let us
// distribute to them
var stillFlexibleComponents:Boolean = true;
while(stillFlexibleComponents && spaceToDistribute > 0)
{
// Start optimistically
stillFlexibleComponents = false;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(children[i]);
config = configurations[i];
childHeight = child.height;
percentHeight = config.percentHeight;
// if they have a percentHeight, and we won't reach their
// maxHeight by giving them one more pixel, then
// give them a pixel
if(!isNaN(percentHeight) &&
config.includeInLayout &&
childHeight < config.maxHeight)
{
wantSpace = Math.ceil(percentHeight/100 * spaceForChildren);
if(wantSpace > childHeight)
{
child.height = childHeight + 1;
spaceToDistribute--;
stillFlexibleComponents = true;
if(spaceToDistribute == 0)
{
return;
}
}
}
}
}
}
/**
* This function distributes excess space among the flexible children
* because of rounding errors where we want to keep children's dimensions
* full pixel amounts. This only distributes the extra space
* if there was some rounding down and there are still
* flexible children.
*
* @param parent The parent container of the children.
*
* @param spaceForChildren The total space for all children
*/
public static function distributeExtraWidth(children:Array, configurations:Array, spaceForChildren:Number):void
{
// We should only get here after distributing the majority of the
// space already. This is done in flexChildWidthsProportionally.
// Strategy here is to keep adding 1 pixel at a time to each
// component that's flexible (percentWidth defined and hasn't
// reached maxWidth yet). We could use another approach where
// we add more than a pixel at a time, but we'd have to first
// calculate exactly how many flexible components we have first
// and see how much space we can add to them without hitting
// their maxWidth. Since we're just dealing with rounding
// issues, we should only make one pass here (if we hit maxWidth
// problems, we might make more than one, but not many more).
// We just distribute from the top-down and don't care about
// who was "rounded down the most"
// First check if we should distribute any extra space. To do
// this, we check to see if someone suffers from rounding error.
var childCount:int = children.length;
var wantToGrow:Boolean = false;
var percentWidth:Number;
var spaceToDistribute:Number = spaceForChildren;
var spaceUsed:Number = 0;
var childWidth:Number;
var wantSpace:Number;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
var config:Object = configurations[i];
if(!config.includeInLayout)
{
continue;
}
childWidth = child.width;
percentWidth = config.percentWidth;
spaceUsed += childWidth;
if(!isNaN(percentWidth))
{
wantSpace = Math.ceil(percentWidth / 100 * spaceForChildren);
if(wantSpace > childWidth)
{
wantToGrow = true;
}
}
}
// No need to distribute extra size
if(!wantToGrow)
{
return;
}
// Start distributing...
spaceToDistribute -= spaceUsed;
// If we still have components that will let us
// distribute to them
var stillFlexibleComponents:Boolean = true;
while(stillFlexibleComponents && spaceToDistribute > 0)
{
// Start optimistically
stillFlexibleComponents = false;
for(i = 0; i < childCount; i++)
{
child = DisplayObject(children[i]);
config = configurations[i];
childWidth = child.width;
percentWidth = config.percentWidth;
// if they have a percentWidth, and we won't reach their
// maxWidth by giving them one more pixel, then
// give them a pixel
if(!isNaN(percentWidth) && config.includeInLayout && childWidth < config.maxWidth)
{
wantSpace = Math.ceil(percentWidth / 100 * spaceForChildren);
if(wantSpace > childWidth)
{
child.width = childWidth + 1;
spaceToDistribute--;
stillFlexibleComponents = true;
if(spaceToDistribute == 0)
{
return;
}
}
}
}
}
}
}
}
import flash.display.DisplayObject;
class ChildInfo
{
public var max:Number;
public var min:Number;
public var child:DisplayObject;
public var percent:Number;
public var size:Number;
public var opposite:Number;
}

View File

@ -0,0 +1,362 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
import com.yahoo.astra.layout.events.LayoutEvent;
import com.yahoo.astra.utils.DisplayObjectUtil;
import flash.display.DisplayObject;
import flash.geom.Rectangle;
/**
* Arranges a DisplayObjectContainer's children using a tiling algorithm.
* All tiles are the same size and tile dimensions are determined from the
* maximum width or height values of the available children.
*
* @example The following code configures a TileLayout instance and passes it to a container:
* <listing version="3.0">
* var tile:TileLayout = new TileLayout();
* tile.direction = "horizontal";
* tile.horizontalGap = 1;
* tile.verticalGap = 4;
* tile.horizontalAlign = HorizontalAlignment.CENTER;
* tile.verticalAlign = VerticalAlignment.MIDDLE;
*
* var container:LayoutContainer = new LayoutContainer();
* container.layoutMode = tile;
* this.addChild( container );
* </listing>
*
* <p><strong>Advanced Client Options:</strong></p>
* <p>Optional client configuration parameters allow a developer to specify
* behaviors for individual children of the target container. To set these
* advanced options, one must call <code>addClient()</code> on the TileLayout
* instance and pass the child to configure along with an object specifying
* the configuration parameters. The following client parameters are available to
* the TileLayout algorithm:</p>
*
* <dl>
* <dt><strong><code>includeInLayout</code></strong> : Boolean</dt>
* <dd>If <code>false</code>, the target will not be included in layout calculations. The default value is <code>true</code>.</dd>
* </dl>
*
* @author Josh Tynjala
* @see com.yahoo.astra.layout.LayoutContainer
*/
public class TileLayout extends BaseLayoutMode implements IAdvancedLayoutMode
{
//--------------------------------------
// Constructor
//--------------------------------------
/**
* Constructor.
*/
public function TileLayout()
{
}
//--------------------------------------
// Properties
//--------------------------------------
/**
* @private
* Storage for the direction property.
*/
private var _direction:String = "horizontal";
/**
* The direction in which children of the target are laid out. Once
* the edge of the container is reached, the children will begin
* appearing on the next row or column. Valid direction values include
* <code>"vertical"</code> or <code>"horizontal"</code>.
*/
public function get direction():String
{
return this._direction;
}
/**
* @private
*/
public function set direction(value:String):void
{
this._direction = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the verticalGap property.
*/
private var _verticalGap:Number = 0;
/**
* The number of pixels appearing between the target's children
* vertically.
*/
public function get verticalGap():Number
{
return this._verticalGap;
}
/**
* @private
*/
public function set verticalGap(value:Number):void
{
this._verticalGap = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the horizontalGap property.
*/
private var _horizontalGap:Number = 0;
/**
* The number of pixels appearing between the target's children
* horizontally.
*/
public function get horizontalGap():Number
{
return this._horizontalGap;
}
/**
* @private
*/
public function set horizontalGap(value:Number):void
{
this._horizontalGap = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the verticalAlign property.
*/
private var _verticalAlign:String = "top";
/**
* The children of the target may be aligned vertically within their
* respective tiles.
*
* @see VerticalAlignment
*/
public function get verticalAlign():String
{
return this._verticalAlign;
}
/**
* @private
*/
public function set verticalAlign(value:String):void
{
this._verticalAlign = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the horizontalAlign property.
*/
private var _horizontalAlign:String = "left";
/**
* The children of the target may be aligned horizontally within their
* respective tiles.
*
* @see HorizontalAlignment
*/
public function get horizontalAlign():String
{
return this._horizontalAlign;
}
/**
* @private
*/
public function set horizontalAlign(value:String):void
{
this._horizontalAlign = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the tileWidth property.
*/
private var _tileWidth:Number = NaN;
/**
* The width of tiles displayed in the target. If NaN, the tile width
* will be calculated based on the maximum width among the target's children.
*/
public function get tileWidth():Number
{
return this._tileWidth;
}
/**
* @private
*/
public function set tileWidth(value:Number):void
{
this._tileWidth = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* Storage for the tileHeight property.
*/
private var _tileHeight:Number = NaN;
/**
* The height of tiles displayed in the target. If NaN, the tile height
* will be calculated based on the maximum height among the target's children.
*/
public function get tileHeight():Number
{
return this._tileHeight;
}
/**
* @private
*/
public function set tileHeight(value:Number):void
{
this._tileHeight = value;
this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE));
}
/**
* @private
* The maximum width value from among the current target's children.
*/
protected var maxChildWidth:Number;
/**
* @private
* The maximum height value from among the current target's children.
*/
protected var maxChildHeight:Number;
//--------------------------------------
// Public Methods
//--------------------------------------
/**
* @inheritDoc
*/
override public function layoutObjects(displayObjects:Array, bounds:Rectangle):Rectangle
{
var childrenInLayout:Array = this.configureChildren(displayObjects);
this.maxChildWidth = this.maxChildHeight = 0;
var childCount:int = displayObjects.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(displayObjects[i]);
this.maxChildWidth = Math.max(this.maxChildWidth, child.width);
this.maxChildHeight = Math.max(this.maxChildHeight, child.height);
}
if(!isNaN(this.tileWidth))
{
this.maxChildWidth = this.tileWidth;
}
if(!isNaN(this.tileHeight))
{
this.maxChildHeight = this.tileHeight;
}
if(this.direction == "vertical")
{
this.layoutChildrenVertically(childrenInLayout, bounds);
}
else
{
this.layoutChildrenHorizontally(childrenInLayout, bounds);
}
var bounds:Rectangle = LayoutModeUtil.calculateChildBounds(childrenInLayout);
bounds.width += this.paddingRight;
bounds.height += this.paddingBottom;
return bounds;
}
//--------------------------------------
// Protected Methods
//--------------------------------------
/**
* @private
* Positions the children when direction is vertical.
*/
protected function layoutChildrenVertically(children:Array, bounds:Rectangle):void
{
const START_Y:Number = bounds.y + this.paddingTop;
var xPosition:Number = bounds.x + this.paddingLeft;
var yPosition:Number = START_Y;
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
var endOfColumn:Number = yPosition + this.maxChildHeight + this.paddingBottom;
if(endOfColumn - bounds.y >= bounds.height && yPosition != START_Y)
{
//next column if we're over the height,
//but not if we're at yposition == START_Y
yPosition = START_Y;
xPosition += this.maxChildWidth + this.horizontalGap;
}
DisplayObjectUtil.align(child, new Rectangle(xPosition, yPosition, this.maxChildWidth, this.maxChildHeight), this.horizontalAlign, this.verticalAlign);
yPosition += this.maxChildHeight + this.verticalGap;
}
}
/**
* @private
* Positions the children when direction is horizontal.
*/
protected function layoutChildrenHorizontally(children:Array, bounds:Rectangle):void
{
const START_X:Number = bounds.x + this.paddingLeft;
var xPosition:Number = START_X;
var yPosition:Number = bounds.y + this.paddingTop;
var childCount:int = children.length;
for(var i:int = 0; i < childCount; i++)
{
var child:DisplayObject = DisplayObject(children[i]);
var endOfRow:Number = xPosition + this.maxChildWidth + this.paddingRight;
if(endOfRow - bounds.x >= bounds.width && xPosition != START_X)
{
//next row if we're over the width,
//but not if we're at xposition == START_X
xPosition = START_X;
yPosition += this.maxChildHeight + this.verticalGap;
}
DisplayObjectUtil.align(child, new Rectangle(xPosition, yPosition, this.maxChildWidth, this.maxChildHeight), this.horizontalAlign, this.verticalAlign);
xPosition += this.maxChildWidth + this.horizontalGap;
}
}
}
}

View File

@ -0,0 +1,35 @@
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.layout.modes
{
/**
* A set of constants for vertical alignment values used by
* the layout engine.
*
* @author Josh Tynjala
*/
public class VerticalAlignment
{
//--------------------------------------
// Static Properties
//--------------------------------------
/**
* Items will be aligned to the top of the available bounds.
*/
public static const TOP:String = "top";
/**
* Items will be aligned to the middle of the available bounds.
*/
public static const MIDDLE:String = "middle";
/**
* Items will be aligned to the bottom of the available bounds.
*/
public static const BOTTOM:String = "bottom";
}
}