| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (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.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package spark.components |
| { |
| import flash.display.DisplayObject; |
| import flash.events.Event; |
| import flash.events.IOErrorEvent; |
| import flash.events.SecurityErrorEvent; |
| import flash.events.TimerEvent; |
| import flash.net.URLRequest; |
| import flash.utils.Timer; |
| |
| import mx.controls.listClasses.*; |
| import mx.core.DPIClassification; |
| import mx.core.FlexGlobals; |
| import mx.core.mx_internal; |
| import mx.graphics.BitmapFillMode; |
| import mx.graphics.BitmapScaleMode; |
| import mx.styles.CSSStyleDeclaration; |
| import mx.utils.DensityUtil; |
| |
| import spark.components.supportClasses.StyleableTextField; |
| import spark.core.ContentCache; |
| import spark.core.DisplayObjectSharingMode; |
| import spark.core.IContentLoader; |
| import spark.core.IGraphicElement; |
| import spark.core.IGraphicElementContainer; |
| import spark.core.ISharedDisplayObject; |
| import spark.primitives.BitmapImage; |
| import spark.utils.MultiDPIBitmapSource; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Styles |
| //-------------------------------------- |
| |
| include "../styles/metadata/GapStyles.as" |
| |
| /** |
| * The delay value before attempting to load the |
| * icon's source if it has not been cached already. |
| * |
| * <p>The reason a delay is useful is while scrolling around, you |
| * don't necessarily want the image to load up immediately. Instead, |
| * you should wait a certain delay period to make sure the user actually |
| * wants to see this item renderer.</p> |
| * |
| * @default 500 |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| [Style(name="iconDelay", type="Number", format="Time", inherit="no")] |
| |
| /** |
| * Name of the CSS Style declaration to use for the styles for the |
| * message component. |
| * |
| * @default iconItemRendererMessageStyle |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| [Style(name="messageStyleName", type="String", inherit="no")] |
| |
| /** |
| * The IconItemRenderer class is a performant item |
| * renderer optimized for mobile devices. |
| * It displays four optional parts for each item in the |
| * list-based control: |
| * |
| * <ul> |
| * <li>An icon on the left defined by the <code>iconField</code> or |
| * <code>iconFunction</code> property.</li> |
| * <li>A single-line text label next to the icon defined by the |
| * <code>labelField</code> or <code>labelFunction</code> property.</li> |
| * <li>A multi-line message below the text label defined by the |
| * <code>messageField</code> or <code>messageFunction</code> property.</li> |
| * <li>A decorator icon on the right defined by the |
| * <code>decorator</code> property.</li> |
| * </ul> |
| * |
| * <p>To apply CSS styles to the single-line text label, such as font size and color, |
| * set the styles on the IconItemRenderer class. |
| * To set styles on the multi-line message, use the <code>messageStyleNameM</code> style property. |
| * The following example sets the text styles for both the text label and message:</p> |
| * |
| * <pre> |
| * <fx:Style> |
| * .myFontStyle { |
| * fontSize: 15; |
| * color: #9933FF; |
| * } |
| * |
| * </fx:Style> |
| * |
| * <s:List id="myList" |
| * width="100%" height="100%" |
| * labelField="firstName"> |
| * <s:itemRenderer> |
| * <fx:Component> |
| * <s:IconItemRenderer messageStyleName="myFontStyle" fontSize="25" |
| * labelField="firstName" |
| * messageField="lastName" |
| * decorator="@Embed(source='assets/logo_small.jpg')"/> |
| * </fx:Component> |
| * </s:itemRenderer> |
| * <s:ArrayCollection> |
| * <fx:Object firstName="Dave" lastName="Duncam" company="Adobe" phone="413-555-1212"/> |
| * <fx:Object firstName="Sally" lastName="Smith" company="Acme" phone="617-555-1491"/> |
| * <fx:Object firstName="Jim" lastName="Jackson" company="Beta" phone="413-555-2345"/> |
| * <fx:Object firstName="Mary" lastName="Moore" company="Gamma" phone="617-555-1899"/> |
| * </s:ArrayCollection> |
| * </s:List> |
| * </pre> |
| * |
| * @mxml |
| * |
| * <p>The <code><s:IconItemRenderer></code> tag inherits all of the tag |
| * attributes of its superclass and adds the following tag attributes:</p> |
| * |
| * <pre> |
| * <s:IconItemRenderer |
| * <strong>Properties</strong> |
| * decorator="" |
| * iconContentLoader="<i>See property description</i>" |
| * iconField="null" |
| * iconFillMode=""scale |
| * iconFunction="null" |
| * iconHeight="NaN" |
| * iconPlaceholder="null" |
| * iconScaleMode="stretch" |
| * iconWidth="NaN" |
| * label="" |
| * labelField="null" |
| * labelFunction="null" |
| * messageField="null" |
| * messageFunction="null" |
| * |
| * <strong>Common Styles</strong> |
| * horizontalGap="8" |
| * iconDelay="500" |
| * messageStyleName="iconItemRendererMessageStyle" |
| * verticalGap="6" |
| * > |
| * </pre> |
| * |
| * @see spark.components.List |
| * @see mx.core.IDataRenderer |
| * @see spark.components.IItemRenderer |
| * @see spark.components.supportClasses.ItemRenderer |
| * @see spark.components.LabelItemRenderer |
| * @includeExample examples/IconItemRendererExample.mxml -noswf |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public class IconItemRenderer extends LabelItemRenderer |
| implements IGraphicElementContainer, ISharedDisplayObject |
| { |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Static icon image cache. This is the default for iconContentLoader. |
| */ |
| mx_internal static var _imageCache:ContentCache; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function IconItemRenderer() |
| { |
| super(); |
| |
| if (_imageCache == null) { |
| _imageCache = new ContentCache(); |
| _imageCache.enableCaching = true; |
| _imageCache.maxCacheEntries = 100; |
| } |
| |
| // set default messageDisplay width |
| switch (applicationDPI) |
| { |
| case DPIClassification.DPI_640: |
| { |
| oldUnscaledWidth = 1280; |
| break; |
| } |
| case DPIClassification.DPI_480: |
| { |
| oldUnscaledWidth = 960; |
| break; |
| } |
| case DPIClassification.DPI_320: |
| { |
| oldUnscaledWidth = 640; |
| break; |
| } |
| case DPIClassification.DPI_240: |
| { |
| oldUnscaledWidth = 480; |
| break; |
| } |
| case DPIClassification.DPI_120: |
| { |
| oldUnscaledWidth = 240; |
| break; |
| } |
| default: |
| { |
| // default PPI160 |
| oldUnscaledWidth = 320; |
| break; |
| } |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Stores the text of the label component. This is calculated in |
| * commitProperties() based on labelFunction, labelField, and label. |
| * |
| * <p>We can't just use labelDisplay.text because it may contain |
| * a truncated value.</p> |
| */ |
| mx_internal var labelText:String = ""; |
| |
| /** |
| * @private |
| * Stores the text of the message component. This is calculated in |
| * commitProperties() based on messageField and messageFunction. |
| * |
| * <p>We can't just use messageDisplay.text because it may contain |
| * a truncated value (Technically we don't truncate message's text |
| * at the moment because it's multi-line text, but in the future |
| * we may not do that, and this feels more consistent with |
| * how we deal with labels, so we still keep this "extra" |
| * variable around even though technically it's not needed).</p> |
| */ |
| mx_internal var messageText:String = ""; |
| |
| /** |
| * @private |
| * Since iconDisplay is a GraphicElement, we have to call its lifecycle methods |
| * directly. |
| */ |
| private var iconNeedsValidateProperties:Boolean = false; |
| |
| /** |
| * @private |
| * Since iconDisplay is a GraphicElement, we have to call its lifecycle methods |
| * directly. |
| */ |
| private var iconNeedsValidateSize:Boolean = false; |
| |
| /** |
| * @private |
| * Since iconDisplay is a GraphicElement, we have to call help assign |
| * its display object |
| */ |
| private var iconNeedsDisplayObjectAssignment:Boolean = false; |
| |
| /** |
| * @private |
| * Timer, used to delay setting the source on the icon. |
| */ |
| private var iconSetterDelayTimer:Timer; |
| |
| /** |
| * @private |
| * The source to set iconDisplay to, after waiting an appropriate delay period |
| */ |
| private var iconSourceToLoad:Object; |
| |
| /** |
| * @private |
| * The width of the component on the previous layout manager |
| * pass. This gets set in updateDisplayList() and used in measure() on |
| * the next layout pass. This is so our "guessed width" in measure() |
| * will be as accurate as possible since messageDisplay is multiline and |
| * the messageDisplay height is dependent on the width. |
| * |
| * In the constructor, this is actually set based on the DPI. |
| */ |
| mx_internal var oldUnscaledWidth:Number; |
| |
| /** |
| * @private |
| * Since decoratorDisplay is a GraphicElement, we have to call its lifecycle methods |
| * directly. |
| */ |
| private var decoratorNeedsValidateProperties:Boolean = false; |
| |
| /** |
| * @private |
| * Since decoratorDisplay is a GraphicElement, we have to call its lifecycle methods |
| * directly. |
| */ |
| private var decoratorNeedsValidateSize:Boolean = false; |
| |
| /** |
| * @private |
| * Since decoratorDisplay is a GraphicElement, we have to call help assign |
| * its display object |
| */ |
| private var decoratorNeedsDisplayObjectAssignment:Boolean = false; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Public Properties: Overridden |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function set data(value:Object):void |
| { |
| super.data = value; |
| |
| iconChanged = true; |
| labelChanged = true; |
| messageChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| /** |
| * <p>If <code>labelFunction</code> = <code>labelField</code> = null, |
| * then use the <code>label</code> property that gets |
| * pushed in from the list control. |
| * However if <code>labelField</code> is explicitly set to |
| * <code>""</code> (the empty string), then no label appears.</p> |
| * |
| * @inheritDoc |
| * |
| * @see spark.components.IconItemRenderer#labelField |
| * @see spark.components.IconItemRenderer#labelFunction |
| * @see spark.components.IItemRenderer#label |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| override public function set label(value:String):void |
| { |
| if (value == label) |
| return; |
| |
| super.label = value; |
| |
| labelChanged = true; |
| invalidateProperties(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Public Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // iconContentLoader |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconContentLoader:IContentLoader = _imageCache; |
| |
| |
| /** |
| * Optional custom image loader, such as an image cache or queue, to |
| * associate with content loader client. |
| * |
| * <p>The default value is a static content cache defined on IconItemRenderer |
| * that allows up to 100 entries.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconContentLoader():IContentLoader |
| { |
| return _iconContentLoader; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconContentLoader(value:IContentLoader):void |
| { |
| if (value == _iconContentLoader) |
| return; |
| |
| _iconContentLoader = value; |
| |
| if (iconDisplay) |
| iconDisplay.contentLoader = _iconContentLoader; |
| } |
| |
| //---------------------------------- |
| // decorator |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _decorator:Object; |
| |
| /** |
| * @private |
| */ |
| private var decoratorChanged:Boolean; |
| |
| /** |
| * @private |
| * The class to use when instantiating the decorator for IconItemRenderer. |
| * This class must extend spark.primitives.BitmapImage. |
| * This property was added for Design View so they can set this to a special |
| * subclass of BitmapImage that knows how to load and resolve resources in Design View. |
| */ |
| mx_internal var decoratorDisplayClass:Class = BitmapImage; |
| |
| /** |
| * The display object component used to |
| * display the decorator for this item renderer. |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected var decoratorDisplay:BitmapImage; |
| |
| /** |
| * The decorator icon that appears on the right side |
| * of this item renderer. |
| * |
| * <p>The decorator icon ignores the <code>verticalAlign</code> style |
| * and is always centered vertically.</p> |
| * |
| * <p>The decorator icon is expected to be an embedded asset. There can |
| * be performance degradation if using external assets.</p> |
| * |
| * @default "" |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get decorator():Object |
| { |
| return _decorator; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set decorator(value:Object):void |
| { |
| if (value == _decorator) |
| return; |
| |
| _decorator = value; |
| |
| decoratorChanged = true; |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // labelField |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _labelField:String = null; |
| |
| /** |
| * @private |
| */ |
| private var labelFieldOrFunctionChanged:Boolean; |
| |
| /** |
| * @private |
| */ |
| private var labelChanged:Boolean; |
| |
| /** |
| * The name of the field in the data provider items to display |
| * as the label. |
| * The <code>labelFunction</code> property overrides this property. |
| * |
| * <p>If <code>labelFunction</code> = <code>labelField</code> = null, |
| * then use the <code>label</code> property that gets |
| * pushed in from the list-based control. |
| * However if <code>labelField</code> is explicitly set to |
| * <code>""</code> (the empty string), |
| * then no label appears.</p> |
| * |
| * @see spark.components.IconItemRenderer#labelFunction |
| * @see spark.components.IItemRenderer#label |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get labelField():String |
| { |
| return _labelField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set labelField(value:String):void |
| { |
| if (value == _labelField) |
| return; |
| |
| _labelField = value; |
| labelFieldOrFunctionChanged = true; |
| labelChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // labelFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _labelFunction:Function; |
| |
| /** |
| * A user-supplied function to run on each item to determine its label. |
| * The <code>labelFunction</code> property overrides |
| * the <code>labelField</code> property. |
| * |
| * <p>You can supply a <code>labelFunction</code> that finds the |
| * appropriate fields and returns a displayable string. The |
| * <code>labelFunction</code> is also good for handling formatting and |
| * localization.</p> |
| * |
| * <p>The label function takes a single argument which is the item in |
| * the data provider and returns a String.</p> |
| * <pre> |
| * myLabelFunction(item:Object):String</pre> |
| * |
| * <p>If <code>labelFunction</code> = <code>labelField</code> = null, |
| * then use the <code>label</code> property that gets |
| * pushed in from the list-based control. |
| * However if <code>labelField</code> is explicitly set to |
| * <code>""</code> (the empty string), |
| * then no label appears.</p> |
| * |
| * @see spark.components.IconItemRenderer#labelFunction |
| * @see spark.components.IItemRenderer#label |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get labelFunction():Function |
| { |
| return _labelFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set labelFunction(value:Function):void |
| { |
| if (value == _labelFunction) |
| return; |
| |
| _labelFunction = value; |
| labelFieldOrFunctionChanged = true; |
| labelChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // iconField |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconField:String; |
| |
| /** |
| * @private |
| */ |
| private var iconFieldOrFunctionChanged:Boolean; |
| |
| /** |
| * @private |
| */ |
| private var iconChanged:Boolean; |
| |
| /** |
| * @private |
| * The class to use when instantiating the icon for IconItemRenderer. |
| * This class must extend spark.primitives.BitmapImage. |
| * This property was added for Design View so they can set this to a special |
| * subclass of BitmapImage that knows how to load and resolve resources in Design View. |
| */ |
| mx_internal var iconDisplayClass:Class = BitmapImage; |
| |
| /** |
| * The bitmap image component used to |
| * display the icon data of the item renderer. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected var iconDisplay:BitmapImage; |
| |
| /** |
| * The name of the field in the data item to display as the icon. |
| * By default <code>iconField</code> is <code>null</code>, and the item renderer |
| * does not display an icon. |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconField():String |
| { |
| return _iconField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconField(value:String):void |
| { |
| if (value == _iconField) |
| return; |
| |
| _iconField = value; |
| iconFieldOrFunctionChanged = true; |
| iconChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // iconFillMode |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconFillMode:String = BitmapFillMode.SCALE; |
| |
| [Inspectable(category="General", enumeration="clip,repeat,scale", defaultValue="scale")] |
| |
| /** |
| * @copy spark.primitives.BitmapImage#fillMode |
| * |
| * @default <code>mx.graphics.BitmapFillMode.SCALE</code> |
| * |
| * @see mx.graphics.BitmapFillMode |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconFillMode():String |
| { |
| return _iconFillMode; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconFillMode(value:String):void |
| { |
| if (value == _iconFillMode) |
| return; |
| |
| _iconFillMode = value; |
| |
| if (iconDisplay) |
| iconDisplay.fillMode = _iconFillMode; |
| } |
| |
| //---------------------------------- |
| // iconFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconFunction:Function; |
| |
| /** |
| * A user-supplied function to run on each item to determine its icon. |
| * The <code>iconFunction</code> property overrides |
| * the <code>iconField</code> property. |
| * |
| * <p>You can supply an <code>iconFunction</code> that finds the |
| * appropriate fields and returns a valid URL or object to be used as |
| * the icon.</p> |
| * |
| * <p>The icon function takes a single argument which is the item in |
| * the data provider and returns an Object that gets passed to a |
| * <code>spark.primitives.BitmapImage</code> object as the <code>source</code> |
| * property. Icon function can return a valid URL pointing to an image |
| * or a Class file that represents an image. To see what other types |
| * of objects can be returned from the icon |
| * function, see the <code>BitmapImage</code>'s documentation</p> |
| * <pre> |
| * myIconFunction(item:Object):Object</pre> |
| * |
| * @default null |
| * |
| * @see spark.primitives.BitmapImage#source |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconFunction():Function |
| { |
| return _iconFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconFunction(value:Function):void |
| { |
| if (value == _iconFunction) |
| return; |
| |
| _iconFunction = value; |
| iconFieldOrFunctionChanged = true; |
| iconChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // iconHeight |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconHeight:Number; |
| |
| /** |
| * The height of the icon. If unspecified, the |
| * intrinsic height of the image is used. |
| * |
| * @default NaN |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconHeight():Number |
| { |
| return _iconHeight; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconHeight(value:Number):void |
| { |
| if (value == _iconHeight) |
| return; |
| |
| _iconHeight = value; |
| |
| if (iconDisplay) |
| iconDisplay.explicitHeight = _iconHeight; |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // iconPlaceholder |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconPlaceholder:Object; |
| |
| /** |
| * The icon asset to use while an externally loaded asset is |
| * being downloaded. |
| * |
| * <p>This asset should be an embedded image and not an externally |
| * loaded image.</p> |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconPlaceholder():Object |
| { |
| return _iconPlaceholder; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconPlaceholder(value:Object):void |
| { |
| if (value == _iconPlaceholder) |
| return; |
| |
| _iconPlaceholder = value; |
| |
| iconChanged = true; |
| invalidateProperties(); |
| |
| // clear clearOnLoad if necessary |
| if (iconDisplay) |
| iconDisplay.clearOnLoad = (iconPlaceholder == null); |
| } |
| |
| //---------------------------------- |
| // iconScaleMode |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconScaleMode:String = BitmapScaleMode.STRETCH; |
| |
| [Inspectable(category="General", enumeration="stretch,letterbox", defaultValue="stretch")] |
| |
| /** |
| * @copy spark.primitives.BitmapImage#scaleMode |
| * |
| * @default <code>mx.graphics.BitmapScaleMode.STRETCH</code> |
| * |
| * @see mx.graphics.BitmapScaleMode |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconScaleMode():String |
| { |
| return _iconScaleMode; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconScaleMode(value:String):void |
| { |
| if (value == _iconScaleMode) |
| return; |
| |
| _iconScaleMode = value; |
| |
| if (iconDisplay) |
| iconDisplay.scaleMode = _iconScaleMode; |
| } |
| |
| //---------------------------------- |
| // iconWidth |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _iconWidth:Number; |
| |
| /** |
| * The width of the icon. If unspecified, the |
| * intrinsic width of the image is used. |
| * |
| * @default NaN |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get iconWidth():Number |
| { |
| return _iconWidth; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set iconWidth(value:Number):void |
| { |
| if (value == _iconWidth) |
| return; |
| |
| _iconWidth = value; |
| |
| if (iconDisplay) |
| iconDisplay.explicitWidth = _iconWidth; |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // messageField |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _messageField:String; |
| |
| /** |
| * The text component used to |
| * display the message data of the item renderer. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected var messageDisplay:StyleableTextField; |
| |
| /** |
| * @private |
| */ |
| private var messageFieldOrFunctionChanged:Boolean; |
| |
| /** |
| * @private |
| */ |
| private var messageChanged:Boolean; |
| |
| /** |
| * The name of the field in the data items to display |
| * as the message. |
| * The <code>messageFunction</code> property overrides this property. |
| * |
| * <p>Use the <code>messageStyleName</code> style to control the |
| * appearance of the text.</p> |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get messageField():String |
| { |
| return _messageField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set messageField(value:String):void |
| { |
| if (value == _messageField) |
| return; |
| |
| _messageField = value; |
| messageFieldOrFunctionChanged = true; |
| messageChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // messageFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _messageFunction:Function; |
| |
| /** |
| * A user-supplied function to run on each item to determine its message. |
| * The <code>messageFunction</code> property overrides |
| * the <code>messageField</code> property. |
| * |
| * <p>You can supply a <code>messageFunction</code> that finds the |
| * appropriate fields and returns a displayable string. The |
| * <code>messageFunction</code> is also good for handling formatting and |
| * localization.</p> |
| * |
| * <p>The message function takes a single argument which is the item in |
| * the data provider and returns a String.</p> |
| * <pre> |
| * myMessageFunction(item:Object):String</pre> |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function get messageFunction():Function |
| { |
| return _messageFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set messageFunction(value:Function):void |
| { |
| if (value == _messageFunction) |
| return; |
| |
| _messageFunction = value; |
| messageFieldOrFunctionChanged = true; |
| messageChanged = true; |
| |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // redrawRequested |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _redrawRequested:Boolean = false; |
| |
| /** |
| * @inheritDoc |
| * |
| * <p>We implement this as part of ISharedDisplayObject so the iconDisplay |
| * can share our display object.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get redrawRequested():Boolean |
| { |
| return _redrawRequested; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set redrawRequested(value:Boolean):void |
| { |
| _redrawRequested = value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // IGraphicElementContainer |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Notify the host that an element layer has changed. |
| * |
| * The <code>IGraphicElementHost</code> must re-evaluates the sequences of |
| * graphic elements with shared DisplayObjects and may need to re-assign the |
| * DisplayObjects and redraw the sequences as a result. |
| * |
| * Typically the host will perform this in its |
| * <code>validateProperties()</code> method. |
| * |
| * @param element The element that has changed size. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function invalidateGraphicElementSharing(element:IGraphicElement):void |
| { |
| // since the only graphic elements are hooked up to drawing with the background, |
| // just invalidate display list |
| if (element == iconDisplay) |
| iconNeedsDisplayObjectAssignment = true; |
| else if (element == decoratorDisplay) |
| decoratorNeedsDisplayObjectAssignment = true; |
| invalidateProperties(); |
| } |
| |
| /** |
| * Notify the host component that an element changed and needs to validate properties. |
| * |
| * The <code>IGraphicElementHost</code> must call the <code>validateProperties()</code> |
| * method on the IGraphicElement to give it a chance to commit its properties. |
| * |
| * Typically the host will validate the elements' properties in its |
| * <code>validateProperties()</code> method. |
| * |
| * @param element The element that has changed. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function invalidateGraphicElementProperties(element:IGraphicElement):void |
| { |
| if (element == iconDisplay) |
| iconNeedsValidateProperties = true; |
| else if (element == decoratorDisplay) |
| decoratorNeedsValidateProperties = true; |
| invalidateProperties(); |
| } |
| |
| /** |
| * Notify the host component that an element size has changed. |
| * |
| * The <code>IGraphicElementHost</code> must call the <code>validateSize()</code> |
| * method on the IGraphicElement to give it a chance to validate its size. |
| * |
| * Typically the host will validate the elements' size in its |
| * <code>validateSize()</code> method. |
| * |
| * @param element The element that has changed size. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function invalidateGraphicElementSize(element:IGraphicElement):void |
| { |
| if (element == iconDisplay) |
| iconNeedsValidateSize = true; |
| else if (element == decoratorDisplay) |
| decoratorNeedsValidateSize = true; |
| invalidateSize(); |
| } |
| |
| /** |
| * Notify the host component that an element has changed and needs to be redrawn. |
| * |
| * The <code>IGraphicElementHost</code> must call the <code>validateDisplayList()</code> |
| * method on the IGraphicElement to give it a chance to redraw. |
| * |
| * Typically the host will validate the elements' display lists in its |
| * <code>validateDisplayList()</code> method. |
| * |
| * @param element The element that has changed. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| public function invalidateGraphicElementDisplayList(element:IGraphicElement):void |
| { |
| if (element.displayObject is ISharedDisplayObject) |
| ISharedDisplayObject(element.displayObject).redrawRequested = true; |
| |
| invalidateDisplayList(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden methods: UIComponent |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| override protected function createChildren():void |
| { |
| super.createChildren(); |
| |
| // create any children you need in here |
| |
| // iconDisplay, messageDisplay, and decoratorDisplay are created in |
| // commitProperties() since they are dependent on |
| // other properties and we don't always create them |
| // labelText just uses labelElement to display its data |
| } |
| |
| /** |
| * @private |
| */ |
| override public function styleChanged(styleName:String):void |
| { |
| var allStyles:Boolean = !styleName || styleName == "styleName"; |
| |
| super.styleChanged(styleName); |
| |
| // if message styles may have changed, let's null out the old |
| // value and notify messageDisplay |
| if (allStyles || styleName == "messageStyleName") |
| { |
| if (messageDisplay) |
| { |
| var messageStyleName:String = getStyle("messageStyleName"); |
| if (messageStyleName) |
| { |
| var styleDecl:CSSStyleDeclaration = |
| styleManager.getMergedStyleDeclaration("." + messageStyleName); |
| |
| if (styleDecl) |
| { |
| messageDisplay.styleDeclaration = styleDecl; |
| messageDisplay.styleChanged("styleName"); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function commitProperties():void |
| { |
| super.commitProperties(); |
| |
| if (decoratorChanged) |
| { |
| decoratorChanged = false; |
| |
| // let's see if we need to create or remove it |
| if (decorator && !decoratorDisplay) |
| { |
| createDecoratorDisplay(); |
| } |
| else if (!decorator && decoratorDisplay) |
| { |
| destroyDecoratorDisplay(); |
| } |
| |
| // if we have a decorator display and decoratorChanged was |
| // set to true, then we should make sure we're pointing to |
| // the right decorator source to display. |
| if (decoratorDisplay) |
| decoratorDisplay.source = decorator; |
| |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| |
| if (iconFieldOrFunctionChanged) |
| { |
| iconFieldOrFunctionChanged = false; |
| |
| // let's see if we need to create or remove it |
| if ((iconField || (iconFunction != null)) && !iconDisplay) |
| { |
| createIconDisplay(); |
| |
| if (iconDisplay) |
| attachLoadingListenersToIconDisplay(); |
| } |
| else if (!(iconField || (iconFunction != null)) && iconDisplay) |
| { |
| destroyIconDisplay(); |
| } |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| |
| if (messageFieldOrFunctionChanged) |
| { |
| messageFieldOrFunctionChanged = false; |
| |
| // let's see if we need to create or remove it |
| if ((messageField || (messageFunction != null)) && !messageDisplay) |
| { |
| createMessageDisplay(); |
| } |
| else if (!(messageField || (messageFunction != null)) && messageDisplay) |
| { |
| destroyMessageDisplay(); |
| } |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| |
| // label is created in super.createChildren() |
| |
| if (iconChanged) |
| { |
| iconChanged = false; |
| |
| // we set the icon after a delay for performance benefits and |
| // so we can cancel the load if we're scrolling fast |
| // we still grab the source here so we can make sure we don't |
| // cancel a load when the data is reset (if the source is the same) |
| // if icon, try setting that |
| |
| if (iconFunction != null) |
| { |
| setIconDisplaySource(iconFunction(data)); |
| } |
| else if (iconField) |
| { |
| try |
| { |
| if (iconField in data && data[iconField] != null) |
| setIconDisplaySource(data[iconField]); |
| else |
| setIconDisplaySource(null); |
| } |
| catch(e:Error) |
| { |
| setIconDisplaySource(null); |
| } |
| } |
| } |
| |
| if (messageChanged) |
| { |
| messageChanged = false; |
| |
| if (messageFunction != null) |
| { |
| messageText = messageFunction(data); |
| messageDisplay.text = messageText; |
| } |
| else if (messageField) |
| { |
| try |
| { |
| if (messageField in data && data[messageField] != null) |
| { |
| messageText = data[messageField]; |
| messageDisplay.text = messageText; |
| } |
| else |
| { |
| messageText = ""; |
| messageDisplay.text = messageText; |
| } |
| } |
| catch(e:Error) |
| { |
| messageText = ""; |
| messageDisplay.text = messageText; |
| } |
| } |
| } |
| |
| if (labelChanged) |
| { |
| labelChanged = false; |
| |
| // if label, try setting that |
| if (labelFunction != null) |
| { |
| labelText = labelFunction(data); |
| if (!labelDisplay) |
| createLabelDisplay(); |
| labelDisplay.text = labelText; |
| } |
| else if (labelField) // if labelField is not null or "", then this is a user-set value |
| { |
| try |
| { |
| if (labelField in data && data[labelField] != null) |
| { |
| labelText = data[labelField]; |
| if (!labelDisplay) |
| createLabelDisplay(); |
| labelDisplay.text = labelText; |
| } |
| else |
| { |
| labelText = ""; |
| if (!labelDisplay) |
| createLabelDisplay(); |
| labelDisplay.text = labelText; |
| } |
| } |
| catch(e:Error) |
| { |
| labelText = ""; |
| if (!labelDisplay) |
| createLabelDisplay(); |
| labelDisplay.text = labelText; |
| } |
| } |
| else if (label && labelField === null) // if there's a label and labelField === null, then show label |
| { |
| labelText = label; |
| if (!labelDisplay) |
| createLabelDisplay(); |
| labelDisplay.text = labelText; |
| } |
| else // if labelField === "" |
| { |
| // get rid of labelDisplay if present |
| if (labelDisplay) |
| destroyLabelDisplay(); |
| } |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| |
| if (iconNeedsDisplayObjectAssignment) |
| { |
| iconNeedsDisplayObjectAssignment = false; |
| assignDisplayObject(iconDisplay); |
| } |
| |
| if (decoratorNeedsDisplayObjectAssignment) |
| { |
| decoratorNeedsDisplayObjectAssignment = false; |
| assignDisplayObject(decoratorDisplay); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override public function validateProperties():void |
| { |
| super.validateProperties(); |
| |
| // Since IGraphicElement is not ILayoutManagerClient, we need to make sure we |
| // validate properties of the elements |
| if (iconNeedsValidateProperties) |
| { |
| iconNeedsValidateProperties = false; |
| if (iconDisplay) |
| iconDisplay.validateProperties(); |
| } |
| |
| if (decoratorNeedsValidateProperties) |
| { |
| decoratorNeedsValidateProperties = false; |
| if (decoratorDisplay) |
| decoratorDisplay.validateProperties(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function assignDisplayObject(bitmapImage:BitmapImage):void |
| { |
| if (bitmapImage) |
| { |
| // try using this display object first |
| if (bitmapImage.setSharedDisplayObject(this)) |
| { |
| bitmapImage.displayObjectSharingMode = DisplayObjectSharingMode.USES_SHARED_OBJECT; |
| } |
| else |
| { |
| // if we can't use this as the display object, then let's see if |
| // the icon already has and owns a display object |
| var ownsDisplayObject:Boolean = (bitmapImage.displayObjectSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT); |
| |
| // If the element doesn't have a DisplayObject or it doesn't own |
| // the DisplayObject it currently has, then create a new one |
| var displayObject:DisplayObject = bitmapImage.displayObject; |
| if (!ownsDisplayObject || !displayObject) |
| displayObject = bitmapImage.createDisplayObject(); |
| |
| // Add the display object as a child |
| // Check displayObject for null, some graphic elements |
| // may choose not to create a DisplayObject during this pass. |
| if (displayObject) |
| addChild(displayObject); |
| |
| bitmapImage.displayObjectSharingMode = DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Creates the <code>messageDisplay</code> component. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function createMessageDisplay():void |
| { |
| messageDisplay = StyleableTextField(createInFontContext(StyleableTextField)); |
| messageDisplay.styleName = this; |
| messageDisplay.editable = false; |
| messageDisplay.selectable = false; |
| messageDisplay.multiline = true; |
| messageDisplay.wordWrap = true; |
| |
| var messageStyleName:String = getStyle("messageStyleName"); |
| if (messageStyleName) |
| { |
| var styleDecl:CSSStyleDeclaration = |
| styleManager.getMergedStyleDeclaration("." + messageStyleName); |
| |
| if (styleDecl) |
| messageDisplay.styleDeclaration = styleDecl; |
| } |
| |
| addChild(messageDisplay); |
| } |
| |
| /** |
| * @private |
| * Destroys the messageDisplay component. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function destroyMessageDisplay():void |
| { |
| removeChild(messageDisplay); |
| messageDisplay = null; |
| } |
| |
| /** |
| * @private |
| * Creates the iconDisplay component. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function createIconDisplay():void |
| { |
| iconDisplay = new iconDisplayClass(); |
| |
| iconDisplay.contentLoader = iconContentLoader; |
| iconDisplay.fillMode = iconFillMode; |
| iconDisplay.scaleMode = iconScaleMode; |
| |
| if (!isNaN(iconWidth)) |
| iconDisplay.explicitWidth = iconWidth; |
| if (!isNaN(iconHeight)) |
| iconDisplay.explicitHeight = iconHeight; |
| |
| iconDisplay.parentChanged(this); |
| |
| iconNeedsDisplayObjectAssignment = true; |
| } |
| |
| /** |
| * @private |
| * Destroys the iconDisplay component. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function destroyIconDisplay():void |
| { |
| // need to remove the display object |
| var oldDisplayObject:DisplayObject = iconDisplay.displayObject; |
| if (oldDisplayObject) |
| { |
| // If the element created the display object |
| if (iconDisplay.displayObjectSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT && |
| oldDisplayObject.parent == this) |
| { |
| removeChild(oldDisplayObject); |
| } |
| } |
| |
| iconDisplay.parentChanged(null); |
| iconDisplay = null; |
| } |
| |
| /** |
| * @private |
| * Creates the decoratorDisplay component. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function createDecoratorDisplay():void |
| { |
| decoratorDisplay = new decoratorDisplayClass(); |
| decoratorDisplay.parentChanged(this); |
| |
| decoratorNeedsDisplayObjectAssignment = true; |
| } |
| |
| /** |
| * @private |
| * Destroys the decoratorDisplay component. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 2.5 |
| * @productversion Flex 4.5 |
| */ |
| protected function destroyDecoratorDisplay():void |
| { |
| // need to remove the display object |
| var oldDisplayObject:DisplayObject = decoratorDisplay.displayObject; |
| if (oldDisplayObject) |
| { |
| // If the element created the display object |
| if (decoratorDisplay.displayObjectSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT && |
| oldDisplayObject.parent == this) |
| { |
| removeChild(oldDisplayObject); |
| } |
| } |
| |
| decoratorDisplay.parentChanged(null); |
| decoratorDisplay = null; |
| } |
| |
| /** |
| * @private |
| */ |
| private function attachLoadingListenersToIconDisplay():void |
| { |
| if (iconDisplay) |
| { |
| iconDisplay.addEventListener(IOErrorEvent.IO_ERROR, iconDisplay_loadErrorHandler, false, 0, true); |
| iconDisplay.addEventListener(SecurityErrorEvent.SECURITY_ERROR, iconDisplay_loadErrorHandler, false, 0, true); |
| } |
| } |
| |
| /** |
| * @private |
| * Method is called when an IOError or SecurityError is dispatched |
| * while trying to load the icon display. The default implementation |
| * sets the iconDisplay's source to the iconPlaceholder. |
| */ |
| mx_internal function iconDisplay_loadErrorHandler(event:Event):void |
| { |
| iconDisplay.source = iconPlaceholder; |
| } |
| |
| /** |
| * @private |
| */ |
| private function loadExternalImage(source:Object, iconDelay:Number):void |
| { |
| // set iconDisplay's source now to either iconPlaceholder |
| // or null (if no iconPlaceholder). this is so we don't display the old |
| // data while we're loading. |
| iconDisplay.source = iconPlaceholder; |
| |
| // while we're loading,if iconPlaceholder is set, |
| // we'll keep that image up while we're loading |
| // the external content |
| iconDisplay.clearOnLoad = (iconPlaceholder == null); |
| |
| if (iconDelay > 0) |
| { |
| // set what we're gonna load and start the timer |
| iconSourceToLoad = source; |
| |
| if (!iconSetterDelayTimer) |
| { |
| iconSetterDelayTimer = new Timer(iconDelay, 1); |
| iconSetterDelayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, iconSetterDelayTimer_timerCompleteHandler); |
| } |
| |
| iconSetterDelayTimer.start(); |
| } |
| else // iconDelay == 0 |
| { |
| // load up the image immediately |
| |
| // need to call validateProperties because we need this iconPlaceholder |
| // to actually get loaded up since we set iconDisplay.source to a remote |
| // image on the next line. BitmapImage doesn't actually attempt to load |
| // up the image (even if it's a locally embedded asset) until commitProperties() |
| iconDisplay.validateProperties(); |
| |
| iconDisplay.source = source; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function stopLoadingExternalImage():void |
| { |
| // stop any asynch operation: |
| if (iconSetterDelayTimer) |
| { |
| iconSourceToLoad = null |
| iconSetterDelayTimer.stop(); |
| iconSetterDelayTimer.reset(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function setIconDisplaySource(source:Object):void |
| { |
| var iconDelay:Number = getStyle("iconDelay"); |
| |
| // if not a string or URL request (or null), load it up immediately |
| var isExternalSource:Boolean = (source is String || source is URLRequest); |
| if (!isExternalSource) |
| { |
| // get the icon source to find out if it is external or not |
| if (source is MultiDPIBitmapSource) |
| { |
| var multiSource:Object = MultiDPIBitmapSource(source).getMultiSource(); |
| isExternalSource = (multiSource is String || multiSource is URLRequest); |
| } |
| } |
| |
| // if null or embedded asset do it synchronously |
| if (!isExternalSource) |
| { |
| stopLoadingExternalImage(); |
| |
| // load it up |
| iconDisplay.source = source; |
| |
| return; |
| } |
| |
| // if it's the same source, don't cancel this load--let it continue |
| if (iconSourceToLoad == source) |
| return; |
| |
| // At this point, we know we can cancel the old asynch operation |
| // since we're not going to use it anymore |
| stopLoadingExternalImage(); |
| |
| // we know we're loading external content, check the cache first: |
| var contentCache:ContentCache = iconContentLoader as ContentCache; |
| if (contentCache) |
| { |
| if (contentCache.getCacheEntry(source)) |
| { |
| // We know we have this item cached (or atleast have attempted |
| // to load this item and cache it). Because we're not sure whether |
| // this item has finished loading or not, let's set the icon's |
| // source to the placeholder (or null) first so that we can make sure |
| // we don't leave in a stale image while the load is still happening. |
| iconDisplay.source = iconPlaceholder; |
| |
| // while we're loading,if iconPlaceholder is set, |
| // we'll keep that image up while we're loading |
| // the external content |
| iconDisplay.clearOnLoad = (iconPlaceholder == null); |
| |
| // need to call validateProperties because we need this iconPlaceholder |
| // to actually get loaded up since we set iconDisplay.source to a remote |
| // image on the next line. BitmapImage doesn't actually attempt to load |
| // up the image (even if it's a locally embedded asset) until commitProperties() |
| iconDisplay.validateProperties(); |
| |
| // now attempt to load up our other image (or grab it from the cache |
| // if the load had finished already) |
| iconDisplay.source = source; |
| |
| return; |
| } |
| } |
| |
| // otherwise, we need to load an external asset and use a Timer |
| loadExternalImage(source, iconDelay); |
| } |
| |
| /** |
| * @private |
| */ |
| private function iconSetterDelayTimer_timerCompleteHandler(event:TimerEvent):void |
| { |
| // if we're off-screen, don't do anything |
| // when we get re-included, our data will be reset as well |
| if (!includeInLayout) |
| { |
| iconSourceToLoad = null; |
| return; |
| } |
| |
| if (iconDisplay) |
| iconDisplay.source = iconSourceToLoad; |
| |
| iconSourceToLoad = null; |
| } |
| |
| /** |
| * @private |
| */ |
| override public function validateSize(recursive:Boolean = false):void |
| { |
| // Since IGraphicElement is not ILayoutManagerClient, we need to make sure we |
| // validate sizes of the elements, even in cases where recursive==false. |
| |
| // Validate element size |
| if (iconNeedsValidateSize) |
| { |
| iconNeedsValidateSize = false; |
| if (iconDisplay) |
| iconDisplay.validateSize(); |
| } |
| |
| if (decoratorNeedsValidateSize) |
| { |
| decoratorNeedsValidateSize = false; |
| if (decoratorDisplay) |
| decoratorDisplay.validateSize(); |
| } |
| |
| super.validateSize(recursive); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function measure():void |
| { |
| // don't call super.measure() because there's no need to do the work that's |
| // in there--we do it all in here. |
| //super.measure(); |
| |
| // start them at 0, then go through icon, label, and decorator |
| // and add to these |
| var myMeasuredWidth:Number = 0; |
| var myMeasuredHeight:Number = 0; |
| var myMeasuredMinWidth:Number = 0; |
| var myMeasuredMinHeight:Number = 0; |
| |
| // calculate padding and horizontal gap |
| // verticalGap is already handled above when there's a label |
| // and a message since that's the only place verticalGap matters. |
| // if we handled verticalGap here, it might add it to the icon if |
| // the icon was the tallest item. |
| var numHorizontalSections:int = 0; |
| if (iconDisplay) |
| numHorizontalSections++; |
| |
| if (decoratorDisplay) |
| numHorizontalSections++; |
| |
| if (labelDisplay || messageDisplay) |
| numHorizontalSections++; |
| |
| var paddingAndGapWidth:Number = getStyle("paddingLeft") + getStyle("paddingRight"); |
| if (numHorizontalSections > 0) |
| paddingAndGapWidth += (getStyle("horizontalGap") * (numHorizontalSections - 1)); |
| |
| var hasLabel:Boolean = labelDisplay && labelDisplay.text != ""; |
| var hasMessage:Boolean = messageDisplay && messageDisplay.text != ""; |
| |
| var verticalGap:Number = (hasLabel && hasMessage) ? getStyle("verticalGap") : 0; |
| var paddingHeight:Number = getStyle("paddingTop") + getStyle("paddingBottom"); |
| |
| // Icon is on left |
| var myIconWidth:Number = 0; |
| var myIconHeight:Number = 0; |
| if (iconDisplay) |
| { |
| myIconWidth = (isNaN(iconWidth) ? getElementPreferredWidth(iconDisplay) : iconWidth); |
| myIconHeight = (isNaN(iconHeight) ? getElementPreferredHeight(iconDisplay) : iconHeight); |
| |
| myMeasuredWidth += myIconWidth; |
| myMeasuredMinWidth += myIconWidth; |
| myMeasuredHeight = Math.max(myMeasuredHeight, myIconHeight); |
| myMeasuredMinHeight = Math.max(myMeasuredMinHeight, myIconHeight); |
| } |
| |
| // Decorator is up next |
| var decoratorWidth:Number = 0; |
| var decoratorHeight:Number = 0; |
| |
| if (decoratorDisplay) |
| { |
| decoratorWidth = getElementPreferredWidth(decoratorDisplay); |
| decoratorHeight = getElementPreferredHeight(decoratorDisplay); |
| |
| myMeasuredWidth += decoratorWidth; |
| myMeasuredMinWidth += decoratorWidth; |
| myMeasuredHeight = Math.max(myMeasuredHeight, decoratorHeight); |
| myMeasuredMinHeight = Math.max(myMeasuredHeight, decoratorHeight); |
| } |
| |
| // Text is aligned next to icon |
| var labelWidth:Number = 0; |
| var labelHeight:Number = 0; |
| var messageWidth:Number = 0; |
| var messageHeight:Number = 0; |
| |
| if (hasLabel) |
| { |
| // reset text if it was truncated before. |
| if (labelDisplay.isTruncated) |
| labelDisplay.text = labelText; |
| |
| labelWidth = getElementPreferredWidth(labelDisplay); |
| labelHeight = getElementPreferredHeight(labelDisplay); |
| } |
| |
| if (hasMessage) |
| { |
| // now we need to measure messageDisplay's height. Unfortunately, this is tricky and |
| // is dependent on messageDisplay's width. |
| // Use the old unscaledWidth width as an estimte for the new one. |
| // If we are wrong, we'll find out in updateDisplayList() |
| |
| var messageDisplayEstimatedWidth:Number = oldUnscaledWidth - paddingAndGapWidth - myIconWidth - decoratorWidth; |
| |
| setElementSize(messageDisplay, messageDisplayEstimatedWidth, NaN); |
| |
| messageWidth = getElementPreferredWidth(messageDisplay); |
| messageHeight = getElementPreferredHeight(messageDisplay); |
| } |
| |
| myMeasuredWidth += Math.max(labelWidth, messageWidth); |
| myMeasuredHeight = Math.max(myMeasuredHeight, labelHeight + messageHeight + verticalGap); |
| |
| myMeasuredWidth += paddingAndGapWidth; |
| myMeasuredMinWidth += paddingAndGapWidth; |
| |
| // verticalGap handled in label and message |
| myMeasuredHeight += paddingHeight; |
| myMeasuredMinHeight += paddingHeight; |
| |
| // now set the local variables to the member variables. |
| measuredWidth = myMeasuredWidth |
| measuredHeight = myMeasuredHeight; |
| |
| measuredMinWidth = myMeasuredMinWidth; |
| measuredMinHeight = myMeasuredMinHeight; |
| } |
| |
| /** |
| * @private |
| * If we invalidate display list, we need to redraw any graphic elements sharing |
| * our display object since we call graphics.clear() in super.updateDisplayList() |
| */ |
| override public function invalidateDisplayList():void |
| { |
| redrawRequested = true; |
| super.invalidateDisplayList(); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function validateDisplayList():void |
| { |
| super.validateDisplayList(); |
| |
| // Since IGraphicElement is not ILayoutManagerClient, we need to make sure we |
| // validate properties of the elements |
| |
| // see if we have an icon that needs to be validated |
| if (iconDisplay && |
| iconDisplay.displayObject is ISharedDisplayObject && |
| ISharedDisplayObject(iconDisplay.displayObject).redrawRequested) |
| { |
| ISharedDisplayObject(iconDisplay.displayObject).redrawRequested = false; |
| iconDisplay.validateDisplayList(); |
| // if decoratorDisplay is also using this displayObject than validate |
| // decoratorDisplay as well |
| if (decoratorDisplay && |
| decoratorDisplay.displayObject is ISharedDisplayObject && |
| decoratorDisplay.displayObject == iconDisplay.displayObject) |
| decoratorDisplay.validateDisplayList(); |
| } |
| |
| // check just for decoratorDisplay in case it has a different displayObject |
| // than iconDisplay |
| if (decoratorDisplay && |
| decoratorDisplay.displayObject is ISharedDisplayObject && |
| ISharedDisplayObject(decoratorDisplay.displayObject).redrawRequested) |
| { |
| ISharedDisplayObject(decoratorDisplay.displayObject).redrawRequested = false; |
| decoratorDisplay.validateDisplayList(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function layoutContents(unscaledWidth:Number, |
| unscaledHeight:Number):void |
| { |
| // no need to call super.layoutContents() since we're changing how it happens here |
| |
| // start laying out our children now |
| var iconWidth:Number = 0; |
| var iconHeight:Number = 0; |
| var decoratorWidth:Number = 0; |
| var decoratorHeight:Number = 0; |
| |
| var hasLabel:Boolean = labelDisplay && labelDisplay.text != ""; |
| var hasMessage:Boolean = messageDisplay && messageDisplay.text != ""; |
| |
| var paddingLeft:Number = getStyle("paddingLeft"); |
| var paddingRight:Number = getStyle("paddingRight"); |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| var horizontalGap:Number = getStyle("horizontalGap"); |
| var verticalAlign:String = getStyle("verticalAlign"); |
| var verticalGap:Number = (hasLabel && hasMessage) ? getStyle("verticalGap") : 0; |
| |
| var vAlign:Number; |
| if (verticalAlign == "top") |
| vAlign = 0; |
| else if (verticalAlign == "bottom") |
| vAlign = 1; |
| else // if (verticalAlign == "middle") |
| vAlign = 0.5; |
| // made "middle" last even though it's most likely so it is the default and if someone |
| // types "center", then it will still vertically center itself. |
| |
| var viewWidth:Number = unscaledWidth - paddingLeft - paddingRight; |
| var viewHeight:Number = unscaledHeight - paddingTop - paddingBottom; |
| |
| // icon is on the left |
| if (iconDisplay) |
| { |
| // set the icon's position and size |
| setElementSize(iconDisplay, this.iconWidth, this.iconHeight); |
| |
| iconWidth = iconDisplay.getLayoutBoundsWidth(); |
| iconHeight = iconDisplay.getLayoutBoundsHeight(); |
| |
| // use vAlign to position the icon. |
| var iconDisplayY:Number = Math.round(vAlign * (viewHeight - iconHeight)) + paddingTop; |
| setElementPosition(iconDisplay, paddingLeft, iconDisplayY); |
| } |
| |
| // decorator is aligned next to icon |
| if (decoratorDisplay) |
| { |
| decoratorWidth = getElementPreferredWidth(decoratorDisplay); |
| decoratorHeight = getElementPreferredHeight(decoratorDisplay); |
| |
| setElementSize(decoratorDisplay, decoratorWidth, decoratorHeight); |
| |
| // decorator is always right aligned, vertically centered |
| var decoratorY:Number = Math.round(0.5 * (viewHeight - decoratorHeight)) + paddingTop; |
| setElementPosition(decoratorDisplay, unscaledWidth - paddingRight - decoratorWidth, decoratorY); |
| } |
| |
| // Figure out how much space we have for label and message as well as the |
| // starting left position |
| var labelComponentsViewWidth:Number = viewWidth - iconWidth - decoratorWidth; |
| |
| // don't forget the extra gap padding if these elements exist |
| if (iconDisplay) |
| labelComponentsViewWidth -= horizontalGap; |
| if (decoratorDisplay) |
| labelComponentsViewWidth -= horizontalGap; |
| |
| var labelComponentsX:Number = paddingLeft; |
| if (iconDisplay) |
| labelComponentsX += iconWidth + horizontalGap; |
| |
| // calculte the natural height for the label |
| var labelTextHeight:Number = 0; |
| |
| if (hasLabel) |
| { |
| // reset text if it was truncated before. |
| if (labelDisplay.isTruncated) |
| labelDisplay.text = labelText; |
| |
| // commit styles to make sure it uses updated look |
| labelDisplay.commitStyles(); |
| |
| labelTextHeight = getElementPreferredHeight(labelDisplay); |
| } |
| |
| if (hasMessage) |
| { |
| // commit styles to make sure it uses updated look |
| messageDisplay.commitStyles(); |
| } |
| |
| // now size and position the elements, 3 different configurations we care about: |
| // 1) label and message |
| // 2) label only |
| // 3) message only |
| |
| // label display goes on top |
| // message display goes below |
| |
| var labelWidth:Number = 0; |
| var labelHeight:Number = 0; |
| var messageWidth:Number = 0; |
| var messageHeight:Number = 0; |
| |
| if (hasLabel) |
| { |
| // handle labelDisplay. it can only be 1 line |
| |
| // width of label takes up rest of space |
| // height only takes up what it needs so we can properly place the message |
| // and make sure verticalAlign is operating on a correct value. |
| labelWidth = Math.max(labelComponentsViewWidth, 0); |
| labelHeight = labelTextHeight; |
| |
| if (labelWidth == 0) |
| setElementSize(labelDisplay, NaN, 0); |
| else |
| setElementSize(labelDisplay, labelWidth, labelHeight); |
| |
| // attempt to truncate text |
| labelDisplay.truncateToFit(); |
| } |
| |
| if (hasMessage) |
| { |
| // handle message...because the text is multi-line, measuring and layout |
| // can be somewhat tricky |
| messageWidth = Math.max(labelComponentsViewWidth, 0); |
| |
| // We get called with unscaledWidth = 0 a few times... |
| // rather than deal with this case normally, |
| // we can just special-case it later to do something smarter |
| if (messageWidth == 0) |
| { |
| // if unscaledWidth is 0, we want to make sure messageDisplay is invisible. |
| // we could set messageDisplay's width to 0, but that would cause an extra |
| // layout pass because of the text reflow logic. Because of that, we |
| // can just set its height to 0. |
| setElementSize(messageDisplay, NaN, 0); |
| } |
| else |
| { |
| // grab old textDisplay height before resizing it |
| var oldPreferredMessageHeight:Number = getElementPreferredHeight(messageDisplay); |
| |
| // keep track of oldUnscaledWidth so we have a good guess as to the width |
| // of the messageDisplay on the next measure() pass |
| oldUnscaledWidth = unscaledWidth; |
| |
| // set the width of messageDisplay to messageWidth. |
| // set the height to oldMessageHeight. If the height's actually wrong, |
| // we'll invalidateSize() and go through this layout pass again anyways |
| setElementSize(messageDisplay, messageWidth, oldPreferredMessageHeight); |
| |
| // grab new messageDisplay height after the messageDisplay has taken its final width |
| var newPreferredMessageHeight:Number = getElementPreferredHeight(messageDisplay); |
| |
| // if the resize caused the messageDisplay's height to change (because of |
| // text reflow), then we need to remeasure ourselves with our new width |
| if (oldPreferredMessageHeight != newPreferredMessageHeight) |
| invalidateSize(); |
| |
| messageHeight = newPreferredMessageHeight; |
| } |
| |
| // since it's multi-line, no need to truncate |
| //if (messageDisplay.isTruncated) |
| // messageDisplay.text = messageText; |
| //messageDisplay.truncateToFit(); |
| } |
| else |
| { |
| if (messageDisplay) |
| setElementSize(messageDisplay, 0, 0); |
| } |
| |
| // Position the text components now that we know all heights so we can respect verticalAlign style |
| var totalHeight:Number = 0; |
| var labelComponentsY:Number = 0; |
| |
| // Heights used in our alignment calculations. We only care about the "real" ascent |
| var labelAlignmentHeight:Number = 0; |
| var messageAlignmentHeight:Number = 0; |
| |
| if (hasLabel) |
| labelAlignmentHeight = getElementPreferredHeight(labelDisplay); |
| if (hasMessage) |
| messageAlignmentHeight = getElementPreferredHeight(messageDisplay); |
| |
| totalHeight = labelAlignmentHeight + messageAlignmentHeight + verticalGap; |
| labelComponentsY = Math.round(vAlign * (viewHeight - totalHeight)) + paddingTop; |
| |
| if (labelDisplay) |
| setElementPosition(labelDisplay, labelComponentsX, labelComponentsY); |
| |
| if (messageDisplay) |
| { |
| var messageY:Number = labelComponentsY + labelAlignmentHeight + verticalGap; |
| setElementPosition(messageDisplay, labelComponentsX, messageY); |
| } |
| } |
| |
| } |
| |
| } |