/* 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: * * 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 ); * * *

Advanced Client Options:

*

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 addClient() 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:

* *
*
includeInLayout : Boolean
*
If false, the target will not be included in layout calculations. The default value is true.
*
* * @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 * "vertical" or "horizontal". */ 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; } } } }