//////////////////////////////////////////////////////////////////////////////// // // 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.Graphics; import flash.display.Shape; import flash.geom.Matrix; import flash.utils.getDefinitionByName; import mx.core.FlexShape; import mx.core.IFlexModule; import mx.core.IFlexModuleFactory; import mx.core.IInvalidating; import mx.core.IFlexDisplayObject; import mx.core.IProgrammaticSkin; import mx.core.mx_internal; import mx.core.UIComponentGlobals; import mx.managers.ILayoutManagerClient; import mx.styles.ISimpleStyleClient; import mx.styles.IStyleClient; import mx.styles.IStyleManager2; import mx.styles.StyleManager; import mx.utils.GraphicsUtil; import mx.utils.NameUtil; use namespace mx_internal; /** * This class is the base class for skin elements * which draw themselves programmatically. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class ProgrammaticSkin extends FlexShape implements IFlexDisplayObject, IInvalidating, ILayoutManagerClient, ISimpleStyleClient, IProgrammaticSkin { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class variables // //-------------------------------------------------------------------------- /** * @private * Set by horizontalGradientMatrix() or verticalGradientMatrix(). */ private static var tempMatrix:Matrix = new Matrix(); /** * @private * Soft link to UIComponent. Cache the class for performance. */ private static var uiComponentClass:Class; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function ProgrammaticSkin() { super(); // If nobody explicitly sets a size for this object, // then set its width and height to be its measured size. _width = measuredWidth; _height = measuredHeight; } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private */ private var invalidateDisplayListFlag:Boolean = false; //-------------------------------------------------------------------------- // // Overridden properties // //-------------------------------------------------------------------------- //---------------------------------- // height //---------------------------------- /** * @private * Storage for the height property. */ private var _height:Number; /** * @private */ override public function get height():Number { return _height; } /** * @private */ override public function set height(value:Number):void { _height = value; invalidateDisplayList(); } //---------------------------------- // width //---------------------------------- /** * @private * Storage for the width property. */ private var _width:Number; /** * @private */ override public function get width():Number { return _width; } /** * @private */ override public function set width(value:Number):void { _width = value; invalidateDisplayList(); } //-------------------------------------------------------------------------- // // Properties: IFlexDisplayObject // //-------------------------------------------------------------------------- //---------------------------------- // measuredHeight //---------------------------------- /** * The measured height of this object. * This should be overridden by subclasses to return the preferred height for * the skin. * * @return The measured height of the object, in pixels. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get measuredHeight():Number { return 0; } //---------------------------------- // measuredWidth //---------------------------------- /** * The measured width of this object. * This should be overridden by subclasses to return the preferred width for * the skin. * * @return The measured width of the object, in pixels. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get measuredWidth():Number { return 0; } //-------------------------------------------------------------------------- // // Properties: ILayoutManagerClient // //-------------------------------------------------------------------------- //---------------------------------- // initialized //---------------------------------- /** * @private * Storage for the initialized property. */ private var _initialized:Boolean = false; /** * @copy mx.core.UIComponent#initialized * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get initialized():Boolean { return _initialized; } /** * @private */ public function set initialized(value:Boolean):void { _initialized = value; } //---------------------------------- // nestLevel //---------------------------------- /** * @private * Storage for the nestLevel property. */ private var _nestLevel:int = 0; /** * @copy mx.core.UIComponent#nestLevel * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get nestLevel():int { return _nestLevel; } /** * @private */ public function set nestLevel(value:int):void { _nestLevel = value; // After nestLevel is initialized, add this object to the // LayoutManager's queue, so that it is drawn at least once invalidateDisplayList(); } //---------------------------------- // processedDescriptors //---------------------------------- /** * @private * Storage for the processedDescriptors property. */ private var _processedDescriptors:Boolean = false; /** * @copy mx.core.UIComponent#processedDescriptors * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get processedDescriptors():Boolean { return _processedDescriptors; } /** * @private */ public function set processedDescriptors(value:Boolean):void { _processedDescriptors = value; } //---------------------------------- // updateCompletePendingFlag //---------------------------------- /** * @private * Storage for the updateCompletePendingFlag property. */ private var _updateCompletePendingFlag:Boolean = true; /** * A flag that determines if an object has been through all three phases * of layout validation (provided that any were required). * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get updateCompletePendingFlag():Boolean { return _updateCompletePendingFlag; } /** * @private */ public function set updateCompletePendingFlag(value:Boolean):void { _updateCompletePendingFlag = value; } //-------------------------------------------------------------------------- // // Properties: ISimpleStyleClient // //-------------------------------------------------------------------------- //---------------------------------- // styleName //---------------------------------- /** * @private * Storage for the styleName property. * For skins, it is always a UIComponent. */ private var _styleName:IStyleClient; /** * A parent component used to obtain style values. This is typically set to the * component that created this skin. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get styleName():Object { return _styleName; } /** * @private */ public function set styleName(value:Object):void { if (_styleName != value) { _styleName = value as IStyleClient; invalidateDisplayList(); } } //-------------------------------------------------------------------------- // // Methods: IFlexDisplayObject // //-------------------------------------------------------------------------- /** * Moves this object to the specified x and y coordinates. * * @param x The horizontal position, in pixels. * * @param y The vertical position, in pixels. * * @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; } /** * Sets the height and width of this object. * * @param newWidth The width, in pixels, of this object. * * @param newHeight The height, in pixels, of this object. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function setActualSize(newWidth:Number, newHeight:Number):void { var changed:Boolean = false; if (_width != newWidth) { _width = newWidth; changed = true; } if (_height != newHeight) { _height = newHeight; changed = true; } if (changed) invalidateDisplayList(); } //-------------------------------------------------------------------------- // // Methods: ILayoutManagerClient // //-------------------------------------------------------------------------- /** * This function is an empty stub so that ProgrammaticSkin * can implement the ILayoutManagerClient interface. * Skins do not call LayoutManager.invalidateProperties(), * which would normally trigger a call to this method. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function validateProperties():void { } /** * This function is an empty stub so that ProgrammaticSkin * can implement the ILayoutManagerClient interface. * Skins do not call LayoutManager.invalidateSize(), * which would normally trigger a call to this method. * * @param recursive Determines whether children of this skin are validated. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function validateSize(recursive:Boolean = false):void { } /** * This function is called by the LayoutManager * when it's time for this control to draw itself. * The actual drawing happens in the updateDisplayList * function, which is called by this function. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function validateDisplayList():void { invalidateDisplayListFlag = false; updateDisplayList(width, height); } //-------------------------------------------------------------------------- // // Methods: ISimpleStyleClient // //-------------------------------------------------------------------------- /** * Whenever any style changes, redraw this skin. * Subclasses can override this method * and perform a more specific test before calling invalidateDisplayList(). * * @param styleProp The name of the style property that changed, or null * if all styles have changed. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function styleChanged(styleProp:String):void { invalidateDisplayList(); } //-------------------------------------------------------------------------- // // Methods: Other // //-------------------------------------------------------------------------- /** * @copy mx.core.UIComponent#invalidateDisplayList() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function invalidateDisplayList():void { // Don't try to add the object to the display list queue until we've // been assigned a nestLevel, or we'll get added at the wrong place in // the LayoutManager's priority queue. if (!invalidateDisplayListFlag && nestLevel > 0) { invalidateDisplayListFlag = true; UIComponentGlobals.layoutManager.invalidateDisplayList(this); } } /** * Programmatically draws the graphics for this skin. * *

