//////////////////////////////////////////////////////////////////////////////// // // 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 spark.skins.mobile.supportClasses { import flash.display.DisplayObject; import flash.display.GradientType; import flash.display.Graphics; import flash.geom.ColorTransform; import flash.geom.Matrix; import mx.core.FlexGlobals; import mx.core.IFlexDisplayObject; import mx.core.ILayoutElement; import mx.core.UIComponent; import mx.core.mx_internal; import mx.utils.ColorUtil; import spark.components.supportClasses.SkinnableComponent; import spark.components.supportClasses.StyleableTextField; import spark.core.DisplayObjectSharingMode; import spark.core.IGraphicElement; import spark.skins.IHighlightBitmapCaptureClient; use namespace mx_internal; /** * ActionScript-based skin for mobile applications. This skin is the * base class for all of the ActionScript mobile skins. As an optimization, * it removes state transition support. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public class MobileSkin extends UIComponent implements IHighlightBitmapCaptureClient { //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * @private * Dark chromeColor used in ViewNavigator and * TabbedViewNavigator "chrome" elements: ActionBar and * TabbedViewNavigator#tabBar. */ mx_internal static const MOBILE_THEME_DARK_COLOR:uint = 0x484848; /** * @private * Default chromeColor for the mobile theme. */ mx_internal static const MOBILE_THEME_LIGHT_COLOR:uint = 0xCCCCCC; /** * @private * Default symbol color for symbolColor style. */ mx_internal static const DEFAULT_SYMBOL_COLOR_VALUE:uint = 0x00; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 * */ public function MobileSkin() { } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * Specifies whether or not this skin should be affected by the symbolColor style. * * @default false * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected var useSymbolColor:Boolean = false; /** * @private * Toggles transparent, centered hit-area if the unscaled size is less * than one-quarter inch square. Physical size is based on applicationDPI. */ mx_internal var useMinimumHitArea:Boolean = true; /** * Specifies a default width. measuredWidth returns this value * when the computed measuredWidth is less than * measuredDefaultWidth. * * @default 0 * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected var measuredDefaultWidth:Number = 0; /** * Specifies a default height. measuredHeight returns this value * when the computed measuredHeight is less than * measuredDefaultHeight. * * @default 0 * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected var measuredDefaultHeight:Number = 0; //---------------------------------- // colorMatrix //---------------------------------- private static var _colorMatrix:Matrix = new Matrix(); /** * @private */ mx_internal static function get colorMatrix():Matrix { if (!_colorMatrix) _colorMatrix = new Matrix(); return _colorMatrix; } //---------------------------------- // colorTransform //---------------------------------- private static var _colorTransform:ColorTransform; /** * @private */ mx_internal static function get colorTransform():ColorTransform { if (!_colorTransform) _colorTransform = new ColorTransform(); return _colorTransform; } //---------------------------------- // applicationDPI //---------------------------------- /** * Returns the DPI of the application. This property can only be set in MXML on the root application. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function get applicationDPI():Number { return FlexGlobals.topLevelApplication.applicationDPI; } //---------------------------------- // symbolItems //---------------------------------- /** * Names of items that should have their color property defined by * the symbolColor style. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ protected function get symbolItems():Array { return null; } //---------------------------------- // currentState //---------------------------------- private var _currentState:String; /** * @private */ override public function get currentState():String { return _currentState; } /** * @private */ override public function set currentState(value:String):void { if (value != _currentState) { _currentState = value; commitCurrentState(); } } /** * Called whenever the currentState changes. Skins should override * this function if they make any appearance changes during * a state change. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function commitCurrentState():void { } /** * MobileSkin does not use states. Skins should override this function * to return false for states that are not implemented. * * @param stateName The state name. * * @return false for states that are not implemented. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ override public function hasState(stateName:String):Boolean { return true; } /** * @private */ override public function setCurrentState(stateName:String, playTransition:Boolean = true):void { currentState = stateName; } /** * @private */ override public function get measuredWidth():Number { return Math.max(super.measuredWidth, measuredDefaultWidth); } /** * @private */ override public function get measuredHeight():Number { return Math.max(super.measuredHeight, measuredDefaultHeight); } /** * @private */ override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { graphics.clear(); super.updateDisplayList(unscaledWidth, unscaledHeight); layoutContents(unscaledWidth, unscaledHeight); if (useSymbolColor) applySymbolColor(); if (useMinimumHitArea) drawMinimumHitArea(unscaledWidth, unscaledHeight); drawBackground(unscaledWidth, unscaledHeight); } /** * @private * Make the component's explicitMinWidth property override its skin's. * This is useful for cases where the skin's minWidth constrains * the skin's measured size. In those cases the user could set * explicit limits on the component itself thus relaxing the * hard-coded limits in the skin. See SDK-24741. */ override public function get explicitMinWidth():Number { if (parent is SkinnableComponent) { var parentExplicitMinWidth:Number = SkinnableComponent(parent).explicitMinWidth; if (!isNaN(parentExplicitMinWidth)) return parentExplicitMinWidth; } return super.explicitMinWidth; } /** * @private * Make the component's explicitMinWidth property override its skin's. * This is useful for cases where the skin's minWidth constrains * the skin's measured size. In those cases the user could set * explicit limits on the component itself thus relaxing the * hard-coded limits in the skin. See SDK-24741. */ override public function get explicitMinHeight():Number { if (parent is SkinnableComponent) { var parentExplicitMinHeight:Number = SkinnableComponent(parent).explicitMinHeight; if (!isNaN(parentExplicitMinHeight)) return parentExplicitMinHeight; } return super.explicitMinHeight; } /** * @private * Make the component's explicitMinWidth property override its skin's. * This is useful for cases where the skin's minWidth constrains * the skin's measured size. In those cases the user could set * explicit limits on the component itself thus relaxing the * hard-coded limits in the skin. See SDK-24741. */ override public function get explicitMaxWidth():Number { if (parent is SkinnableComponent) { var parentExplicitMaxWidth:Number = SkinnableComponent(parent).explicitMaxWidth; if (!isNaN(parentExplicitMaxWidth)) return parentExplicitMaxWidth; } return super.explicitMaxWidth; } /** * @private * Make the component's explicitMinWidth property override its skin's. * This is useful for cases where the skin's minWidth constrains * the skin's measured size. In those cases the user could set * explicit limits on the component itself thus relaxing the * hard-coded limits in the skin. See SDK-24741. */ override public function get explicitMaxHeight():Number { if (parent is SkinnableComponent) { var parentExplicitMaxHeight:Number = SkinnableComponent(parent).explicitMaxHeight; if (!isNaN(parentExplicitMaxHeight)) return parentExplicitMaxHeight; } return super.explicitMaxHeight; } //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- mx_internal function drawMinimumHitArea(unscaledWidth:Number, unscaledHeight:Number):void { // minimum hit area is 0.25 inches square var minSize:Number = applicationDPI / 4; // skip if skin size is larger than minimum if ((unscaledWidth > minSize) && (unscaledHeight > minSize)) return; // center a transparent hit area larger than the skin var hitAreaWidth:Number = Math.max(minSize, unscaledWidth); var hitAreaHeight:Number = Math.max(minSize, unscaledHeight); var hitAreaX:Number = (unscaledWidth - hitAreaWidth) / 2; var hitAreaY:Number = (unscaledHeight - hitAreaHeight) / 2; graphics.beginFill(0, 0); graphics.drawRect(hitAreaX, hitAreaY, hitAreaWidth, hitAreaHeight); graphics.endFill(); } /** * Positions the children for this skin. * *

