1package com.yahoo.astra.layout.modes 2{ 3 import com.yahoo.astra.layout.events.LayoutEvent; 4 import com.yahoo.astra.utils.DisplayObjectUtil; 5 6 import flash.display.DisplayObject; 7 import flash.geom.Rectangle; 8 9 /** 10 * Arranges a DisplayObjectContainer's children using a tiling algorithm. 11 * All tiles are the same size and tile dimensions are determined from the 12 * maximum width or height values of the available children. 13 * 14 * @example The following code configures a TileLayout instance and passes it to a container: 15 * <listing version="3.0"> 16 * var tile:TileLayout = new TileLayout(); 17 * tile.direction = "horizontal"; 18 * tile.horizontalGap = 1; 19 * tile.verticalGap = 4; 20 * tile.horizontalAlign = HorizontalAlignment.CENTER; 21 * tile.verticalAlign = VerticalAlignment.MIDDLE; 22 * 23 * var container:LayoutContainer = new LayoutContainer(); 24 * container.layoutMode = tile; 25 * this.addChild( container ); 26 * </listing> 27 * 28 * <p><strong>Advanced Client Options:</strong></p> 29 * <p>Optional client configuration parameters allow a developer to specify 30 * behaviors for individual children of the target container. To set these 31 * advanced options, one must call <code>addClient()</code> on the TileLayout 32 * instance and pass the child to configure along with an object specifying 33 * the configuration parameters. The following client parameters are available to 34 * the TileLayout algorithm:</p> 35 * 36 * <dl> 37 * <dt><strong><code>includeInLayout</code></strong> : Boolean</dt> 38 * <dd>If <code>false</code>, the target will not be included in layout calculations. The default value is <code>true</code>.</dd> 39 * </dl> 40 * 41 * @author Josh Tynjala 42 * @see com.yahoo.astra.layout.LayoutContainer 43 */ 44 public class TileLayout extends BaseLayoutMode implements IAdvancedLayoutMode 45 { 46 47 //-------------------------------------- 48 // Constructor 49 //-------------------------------------- 50 51 /** 52 * Constructor. 53 */ 54 public function TileLayout() 55 { 56 } 57 58 //-------------------------------------- 59 // Properties 60 //-------------------------------------- 61 62 /** 63 * @private 64 * Storage for the direction property. 65 */ 66 private var _direction:String = "horizontal"; 67 68 /** 69 * The direction in which children of the target are laid out. Once 70 * the edge of the container is reached, the children will begin 71 * appearing on the next row or column. Valid direction values include 72 * <code>"vertical"</code> or <code>"horizontal"</code>. 73 */ 74 public function get direction():String 75 { 76 return this._direction; 77 } 78 79 /** 80 * @private 81 */ 82 public function set direction(value:String):void 83 { 84 this._direction = value; 85 this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE)); 86 } 87 88 /** 89 * @private 90 * Storage for the verticalGap property. 91 */ 92 private var _verticalGap:Number = 0; 93 94 /** 95 * The number of pixels appearing between the target's children 96 * vertically. 97 */ 98 public function get verticalGap():Number 99 { 100 return this._verticalGap; 101 } 102 103 /** 104 * @private 105 */ 106 public function set verticalGap(value:Number):void 107 { 108 this._verticalGap = value; 109 this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE)); 110 } 111 112 /** 113 * @private 114 * Storage for the horizontalGap property. 115 */ 116 private var _horizontalGap:Number = 0; 117 118 /** 119 * The number of pixels appearing between the target's children 120 * horizontally. 121 */ 122 public function get horizontalGap():Number 123 { 124 return this._horizontalGap; 125 } 126 127 /** 128 * @private 129 */ 130 public function set horizontalGap(value:Number):void 131 { 132 this._horizontalGap = value; 133 this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE)); 134 } 135 136 /** 137 * @private 138 * Storage for the verticalAlign property. 139 */ 140 private var _verticalAlign:String = "top"; 141 142 /** 143 * The children of the target may be aligned vertically within their 144 * respective tiles. 145 * 146 * @see VerticalAlignment 147 */ 148 public function get verticalAlign():String 149 { 150 return this._verticalAlign; 151 } 152 153 /** 154 * @private 155 */ 156 public function set verticalAlign(value:String):void 157 { 158 this._verticalAlign = value; 159 this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE)); 160 } 161 162 /** 163 * @private 164 * Storage for the horizontalAlign property. 165 */ 166 private var _horizontalAlign:String = "left"; 167 168 /** 169 * The children of the target may be aligned horizontally within their 170 * respective tiles. 171 * 172 * @see HorizontalAlignment 173 */ 174 public function get horizontalAlign():String 175 { 176 return this._horizontalAlign; 177 } 178 179 /** 180 * @private 181 */ 182 public function set horizontalAlign(value:String):void 183 { 184 this._horizontalAlign = value; 185 this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE)); 186 } 187 188 /** 189 * @private 190 * Storage for the tileWidth property. 191 */ 192 private var _tileWidth:Number = NaN; 193 194 /** 195 * The width of tiles displayed in the target. If NaN, the tile width 196 * will be calculated based on the maximum width among the target's children. 197 */ 198 public function get tileWidth():Number 199 { 200 return this._tileWidth; 201 } 202 203 /** 204 * @private 205 */ 206 public function set tileWidth(value:Number):void 207 { 208 this._tileWidth = value; 209 this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE)); 210 } 211 212 /** 213 * @private 214 * Storage for the tileHeight property. 215 */ 216 private var _tileHeight:Number = NaN; 217 218 /** 219 * The height of tiles displayed in the target. If NaN, the tile height 220 * will be calculated based on the maximum height among the target's children. 221 */ 222 public function get tileHeight():Number 223 { 224 return this._tileHeight; 225 } 226 227 /** 228 * @private 229 */ 230 public function set tileHeight(value:Number):void 231 { 232 this._tileHeight = value; 233 this.dispatchEvent(new LayoutEvent(LayoutEvent.LAYOUT_CHANGE)); 234 } 235 236 /** 237 * @private 238 * The maximum width value from among the current target's children. 239 */ 240 protected var maxChildWidth:Number; 241 242 /** 243 * @private 244 * The maximum height value from among the current target's children. 245 */ 246 protected var maxChildHeight:Number; 247 248 //-------------------------------------- 249 // Public Methods 250 //-------------------------------------- 251 252 /** 253 * @inheritDoc 254 */ 255 override public function layoutObjects(displayObjects:Array, bounds:Rectangle):Rectangle 256 { 257 var childrenInLayout:Array = this.configureChildren(displayObjects); 258 259 this.maxChildWidth = this.maxChildHeight = 0; 260 var childCount:int = displayObjects.length; 261 for(var i:int = 0; i < childCount; i++) 262 { 263 var child:DisplayObject = DisplayObject(displayObjects[i]); 264 this.maxChildWidth = Math.max(this.maxChildWidth, child.width); 265 this.maxChildHeight = Math.max(this.maxChildHeight, child.height); 266 } 267 268 if(!isNaN(this.tileWidth)) 269 { 270 this.maxChildWidth = this.tileWidth; 271 } 272 273 if(!isNaN(this.tileHeight)) 274 { 275 this.maxChildHeight = this.tileHeight; 276 } 277 278 if(this.direction == "vertical") 279 { 280 this.layoutChildrenVertically(childrenInLayout, bounds); 281 } 282 else 283 { 284 this.layoutChildrenHorizontally(childrenInLayout, bounds); 285 } 286 287 var bounds:Rectangle = LayoutModeUtil.calculateChildBounds(childrenInLayout); 288 bounds.width += this.paddingRight; 289 bounds.height += this.paddingBottom; 290 return bounds; 291 } 292 293 //-------------------------------------- 294 // Protected Methods 295 //-------------------------------------- 296 297 /** 298 * @private 299 * Positions the children when direction is vertical. 300 */ 301 protected function layoutChildrenVertically(children:Array, bounds:Rectangle):void 302 { 303 const START_Y:Number = bounds.y + this.paddingTop; 304 var xPosition:Number = bounds.x + this.paddingLeft; 305 var yPosition:Number = START_Y; 306 307 var childCount:int = children.length; 308 for(var i:int = 0; i < childCount; i++) 309 { 310 var child:DisplayObject = DisplayObject(children[i]); 311 312 var endOfColumn:Number = yPosition + this.maxChildHeight + this.paddingBottom; 313 if(endOfColumn - bounds.y >= bounds.height && yPosition != START_Y) 314 { 315 //next column if we're over the height, 316 //but not if we're at yposition == START_Y 317 yPosition = START_Y; 318 xPosition += this.maxChildWidth + this.horizontalGap; 319 } 320 321 DisplayObjectUtil.align(child, new Rectangle(xPosition, yPosition, this.maxChildWidth, this.maxChildHeight), this.horizontalAlign, this.verticalAlign); 322 323 yPosition += this.maxChildHeight + this.verticalGap; 324 } 325 } 326 327 /** 328 * @private 329 * Positions the children when direction is horizontal. 330 */ 331 protected function layoutChildrenHorizontally(children:Array, bounds:Rectangle):void 332 { 333 const START_X:Number = bounds.x + this.paddingLeft; 334 var xPosition:Number = START_X; 335 var yPosition:Number = bounds.y + this.paddingTop; 336 337 var childCount:int = children.length; 338 for(var i:int = 0; i < childCount; i++) 339 { 340 var child:DisplayObject = DisplayObject(children[i]); 341 342 var endOfRow:Number = xPosition + this.maxChildWidth + this.paddingRight; 343 if(endOfRow - bounds.x >= bounds.width && xPosition != START_X) 344 { 345 //next row if we're over the width, 346 //but not if we're at xposition == START_X 347 xPosition = START_X; 348 yPosition += this.maxChildHeight + this.verticalGap; 349 } 350 351 DisplayObjectUtil.align(child, new Rectangle(xPosition, yPosition, this.maxChildWidth, this.maxChildHeight), this.horizontalAlign, this.verticalAlign); 352 353 xPosition += this.maxChildWidth + this.horizontalGap; 354 } 355 } 356 357 } 358}