first commit
This commit is contained in:
267
com/yahoo/astra/layout/modes/BaseLayoutMode.as
Executable file
267
com/yahoo/astra/layout/modes/BaseLayoutMode.as
Executable 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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
96
com/yahoo/astra/layout/modes/BorderConstraints.as
Executable file
96
com/yahoo/astra/layout/modes/BorderConstraints.as
Executable 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";
|
||||
}
|
||||
}
|
||||
496
com/yahoo/astra/layout/modes/BorderLayout.as
Executable file
496
com/yahoo/astra/layout/modes/BorderLayout.as
Executable 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
444
com/yahoo/astra/layout/modes/BoxLayout.as
Executable file
444
com/yahoo/astra/layout/modes/BoxLayout.as
Executable 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
366
com/yahoo/astra/layout/modes/FlowLayout.as
Executable file
366
com/yahoo/astra/layout/modes/FlowLayout.as
Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
com/yahoo/astra/layout/modes/HorizontalAlignment.as
Executable file
35
com/yahoo/astra/layout/modes/HorizontalAlignment.as
Executable 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";
|
||||
}
|
||||
}
|
||||
41
com/yahoo/astra/layout/modes/IAdvancedLayoutMode.as
Executable file
41
com/yahoo/astra/layout/modes/IAdvancedLayoutMode.as
Executable 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;
|
||||
}
|
||||
}
|
||||
40
com/yahoo/astra/layout/modes/ILayoutMode.as
Executable file
40
com/yahoo/astra/layout/modes/ILayoutMode.as
Executable 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;
|
||||
}
|
||||
}
|
||||
50
com/yahoo/astra/layout/modes/LayoutModeUtil.as
Executable file
50
com/yahoo/astra/layout/modes/LayoutModeUtil.as
Executable 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
639
com/yahoo/astra/layout/modes/PercentageSizeUtil.as
Executable file
639
com/yahoo/astra/layout/modes/PercentageSizeUtil.as
Executable 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;
|
||||
}
|
||||
362
com/yahoo/astra/layout/modes/TileLayout.as
Executable file
362
com/yahoo/astra/layout/modes/TileLayout.as
Executable 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
35
com/yahoo/astra/layout/modes/VerticalAlignment.as
Executable file
35
com/yahoo/astra/layout/modes/VerticalAlignment.as
Executable 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";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user