1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2003-2007 Adobe Systems Incorporated 5// All Rights Reserved. 6// 7// NOTICE: Adobe permits you to use, modify, and distribute this file 8// in accordance with the terms of the license agreement accompanying it. 9// 10//////////////////////////////////////////////////////////////////////////////// 11 12package mx.skins 13{ 14 15import flash.display.DisplayObject; 16import flash.display.DisplayObjectContainer; 17import flash.display.Graphics; 18import flash.display.Loader; 19import flash.display.LoaderInfo; 20import flash.display.Shape; 21import flash.events.ErrorEvent; 22import flash.events.Event; 23import flash.events.IOErrorEvent; 24import flash.geom.Rectangle; 25import flash.net.URLRequest; 26import flash.system.ApplicationDomain; 27import flash.system.LoaderContext; 28import flash.utils.getDefinitionByName; 29import mx.core.EdgeMetrics; 30import mx.core.FlexLoader; 31import mx.core.FlexShape; 32import mx.core.IChildList; 33import mx.core.IContainer; 34import mx.core.IRawChildrenContainer; 35import mx.core.mx_internal; 36import mx.core.IRectangularBorder; 37import mx.managers.ISystemManager; 38import mx.managers.SystemManager; 39import mx.resources.IResourceManager; 40import mx.resources.ResourceManager; 41import mx.styles.ISimpleStyleClient; 42 43use namespace mx_internal; 44 45[ResourceBundle("skins")] 46 47/** 48 * The RectangularBorder class is an abstract base class for various classes 49 * that draw rectangular borders around UIComponents. 50 * 51 * <p>This class implements support for the <code>backgroundImage</code>, 52 * <code>backgroundSize</code>, and <code>backgroundAttachment</code> styles.</p> 53 */ 54public class RectangularBorder extends Border implements IRectangularBorder 55{ 56 include "../core/Version.as"; 57 58 //-------------------------------------------------------------------------- 59 // 60 // Constructor 61 // 62 //-------------------------------------------------------------------------- 63 64 /** 65 * Constructor. 66 */ 67 public function RectangularBorder() 68 { 69 super(); 70 71 addEventListener(Event.REMOVED, removedHandler); 72 } 73 74 //-------------------------------------------------------------------------- 75 // 76 // Variables 77 // 78 //-------------------------------------------------------------------------- 79 80 /** 81 * @private 82 * The value of the backgroundImage style may be either a string 83 * or a Class pointer. Either way, the value of the backgroundImage 84 * style is stored here, so that we can detect when it changes. 85 */ 86 private var backgroundImageStyle:Object 87 88 /** 89 * @private 90 * Original width of background image, before it is scaled. 91 */ 92 private var backgroundImageWidth:Number; 93 94 /** 95 * @private 96 * Original height of background image, before it is scaled. 97 */ 98 private var backgroundImageHeight:Number; 99 100 /** 101 * @private 102 * Used for accessing localized Error messages. 103 */ 104 private var resourceManager:IResourceManager = 105 ResourceManager.getInstance(); 106 107 //-------------------------------------------------------------------------- 108 // 109 // Properties 110 // 111 //-------------------------------------------------------------------------- 112 113 //---------------------------------- 114 // backgroundImage 115 //---------------------------------- 116 117 /** 118 * The DisplayObject instance that contains the background image, if any. 119 * This object is a sibling of the RectangularBorder instance. 120 */ 121 private var backgroundImage:DisplayObject; 122 123 /** 124 * Contains <code>true</code> if the RectangularBorder instance 125 * contains a background image. 126 */ 127 public function get hasBackgroundImage():Boolean 128 { 129 return backgroundImage != null; 130 } 131 132 //---------------------------------- 133 // backgroundImageBounds 134 //---------------------------------- 135 136 /** 137 * @private 138 * Storage for backgroundImageBounds property. 139 */ 140 private var _backgroundImageBounds:Rectangle; 141 142 /** 143 * Rectangular area within which to draw the background image. 144 * 145 * This can be larger than the dimensions of the border 146 * if the parent container has scrollable content. 147 * If this property is null, the border can use 148 * the parent's size and <code>viewMetrics</code> property to determine its value. 149 */ 150 public function get backgroundImageBounds():Rectangle 151 { 152 return _backgroundImageBounds; 153 } 154 155 /** 156 * @private 157 */ 158 public function set backgroundImageBounds(value:Rectangle):void 159 { 160 if (_backgroundImageBounds && value && _backgroundImageBounds.equals(value)) 161 return; 162 163 _backgroundImageBounds = value; 164 165 invalidateDisplayList(); 166 } 167 168 //-------------------------------------------------------------------------- 169 // 170 // Overridden methods 171 // 172 //-------------------------------------------------------------------------- 173 174 /** 175 * @private 176 */ 177 override protected function updateDisplayList(unscaledWidth:Number, 178 unscaledHeight:Number):void 179 { 180 if (!parent) 181 return; 182 183 // If background image has changed, then load new one. 184 var newStyle:Object = getStyle("backgroundImage"); 185 if (newStyle != backgroundImageStyle) 186 { 187 // Discard old background image. 188 removedHandler(null); 189 190 backgroundImageStyle = newStyle; 191 192 // The code below looks a lot like Loader.loadContent(). 193 var cls:Class; 194 195 // The "as" operator checks to see if newStyle 196 // can be coerced to a Class. 197 if (newStyle && newStyle as Class) 198 { 199 // Load background image given a class pointer 200 cls = Class(newStyle); 201 initBackgroundImage(new cls()); 202 } 203 else if (newStyle && newStyle is String) 204 { 205 try 206 { 207 cls = Class(getDefinitionByName(String(newStyle))); 208 } 209 catch(e:Error) 210 { 211 // ignore 212 } 213 214 if (cls) 215 { 216 var newStyleObj:DisplayObject = new cls(); 217 initBackgroundImage(newStyleObj); 218 } 219 else 220 { 221 // This code is a subset of Loader.loadContent(). 222 223 // Load background image from external URL. 224 const loader:Loader = new FlexLoader(); 225 loader.contentLoaderInfo.addEventListener( 226 Event.COMPLETE, completeEventHandler); 227 loader.contentLoaderInfo.addEventListener( 228 IOErrorEvent.IO_ERROR, errorEventHandler); 229 loader.contentLoaderInfo.addEventListener( 230 ErrorEvent.ERROR, errorEventHandler); 231 var loaderContext:LoaderContext = new LoaderContext(); 232 loaderContext.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain); 233 loader.load(new URLRequest(String(newStyle)), loaderContext); 234 } 235 } 236 else if (newStyle) 237 { 238 var message:String = resourceManager.getString( 239 "skins", "notLoaded", [ newStyle ]); 240 throw new Error(message); 241 } 242 } 243 244 if (backgroundImage) 245 layoutBackgroundImage(); 246 } 247 248 //-------------------------------------------------------------------------- 249 // 250 // Methods 251 // 252 //-------------------------------------------------------------------------- 253 254 /** 255 * @private 256 */ 257 private function initBackgroundImage(image:DisplayObject):void 258 { 259 backgroundImage = image; 260 261 if (image is Loader) 262 { 263 backgroundImageWidth = Loader(image).contentLoaderInfo.width; 264 backgroundImageHeight = Loader(image).contentLoaderInfo.height; 265 } 266 else 267 { 268 backgroundImageWidth = backgroundImage.width; 269 backgroundImageHeight = backgroundImage.height; 270 271 if (image is ISimpleStyleClient) 272 { 273 // Set the image's styleName to our styleName. We 274 // can't set styleName to this since we aren't an 275 // IStyleClient. 276 ISimpleStyleClient(image).styleName = styleName; 277 } 278 } 279 // To optimize memory use, we've declared RectangularBorder to be a Shape. 280 // As a result, it cannot have any children. 281 // Make the backgroundImage a sibling of this RectangularBorder, 282 // which is positioned just on top of the RectangularBorder. 283 var childrenList:IChildList = parent is IRawChildrenContainer ? 284 IRawChildrenContainer(parent).rawChildren : 285 IChildList(parent); 286 287 const backgroundMask:Shape = new FlexShape(); 288 backgroundMask.name = "backgroundMask"; 289 backgroundMask.x = 0; 290 backgroundMask.y = 0; 291 childrenList.addChild(backgroundMask); 292 293 var myIndex:int = childrenList.getChildIndex(this); 294 childrenList.addChildAt(backgroundImage, myIndex + 1); 295 296 backgroundImage.mask = backgroundMask; 297 } 298 299 /** 300 * Layout the background image. 301 */ 302 public function layoutBackgroundImage():void 303 { 304 var p:DisplayObject = parent; 305 306 var bm:EdgeMetrics = p is IContainer ? 307 IContainer(p).viewMetrics : 308 borderMetrics; 309 310 var scrollableBk:Boolean = 311 getStyle("backgroundAttachment") != "fixed"; 312 313 var sW:Number, 314 sH:Number; 315 if (_backgroundImageBounds) 316 { 317 sW = _backgroundImageBounds.width; 318 sH = _backgroundImageBounds.height; 319 } 320 else 321 { 322 sW = width - bm.left - bm.right; 323 sH = height - bm.top - bm.bottom; 324 } 325 326 // Scale according to backgroundSize. 327 var percentage:Number = getBackgroundSize(); 328 329 var sX:Number, 330 sY:Number; 331 if (isNaN(percentage)) 332 { 333 sX = 1.0; 334 sY = 1.0; 335 } 336 else 337 { 338 var scale:Number = percentage * 0.01; 339 sX = scale * sW / backgroundImageWidth; 340 sY = scale * sH / backgroundImageHeight; 341 } 342 backgroundImage.scaleX = sX; 343 backgroundImage.scaleY = sY; 344 345 // Center everything. 346 // Use a scrollRect to position and clip the image. 347 var offsetX:Number = 348 Math.round(0.5 * (sW - backgroundImageWidth * sX)); 349 var offsetY:Number = 350 Math.round(0.5 * (sH - backgroundImageHeight * sY)); 351 352 backgroundImage.x = bm.left; 353 backgroundImage.y = bm.top; 354 355 const backgroundMask:Shape = Shape(backgroundImage.mask); 356 backgroundMask.x = bm.left; 357 backgroundMask.y = bm.top; 358 359 // Adjust offsets by scroll positions. 360 if (scrollableBk && p is IContainer) 361 { 362 offsetX -= IContainer(p).horizontalScrollPosition; 363 offsetY -= IContainer(p).verticalScrollPosition; 364 } 365 366 // Adjust alpha to match backgroundAlpha 367 backgroundImage.alpha = getStyle("backgroundAlpha"); 368 369 backgroundImage.x += offsetX; 370 backgroundImage.y += offsetY; 371 372 var maskWidth:Number = width - bm.left - bm.right; 373 var maskHeight:Number = height - bm.top - bm.bottom; 374 if (backgroundMask.width != maskWidth || 375 backgroundMask.height != maskHeight) 376 { 377 var g:Graphics = backgroundMask.graphics; 378 g.clear(); 379 g.beginFill(0xFFFFFF); 380 g.drawRect(0, 0, maskWidth, maskHeight); 381 g.endFill(); 382 } 383 } 384 385 /** 386 * @private 387 */ 388 private function getBackgroundSize():Number 389 { 390 var percentage:Number = NaN; 391 var backgroundSize:Object = getStyle("backgroundSize"); 392 393 if (backgroundSize && backgroundSize is String) 394 { 395 var index:int = backgroundSize.indexOf("%"); 396 if (index != -1) 397 percentage = Number(backgroundSize.substr(0, index)); 398 } 399 400 return percentage; 401 } 402 403 //-------------------------------------------------------------------------- 404 // 405 // Event handlers 406 // 407 //-------------------------------------------------------------------------- 408 409 /** 410 * @private 411 */ 412 private function errorEventHandler(event:Event):void 413 { 414 // Ignore errors that occure during background image loading. 415 } 416 417 /** 418 * @private 419 */ 420 private function completeEventHandler(event:Event):void 421 { 422 if (!parent) 423 return; 424 425 var target:DisplayObject = DisplayObject(LoaderInfo(event.target).loader); 426 initBackgroundImage(target); 427 layoutBackgroundImage(); 428 // rebroadcast for automation support 429 dispatchEvent(event.clone()); 430 } 431 432 /** 433 * Discard old background image. 434 * 435 * @private 436 */ 437 private function removedHandler(event:Event):void 438 { 439 if (backgroundImage) 440 { 441 var childrenList:IChildList = parent is IRawChildrenContainer ? 442 IRawChildrenContainer(parent).rawChildren : 443 IChildList(parent); 444 445 childrenList.removeChild(backgroundImage.mask); 446 childrenList.removeChild(backgroundImage); 447 backgroundImage = null; 448 } 449 } 450} 451 452} 453