blob: 6e65e1ca96254612c2a37545a558b1317909d1ff [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.skins
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Graphics;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Shape;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.utils.getDefinitionByName;
import mx.core.EdgeMetrics;
import mx.core.FlexLoader;
import mx.core.FlexShape;
import mx.core.IChildList;
import mx.core.IContainer;
import mx.core.IRawChildrenContainer;
import mx.core.mx_internal;
import mx.core.IRectangularBorder;
import mx.managers.ISystemManager;
import mx.managers.SystemManager;
import mx.resources.IResourceManager;
import mx.resources.ResourceManager;
import mx.styles.ISimpleStyleClient;
use namespace mx_internal;
[ResourceBundle("skins")]
/**
* The RectangularBorder class is an abstract base class for various classes
* that draw rectangular borders around UIComponents.
*
* <p>This class implements support for the <code>backgroundImage</code>,
* <code>backgroundSize</code>, and <code>backgroundAttachment</code> styles.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class RectangularBorder extends Border implements IRectangularBorder
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function RectangularBorder()
{
super();
addEventListener(Event.REMOVED, removedHandler);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* The value of the backgroundImage style may be either a string
* or a Class pointer. Either way, the value of the backgroundImage
* style is stored here, so that we can detect when it changes.
*/
private var backgroundImageStyle:Object
/**
* @private
* Original width of background image, before it is scaled.
*/
private var backgroundImageWidth:Number;
/**
* @private
* Original height of background image, before it is scaled.
*/
private var backgroundImageHeight:Number;
/**
* @private
* Used for accessing localized Error messages.
*/
private var resourceManager:IResourceManager =
ResourceManager.getInstance();
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// backgroundImage
//----------------------------------
/**
* The DisplayObject instance that contains the background image, if any.
* This object is a sibling of the RectangularBorder instance.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private var backgroundImage:DisplayObject;
/**
* Contains <code>true</code> if the RectangularBorder instance
* contains a background image.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get hasBackgroundImage():Boolean
{
return backgroundImage != null;
}
//----------------------------------
// backgroundImageBounds
//----------------------------------
/**
* @private
* Storage for backgroundImageBounds property.
*/
private var _backgroundImageBounds:Rectangle;
/**
* Rectangular area within which to draw the background image.
*
* This can be larger than the dimensions of the border
* if the parent container has scrollable content.
* If this property is null, the border can use
* the parent's size and <code>viewMetrics</code> property to determine its value.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get backgroundImageBounds():Rectangle
{
return _backgroundImageBounds;
}
/**
* @private
*/
public function set backgroundImageBounds(value:Rectangle):void
{
if (_backgroundImageBounds && value && _backgroundImageBounds.equals(value))
return;
_backgroundImageBounds = value;
invalidateDisplayList();
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
if (!parent)
return;
// If background image has changed, then load new one.
var newStyle:Object = getStyle("backgroundImage");
if (newStyle != backgroundImageStyle)
{
// Discard old background image.
removedHandler(null);
backgroundImageStyle = newStyle;
// The code below looks a lot like Loader.loadContent().
var cls:Class;
// The "as" operator checks to see if newStyle
// can be coerced to a Class.
if (newStyle && newStyle as Class)
{
// Load background image given a class pointer
cls = Class(newStyle);
initBackgroundImage(new cls());
}
else if (newStyle && newStyle is String)
{
try
{
cls = Class(getDefinitionByName(String(newStyle)));
}
catch(e:Error)
{
// ignore
}
if (cls)
{
var newStyleObj:DisplayObject = new cls();
initBackgroundImage(newStyleObj);
}
else
{
// This code is a subset of Loader.loadContent().
// Load background image from external URL.
const loader:Loader = new FlexLoader();
loader.contentLoaderInfo.addEventListener(
Event.COMPLETE, completeEventHandler);
loader.contentLoaderInfo.addEventListener(
IOErrorEvent.IO_ERROR, errorEventHandler);
loader.contentLoaderInfo.addEventListener(
ErrorEvent.ERROR, errorEventHandler);
var loaderContext:LoaderContext = new LoaderContext();
loaderContext.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
loader.load(new URLRequest(String(newStyle)), loaderContext);
}
}
else if (newStyle)
{
var message:String = resourceManager.getString(
"skins", "notLoaded", [ newStyle ]);
throw new Error(message);
}
}
if (backgroundImage)
layoutBackgroundImage();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function initBackgroundImage(image:DisplayObject):void
{
backgroundImage = image;
if (image is Loader)
{
backgroundImageWidth = Loader(image).contentLoaderInfo.width;
backgroundImageHeight = Loader(image).contentLoaderInfo.height;
}
else
{
backgroundImageWidth = backgroundImage.width;
backgroundImageHeight = backgroundImage.height;
if (image is ISimpleStyleClient)
{
// Set the image's styleName to our styleName. We
// can't set styleName to this since we aren't an
// IStyleClient.
ISimpleStyleClient(image).styleName = styleName;
}
}
// To optimize memory use, we've declared RectangularBorder to be a Shape.
// As a result, it cannot have any children.
// Make the backgroundImage a sibling of this RectangularBorder,
// which is positioned just on top of the RectangularBorder.
var childrenList:IChildList = parent is IRawChildrenContainer ?
IRawChildrenContainer(parent).rawChildren :
IChildList(parent);
const backgroundMask:Shape = new FlexShape();
backgroundMask.name = "backgroundMask";
backgroundMask.x = 0;
backgroundMask.y = 0;
childrenList.addChild(backgroundMask);
var myIndex:int = childrenList.getChildIndex(this);
childrenList.addChildAt(backgroundImage, myIndex + 1);
backgroundImage.mask = backgroundMask;
}
/**
* Layout the background image.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function layoutBackgroundImage():void
{
var p:DisplayObject = parent;
var bm:EdgeMetrics = p is IContainer ?
IContainer(p).viewMetrics :
borderMetrics;
var scrollableBk:Boolean =
getStyle("backgroundAttachment") != "fixed";
var sW:Number,
sH:Number;
if (_backgroundImageBounds)
{
sW = _backgroundImageBounds.width;
sH = _backgroundImageBounds.height;
}
else
{
sW = width - bm.left - bm.right;
sH = height - bm.top - bm.bottom;
}
// Scale according to backgroundSize.
var percentage:Number = getBackgroundSize();
var sX:Number,
sY:Number;
if (isNaN(percentage))
{
sX = 1.0;
sY = 1.0;
}
else
{
var scale:Number = percentage * 0.01;
sX = scale * sW / backgroundImageWidth;
sY = scale * sH / backgroundImageHeight;
}
backgroundImage.scaleX = sX;
backgroundImage.scaleY = sY;
// Center everything.
// Use a scrollRect to position and clip the image.
var offsetX:Number =
Math.round(0.5 * (sW - backgroundImageWidth * sX));
var offsetY:Number =
Math.round(0.5 * (sH - backgroundImageHeight * sY));
backgroundImage.x = bm.left;
backgroundImage.y = bm.top;
const backgroundMask:Shape = Shape(backgroundImage.mask);
backgroundMask.x = bm.left;
backgroundMask.y = bm.top;
// Adjust offsets by scroll positions.
if (scrollableBk && p is IContainer)
{
offsetX -= IContainer(p).horizontalScrollPosition;
offsetY -= IContainer(p).verticalScrollPosition;
}
// Adjust alpha to match backgroundAlpha
backgroundImage.alpha = getStyle("backgroundAlpha");
backgroundImage.x += offsetX;
backgroundImage.y += offsetY;
var maskWidth:Number = width - bm.left - bm.right;
var maskHeight:Number = height - bm.top - bm.bottom;
if (backgroundMask.width != maskWidth ||
backgroundMask.height != maskHeight)
{
var g:Graphics = backgroundMask.graphics;
g.clear();
g.beginFill(0xFFFFFF);
g.drawRect(0, 0, maskWidth, maskHeight);
g.endFill();
}
}
/**
* @private
*/
private function getBackgroundSize():Number
{
var percentage:Number = NaN;
var backgroundSize:Object = getStyle("backgroundSize");
if (backgroundSize && backgroundSize is String)
{
var index:int = backgroundSize.indexOf("%");
if (index != -1)
percentage = Number(backgroundSize.substr(0, index));
}
return percentage;
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function errorEventHandler(event:Event):void
{
// Ignore errors that occure during background image loading.
}
/**
* @private
*/
private function completeEventHandler(event:Event):void
{
if (!parent)
return;
var target:DisplayObject = DisplayObject(LoaderInfo(event.target).loader);
initBackgroundImage(target);
layoutBackgroundImage();
// rebroadcast for automation support
dispatchEvent(event.clone());
}
/**
* Discard old background image.
*
* @private
*/
private function removedHandler(event:Event):void
{
if (backgroundImage)
{
var childrenList:IChildList = parent is IRawChildrenContainer ?
IRawChildrenContainer(parent).rawChildren :
IChildList(parent);
childrenList.removeChild(backgroundImage.mask);
childrenList.removeChild(backgroundImage);
backgroundImage = null;
}
}
}
}