This method, along with colorizeContents(), is called * by the updateDisplayList() method.

* *

This method positions skin parts and graphic children of the skin. * Subclasses should override this to position their children.

* * @param unscaledWidth Specifies the width of the component, in pixels, * in the component's coordinates, regardless of the value of the * scaleX property of the component. * * @param unscaledHeight Specifies the height of the component, in pixels, * in the component's coordinates, regardless of the value of the * scaleY property of the component. * * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void { } /** * A helper method to set a color transform on a DisplayObject. * * @param displayObject The display object to transform * @param originalColor The original color * @param tintColor The desired color * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function applyColorTransform(displayObject:DisplayObject, originalColor:uint, tintColor:uint):void { colorTransform.redOffset = ((tintColor & (0xFF << 16)) >> 16) - ((originalColor & (0xFF << 16)) >> 16); colorTransform.greenOffset = ((tintColor & (0xFF << 8)) >> 8) - ((originalColor & (0xFF << 8)) >> 8); colorTransform.blueOffset = (tintColor & 0xFF) - (originalColor & 0xFF); colorTransform.alphaMultiplier = alpha; displayObject.transform.colorTransform = colorTransform; } /** * Renders a background for the skin. * *

This method, along with layoutContents(), is called * by the updateDisplayList().

* *

This method draws the background chromeColor. * Override this method to change the appearance of the chromeColor.

* * @param unscaledWidth Specifies the width of the component, in pixels, * in the component's coordinates, regardless of the value of the * scaleX property of the component. * * @param unscaledHeight Specifies the height of the component, in pixels, * in the component's coordinates, regardless of the value of the * scaleY property of the component. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void { } /** * @private */ mx_internal function applySymbolColor():void { var symbols:Array = symbolItems; var len:uint = (symbols) ? symbols.length : 0; if (len > 0) { var symbolColor:uint = getStyle("symbolColor"); var symbolObj:Object; var transformInitialized:Boolean = false; for (var i:uint = 0; i < len; i++) { symbolObj = this[symbols[i]]; // SparkSkin assumed symbols were IFill objects // with a color property. MobileSkin instead assumes symbols // are DisplayObjects. if (symbolObj is DisplayObject) { if (!transformInitialized) { colorTransform.redOffset = ((symbolColor & (0xFF << 16)) >> 16) - DEFAULT_SYMBOL_COLOR_VALUE; colorTransform.greenOffset = ((symbolColor & (0xFF << 8)) >> 8) - DEFAULT_SYMBOL_COLOR_VALUE; colorTransform.blueOffset = (symbolColor & 0xFF) - DEFAULT_SYMBOL_COLOR_VALUE; colorTransform.alphaMultiplier = alpha; transformInitialized = true; } DisplayObject(symbolObj).transform.colorTransform = colorTransform; } } } } /** * A helper method to position children elements of this component. * *

This method is the recommended way to position children elements. You can * use this method instead of checking for and using * various interfaces/classes such as ILayoutElement, IFlexDisplayObject, * or StyleableTextField.

* *

Call this method after calling setElementSize()

* * @param element The child element to position. The element could be an * ILayoutElement, IFlexDisplayObject, StyleableTextField, or a generic * DisplayObject. * * @param x The x-coordinate of the child. * * @param y The y-coordinate of the child. * * @see #setElementSize * * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function setElementPosition(element:Object, x:Number, y:Number):void { if (element is ILayoutElement) { ILayoutElement(element).setLayoutBoundsPosition(x, y, false); } else if (element is IFlexDisplayObject) { IFlexDisplayObject(element).move(x, y); } else { element.x = x; element.y = y; } } /** * A helper method to size children elements of this component. * *

This method is the recommended way to size children elements. You can * use this method instead of checking for and using * interfaces/classes such as ILayoutElement, IFlexDisplayObject, * or StyleableTextField.

* *

Call this method before calling the setElementPosition() method.

* * @param element The child element to size. The element could be an * ILayoutElement, IFlexDisplayObject, StyleableTextField, or a generic * DisplayObject. * * @param width The width of the child. * * @param height The height of the child. * * @see #setElementPosition * * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function setElementSize(element:Object, width:Number, height:Number):void { if (element is ILayoutElement) { ILayoutElement(element).setLayoutBoundsSize(width, height, false); } else if (element is IFlexDisplayObject) { IFlexDisplayObject(element).setActualSize(width, height); } else { element.width = width; element.height = height; } } /** * A helper method to retrieve the preferred width of a child element. * *

This method is the recommended way to get a child element's preferred * width. You can use this method instead of checking for and using * various interfaces/classes such as ILayoutElement, IFlexDisplayObject, * or StyleableTextField.

* * @param element The child element to retrieve the width for. The element could * be an ILayoutElement, IFlexDisplayObject, StyleableTextField, or a generic * DisplayObject. * * @return The child element's preferred width. * * @see #sizeElement * @see #getElementPreferredHeight * * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function getElementPreferredWidth(element:Object):Number { if (element is ILayoutElement) { return ILayoutElement(element).getPreferredBoundsWidth(); } else if (element is IFlexDisplayObject) { return IFlexDisplayObject(element).measuredWidth; } else { return element.width; } } /** * A helper method to retrieve the preferred height of a child element. * *

This method is the recommended way to get a child element's preferred * height. You can use this method instead of checking for and using * various interfaces/classes such as ILayoutElement, IFlexDisplayObject, * or StyleableTextField.

* * @param element The child element to retrieve the height for. The element could * be an ILayoutElement, IFlexDisplayObject, StyleableTextField, or a generic * DisplayObject. * * @return The child element's preferred height. * * @see #sizeElement * @see #getElementPreferredWidth * * @langversion 3.0 * @playerversion Flash 10.1 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function getElementPreferredHeight(element:Object):Number { if (element is ILayoutElement) { return ILayoutElement(element).getPreferredBoundsHeight(); } else if (element is IFlexDisplayObject) { return IFlexDisplayObject(element).measuredHeight; } else { return element.height; } } /** * List of IDs of items that should be excluded when rendering the focus ring. * Only items of type DisplayObject or GraphicElement should be excluded. Items * of other types are ignored. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ protected function get focusSkinExclusions():Array { return null; } private static var exclusionAlphaValues:Array; private static var oldContentBackgroundAlpha:Number; private static var contentBackgroundAlphaSetLocally:Boolean; /** * Called before a bitmap capture is made for this skin. The default implementation * excludes items in the focusSkinExclusions array. * * @return true if the component needs to be redrawn; otherwise false. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function beginHighlightBitmapCapture():Boolean { var exclusions:Array = focusSkinExclusions; if (!exclusions) { if (("hostComponent" in this) && this["hostComponent"] is SkinnableComponent) exclusions = SkinnableComponent(this["hostComponent"]).suggestedFocusSkinExclusions; } var exclusionCount:Number = (exclusions == null) ? 0 : exclusions.length; /* we'll store off the previous alpha of the exclusions so we can restore them when we're done */ exclusionAlphaValues = []; var needRedraw:Boolean = false; for (var i:int = 0; i < exclusionCount; i++) { // skip if the part isn't there if (!(exclusions[i] in this)) continue; var ex:Object = this[exclusions[i]]; /* we're going to go under the covers here to try and modify alpha with the least amount of disruption to the component. For UIComponents, we go to Sprite's alpha property; */ if (ex is UIComponent) { exclusionAlphaValues[i] = (ex as UIComponent).$alpha; (ex as UIComponent).$alpha = 0; } else if (ex is DisplayObject) { exclusionAlphaValues[i] = (ex as DisplayObject).alpha; (ex as DisplayObject).alpha = 0; } else if (ex is IGraphicElement) { /* if we're lucky, the IGE has its own DisplayObject, and we can just trip its alpha. If not, we're going to have to set it to 0, and force a redraw of the whole component */ var ge:IGraphicElement = ex as IGraphicElement; if (ge.displayObjectSharingMode == DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT) { exclusionAlphaValues[i] = ge.displayObject.alpha; ge.displayObject.alpha = 0; } else { exclusionAlphaValues[i] = ge.alpha; ge.alpha = 0; needRedraw = true; } } } // If we have a mostly-transparent content background, temporarily bump // up the contentBackgroundAlpha so the captured bitmap includes an opaque // snapshot of the background. if (getStyle("contentBackgroundAlpha") < 0.5) { if (styleDeclaration && styleDeclaration.getStyle("contentBackgroundAlpha") !== null) contentBackgroundAlphaSetLocally = true; else contentBackgroundAlphaSetLocally = false; oldContentBackgroundAlpha = getStyle("contentBackgroundAlpha"); setStyle("contentBackgroundAlpha", 0.5); needRedraw = true; } /* if we excluded an IGE without its own DO, we need to update the component before grabbing the bitmap */ return needRedraw; } /** * Called after a bitmap capture is made for this skin. The default implementation * restores the items in the focusSkinExclusions array. * * @return true if the component needs to be redrawn; otherwise false. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function endHighlightBitmapCapture():Boolean { var exclusions:Array = focusSkinExclusions; if (!exclusions) { if (this["hostComponent"] is SkinnableComponent) exclusions = SkinnableComponent(this["hostComponent"]).suggestedFocusSkinExclusions; } var exclusionCount:Number = (exclusions == null) ? 0 : exclusions.length; var needRedraw:Boolean = false; for (var i:int=0; i < exclusionCount; i++) { // skip if the part isn't there if (!(exclusions[i] in this)) continue; var ex:Object = this[exclusions[i]]; if (ex is UIComponent) { (ex as UIComponent).$alpha = exclusionAlphaValues[i]; } else if (ex is DisplayObject) { (ex as DisplayObject).alpha = exclusionAlphaValues[i]; } else if (ex is IGraphicElement) { var ge:IGraphicElement = ex as IGraphicElement; if (ge.displayObjectSharingMode == DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT) { ge.displayObject.alpha = exclusionAlphaValues[i]; } else { ge.alpha = exclusionAlphaValues[i]; needRedraw = true; } } } exclusionAlphaValues = null; if (!isNaN(oldContentBackgroundAlpha)) { if (contentBackgroundAlphaSetLocally) setStyle("contentBackgroundAlpha", oldContentBackgroundAlpha); else clearStyle("contentBackgroundAlpha"); needRedraw = true; oldContentBackgroundAlpha = NaN; } return needRedraw; } } }