Subclasses should override this method and include calls * to methods such as graphics.moveTo() and * graphics.lineTo().

* *

This occurs before any scaling from sources * such as user code or zoom effects. * The component is unaware of the scaling that takes place later.

* * @param unscaledWidth * The width, in pixels, of this object before any scaling. * * @param unscaledHeight * The height, in pixels, of this object before any scaling. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { } /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function invalidateSize():void { } /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function invalidateProperties():void { } /** * Validate and update the properties and layout of this object * and redraw it, if necessary. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function validateNow():void { // Since we don't have commit/measure/layout phases, // all we need to do here is the draw phase if (invalidateDisplayListFlag) validateDisplayList(); } /** * Returns the value of the specified style property. * * @param styleProp Name of the style property. * * @return The style value. This can be any type of object that style properties can be, such as * int, Number, String, etc. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function getStyle(styleProp:String):* { return _styleName ? _styleName.getStyle(styleProp) : null; } /** * @private * Get the style manager for this skin. The style manager is * based on the UIComponent that owns the skin. If this skin does not * have a styleName then use the top-level style manager. */ protected function get styleManager():IStyleManager2 { if (uiComponentClass == null) uiComponentClass = Class(getDefinitionByName("mx.core.UIComponent")); if (styleName is uiComponentClass) return styleName.styleManager; else return StyleManager.getStyleManager(null); } /** * Utility function to create a horizontal gradient matrix. * * @param x The left edge of the gradient. * * @param y The top edge of the gradient. * * @param width The width of the gradient. * * @param height The height of the gradient. * * @return The horizontal gradient matrix. This is a temporary * object that should only be used for a single subsequent call * to the drawRoundRect() method. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function horizontalGradientMatrix(x:Number, y:Number, width:Number, height:Number):Matrix { return rotatedGradientMatrix(x, y, width, height, 0); } /** * Utility function to create a vertical gradient matrix. * * @param x The left edge of the gradient. * * @param y The top edge of the gradient. * * @param width The width of the gradient. * * @param height The height of the gradient. * * @return The horizontal gradient matrix. This is a temporary * object that should only be used for a single subsequent call * to the drawRoundRect() method. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function verticalGradientMatrix(x:Number, y:Number, width:Number, height:Number):Matrix { return rotatedGradientMatrix(x, y, width, height, 90); } /** * Utility function to create a rotated gradient matrix. * * @param x The left edge of the gradient. * * @param y The top edge of the gradient. * * @param width The width of the gradient. * * @param height The height of the gradient. * * @param rotation The amount to rotate, in degrees. * * @return The horizontal gradient matrix. This is a temporary * object that should only be used for a single subsequent call * to the drawRoundRect() method. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function rotatedGradientMatrix(x:Number, y:Number, width:Number, height:Number, rotation:Number):Matrix { tempMatrix.createGradientBox(width, height, rotation * Math.PI / 180, x, y); return tempMatrix; } /** * Programatically draws a rectangle into this skin's Graphics object. * *

The rectangle can have rounded corners. * Its edges are stroked with the current line style * of the Graphics object. * It can have a solid color fill, a gradient fill, or no fill. * A solid fill can have an alpha transparency. * A gradient fill can be linear or radial. You can specify * up to 15 colors and alpha values at specified points along * the gradient, and you can specify a rotation angle * or transformation matrix for the gradient. * Finally, the rectangle can have a rounded rectangular hole * carved out of it.

* *

This versatile rectangle-drawing routine is used by many skins. * It calls the drawRect() or * drawRoundRect() * methods (in the flash.display.Graphics class) to draw into this * skin's Graphics object.

* * @param x Horizontal position of upper-left corner * of rectangle within this skin. * * @param y Vertical position of upper-left corner * of rectangle within this skin. * * @param width Width of rectangle, in pixels. * * @param height Height of rectangle, in pixels. * * @param cornerRadius Corner radius/radii of rectangle. * Can be null, a Number, or an Object. * If it is null, it specifies that the corners should be square * rather than rounded. * If it is a Number, it specifies the same radius, in pixels, * for all four corners. * If it is an Object, it should have properties named * tl, tr, bl, and * br, whose values are Numbers specifying * the radius, in pixels, for the top left, top right, * bottom left, and bottom right corners. * For example, you can pass a plain Object such as * { tl: 5, tr: 5, bl: 0, br: 0 }. * The default value is null (square corners). * * @param color The RGB color(s) for the fill. * Can be null, a uint, or an Array. * If it is null, the rectangle not filled. * If it is a uint, it specifies an RGB fill color. * For example, pass 0xFF0000 to fill with red. * If it is an Array, it should contain uints * specifying the gradient colors. * For example, pass [ 0xFF0000, 0xFFFF00, 0x0000FF ] * to fill with a red-to-yellow-to-blue gradient. * You can specify up to 15 colors in the gradient. * The default value is null (no fill). * * @param alpha Alpha value(s) for the fill. * Can be null, a Number, or an Array. * This argument is ignored if color is null. * If color is a uint specifying an RGB fill color, * then alpha should be a Number specifying * the transparency of the fill, where 0.0 is completely transparent * and 1.0 is completely opaque. * You can also pass null instead of 1.0 in this case * to specify complete opaqueness. * If color is an Array specifying gradient colors, * then alpha should be an Array of Numbers, of the * same length, that specifies the corresponding alpha values * for the gradient. * In this case, the default value is null (completely opaque). * * @param gradientMatrix Matrix object used for the gradient fill. * The utility methods horizontalGradientMatrix(), * verticalGradientMatrix(), and * rotatedGradientMatrix() can be used to create the value for * this parameter. * * @param gradientType Type of gradient fill. The possible values are * GradientType.LINEAR or GradientType.RADIAL. * (The GradientType class is in the package flash.display.) * * @param gradientRatios (optional default [0,255]) * Specifies the distribution of colors. The number of entries must match * the number of colors defined in the color parameter. * Each value defines the percentage of the width where the color is * sampled at 100%. The value 0 represents the left-hand position in * the gradient box, and 255 represents the right-hand position in the * gradient box. * * @param hole (optional) A rounded rectangular hole * that should be carved out of the middle * of the otherwise solid rounded rectangle * { x: #, y: #, w: #, h: #, r: # or { br: #, bl: #, tl: #, tr: # } } * * @see flash.display.Graphics#beginGradientFill() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function drawRoundRect( x:Number, y:Number, width:Number, height:Number, cornerRadius:Object = null, color:Object = null, alpha:Object = null, gradientMatrix:Matrix = null, gradientType:String = "linear", gradientRatios:Array /* of Number */ = null, hole:Object = null):void { var g:Graphics = graphics; // Quick exit if weight or height is zero. // This happens when scaling a component to a very small value, // which then gets rounded to 0. if (width == 0 || height == 0) return; // If color is an object then allow for complex fills. if (color !== null) { if (color is uint) { g.beginFill(uint(color), Number(alpha)); } else if (color is Array) { var alphas:Array = alpha is Array ? alpha as Array : [ alpha, alpha ]; if (!gradientRatios) gradientRatios = [ 0, 0xFF ]; g.beginGradientFill(gradientType, color as Array, alphas, gradientRatios, gradientMatrix); } } var ellipseSize:Number; // Stroke the rectangle. if (!cornerRadius) { g.drawRect(x, y, width, height); } else if (cornerRadius is Number) { ellipseSize = Number(cornerRadius) * 2; g.drawRoundRect(x, y, width, height, ellipseSize, ellipseSize); } else { GraphicsUtil.drawRoundRectComplex(g, x, y, width, height, cornerRadius.tl, cornerRadius.tr, cornerRadius.bl, cornerRadius.br); } // Carve a rectangular hole out of the middle of the rounded rect. if (hole) { var holeR:Object = hole.r; if (holeR is Number) { ellipseSize = Number(holeR) * 2; g.drawRoundRect(hole.x, hole.y, hole.w, hole.h, ellipseSize, ellipseSize); } else { GraphicsUtil.drawRoundRectComplex(g, hole.x, hole.y, hole.w, hole.h, holeR.tl, holeR.tr, holeR.bl, holeR.br); } } if (color !== null) g.endFill(); } } }