/*
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.fl.controls.carouselClasses
{
import com.yahoo.astra.fl.controls.Carousel;
import com.yahoo.astra.layout.modes.BoxLayout;
import fl.controls.listClasses.ICellRenderer;
import fl.core.InvalidationType;
import fl.core.UIComponent;
import fl.events.ComponentEvent;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
/**
* A Carousel renderer that displays items in a horizontal row or a vertical
* column. Resizes so that all cell renderers are displayed.
*
* @see com.yahoo.astra.fl.controls.Carousel
* @author Josh Tynjala
*/
public class BoxCarouselRenderer extends UIComponent implements ICarouselLayoutRenderer
{
//--------------------------------------
// Constructor
//--------------------------------------
/**
* Constructor.
*/
public function BoxCarouselRenderer()
{
super();
this.scrollRect = new Rectangle();
}
//--------------------------------------
// Properties
//--------------------------------------
/**
* @private
* The active cell renderers being used by this layout renderer.
*/
protected var renderers:Array = [];
/**
* @private
* Storage for the carousel property.
*/
private var _carousel:Carousel;
/**
* @inheritDoc
*/
public function get carousel():Carousel
{
return this._carousel;
}
/**
* @private
*/
public function set carousel(value:Carousel):void
{
this._carousel = value;
this.invalidate(InvalidationType.DATA);
}
/**
* @private
* Storage for the direction property.
*/
private var _direction:String = "horizontal";
/**
* The direction in which item renderers are positioned.
* May be "vertical"
or "horizontal"
.
*/
public function get direction():String
{
return this._direction;
}
/**
* @private
*/
public function set direction(value:String):void
{
this._direction = value;
this.invalidate(InvalidationType.STATE);
}
/**
* @private
* Storage for the horizontalGap property.
*/
private var _horizontalGap:Number = 0;
/**
* The number of extra pixels placed between each cell renderer
* when direction
is set to "horizontal"
.
*/
public function get horizontalGap():Number
{
return this._horizontalGap;
}
/**
* @private
*/
public function set horizontalGap(value:Number):void
{
this._horizontalGap = value;
this.invalidate(InvalidationType.STATE);
}
/**
* @private
* Storage for the verticalGap property.
*/
private var _verticalGap:Number = 0;
/**
* The number of extra pixels placed between each cell renderer
* when direction
is set to "vertical"
.
*/
public function get verticalGap():Number
{
return this._verticalGap;
}
/**
* @private
*/
public function set verticalGap(value:Number):void
{
this._verticalGap = value;
this.invalidate(InvalidationType.STATE);
}
/**
* @private
* Storage for the horizontalAlign property.
*/
private var _horizontalAlign:String = "center";
/**
* When direction
is set to "horizontal"
,
* this value indicates the alignment of the selected item. Accepted
* values include "left"
, "center"
, and
* "right"
*
* @default "center"
*/
public function get horizontalAlign():String
{
return this._horizontalAlign;
}
/**
* @private
*/
public function set horizontalAlign(value:String):void
{
this._horizontalAlign = value;
this.invalidate(InvalidationType.STATE);
}
/**
* @private
* Storage for the verticalAlign property.
*/
private var _verticalAlign:String = "middle";
/**
* When direction
is set to "vertical"
,
* this value indicates the alignment of the selected item. Accepted
* values include "top"
, "middle"
, and
* "bottom"
*
* @default "middle"
*/
public function get verticalAlign():String
{
return this._verticalAlign;
}
/**
* @private
*/
public function set verticalAlign(value:String):void
{
this._verticalAlign = value;
this.invalidate(InvalidationType.STATE);
}
/**
* @private
* Storage for the displayedItemCount property.
*/
private var _displayedItemCount:int = 0;
/**
* Sets a specific number of items to display in the carousel at one
* time and resizes the carousel to make them all visible. This property
* is useful for developers who want to display carousel items in
* "pages" with a specific number of items per page.
The default value is 0
(zero), which means that no
* changes to the width and height will be made. Some items may only
* be partially visible (cut off).
The visual effect of such rendering delays is often compared to * "flickering".
*/ public function get drawAllRenderers():Boolean { return this._drawAllRenderers; } /** * @private */ public function set drawAllRenderers(value:Boolean):void { this._drawAllRenderers = value; this.invalidate(InvalidationType.DATA); } /** * @private * Storage for the clickToSelect property. */ private var _clickToSelect:Boolean = true; /** * If true, clicking on an item renderer will select its item. If false, * selection must be changed by setting selectedIndex or selectedItem on * the Carousel from an external interaction. */ public function get clickToSelect():Boolean { return this._clickToSelect; } /** * @private */ public function set clickToSelect(value:Boolean):void { this._clickToSelect = value; //no need to validate } //-------------------------------------- // Public Methods //-------------------------------------- /** * @private */ public function cleanUp():void { //nothing to clean up in this implementation var rendererCount:int = this.renderers.length; for(var i:int = 0; i < rendererCount; i++) { var renderer:Sprite = Sprite(this.renderers[i]); renderer.removeEventListener(MouseEvent.CLICK, rendererClickHandler); } } //-------------------------------------- // Protected Methods //-------------------------------------- /** * @private */ override protected function draw():void { var oldWidth:Number = this.width; var oldHeight:Number = this.height; if(this.carousel.length == 0 || this.carousel.selectedIndex < 0) { //nothing to draw this._width = 0; this._height = 0; } else { this.renderers = this.createRenderers(); this.layoutRenderers(this.renderers); this.calculateDimensions(this.renderers); } var scrollRect:Rectangle = this.scrollRect; scrollRect.width = this.width; scrollRect.height = this.height; this.scrollRect = scrollRect; super.draw(); if(this.displayedItemCount > 0 && (oldWidth != this.width || oldHeight != this.height)) { this.dispatchEvent(new ComponentEvent(ComponentEvent.RESIZE)); } } /** * @private * Under certain conditions, this renderer will update its own width and * height to ensure that a specific number of renderers are displayed. */ protected function calculateDimensions(renderers:Array):void { var selectedRenderer:DisplayObject = DisplayObject(this.carousel.itemToCellRenderer(this.carousel.selectedItem)); if(this.displayedItemCount > 0) { if(this.direction == "horizontal") { this._width = selectedRenderer.width * this.displayedItemCount + this.horizontalGap * (this.displayedItemCount - 1); this._height = selectedRenderer.height; } else { this._width = selectedRenderer.width; this._height = selectedRenderer.height * this.displayedItemCount + this.verticalGap * (this.displayedItemCount - 1); } } } /** * @private * Creates the required renderers. */ protected function createRenderers():Array { this.carousel.astra_carousel_internal::invalidateCellRenderers(); var renderers:Array = []; var rendererCount:int = this.carousel.length; for(var i:int = 0; i < rendererCount; i++) { var item:Object = this.carousel.dataProvider.getItemAt(i); var renderer:ICellRenderer = this.carousel.astra_carousel_internal::createCellRenderer(item); Sprite(renderer).addEventListener(MouseEvent.CLICK, rendererClickHandler, false, 0, true); if(renderer is UIComponent) { UIComponent(renderer).drawNow(); } renderers.push(renderer); } this.carousel.astra_carousel_internal::validateCellRenderers(); return renderers; } /** * @private * Determines the number of renderers that will be displayed. */ protected function calculateRendererCount():int { var rendererCount:int = this.carousel.length; if(rendererCount > 0 && !this.drawAllRenderers) { var firstItem:Object = this.carousel.dataProvider.getItemAt(0); var firstRenderer:DisplayObject = this.carousel.astra_carousel_internal::createCellRenderer(firstItem); var totalDisplayedItemCount:int = this.displayedItemCount; if(this.displayedItemCount == 0) { if(this.direction == "horizontal") { totalDisplayedItemCount = Math.ceil(this.width / firstRenderer.width); } else { totalDisplayedItemCount = Math.ceil(this.height / firstRenderer.height); } } rendererCount = totalDisplayedItemCount; } return Math.min(this.carousel.length, rendererCount); } /** * @private * Positions the renderers. */ protected function layoutRenderers(renderers:Array):void { var boxLayout:BoxLayout = new BoxLayout(); boxLayout.direction = this.direction; boxLayout.verticalGap = this.verticalGap; boxLayout.horizontalGap = this.horizontalGap; boxLayout.layoutObjects(renderers, new Rectangle(0, 0, this.width, this.height)); } /** * @private * Updates the carousel's selection when an item is clicked. */ protected function rendererClickHandler(event:MouseEvent):void { if(!this.clickToSelect) { return; } var renderer:ICellRenderer = ICellRenderer(event.currentTarget); this.carousel.selectedItem = renderer.data; this.carousel.dispatchEvent(new Event(Event.CHANGE)); } } }