blob: 0b805e672027a657dac2e6ef4631b35d53e56f35 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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 mx.core
{
import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.geom.Point;
import flash.system.ApplicationDomain;
/**
* SpriteAsset is a subclass of the flash.display.Sprite class which
* represents vector graphic images that you embed in an application.
* It implements the IFlexDisplayObject interface, which makes it
* possible for an embedded vector graphic image to be displayed in an Image
* control, or to be used as a container background or a component skin.
*
* <p>The vector graphic image that you're embedding can be in an SVG file.
* You can also embed a sprite symbol that is in a SWF file produced
* by Flash.
* In both cases, the MXML compiler autogenerates a class that extends
* SpriteAsset to represent the embedded vector graphic image.</p>
*
* <p>You don't generally have to use the SpriteAsset class directly
* when you write a Flex application.
* For example, you can embed a sprite symbol from a SWF file and display
* it in an Image control by writing the following:</p>
*
* <pre>
* &lt;mx:Image id="logo" source="&#64;Embed(source='Assets.swf', symbol='Logo')"/&gt;</pre>
*
* <p>Or use it as the application's background image in CSS syntax
* by writing the following:</p>
*
* <pre>
* &lt;fx:Style&gt;
* &#64;namespace mx "library://ns.adobe.com/flex/mx"
* mx|Application {
* backgroundImage: Embed(source="Assets.swf", symbol='Logo')
* }
* &lt;fx:Style/&gt;</pre>
*
* <p>without having to understand that the MXML compiler has created
* a subclass of BitmapAsset for you.</p>
*
* <p>However, it may be useful to understand what is happening
* at the ActionScript level.
* To embed a vector graphic image in ActionScript, you declare a variable
* of type Class, and put <code>[Embed]</code> metadata on it.
* For example, you embed a sprite symbol from a SWF file like this:</p>
*
* <pre>
* [Bindable]
* [Embed(source="Assets.swf", symbol="Logo")]
* private var logoClass:Class;</pre>
*
* <p>The MXML compiler notices that the Logo symbol in Assets.swf
* is a sprite, autogenerates a subclass of the SpriteAsset class
* to represent it, and sets your variable to be a reference to this
* autogenerated class.
* You can then use this class reference to create instances of the
* SpriteAsset using the <code>new</code> operator, and use APIs
* of the Sprite class on them:</p>
*
* <pre>
* var logo:SpriteAsset = SpriteAsset(new logoClass());
* logo.rotation=45;</pre>
*
* <p>However, you rarely need to create SpriteAsset instances yourself
* because image-related properties and styles can simply be set to an
* image-producing class, and components will create image instances
* as necessary.
* For example, to display this vector graphic image in an Image control,
* you can set the Image's <code>source</code> property to
* <code>logoClass</code>.
* In MXML you could do this as follows:</p>
*
* <pre>
* &lt;mx:Image id="logo" source="{logoClass}"/&gt;</pre>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class SpriteAsset extends FlexSprite
implements IFlexAsset, IFlexDisplayObject, IBorder,
ILayoutDirectionElement
{
include "../core/Version.as";
// Softlink FlexVersion and MatrixUtil to remove dependencies of embeds on
// framework classes. This helps to reduce swf size in AS-only projects.
private static var FlexVersionClass:Class;
private static var MatrixUtilClass:Class;
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function SpriteAsset()
{
super();
// Remember initial size as our measured size.
_measuredWidth = width;
_measuredHeight = height;
if (FlexVersionClass == null)
{
var appDomain:ApplicationDomain = ApplicationDomain.currentDomain;
if (appDomain.hasDefinition("mx.core::FlexVersion"))
FlexVersionClass = Class(appDomain.getDefinition("mx.core::FlexVersion"));
}
if (FlexVersionClass && FlexVersionClass["compatibilityVersion"] >= FlexVersionClass["VERSION_4_0"])
this.addEventListener(Event.ADDED, addedHandler);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
// Softlink AdvancedLayoutFeatures to remove dependencies of embeds on
// framework classes. This helps to reduce swf size in AS-only projects.
private var layoutFeaturesClass:Class;
private var layoutFeatures:IAssetLayoutFeatures;
//--------------------------------------------------------------------------
//
// Overridden Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// x
//----------------------------------
/**
* @private
*/
override public function get x():Number
{
// TODO(hmuller): by default get x returns transform.matrix.tx rounded to the nearest 20th.
// should do the same here, if we're returning layoutFeatures.layoutX.
return (layoutFeatures == null) ? super.x : layoutFeatures.layoutX;
}
/**
* @private
*/
override public function set x(value:Number):void
{
if (x == value)
return;
if (layoutFeatures == null)
{
super.x = value;
}
else
{
layoutFeatures.layoutX = value;
validateTransformMatrix();
}
}
//----------------------------------
// y
//----------------------------------
/**
* @private
*/
override public function get y():Number
{
return (layoutFeatures == null) ? super.y : layoutFeatures.layoutY;
}
/**
* @private
*/
override public function set y(value:Number):void
{
if (y == value)
return;
if (layoutFeatures == null)
{
super.y = value;
}
else
{
layoutFeatures.layoutY = value;
validateTransformMatrix();
}
}
//----------------------------------
// z
//----------------------------------
/**
* @private
*/
override public function get z():Number
{
return (layoutFeatures == null) ? super.z : layoutFeatures.layoutZ;
}
/**
* @private
*/
override public function set z(value:Number):void
{
if (z == value)
return;
if (layoutFeatures == null)
{
super.z = value;
}
else
{
layoutFeatures.layoutZ = value;
validateTransformMatrix();
}
}
//----------------------------------
// width
//----------------------------------
/**
* @private
*/
override public function get width():Number
{
if (layoutFeatures == null)
return super.width;
// Return bounding box width in mirroring case
var p:Point;
if (MatrixUtilClass != null)
p = MatrixUtilClass["transformSize"](layoutFeatures.layoutWidth, _height, transform.matrix);
return p ? p.x : super.width;
}
/**
* @private
*/
override public function set width(value:Number):void
{
if (width == value)
return;
if (layoutFeatures == null)
{
super.width = value;
}
else
{
layoutFeatures.layoutWidth = value;
// Calculate scaleX based on initial width. We set scaleX
// here because resizing a BitmapAsset normally would adjust
// the scale to match.
layoutFeatures.layoutScaleX = measuredWidth != 0 ? value / measuredWidth : 0;
validateTransformMatrix();
}
}
//----------------------------------
// height
//----------------------------------
private var _height:Number;
/**
* @private
*/
override public function get height():Number
{
if (layoutFeatures == null)
return super.height;
// Return bounding box height in mirroring case
var p:Point;
if (MatrixUtilClass != null)
p = MatrixUtilClass["transformSize"](layoutFeatures.layoutWidth, _height, transform.matrix);
return p ? p.y : super.height;
}
/**
* @private
*/
override public function set height(value:Number):void
{
if (height == value)
return;
if (layoutFeatures == null)
{
super.height = value;
}
else
{
_height = value;
// Calculate scaleY based on initial height. We set scaleY
// here because resizing a BitmapAsset normally would adjust
// the scale to match.
layoutFeatures.layoutScaleY = measuredHeight != 0 ? value / measuredHeight : 0;
validateTransformMatrix();
}
}
//----------------------------------
// rotation
//----------------------------------
/**
* @private
*/
override public function get rotationX():Number
{
return (layoutFeatures == null) ? super.rotationX : layoutFeatures.layoutRotationX;
}
/**
* @private
*/
override public function set rotationX(value:Number):void
{
if (rotationX == value)
return;
if (layoutFeatures == null)
{
super.rotationX = value;
}
else
{
layoutFeatures.layoutRotationX = value;
validateTransformMatrix();
}
}
/**
* @private
*/
override public function get rotationY():Number
{
return (layoutFeatures == null) ? super.rotationY : layoutFeatures.layoutRotationY;
}
/**
* @private
*/
override public function set rotationY(value:Number):void
{
if (rotationY == value)
return;
if (layoutFeatures == null)
{
super.rotationY = value;
}
else
{
layoutFeatures.layoutRotationY = value;
validateTransformMatrix();
}
}
/**
* @private
*/
override public function get rotationZ():Number
{
return (layoutFeatures == null) ? super.rotationZ : layoutFeatures.layoutRotationZ;
}
/**
* @private
*/
override public function set rotationZ(value:Number):void
{
if (rotationZ == value)
return;
if (layoutFeatures == null)
{
super.rotationZ = value;
}
else
{
layoutFeatures.layoutRotationZ = value;
validateTransformMatrix();
}
}
/**
* @private
*/
override public function get rotation():Number
{
return (layoutFeatures == null) ? super.rotation : layoutFeatures.layoutRotationZ;
}
/**
* @private
*/
override public function set rotation(value:Number):void
{
// TODO (klin): rotation actually affects width and height as well.
if (rotation == value)
return;
if (layoutFeatures == null)
{
super.rotation = value;
}
else
{
layoutFeatures.layoutRotationZ = value;
validateTransformMatrix();
}
}
//----------------------------------
// scaleX
//----------------------------------
/**
* @private
*/
override public function get scaleX():Number
{
return (layoutFeatures == null) ? super.scaleX : layoutFeatures.layoutScaleX;
}
/**
* @private
*/
override public function set scaleX(value:Number):void
{
if (scaleX == value)
return;
if (layoutFeatures == null)
{
super.scaleX = value;
}
else
{
layoutFeatures.layoutScaleX = value;
layoutFeatures.layoutWidth = Math.abs(value) * measuredWidth;
validateTransformMatrix();
}
}
//----------------------------------
// scaleY
//----------------------------------
/**
* @private
*/
override public function get scaleY():Number
{
return (layoutFeatures == null) ? super.scaleY : layoutFeatures.layoutScaleY;
}
/**
* @private
*/
override public function set scaleY(value:Number):void
{
if (scaleY == value)
return;
if (layoutFeatures == null)
{
super.scaleY = value;
}
else
{
layoutFeatures.layoutScaleY = value;
_height = Math.abs(value) * measuredHeight;
validateTransformMatrix();
}
}
//----------------------------------
// scaleZ
//----------------------------------
/**
* @private
*/
override public function get scaleZ():Number
{
return (layoutFeatures == null) ? super.scaleZ : layoutFeatures.layoutScaleZ;
}
/**
* @private
*/
override public function set scaleZ(value:Number):void
{
if (scaleZ == value)
return;
if (layoutFeatures == null)
{
super.scaleZ = value;
}
else
{
layoutFeatures.layoutScaleZ = value;
validateTransformMatrix();
}
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// layoutDirection
//----------------------------------
// Use "ltr" instead of LayoutDirection.LTR to avoid depending
// on that framework class.
private var _layoutDirection:String = "ltr";
[Inspectable(category="General", enumeration="ltr,rtl")]
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4.1
*/
public function get layoutDirection():String
{
return _layoutDirection;
}
public function set layoutDirection(value:String):void
{
if (value == _layoutDirection)
return;
_layoutDirection = value;
invalidateLayoutDirection();
}
//----------------------------------
// measuredHeight
//----------------------------------
/**
* @private
* Storage for the measuredHeight property.
*/
private var _measuredHeight:Number;
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get measuredHeight():Number
{
return _measuredHeight;
}
//----------------------------------
// measuredWidth
//----------------------------------
/**
* @private
* Storage for the measuredWidth property.
*/
private var _measuredWidth:Number;
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get measuredWidth():Number
{
return _measuredWidth;
}
//----------------------------------
// borderMetrics
//----------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get borderMetrics():EdgeMetrics
{
if (scale9Grid == null)
{
return EdgeMetrics.EMPTY;
}
else
{
return new EdgeMetrics(scale9Grid.left,
scale9Grid.top,
Math.ceil(measuredWidth - scale9Grid.right),
Math.ceil(measuredHeight - scale9Grid.bottom));
}
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4.1
*/
public function invalidateLayoutDirection():void
{
var p:DisplayObjectContainer = parent;
// We check the closest parent's layoutDirection property
// to create or destroy layoutFeatures if needed.
while (p)
{
if (p is ILayoutDirectionElement)
{
// mirror is true if our layoutDirection differs from our parent's.
var mirror:Boolean = _layoutDirection != null &&
ILayoutDirectionElement(p).layoutDirection != null &&
(_layoutDirection != ILayoutDirectionElement(p).layoutDirection);
// If our layoutDirection is different from our parent's and if it used to
// be the same, create layoutFeatures to handle mirroring.
if (mirror && layoutFeatures == null)
{
initAdvancedLayoutFeatures();
if (layoutFeatures != null)
{
layoutFeatures.mirror = mirror;
validateTransformMatrix();
}
}
else if (!mirror && layoutFeatures)
{
// If our layoutDirection is not different from our parent's and if
// it used to be different, then recover our matrix and remove layoutFeatures.
layoutFeatures.mirror = mirror;
validateTransformMatrix();
layoutFeatures = null;
}
break;
}
p = p.parent;
}
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function move(x:Number, y:Number):void
{
this.x = x;
this.y = y;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function setActualSize(newWidth:Number, newHeight:Number):void
{
width = newWidth;
height = newHeight;
}
/**
* @private
*/
private function addedHandler(event:Event):void
{
invalidateLayoutDirection();
}
/**
* @private
* Initializes AdvancedLayoutFeatures for this asset when mirroring.
*/
private function initAdvancedLayoutFeatures():void
{
// Get AdvancedLayoutFeatures if it exists.
if (layoutFeaturesClass == null)
{
var appDomain:ApplicationDomain = ApplicationDomain.currentDomain;
if (appDomain.hasDefinition("mx.core::AdvancedLayoutFeatures"))
layoutFeaturesClass = Class(appDomain.getDefinition("mx.core::AdvancedLayoutFeatures"));
// Get MatrixUtil class if it exists
if (MatrixUtilClass == null)
{
if (appDomain.hasDefinition("mx.utils::MatrixUtil"))
MatrixUtilClass = Class(appDomain.getDefinition("mx.utils::MatrixUtil"));
}
}
if (layoutFeaturesClass != null)
{
var features:IAssetLayoutFeatures = new layoutFeaturesClass();
features.layoutScaleX = scaleX;
features.layoutScaleY = scaleY;
features.layoutScaleZ = scaleZ;
features.layoutRotationX = rotationX;
features.layoutRotationY = rotationY;
features.layoutRotationZ = rotation;
features.layoutX = x;
features.layoutY = y;
features.layoutZ = z;
features.layoutWidth = width; // for the mirror transform
_height = height; // for backing storage
layoutFeatures = features;
}
}
/**
* @private
* Applies the transform matrix calculated by AdvancedLayoutFeatures
* so that this bitmap will not be mirrored if a parent is mirrored.
*/
private function validateTransformMatrix():void
{
if (layoutFeatures != null)
{
if (layoutFeatures.is3D)
super.transform.matrix3D = layoutFeatures.computedMatrix3D;
else
super.transform.matrix = layoutFeatures.computedMatrix;
}
}
}
}