1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2005-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.Graphics; 16import flash.display.Shape; 17import flash.geom.Matrix; 18import mx.core.IInvalidating; 19import mx.core.IFlexDisplayObject; 20import mx.core.UIComponentGlobals; 21import mx.core.mx_internal; 22import mx.managers.ILayoutManagerClient; 23import mx.styles.ISimpleStyleClient; 24import mx.styles.IStyleClient; 25import mx.utils.GraphicsUtil; 26import mx.utils.NameUtil; 27import mx.core.FlexShape; 28import mx.core.IProgrammaticSkin; 29 30use namespace mx_internal; 31 32/** 33 * This class is the base class for skin elements 34 * which draw themselves programmatically. 35 */ 36public class ProgrammaticSkin extends FlexShape 37 implements IFlexDisplayObject, IInvalidating, 38 ILayoutManagerClient, ISimpleStyleClient, IProgrammaticSkin 39{ 40 include "../core/Version.as"; 41 42 //-------------------------------------------------------------------------- 43 // 44 // Class variables 45 // 46 //-------------------------------------------------------------------------- 47 48 /** 49 * @private 50 * Set by horizontalGradientMatrix() or verticalGradientMatrix(). 51 */ 52 private static var tempMatrix:Matrix = new Matrix(); 53 54 //-------------------------------------------------------------------------- 55 // 56 // Constructor 57 // 58 //-------------------------------------------------------------------------- 59 60 /** 61 * Constructor. 62 */ 63 public function ProgrammaticSkin() 64 { 65 super(); 66 67 // If nobody explicitly sets a size for this object, 68 // then set its width and height to be its measured size. 69 _width = measuredWidth; 70 _height = measuredHeight; 71 } 72 73 //-------------------------------------------------------------------------- 74 // 75 // Variables 76 // 77 //-------------------------------------------------------------------------- 78 79 /** 80 * @private 81 */ 82 private var invalidateDisplayListFlag:Boolean = false; 83 84 //-------------------------------------------------------------------------- 85 // 86 // Overridden properties 87 // 88 //-------------------------------------------------------------------------- 89 90 //---------------------------------- 91 // height 92 //---------------------------------- 93 94 /** 95 * @private 96 * Storage for the height property. 97 */ 98 private var _height:Number; 99 100 /** 101 * @private 102 */ 103 override public function get height():Number 104 { 105 return _height; 106 } 107 108 /** 109 * @private 110 */ 111 override public function set height(value:Number):void 112 { 113 _height = value; 114 115 invalidateDisplayList(); 116 } 117 118 //---------------------------------- 119 // width 120 //---------------------------------- 121 122 /** 123 * @private 124 * Storage for the width property. 125 */ 126 private var _width:Number; 127 128 /** 129 * @private 130 */ 131 override public function get width():Number 132 { 133 return _width; 134 } 135 136 /** 137 * @private 138 */ 139 override public function set width(value:Number):void 140 { 141 _width = value; 142 143 invalidateDisplayList(); 144 } 145 146 //-------------------------------------------------------------------------- 147 // 148 // Properties: IFlexDisplayObject 149 // 150 //-------------------------------------------------------------------------- 151 152 //---------------------------------- 153 // measuredHeight 154 //---------------------------------- 155 156 /** 157 * The measured height of this object. 158 * This should be overridden by subclasses to return the preferred height for 159 * the skin. 160 * 161 * @return The measured height of the object, in pixels. 162 */ 163 public function get measuredHeight():Number 164 { 165 return 0; 166 } 167 168 //---------------------------------- 169 // measuredWidth 170 //---------------------------------- 171 172 /** 173 * The measured width of this object. 174 * This should be overridden by subclasses to return the preferred width for 175 * the skin. 176 * 177 * @return The measured width of the object, in pixels. 178 */ 179 public function get measuredWidth():Number 180 { 181 return 0; 182 } 183 184 //-------------------------------------------------------------------------- 185 // 186 // Properties: ILayoutManagerClient 187 // 188 //-------------------------------------------------------------------------- 189 190 //---------------------------------- 191 // initialized 192 //---------------------------------- 193 194 /** 195 * @private 196 * Storage for the initialized property. 197 */ 198 private var _initialized:Boolean = false; 199 200 /** 201 * @copy mx.core.UIComponent#initialized 202 */ 203 public function get initialized():Boolean 204 { 205 return _initialized; 206 } 207 208 /** 209 * @private 210 */ 211 public function set initialized(value:Boolean):void 212 { 213 _initialized = value; 214 } 215 216 //---------------------------------- 217 // nestLevel 218 //---------------------------------- 219 220 /** 221 * @private 222 * Storage for the nestLevel property. 223 */ 224 private var _nestLevel:int = 0; 225 226 /** 227 * @copy mx.core.UIComponent#nestLevel 228 */ 229 public function get nestLevel():int 230 { 231 return _nestLevel; 232 } 233 234 /** 235 * @private 236 */ 237 public function set nestLevel(value:int):void 238 { 239 _nestLevel = value; 240 241 // After nestLevel is initialized, add this object to the 242 // LayoutManager's queue, so that it is drawn at least once 243 invalidateDisplayList(); 244 } 245 246 //---------------------------------- 247 // processedDescriptors 248 //---------------------------------- 249 250 /** 251 * @private 252 * Storage for the processedDescriptors property. 253 */ 254 private var _processedDescriptors:Boolean = false; 255 256 /** 257 * @copy mx.core.UIComponent#processedDescriptors 258 */ 259 public function get processedDescriptors():Boolean 260 { 261 return _processedDescriptors; 262 } 263 264 /** 265 * @private 266 */ 267 public function set processedDescriptors(value:Boolean):void 268 { 269 _processedDescriptors = value; 270 } 271 272 //---------------------------------- 273 // updateCompletePendingFlag 274 //---------------------------------- 275 276 /** 277 * @private 278 * Storage for the updateCompletePendingFlag property. 279 */ 280 private var _updateCompletePendingFlag:Boolean = true; 281 282 /** 283 * A flag that determines if an object has been through all three phases 284 * of layout validation (provided that any were required). 285 */ 286 public function get updateCompletePendingFlag():Boolean 287 { 288 return _updateCompletePendingFlag; 289 } 290 291 /** 292 * @private 293 */ 294 public function set updateCompletePendingFlag(value:Boolean):void 295 { 296 _updateCompletePendingFlag = value; 297 } 298 299 //-------------------------------------------------------------------------- 300 // 301 // Properties: ISimpleStyleClient 302 // 303 //-------------------------------------------------------------------------- 304 305 //---------------------------------- 306 // styleName 307 //---------------------------------- 308 309 /** 310 * @private 311 * Storage for the styleName property. 312 * For skins, it is always a UIComponent. 313 */ 314 private var _styleName:IStyleClient; 315 316 /** 317 * A parent component used to obtain style values. This is typically set to the 318 * component that created this skin. 319 */ 320 public function get styleName():Object 321 { 322 return _styleName; 323 } 324 325 /** 326 * @private 327 */ 328 public function set styleName(value:Object):void 329 { 330 if (_styleName != value) 331 { 332 _styleName = value as IStyleClient; 333 invalidateDisplayList(); 334 } 335 } 336 337 //-------------------------------------------------------------------------- 338 // 339 // Methods: IFlexDisplayObject 340 // 341 //-------------------------------------------------------------------------- 342 343 /** 344 * Moves this object to the specified x and y coordinates. 345 * 346 * @param x The horizontal position, in pixels. 347 * 348 * @param y The vertical position, in pixels. 349 */ 350 public function move(x:Number, y:Number):void 351 { 352 this.x = x; 353 this.y = y; 354 } 355 356 /** 357 * Sets the height and width of this object. 358 * 359 * @param newWidth The width, in pixels, of this object. 360 * 361 * @param newHeight The height, in pixels, of this object. 362 */ 363 public function setActualSize(newWidth:Number, newHeight:Number):void 364 { 365 var changed:Boolean = false; 366 367 if (_width != newWidth) 368 { 369 _width = newWidth; 370 changed = true; 371 } 372 373 if (_height != newHeight) 374 { 375 _height = newHeight; 376 changed = true; 377 } 378 379 if (changed) 380 invalidateDisplayList(); 381 } 382 383 //-------------------------------------------------------------------------- 384 // 385 // Methods: ILayoutManagerClient 386 // 387 //-------------------------------------------------------------------------- 388 389 /** 390 * This function is an empty stub so that ProgrammaticSkin 391 * can implement the ILayoutManagerClient interface. 392 * Skins do not call <code>LayoutManager.invalidateProperties()</code>, 393 * which would normally trigger a call to this method. 394 */ 395 public function validateProperties():void 396 { 397 } 398 399 /** 400 * This function is an empty stub so that ProgrammaticSkin 401 * can implement the ILayoutManagerClient interface. 402 * Skins do not call <code>LayoutManager.invalidateSize()</code>, 403 * which would normally trigger a call to this method. 404 * 405 * @param recursive Determines whether children of this skin are validated. 406 */ 407 public function validateSize(recursive:Boolean = false):void 408 { 409 } 410 411 /** 412 * This function is called by the LayoutManager 413 * when it's time for this control to draw itself. 414 * The actual drawing happens in the <code>updateDisplayList</code> 415 * function, which is called by this function. 416 */ 417 public function validateDisplayList():void 418 { 419 invalidateDisplayListFlag = false; 420 421 updateDisplayList(width, height); 422 } 423 424 //-------------------------------------------------------------------------- 425 // 426 // Methods: ISimpleStyleClient 427 // 428 //-------------------------------------------------------------------------- 429 430 /** 431 * Whenever any style changes, redraw this skin. 432 * Subclasses can override this method 433 * and perform a more specific test before calling invalidateDisplayList(). 434 * 435 * @param styleProp The name of the style property that changed, or null 436 * if all styles have changed. 437 */ 438 public function styleChanged(styleProp:String):void 439 { 440 invalidateDisplayList(); 441 } 442 443 //-------------------------------------------------------------------------- 444 // 445 // Methods: Other 446 // 447 //-------------------------------------------------------------------------- 448 449 /** 450 * @copy mx.core.UIComponent#invalidateDisplayList() 451 */ 452 public function invalidateDisplayList():void 453 { 454 // Don't try to add the object to the display list queue until we've 455 // been assigned a nestLevel, or we'll get added at the wrong place in 456 // the LayoutManager's priority queue. 457 if (!invalidateDisplayListFlag && nestLevel > 0) 458 { 459 invalidateDisplayListFlag = true; 460 UIComponentGlobals.layoutManager.invalidateDisplayList(this); 461 } 462 } 463 464 /** 465 * Programmatically draws the graphics for this skin. 466 * 467 * <p>Subclasses should override this method and include calls 468 * to methods such as <code>graphics.moveTo()</code> and 469 * <code>graphics.lineTo()</code>.</p> 470 * 471 * <p>This occurs before any scaling from sources 472 * such as user code or zoom effects. 473 * The component is unaware of the scaling that takes place later.</p> 474 * 475 * @param unscaledWidth 476 * The width, in pixels, of this object before any scaling. 477 * 478 * @param unscaledHeight 479 * The height, in pixels, of this object before any scaling. 480 */ 481 protected function updateDisplayList(unscaledWidth:Number, 482 unscaledHeight:Number):void 483 { 484 } 485 486 /** 487 * @inheritDoc 488 */ 489 public function invalidateSize():void 490 { 491 } 492 493 /** 494 * @inheritDoc 495 */ 496 public function invalidateProperties():void 497 { 498 } 499 500 /** 501 * Validate and update the properties and layout of this object 502 * and redraw it, if necessary. 503 */ 504 public function validateNow():void 505 { 506 // Since we don't have commit/measure/layout phases, 507 // all we need to do here is the draw phase 508 if (invalidateDisplayListFlag) 509 validateDisplayList(); 510 } 511 512 /** 513 * Returns the value of the specified style property. 514 * 515 * @param styleProp Name of the style property. 516 * 517 * @return The style value. This can be any type of object that style properties can be, such as 518 * int, Number, String, etc. 519 */ 520 public function getStyle(styleProp:String):* 521 { 522 return _styleName ? _styleName.getStyle(styleProp) : null; 523 } 524 525 /** 526 * Utility function to create a horizontal gradient matrix. 527 * 528 * @param x The left edge of the gradient. 529 * 530 * @param y The top edge of the gradient. 531 * 532 * @param width The width of the gradient. 533 * 534 * @param height The height of the gradient. 535 * 536 * @return The horizontal gradient matrix. This is a temporary 537 * object that should only be used for a single subsequent call 538 * to the <code>drawRoundRect()</code> method. 539 */ 540 protected function horizontalGradientMatrix(x:Number, y:Number, 541 width:Number, 542 height:Number):Matrix 543 { 544 return rotatedGradientMatrix(x, y, width, height, 0); 545 } 546 547 /** 548 * Utility function to create a vertical gradient matrix. 549 * 550 * @param x The left edge of the gradient. 551 * 552 * @param y The top edge of the gradient. 553 * 554 * @param width The width of the gradient. 555 * 556 * @param height The height of the gradient. 557 * 558 * @return The horizontal gradient matrix. This is a temporary 559 * object that should only be used for a single subsequent call 560 * to the <code>drawRoundRect()</code> method. 561 */ 562 protected function verticalGradientMatrix(x:Number, y:Number, 563 width:Number, 564 height:Number):Matrix 565 { 566 return rotatedGradientMatrix(x, y, width, height, 90); 567 } 568 569 /** 570 * Utility function to create a rotated gradient matrix. 571 * 572 * @param x The left edge of the gradient. 573 * 574 * @param y The top edge of the gradient. 575 * 576 * @param width The width of the gradient. 577 * 578 * @param height The height of the gradient. 579 * 580 * @param rotation The amount to rotate, in degrees. 581 * 582 * @return The horizontal gradient matrix. This is a temporary 583 * object that should only be used for a single subsequent call 584 * to the <code>drawRoundRect()</code> method. 585 */ 586 protected function rotatedGradientMatrix(x:Number, y:Number, 587 width:Number, 588 height:Number, 589 rotation:Number):Matrix 590 { 591 tempMatrix.createGradientBox(width, height, 592 rotation * Math.PI / 180, x, y); 593 return tempMatrix; 594 } 595 596 /** 597 * Programatically draws a rectangle into this skin's Graphics object. 598 * 599 * <p>The rectangle can have rounded corners. 600 * Its edges are stroked with the current line style 601 * of the Graphics object. 602 * It can have a solid color fill, a gradient fill, or no fill. 603 * A solid fill can have an alpha transparency. 604 * A gradient fill can be linear or radial. You can specify 605 * up to 15 colors and alpha values at specified points along 606 * the gradient, and you can specify a rotation angle 607 * or transformation matrix for the gradient. 608 * Finally, the rectangle can have a rounded rectangular hole 609 * carved out of it.</p> 610 * 611 * <p>This versatile rectangle-drawing routine is used by many skins. 612 * It calls the <code>drawRect()</code> or 613 * <code>drawRoundRect()</code> 614 * methods (in the flash.display.Graphics class) to draw into this 615 * skin's Graphics object.</p> 616 * 617 * @param x Horizontal position of upper-left corner 618 * of rectangle within this skin. 619 * 620 * @param y Vertical position of upper-left corner 621 * of rectangle within this skin. 622 * 623 * @param width Width of rectangle, in pixels. 624 * 625 * @param height Height of rectangle, in pixels. 626 * 627 * @param cornerRadius Corner radius/radii of rectangle. 628 * Can be <code>null</code>, a Number, or an Object. 629 * If it is <code>null</code>, it specifies that the corners should be square 630 * rather than rounded. 631 * If it is a Number, it specifies the same radius, in pixels, 632 * for all four corners. 633 * If it is an Object, it should have properties named 634 * <code>tl</code>, <code>tr</code>, <code>bl</code>, and 635 * <code>br</code>, whose values are Numbers specifying 636 * the radius, in pixels, for the top left, top right, 637 * bottom left, and bottom right corners. 638 * For example, you can pass a plain Object such as 639 * <code>{ tl: 5, tr: 5, bl: 0, br: 0 }</code>. 640 * The default value is null (square corners). 641 * 642 * @param color The RGB color(s) for the fill. 643 * Can be <code>null</code>, a uint, or an Array. 644 * If it is <code>null</code>, the rectangle not filled. 645 * If it is a uint, it specifies an RGB fill color. 646 * For example, pass <code>0xFF0000</code> to fill with red. 647 * If it is an Array, it should contain uints 648 * specifying the gradient colors. 649 * For example, pass <code>[ 0xFF0000, 0xFFFF00, 0x0000FF ]</code> 650 * to fill with a red-to-yellow-to-blue gradient. 651 * You can specify up to 15 colors in the gradient. 652 * The default value is null (no fill). 653 * 654 * @param alpha Alpha value(s) for the fill. 655 * Can be null, a Number, or an Array. 656 * This argument is ignored if <code>color</code> is null. 657 * If <code>color</code> is a uint specifying an RGB fill color, 658 * then <code>alpha</code> should be a Number specifying 659 * the transparency of the fill, where 0.0 is completely transparent 660 * and 1.0 is completely opaque. 661 * You can also pass null instead of 1.0 in this case 662 * to specify complete opaqueness. 663 * If <code>color</code> is an Array specifying gradient colors, 664 * then <code>alpha</code> should be an Array of Numbers, of the 665 * same length, that specifies the corresponding alpha values 666 * for the gradient. 667 * In this case, the default value is <code>null</code> (completely opaque). 668 * 669 * @param gradientMatrix Matrix object used for the gradient fill. 670 * The utility methods <code>horizontalGradientMatrix()</code>, 671 * <code>verticalGradientMatrix()</code>, and 672 * <code>rotatedGradientMatrix()</code> can be used to create the value for 673 * this parameter. 674 * 675 * @param gradientType Type of gradient fill. The possible values are 676 * <code>GradientType.LINEAR</code> or <code>GradientType.RADIAL</code>. 677 * (The GradientType class is in the package flash.display.) 678 * 679 * @param gradientRatios (optional default [0,255]) 680 * Specifies the distribution of colors. The number of entries must match 681 * the number of colors defined in the <code>color</code> parameter. 682 * Each value defines the percentage of the width where the color is 683 * sampled at 100%. The value 0 represents the left-hand position in 684 * the gradient box, and 255 represents the right-hand position in the 685 * gradient box. 686 * 687 * @param hole (optional) A rounded rectangular hole 688 * that should be carved out of the middle 689 * of the otherwise solid rounded rectangle 690 * { x: #, y: #, w: #, h: #, r: # or { br: #, bl: #, tl: #, tr: # } } 691 * 692 * @see flash.display.Graphics#beginGradientFill() 693 */ 694 protected function drawRoundRect( 695 x:Number, y:Number, width:Number, height:Number, 696 cornerRadius:Object = null, 697 color:Object = null, 698 alpha:Object = null, 699 gradientMatrix:Matrix = null, 700 gradientType:String = "linear", 701 gradientRatios:Array /* of Number */ = null, 702 hole:Object = null):void 703 { 704 var g:Graphics = graphics; 705 706 // Quick exit if weight or height is zero. 707 // This happens when scaling a component to a very small value, 708 // which then gets rounded to 0. 709 if (width == 0 || height == 0) 710 return; 711 712 // If color is an object then allow for complex fills. 713 if (color !== null) 714 { 715 if (color is uint) 716 { 717 g.beginFill(uint(color), Number(alpha)); 718 } 719 else if (color is Array) 720 { 721 var alphas:Array = alpha is Array ? 722 alpha as Array : 723 [ alpha, alpha ]; 724 725 if (!gradientRatios) 726 gradientRatios = [ 0, 0xFF ]; 727 728 g.beginGradientFill(gradientType, 729 color as Array, alphas, 730 gradientRatios, gradientMatrix); 731 } 732 } 733 734 var ellipseSize:Number; 735 736 // Stroke the rectangle. 737 if (!cornerRadius) 738 { 739 g.drawRect(x, y, width, height); 740 } 741 else if (cornerRadius is Number) 742 { 743 ellipseSize = Number(cornerRadius) * 2; 744 g.drawRoundRect(x, y, width, height, 745 ellipseSize, ellipseSize); 746 } 747 else 748 { 749 GraphicsUtil.drawRoundRectComplex(g, 750 x, y, width, height, 751 cornerRadius.tl, cornerRadius.tr, 752 cornerRadius.bl, cornerRadius.br); 753 } 754 755 // Carve a rectangular hole out of the middle of the rounded rect. 756 if (hole) 757 { 758 var holeR:Object = hole.r; 759 if (holeR is Number) 760 { 761 ellipseSize = Number(holeR) * 2; 762 g.drawRoundRect(hole.x, hole.y, hole.w, hole.h, 763 ellipseSize, ellipseSize); 764 } 765 else 766 { 767 GraphicsUtil.drawRoundRectComplex(g, 768 hole.x, hole.y, hole.w, hole.h, 769 holeR.tl, holeR.tr, holeR.bl, holeR.br); 770 } 771 } 772 773 if (color !== null) 774 g.endFill(); 775 } 776} 777 778} 779