| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.containers |
| { |
| import flash.display.BlendMode; |
| import flash.display.DisplayObject; |
| |
| import mx.core.ContainerCreationPolicy; |
| import mx.core.IDeferredContentOwner; |
| import mx.core.IDeferredInstance; |
| import mx.core.IFlexModule; |
| import mx.core.IFlexModuleFactory; |
| import mx.core.IFontContextComponent; |
| import mx.core.IInvalidating; |
| import mx.core.IUIComponent; |
| import mx.core.IVisualElement; |
| import mx.core.IVisualElementContainer; |
| import mx.core.UIComponent; |
| import mx.core.mx_internal; |
| import mx.events.FlexEvent; |
| import mx.graphics.shaderClasses.ColorBurnShader; |
| import mx.graphics.shaderClasses.ColorDodgeShader; |
| import mx.graphics.shaderClasses.ColorShader; |
| import mx.graphics.shaderClasses.ExclusionShader; |
| import mx.graphics.shaderClasses.HueShader; |
| import mx.graphics.shaderClasses.LuminosityShader; |
| import mx.graphics.shaderClasses.SaturationShader; |
| import mx.graphics.shaderClasses.SoftLightShader; |
| import mx.resources.ResourceManager; |
| |
| import spark.components.supportClasses.GroupBase; |
| import spark.core.IGraphicElement; |
| import spark.events.ElementExistenceEvent; |
| import spark.layouts.BasicLayout; |
| import spark.layouts.supportClasses.LayoutBase; |
| |
| import spark.containers.supportClasses.DeferredCreationPolicy; |
| |
| use namespace mx_internal; |
| |
| // for asdoc |
| [Experimental] |
| |
| //-------------------------------------- |
| // Events |
| //-------------------------------------- |
| |
| /** |
| * Dispatched when the deferred content of this component is created. |
| * |
| * @eventType mx.events.FlexEvent.CONTENT_CREATION_COMPLETE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Event(name="contentCreationComplete", type="mx.events.FlexEvent")] |
| |
| [DefaultProperty("mxmlContentFactory")] |
| |
| /** |
| * The DeferredGroup class enables deferred instatiation of child elements |
| * via is <code>creationPolicy</code> property. |
| * |
| * <p>You cannot use GraphicElement objects with the DeferredGroup due to Adobe |
| * data typing the paramater passed to <code>GraphicElement.parentChanged()</code> |
| * as a <code>Group</code>. |
| * See <a href="https://bugs.adobe.com/jira/browse/SDK-25601">Don't not hard code GraphicElement to Group</a> |
| * and <a href="https://bugs.adobe.com/jira/browse/SDK-25333">Make it easier to extend Group</a>.</p> |
| * |
| * <p>The Group container has the following default characteristics:</p> |
| * <table class="innertable"> |
| * <tr><th>Characteristic</th><th>Description</th></tr> |
| * <tr><td>Default size</td><td>Large enough to display its children</td></tr> |
| * <tr><td>Minimum size</td><td>0 pixels</td></tr> |
| * <tr><td>Maximum size</td><td>10000 pixels wide and 10000 pixels high</td></tr> |
| * </table> |
| * |
| * @mxml |
| * |
| * <p>The <code><s:DeferredGroup></code> tag inherits all of the tag |
| * attributes of its superclass and adds the following tag attributes:</p> |
| * |
| * <pre> |
| * <s:GroupBase |
| * <strong>Properties</strong> |
| * blendMode="auto" |
| * creationPolicy="visible" |
| * /> |
| * </pre> |
| * |
| * @see spark.containers.supportClasses.DeferredCreationPolicy |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public class DeferredGroup extends GroupBase implements IDeferredContentOwner |
| { |
| |
| private static const ITEM_ORDERED_LAYERING:uint = 0; |
| private static const SPARSE_LAYERING:uint = 1; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function DeferredGroup() |
| { |
| super(); |
| |
| creationPolicy = DeferredCreationPolicy.VISIBLE; |
| } |
| |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Storage property for whether <code>createChildren</code> |
| * has been invoked. |
| */ |
| private var _childrenCreated : Boolean; |
| |
| private var needsDisplayObjectAssignment:Boolean = false; |
| private var layeringMode:uint = ITEM_ORDERED_LAYERING; |
| |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // useVirtualLayout |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage property for useVirtualLayout. |
| */ |
| private var _useVirtualLayout:Boolean = true; |
| |
| /** |
| * Returns whether a virtual layout is being used which is dependant |
| * on the <code>creationPolicy</code>. |
| * |
| * <p>The value of this property overrides <code>useVirtualLayout</code> |
| * set directly on the layout property.</p> |
| * |
| * @see #creationPolicy |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get useVirtualLayout():Boolean |
| { |
| return ( layout ) ? layout.useVirtualLayout : _useVirtualLayout; |
| } |
| |
| |
| //---------------------------------- |
| // creationPolicy |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage property for creationPolicy. |
| */ |
| private var _creationPolicy : String; |
| |
| [Inspectable(enumeration="visible,construct,all,none", defaultValue="visible")] |
| /** |
| * Content creation policy for this component. |
| * |
| * <p>Possible values are:</p> |
| * <ul> |
| * <li><code>none</code> - Content must be created manually by calling the <code>createDeferredContent()</code> method.</li> |
| * <li><code>visible</code> - Only construct the immediate descendants and initialize those that are visible.</li> |
| * <li><code>construct</code> - Construct all decendants immediately but only inialize those that are visible.</li> |
| * <li><code>all</code> - Create the content as soon as the parent component is created. This |
| * option should only be used as a last resort because it increases startup time and memory usage.</li> |
| * </ul> |
| * |
| * |
| * <p>If no <code>creationPolicy</code> is specified for a container, that container inherits the value of |
| * its parent's <code>creationPolicy</code> property.</p> |
| * |
| * <p>The <code>creationPolicy</code> affects <code>useVirtualLayout</code> in he following way:</p> |
| * <table class="innertable"> |
| * <tr> |
| * <th>creationPolicy</th> |
| * <th>useVirtualLayout</th> |
| * </tr> |
| * <tr> |
| * <td>none</td> |
| * <td>false</td> |
| * </tr> |
| * <tr> |
| * <td>visible</td> |
| * <td>true</td> |
| * </tr> |
| * <tr> |
| * <td>construct</td> |
| * <td>true</td> |
| * </tr> |
| * <tr> |
| * <td>all</td> |
| * <td>false</td> |
| * </tr> |
| * </table> |
| * |
| * @default visible |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get creationPolicy():String |
| { |
| return _creationPolicy; |
| } |
| /** |
| * @private |
| */ |
| public function set creationPolicy(value:String):void |
| { |
| if( _creationPolicy == value ) return; |
| |
| _creationPolicy = value; |
| _useVirtualLayout = _creationPolicy != DeferredCreationPolicy.ALL; |
| |
| if( layout ) |
| { |
| if( layout is BasicLayout ) |
| { |
| if( _useVirtualLayout ) throw new Error( ResourceManager.getInstance().getString("layout", "basicLayoutNotVirtualized")); |
| } |
| else |
| { |
| layout.useVirtualLayout = _useVirtualLayout; |
| } |
| } |
| |
| createContentIfNeeded(); |
| } |
| |
| |
| //---------------------------------- |
| // mxmlContentFactory |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Backing variable for the contentFactory property. |
| */ |
| private var _mxmlContentFactory:IDeferredInstance; |
| |
| /** |
| * @private |
| * Flag that indicates whether or not the content has been created. |
| */ |
| private var _mxmlContentCreated:Boolean = false; |
| |
| [InstanceType("Array")] |
| [ArrayElementType("mx.core.IVisualElement")] |
| |
| /** |
| * A factory object that creates the initial value for the |
| * <code>content</code> property. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function set mxmlContentFactory(value:IDeferredInstance):void |
| { |
| if( value == _mxmlContentFactory ) return; |
| |
| _mxmlContentFactory = value; |
| _mxmlContentCreated = false; |
| |
| createContentIfNeeded(); |
| } |
| |
| |
| //---------------------------------- |
| // mxmlContent |
| //---------------------------------- |
| |
| protected var _mxmlContent:Array; |
| |
| [ArrayElementType("mx.core.IVisualElement")] |
| |
| /** |
| * @copy spark.components.Group#mxmlContent |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function set mxmlContent(value:Array):void |
| { |
| _mxmlContent = value; |
| |
| invalidateDisplayList(); |
| } |
| |
| |
| //---------------------------------- |
| // deferredContentCreated |
| //---------------------------------- |
| |
| private var _deferredContentCreated:Boolean; |
| /** |
| * A flag that indicates whether the deferred content has been created. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get deferredContentCreated():Boolean |
| { |
| return _deferredContentCreated; |
| } |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // layout |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function set layout( value:LayoutBase ):void |
| { |
| if( _useVirtualLayout ) |
| { |
| if( value is BasicLayout ) |
| { |
| throw new Error( "BasicLayout does not support virtualLayout and therefore cannot be using with the creationPolicy: " + creationPolicy ); |
| } |
| else |
| { |
| value.useVirtualLayout = _useVirtualLayout; |
| } |
| } |
| else |
| { |
| value.useVirtualLayout = _useVirtualLayout; |
| } |
| |
| super.layout = value; |
| } |
| |
| |
| //---------------------------------- |
| // moduleFactory |
| //---------------------------------- |
| /** |
| * @private |
| */ |
| override public function set moduleFactory( moduleFactory:IFlexModuleFactory ):void |
| { |
| super.moduleFactory = moduleFactory; |
| |
| // Register the _creationPolicy style as inheriting. See the creationPolicy |
| // getter for details on usage of this style. |
| styleManager.registerInheritingStyle("_creationPolicy"); |
| } |
| |
| /** |
| * @private |
| * Override to ensure we set redrawRequested when appropriate. |
| */ |
| override public function set mouseEnabledWhereTransparent(value:Boolean):void |
| { |
| if (value == mouseEnabledWhereTransparent) |
| return; |
| |
| super.mouseEnabledWhereTransparent = value; |
| redrawRequested = true; |
| } |
| |
| //---------------------------------- |
| // numElements |
| //---------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| override public function get numElements():int |
| { |
| if( _mxmlContent == null ) return 0; |
| |
| return _mxmlContent.length; |
| } |
| |
| |
| //---------------------------------- |
| // alpha |
| //---------------------------------- |
| |
| [Inspectable(defaultValue="1.0", category="General", verbose="1")] |
| |
| /** |
| * @private |
| */ |
| override public function set alpha(value:Number):void |
| { |
| if (super.alpha == value) |
| return; |
| |
| if (_blendMode == "auto") |
| { |
| // If alpha changes from an opaque/transparent (1/0) and translucent |
| // (0 < value < 1), then trigger a blendMode change |
| if ((value > 0 && value < 1 && (super.alpha == 0 || super.alpha == 1)) || |
| ((value == 0 || value == 1) && (super.alpha > 0 && super.alpha < 1))) |
| { |
| blendModeChanged = true; |
| invalidateDisplayObjectOrdering(); |
| invalidateProperties(); |
| } |
| } |
| |
| super.alpha = value; |
| } |
| |
| |
| //---------------------------------- |
| // blendMode |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the blendMode property. |
| */ |
| private var _blendMode:String = "auto"; |
| private var blendModeChanged:Boolean; |
| private var blendShaderChanged:Boolean; |
| |
| [Inspectable(category="General", enumeration="auto,add,alpha,darken,difference,erase,hardlight,invert,layer,lighten,multiply,normal,subtract,screen,overlay,colordodge,colorburn,exclusion,softlight,hue,saturation,color,luminosity", defaultValue="auto")] |
| |
| /** |
| * @inheritDoc |
| */ |
| override public function get blendMode():String |
| { |
| return _blendMode; |
| } |
| |
| /** |
| * @private |
| */ |
| override public function set blendMode(value:String):void |
| { |
| if (value == _blendMode) |
| return; |
| |
| invalidateProperties(); |
| blendModeChanged = true; |
| |
| //The default blendMode in FXG is 'auto'. There are only |
| //certain cases where this results in a rendering difference, |
| //one being when the alpha of the Group is > 0 and < 1. In that |
| //case we set the blendMode to layer to avoid the performance |
| //overhead that comes with a non-normal blendMode. |
| |
| if (value == "auto") |
| { |
| _blendMode = value; |
| if (((alpha > 0 && alpha < 1) && super.blendMode != BlendMode.LAYER) || |
| ((alpha == 1 || alpha == 0) && super.blendMode != BlendMode.NORMAL) ) |
| { |
| invalidateDisplayObjectOrdering(); |
| } |
| } |
| else |
| { |
| var oldValue:String = _blendMode; |
| _blendMode = value; |
| |
| // If one of the non-native Flash blendModes is set, |
| // record the new value and set the appropriate |
| // blendShader on the display object. |
| if (isAIMBlendMode(value)) |
| { |
| blendShaderChanged = true; |
| } |
| |
| // Only need to re-do display object assignment if blendmode was normal |
| // and is changing to something else, or the blend mode was something else |
| // and is going back to normal. This is because display object sharing |
| // only happens when blendMode is normal. |
| if ((oldValue == BlendMode.NORMAL || value == BlendMode.NORMAL) && |
| !(oldValue == BlendMode.NORMAL && value == BlendMode.NORMAL)) |
| { |
| invalidateDisplayObjectOrdering(); |
| } |
| |
| } |
| } |
| |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function addElement(element:IVisualElement):IVisualElement |
| { |
| var index:int = numElements; |
| |
| // This handles the case where we call addElement on something |
| // that already is in the list. Let's just handle it silently |
| // and not throw up any errors. |
| if (element.parent == this) |
| index = numElements-1; |
| |
| return addElementAt(element, index); |
| } |
| |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function addElementAt(element:IVisualElement, index:int):IVisualElement |
| { |
| if (element == this) |
| throw new ArgumentError(resourceManager.getString("components", "cannotAddYourselfAsYourChild")); |
| |
| // check for RangeError: |
| checkForRangeError(index, true); |
| |
| var host:DisplayObject = element.parent; |
| |
| // This handles the case where we call addElement on something |
| // that already is in the list. Let's just handle it silently |
| // and not throw up any errors. |
| if (host == this) |
| { |
| setElementIndex(element, index); |
| return element; |
| } |
| else if (host is IVisualElementContainer) |
| { |
| // Remove the item from the group if that group isn't this group |
| IVisualElementContainer(host).removeElement(element); |
| } |
| |
| // If we don't have any content yet, initialize it to an empty array |
| if (_mxmlContent == null) _mxmlContent = []; |
| |
| _mxmlContent.splice( index, 0, element ); |
| if( layout ) layout.elementAdded(index); |
| |
| invalidateDisplayList(); |
| |
| return element; |
| } |
| |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function removeAllElements():void |
| { |
| for (var i:int = numElements - 1; i >= 0; i--) |
| { |
| removeElementAt(i); |
| } |
| } |
| |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function removeElementAt( index:int ):IVisualElement |
| { |
| // check RangeError |
| checkForRangeError( index ); |
| |
| var element:IVisualElement = _mxmlContent[index]; |
| |
| // Need to call elementRemoved before removing the item so anyone listening |
| // for the event can access the item. |
| if( element.parent ) elementRemoved( element, index ); |
| |
| _mxmlContent.splice(index, 1); |
| if( layout ) layout.elementRemoved( index ); |
| |
| return element; |
| } |
| |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function swapElementsAt( index1:int, index2:int ):void |
| { |
| checkForRangeError( index1 ); |
| checkForRangeError( index2 ); |
| |
| if( index1 == index2 ) return; |
| |
| // Make sure that index1 is the smaller index so that addElementAt |
| // doesn't RTE |
| if( index1 > index2) |
| { |
| var temp:int = index2; |
| index2 = index1; |
| index1 = temp; |
| } |
| |
| var element1:IVisualElement = _mxmlContent[ index1 ]; |
| var element2:IVisualElement = _mxmlContent[ index2 ]; |
| |
| var index1Added:Boolean; |
| var index2Added:Boolean; |
| |
| // Make sure we do the proper invalidations, but don't dispatch events |
| if( element1.parent ) |
| { |
| elementRemoved( element1, index1, false /*notifyListeners*/); |
| index1Added = true; |
| } |
| if( element2.parent ) |
| { |
| elementRemoved( element2, index2, false /*notifyListeners*/); |
| index2Added = true; |
| } |
| |
| // Step 1: remove |
| // Make sure we remove the bigger index first |
| _mxmlContent.splice( index2, 1 ); |
| _mxmlContent.splice( index1, 1 ); |
| |
| // Step 2: swap |
| // Add them in reverse order |
| _mxmlContent.splice( index1, 0, element2 ); |
| _mxmlContent.splice( index2, 0, element1 ); |
| |
| // Make sure we do the proper invalidations, but don't dispatch events |
| if( index1Added ) elementAdded( element2, index1, false /*notifyListeners*/); |
| if( index2Added ) elementAdded(element1, index2, false /*notifyListeners*/); |
| } |
| |
| /** |
| * Create the content for this component. If creationPolicy is "auto" or "all", this |
| * function will be called by the flex framework. If creationPolicy is "none", this |
| * function must be called to create the content for the component. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function createDeferredContent():void |
| { |
| if( !_mxmlContentCreated ) |
| { |
| _mxmlContentCreated = true; |
| if (_mxmlContentFactory) |
| { |
| var deferredContent:Object = _mxmlContentFactory.getInstance(); |
| mxmlContent = deferredContent as Array; |
| _deferredContentCreated = true; |
| |
| switch( _creationPolicy ) |
| { |
| |
| case DeferredCreationPolicy.CONSTRUCT : |
| case DeferredCreationPolicy.ALL : |
| { |
| createDeferredChildren(); |
| break; |
| } |
| case DeferredCreationPolicy.NONE : |
| case DeferredCreationPolicy.VISIBLE : |
| { |
| // Do nothing |
| break; |
| } |
| } |
| |
| dispatchEvent( new FlexEvent( FlexEvent.CONTENT_CREATION_COMPLETE ) ); |
| } |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function setElementIndex(element:IVisualElement, index:int):void |
| { |
| // check for RangeError...this is done in addItemAt |
| // but we want to do it before removing the element |
| checkForRangeError(index); |
| |
| removeElement(element); |
| addElementAt(element, index); |
| } |
| |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function removeElement(element:IVisualElement):IVisualElement |
| { |
| return removeElementAt(getElementIndex(element)); |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function getMXMLContent():Array |
| { |
| return ( _mxmlContent ) ? _mxmlContent.concat() : null; |
| } |
| |
| /** |
| * @private |
| */ |
| private function createContentIfNeeded():void |
| { |
| if( !_mxmlContentCreated && |
| _childrenCreated && |
| creationPolicy != ContainerCreationPolicy.NONE ) |
| createDeferredContent(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function createDeferredChildren():void |
| { |
| var content:Array = getMXMLContent(); |
| var element:IVisualElement; |
| |
| var numItems:int = content.length; |
| for( var i:int = 0; i < numItems; i++ ) |
| { |
| element = IVisualElement( content[ i ] ); |
| if( element is IDeferredContentOwner ) IDeferredContentOwner( element ).createDeferredContent(); |
| } |
| } |
| |
| /** |
| * @private |
| * Checks the range of index to make sure it's valid |
| */ |
| private function checkForRangeError(index:int, addingElement:Boolean = false):void |
| { |
| // figure out the maximum allowable index |
| var maxIndex:int = (_mxmlContent == null ? -1 : _mxmlContent.length - 1); |
| |
| // if adding an element, we allow an extra index at the end |
| if (addingElement) |
| maxIndex++; |
| |
| if (index < 0 || index > maxIndex) |
| throw new RangeError(resourceManager.getString("components", "indexOutOfRange", [index])); |
| } |
| |
| /** |
| * @private |
| * |
| * If the displayObject is not a child of this Group, then insert it at the |
| * specified index (or at the end of the list, when index is -1). |
| * Else, if the displayObject is already a child of the Group, then simply |
| * adjust its child index. |
| */ |
| private function addDisplayObjectToDisplayList(child:DisplayObject, index:int = -1):void |
| { |
| var overlayCount:int = _overlay ? _overlay.numDisplayObjects : 0; |
| if (child.parent == this) |
| super.setChildIndex(child, index != -1 ? index : super.numChildren - 1 - overlayCount); |
| else |
| super.addChildAt(child, index != -1 ? index : super.numChildren - overlayCount); |
| } |
| |
| /** |
| * @private |
| * |
| * Invalidates the display object ordering and will run assignDisplayObjects() |
| * if necessary. |
| * |
| * @return true if the display object ordering needed to be invalidated; |
| * false otherwise. |
| */ |
| //FIXME tink cannot be IGraphicElement |
| private function invalidateDisplayObjectOrdering():Boolean |
| { |
| if( layeringMode == SPARSE_LAYERING ) |
| { |
| needsDisplayObjectAssignment = true; |
| invalidateProperties(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * @private |
| */ |
| private function isAIMBlendMode(value:String):Boolean |
| { |
| if (value == "colordodge" || |
| value =="colorburn" || value =="exclusion" || |
| value =="softlight" || value =="hue" || |
| value =="saturation" || value =="color" || |
| value =="luminosity") |
| return true; |
| else return false; |
| } |
| |
| /** |
| * Adds an item to this Group. |
| * Flex calls this method automatically; you do not call it directly. |
| * |
| * @param item The item that was added. |
| * |
| * @param index The index where the item was added. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| mx_internal function elementAdded(element:IVisualElement, index:int, notifyListeners:Boolean = true):void |
| { |
| if (layout) |
| layout.elementAdded(index); |
| |
| if (element.depth != 0) |
| invalidateLayering(); |
| |
| // Set the moduleFactory to the child, but don't overwrite an existing moduleFactory. |
| // Propagate moduleFactory to the child, but don't overwrite an existing moduleFactory. |
| if (element is IFlexModule && IFlexModule(element).moduleFactory == null) |
| { |
| if (moduleFactory != null) |
| IFlexModule(element).moduleFactory = moduleFactory; |
| |
| else if (document is IFlexModule && document.moduleFactory != null) |
| IFlexModule(element).moduleFactory = document.moduleFactory; |
| |
| else if (parent is IFlexModule && IFlexModule(element).moduleFactory != null) |
| IFlexModule(element).moduleFactory = IFlexModule(parent).moduleFactory; |
| } |
| |
| // Set the font context in non-UIComponent children. |
| // UIComponent children use moduleFactory. |
| if (element is IFontContextComponent && !(element is UIComponent) && |
| IFontContextComponent(element).fontContext == null) |
| { |
| IFontContextComponent(element).fontContext = moduleFactory; |
| } |
| |
| if (element is IGraphicElement) |
| { |
| // FIXME tink cannot be IGraphicElement due to Adobe typing the paramater |
| // passed to <code>GraphicElement.parentChanged()</code> as a <code>Group</code>. |
| // see https://bugs.adobe.com/jira/browse/SDK-25601 |
| // https://bugs.adobe.com/jira/browse/SDK-25333 |
| throw new Error( "You cannot use an IGraphicElement in a DeferredGroup, all elements must extend UIComponent" ); |
| //numGraphicElements++; |
| //addingGraphicElementChild(element as IGraphicElement); |
| invalidateDisplayObjectOrdering(); |
| } |
| else |
| { |
| // item must be a DisplayObject |
| |
| // if the display object ordering is invalidated (because we have graphic elements |
| // that aren't actually in the display list), then lets just add our item to the end. |
| // If the ordering isn't invalidated, then let's just try to add it to the proper index. |
| if (invalidateDisplayObjectOrdering()) |
| { |
| // This always adds the child to the end of the display list. Any |
| // ordering discrepancies will be fixed up in assignDisplayObjects(). |
| addDisplayObjectToDisplayList(DisplayObject(element)); |
| } |
| else |
| { |
| // TODO Tink keep and eye on this |
| // addDisplayObjectToDisplayList(DisplayObject(element), index); |
| // We don't pass the index here either and therefore the child will always |
| // be added to the end of the display list. This is to ensure that items don't |
| // need to be shown in order. |
| addDisplayObjectToDisplayList(DisplayObject(element)); |
| } |
| } |
| |
| if (notifyListeners) |
| { |
| if (hasEventListener(ElementExistenceEvent.ELEMENT_ADD)) |
| dispatchEvent(new ElementExistenceEvent( |
| ElementExistenceEvent.ELEMENT_ADD, false, false, element, index)); |
| |
| |
| if (element is IUIComponent && element.hasEventListener(FlexEvent.ADD)) |
| element.dispatchEvent(new FlexEvent(FlexEvent.ADD)); |
| } |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| } |
| |
| /** |
| * Removes an item from this Group. |
| * Flex calls this method automatically; you do not call it directly. |
| * |
| * @param index The index of the item that is being removed. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| mx_internal function elementRemoved(element:IVisualElement, index:int, notifyListeners:Boolean = true):void |
| { |
| var childDO:DisplayObject = element as DisplayObject; |
| |
| if (notifyListeners) |
| { |
| if (hasEventListener(ElementExistenceEvent.ELEMENT_REMOVE)) |
| dispatchEvent(new ElementExistenceEvent( |
| ElementExistenceEvent.ELEMENT_REMOVE, false, false, element, index)); |
| |
| if (element is IUIComponent && element.hasEventListener(FlexEvent.REMOVE)) |
| element.dispatchEvent(new FlexEvent(FlexEvent.REMOVE)); |
| } |
| |
| if (element && (element is IGraphicElement)) |
| { |
| //FIXME tink cannot be IGraphicElement |
| throw new Error( "You cannot use an IGraphicElement in a DeferredGroup, all elements must extend UIComponent" ); |
| // numGraphicElements--; |
| // removingGraphicElementChild(element as IGraphicElement); |
| } |
| else if (childDO && childDO.parent == this) |
| { |
| super.removeChild(childDO); |
| } |
| |
| invalidateDisplayObjectOrdering(); |
| invalidateSize(); |
| invalidateDisplayList(); |
| |
| if (layout) |
| layout.elementRemoved(index); |
| } |
| |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| override public function getElementIndex( element:IVisualElement ):int |
| { |
| var index:int = _mxmlContent ? _mxmlContent.indexOf( element ) : -1; |
| |
| if( index == -1 ) |
| throw ArgumentError(resourceManager.getString("components", "elementNotFoundInGroup", [element])); |
| else |
| return index; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| override public function getVirtualElementAt(index:int, eltWidth:Number=NaN, eltHeight:Number=NaN):IVisualElement |
| { |
| // check for RangeError: |
| checkForRangeError(index); |
| |
| var elt:IVisualElement = IVisualElement( _mxmlContent[index] ); |
| |
| if( !elt.parent ) |
| { |
| elementAdded(elt, numChildren); |
| if( elt is IInvalidating ) IInvalidating( elt ).validateNow(); |
| if( !isNaN( eltWidth ) || !isNaN( eltHeight ) ) elt.setLayoutBoundsSize(eltWidth, eltHeight); |
| } |
| else |
| { |
| if( elt.parent != this ) throw new Error(resourceManager.getString("components", "mxmlElementNoMultipleParents", [elt])); |
| } |
| |
| return elt; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| override public function getElementAt(index:int):IVisualElement |
| { |
| // check for RangeError: |
| checkForRangeError(index); |
| |
| var elt:IVisualElement = IVisualElement( _mxmlContent[index] ); |
| |
| if( elt.parent ) |
| { |
| if( elt.parent != this ) throw new Error(resourceManager.getString("components", "mxmlElementNoMultipleParents", [elt])); |
| } |
| else |
| { |
| elementAdded(elt, index); |
| } |
| |
| return elt; |
| } |
| |
| /** |
| * @private |
| */ |
| override public function addChild(child:DisplayObject):DisplayObject |
| { |
| throw(new Error(resourceManager.getString("components", "addChildError"))); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function addChildAt(child:DisplayObject, index:int):DisplayObject |
| { |
| throw(new Error(resourceManager.getString("components", "addChildAtError"))); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function removeChild(child:DisplayObject):DisplayObject |
| { |
| throw(new Error(resourceManager.getString("components", "removeChildError"))); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function removeChildAt(index:int):DisplayObject |
| { |
| throw(new Error(resourceManager.getString("components", "removeChildAtError"))); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function setChildIndex(child:DisplayObject, index:int):void |
| { |
| throw(new Error(resourceManager.getString("components", "setChildIndexError"))); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function swapChildren(child1:DisplayObject, child2:DisplayObject):void |
| { |
| throw(new Error(resourceManager.getString("components", "swapChildrenError"))); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function swapChildrenAt(index1:int, index2:int):void |
| { |
| throw(new Error(resourceManager.getString("components", "swapChildrenAtError"))); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function invalidateLayering():void |
| { |
| if( layeringMode == ITEM_ORDERED_LAYERING ) layeringMode = SPARSE_LAYERING; |
| invalidateDisplayObjectOrdering(); |
| } |
| |
| /** |
| * Create content children, if the <code>creationPolicy</code> property |
| * is not equal to <code>none</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| override protected function createChildren():void |
| { |
| super.createChildren(); |
| |
| _childrenCreated = true; |
| |
| createContentIfNeeded(); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void |
| { |
| super.updateDisplayList(unscaledWidth, unscaledHeight); |
| |
| // Quick hack to help TimeMchineLayout |
| // When items are added to the displayList they are added |
| // at the front, and the validation of the depth |
| // being changed, doesn't happen until the next frame due |
| // to it being in commitProperties(). |
| if (needsDisplayObjectAssignment) |
| { |
| needsDisplayObjectAssignment = false; |
| assignDisplayObjects(); |
| } |
| |
| // If the DisplayObject assignment is still not completed, then postpone validation |
| // of the GraphicElements |
| if( needsDisplayObjectAssignment && invalidatePropertiesFlag ) return; |
| // if (scaleGridChanged) |
| // { |
| // scaleGridChanged = false; |
| // |
| // if (isValidScaleGrid()) |
| // { |
| // // Check for DisplayObjects other than overlays |
| // var overlayCount:int = _overlay ? _overlay.numDisplayObjects : 0; |
| // if (numChildren - overlayCount > 0) |
| // throw new Error(resourceManager.getString("components", "scaleGridGroupError")); |
| // |
| // super.scale9Grid = new Rectangle(scaleGridLeft, |
| // scaleGridTop, |
| // scaleGridRight - scaleGridLeft, |
| // scaleGridBottom - scaleGridTop); |
| // } |
| // else |
| // { |
| // super.scale9Grid = null; |
| // } |
| // } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function commitProperties():void |
| { |
| super.commitProperties(); |
| |
| if (blendModeChanged) |
| { |
| blendModeChanged = false; |
| |
| // Figure out the correct blendMode value |
| // to set. |
| if (_blendMode == "auto") |
| { |
| if (alpha == 0 || alpha == 1) |
| super.blendMode = BlendMode.NORMAL; |
| else |
| super.blendMode = BlendMode.LAYER; |
| } |
| else if (!isAIMBlendMode(_blendMode)) |
| { |
| super.blendMode = _blendMode; |
| } |
| |
| if (blendShaderChanged) |
| { |
| // The graphic element's blendMode was set to a non-Flash |
| // blendMode. We mimic the look by instantiating the |
| // appropriate shader class and setting the blendShader |
| // property on the displayObject. |
| blendShaderChanged = false; |
| switch(_blendMode) |
| { |
| case "color": |
| { |
| super.blendShader = new ColorShader(); |
| break; |
| } |
| case "colordodge": |
| { |
| super.blendShader = new ColorDodgeShader(); |
| break; |
| } |
| case "colorburn": |
| { |
| super.blendShader = new ColorBurnShader(); |
| break; |
| } |
| case "exclusion": |
| { |
| super.blendShader = new ExclusionShader(); |
| break; |
| } |
| case "hue": |
| { |
| super.blendShader = new HueShader(); |
| break; |
| } |
| case "luminosity": |
| { |
| super.blendShader = new LuminosityShader(); |
| break; |
| } |
| case "saturation": |
| { |
| super.blendShader = new SaturationShader(); |
| break; |
| } |
| case "softlight": |
| { |
| super.blendShader = new SoftLightShader(); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (needsDisplayObjectAssignment) |
| { |
| needsDisplayObjectAssignment = false; |
| assignDisplayObjects(); |
| } |
| |
| //FIXME tink address scaleGrid |
| // if (scaleGridChanged) |
| // { |
| // // Don't reset scaleGridChanged since we also check it in updateDisplayList |
| // if( isValidScaleGrid() ) resizeMode = ResizeMode.SCALE; // Force the resizeMode to scale |
| // } |
| |
| //FIXME tink no support for IGraphicElement |
| // Validate element properties |
| // if (numGraphicElements > 0) |
| // { |
| // var length:int = numElements; |
| // for (var i:int = 0; i < length; i++) |
| // { |
| // var element:IGraphicElement = getElementAt(i) as IGraphicElement; |
| // if (element) |
| // element.validateProperties(); |
| // } |
| // } |
| } |
| |
| |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // ISharedDisplayObject |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _redrawRequested:Boolean = false; |
| |
| /** |
| * @private |
| * Contains <code>true</code> when any of the <code>IGraphicElement</code> objects that share |
| * this <code>DisplayObject</code> object needs to redraw. |
| * This is used internally |
| * by the <code>Group</code> class and developers don't typically use this. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get redrawRequested():Boolean |
| { |
| return _redrawRequested; |
| } |
| /** |
| * @private |
| */ |
| public function set redrawRequested(value:Boolean):void |
| { |
| _redrawRequested = value; |
| } |
| |
| |
| /** |
| * @private |
| * Called to assign display objects to graphic elements |
| */ |
| private function assignDisplayObjects():void |
| { |
| var topLayerItems:Vector.<IVisualElement>; |
| var bottomLayerItems:Vector.<IVisualElement>; |
| var keepLayeringEnabled:Boolean = false; |
| var insertIndex:int = 0; |
| |
| // Keep track of the previous IVisualElement. This is used when |
| // assigning DisplayObjects to the IGraphicElements. |
| // If the Group can share its DisplayObject with the IGraphicElements |
| // then initialize the prevItem with this Group object. |
| var prevItem:IVisualElement; |
| // if (canShareDisplayObject) |
| // prevItem = this; |
| |
| // Iterate through all of the items |
| var len:int = numElements; |
| for (var i:int = 0; i < len; i++) |
| { |
| var item:IVisualElement = _mxmlContent[ i ]; |
| |
| if( super.contains( DisplayObject( item ) ) ) |
| { |
| if (layeringMode != ITEM_ORDERED_LAYERING) |
| { |
| var layer:Number = item.depth; |
| if (layer != 0) |
| { |
| if (layer > 0) |
| { |
| if (topLayerItems == null) topLayerItems = new Vector.<IVisualElement>(); |
| topLayerItems.push(item); |
| continue; |
| } |
| else |
| { |
| if (bottomLayerItems == null) bottomLayerItems = new Vector.<IVisualElement>(); |
| bottomLayerItems.push(item); |
| continue; |
| } |
| } |
| } |
| |
| // this should only get called if layer == 0, or we don't care |
| // about layering (layeringMode == ITEM_ORDERED_LAYERING) |
| insertIndex = assignDisplayObjectTo(item, prevItem, insertIndex); |
| prevItem = item; |
| } |
| } |
| |
| // we've done all layer == 0 items. |
| // now let's put the higher z-index ones on next |
| // then we'll handle the ones on bottom, but we'll |
| // insert them in the very beginning (index = 0) |
| |
| if (topLayerItems != null) |
| { |
| keepLayeringEnabled = true; |
| //topLayerItems.sortOn("layer",Array.NUMERIC); |
| GroupBase.sortOnLayer(topLayerItems); |
| len = topLayerItems.length; |
| for (i=0;i<len;i++) |
| { |
| // For layer != 0, we never share display objects |
| insertIndex = assignDisplayObjectTo(topLayerItems[i], null /*prevElement*/, insertIndex); |
| } |
| } |
| |
| if (bottomLayerItems != null) |
| { |
| keepLayeringEnabled = true; |
| insertIndex = 0; |
| |
| //bottomLayerItems.sortOn("layer",Array.NUMERIC); |
| GroupBase.sortOnLayer(bottomLayerItems); |
| len = bottomLayerItems.length; |
| |
| for (i=0;i<len;i++) |
| { |
| // For layer != 0, we never share dsiplay objects |
| insertIndex = assignDisplayObjectTo(bottomLayerItems[i], null /*prevElement*/, insertIndex); |
| } |
| } |
| |
| // If we tried to layer these visual elements and found that we |
| // don't actually need to because layer=0 for all of them, |
| // then lets optimize this next time and just skip the layering step. |
| // If an element gets added that has layer set to something non-zero, then |
| // layeringMode will get set to SPARSE_LAYERING. |
| // If the layer property changes on a current element, invalidateLayering() |
| // will be called and layeringMode will get set to SPARSE_LAYERING. |
| if (keepLayeringEnabled == false) |
| layeringMode = ITEM_ORDERED_LAYERING; |
| |
| // Make sure we do a pass through the graphic elements and redraw |
| // the invalid ones. We should only redraw, no need to redo the layout. |
| super.$invalidateDisplayList(); |
| } |
| |
| |
| /** |
| * @private |
| * Assigns a DisplayObject to the curElement and ensures the DisplayObject |
| * is at insertIndex in the display object list. |
| * |
| * If <code>curElement</code> implements IGraphicElement, then both its |
| * DisplayObject and displayObjectSharingMode will be updated. |
| * |
| * @curElement The current element to assign DisplayObject to |
| * @prevEelement The previous element in the list of elements or null. |
| * @return Returns the display list index after the current element's |
| * DisplayObject. |
| */ |
| private function assignDisplayObjectTo(curElement:IVisualElement, |
| prevElement:IVisualElement, |
| insertIndex:int):int |
| { |
| if( curElement is DisplayObject ) |
| { |
| if( curElement.parent ) super.setChildIndex(curElement as DisplayObject, insertIndex++); |
| } |
| //FIXME tink no support for IGraphicElement |
| // else if (curElement is IGraphicElement) |
| // { |
| // var current:IGraphicElement = IGraphicElement(curElement); |
| // var previous:IGraphicElement = prevElement as IGraphicElement; |
| // |
| // var oldDisplayObject:DisplayObject = current.displayObject; |
| // var oldSharingMode:String = current.displayObjectSharingMode; |
| // |
| // if (previous && previous.canShareWithNext(current) && current.canShareWithPrevious(previous) && |
| // current.setSharedDisplayObject(previous.displayObject)) |
| // { |
| // // If we are the second element in the shared sequence, |
| // // make sure that the first element has the correct displayObjectSharingMode |
| // if (previous.displayObjectSharingMode == DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT) |
| // previous.displayObjectSharingMode = DisplayObjectSharingMode.OWNS_SHARED_OBJECT; |
| // |
| // current.displayObjectSharingMode = DisplayObjectSharingMode.USES_SHARED_OBJECT; |
| // } |
| // else if (prevElement == this && current.setSharedDisplayObject(this)) |
| // { |
| // current.displayObjectSharingMode = DisplayObjectSharingMode.USES_SHARED_OBJECT; |
| // } |
| // else |
| // { |
| // // We don't want to create new DisplayObjects for elements that |
| // // already have created their own their display objects. |
| // var ownsDisplayObject:Boolean = oldSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT; |
| // |
| // // If the element doesn't have a DisplayObject or it doesn't own |
| // // the DisplayObject it currently has, then create a new one |
| // var displayObject:DisplayObject = oldDisplayObject; |
| // if (!ownsDisplayObject || !displayObject) |
| // displayObject = current.createDisplayObject(); |
| // |
| // // Make sure the DisplayObject is at the correct position. |
| // // Check displayObject for null, some graphic elements |
| // // may choose not to create a DisplayObject during this pass. |
| // if (displayObject) |
| // addDisplayObjectToDisplayList(displayObject, insertIndex++); |
| // |
| // current.displayObjectSharingMode = DisplayObjectSharingMode.OWNS_UNSHARED_OBJECT; |
| // } |
| // invalidateAfterAssignment(current, oldSharingMode, oldDisplayObject); |
| // } |
| return insertIndex; |
| } |
| |
| |
| |
| |
| } |
| } |