blob: 6b490a393438fc7b143fcb51ae238299847d8020 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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.components
{
import flash.display.BlendMode;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import mx.core.FlexVersion;
import mx.core.IFlexModule;
import mx.core.IFontContextComponent;
import mx.core.IUIComponent;
import mx.core.IUITextField;
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.styles.AdvancedStyleClient;
import mx.styles.IAdvancedStyleClient;
import mx.styles.ISimpleStyleClient;
import mx.styles.IStyleClient;
import mx.styles.StyleProtoChain;
import spark.components.supportClasses.GroupBase;
import spark.core.DisplayObjectSharingMode;
import spark.core.IGraphicElement;
import spark.core.IGraphicElementContainer;
import spark.core.ISharedDisplayObject;
import spark.events.ElementExistenceEvent;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched when a visual element is added to the content holder.
* <code>event.element</code> is the visual element that was added.
*
* @eventType spark.events.ElementExistenceEvent.ELEMENT_ADD
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Event(name="elementAdd", type="spark.events.ElementExistenceEvent")]
/**
* Dispatched when a visual element is removed from the content holder.
* <code>event.element</code> is the visual element that's being removed.
*
* @eventType spark.events.ElementExistenceEvent.ELEMENT_REMOVE
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Event(name="elementRemove", type="spark.events.ElementExistenceEvent")]
//--------------------------------------
// Styles
//--------------------------------------
/**
* Color of text shadows.
*
* @default #FFFFFF
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="textShadowColor", type="uint", format="Color", inherit="yes", theme="mobile")]
/**
* Alpha of text shadows.
*
* @default 0.55
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="textShadowAlpha", type="Number",inherit="yes", minValue="0.0", maxValue="1.0", theme="mobile")]
//--------------------------------------
// Excluded APIs
//--------------------------------------
[Exclude(name="addChild", kind="method")]
[Exclude(name="addChildAt", kind="method")]
[Exclude(name="removeChild", kind="method")]
[Exclude(name="removeChildAt", kind="method")]
[Exclude(name="setChildIndex", kind="method")]
[Exclude(name="swapChildren", kind="method")]
[Exclude(name="swapChildrenAt", kind="method")]
[Exclude(name="numChildren", kind="property")]
[Exclude(name="getChildAt", kind="method")]
[Exclude(name="getChildIndex", kind="method")]
//--------------------------------------
// Other metadata
//--------------------------------------
[ResourceBundle("components")]
[DefaultProperty("mxmlContent")]
[IconFile("Group.png")]
/**
* The Group class is the base container class for visual elements.
* The Group container takes as children any components that implement
* the IUIComponent interface, and any components that implement
* the IGraphicElement interface.
* Use this container when you want to manage visual children,
* both visual components and graphical components.
*
* <p>To improve performance and minimize application size,
* the Group container cannot be skinned.
* If you want to apply a skin, use the SkinnableContainer instead.</p>
*
* <p><b>Note:</b> The scale grid might not function correctly when there
* are DisplayObject children inside of the Group, such as a component
* or another Group. If the children are GraphicElement objects, and
* they all share the Group's DisplayObject, then the scale grid works
* properly.</p>
*
* <p>Setting any of the following properties on a GraphicElement child
* requires that GraphicElement to create its own DisplayObject,
* thus negating the scale grid properties on the Group.</p>
*
* <pre>
* alpha
* blendMode other than BlendMode.NORMAL or "auto"
* colorTransform
* filters
* mask
* matrix
* rotation
* scaling
* 3D properties
* bounds outside the extent of the Group
* </pre>
*
* <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>&lt;s:Group&gt;</code> tag inherits all of the tag
* attributes of its superclass and adds the following tag attributes:</p>
*
* <pre>
* &lt;s:Group
* <strong>Properties</strong>
* blendMode="auto"
* mxmlContent="null"
* scaleGridBottom="null"
* scaleGridLeft="null"
* scaleGridRight="null"
* scaleGridTop="null"
*
* <strong>Events</strong>
* elementAdd="<i>No default</i>"
* elementRemove="<i>No default</i>"
* /&gt;
* </pre>
*
* @see spark.components.DataGroup
* @see spark.components.SkinnableContainer
*
* @includeExample examples/GroupExample.mxml
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public class Group extends GroupBase implements IVisualElementContainer,
IGraphicElementContainer,
ISharedDisplayObject
{
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function Group():void
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
private var needsDisplayObjectAssignment:Boolean = false;
private var layeringMode:uint = ITEM_ORDERED_LAYERING;
private var numGraphicElements:uint = 0;
private var deferredStyleClients:Dictionary = null; // of IAdvancedStyleClient
private static const ITEM_ORDERED_LAYERING:uint = 0;
private static const SPARSE_LAYERING:uint = 1;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// baselinePosition
//----------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
override public function get baselinePosition():Number
{
if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_5)
return super.baselinePosition;
if (!validateBaselinePosition())
return NaN;
var bElement:IVisualElement = baselinePositionElement;
// If no baselinePositionElement is specified, use the first element
if (bElement == null)
{
for (var i:int = 0; i < numElements; i++)
{
var elt:IVisualElement = getElementAt(i);
if (elt.includeInLayout)
{
bElement = elt;
break;
}
}
}
if (bElement)
return bElement.baselinePosition + bElement.y;
else
return super.baselinePosition;
}
[Inspectable(category="General", enumeration="noScale,scale", defaultValue="noScale")]
/**
* @private
*/
override public function set resizeMode(value:String):void
{
if (isValidScaleGrid())
{
// Force the resize mode to be scale if we
// have set scaleGrid properties
value = ResizeMode.SCALE;
}
super.resizeMode = value;
}
/**
* @private
*/
override public function set scrollRect(value:Rectangle):void
{
// Work-around for Flash Player bug: if GraphicElements share
// the Group's Display Object and cacheAsBitmap is true, the
// scrollRect won't function correctly.
var previous:Boolean = canShareDisplayObject;
super.scrollRect = value;
if (numGraphicElements > 0 && previous != canShareDisplayObject)
invalidateDisplayObjectOrdering();
if (mouseEnabledWhereTransparent && hasMouseListeners)
{
// Re-render our mouse event fill if necessary.
redrawRequested = true;
super.$invalidateDisplayList();
}
}
/**
* @private
*/
override mx_internal function set hasMouseListeners(value:Boolean):void
{
if (mouseEnabledWhereTransparent)
redrawRequested = true;
super.hasMouseListeners = value;
}
/**
* @private
*/
override public function set width(value:Number):void
{
if (_width != value)
{
if (mouseEnabledWhereTransparent && hasMouseListeners)
{
// Re-render our mouse event fill if necessary.
redrawRequested = true;
super.$invalidateDisplayList();
}
}
super.width = value;
}
/**
* @private
*/
override public function set height(value:Number):void
{
if (_height != value)
{
if (mouseEnabledWhereTransparent && hasMouseListeners)
{
// Re-render our mouse event fill if necessary.
redrawRequested = true;
super.$invalidateDisplayList();
}
}
super.height = value;
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// 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;
}
//----------------------------------
// baselinePositionElement
//----------------------------------
private var _baselinePositionElement:IVisualElement;
/**
* The element used to calculate the GroupBase's baselinePosition
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get baselinePositionElement():IVisualElement
{
return _baselinePositionElement;
}
/**
* @private
*/
public function set baselinePositionElement(value:IVisualElement):void
{
if (value === _baselinePositionElement)
return;
_baselinePositionElement = value;
invalidateParentSizeAndDisplayList();
}
//----------------------------------
// 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")]
/**
* A value from the BlendMode class that specifies which blend mode to use.
* A bitmap can be drawn internally in two ways.
* If you have a blend mode enabled or an external clipping mask, the bitmap is drawn
* by adding a bitmap-filled square shape to the vector render.
* If you attempt to set this property to an invalid value,
* Flash Player or Adobe AIR sets the value to <code>BlendMode.NORMAL</code>.
*
* <p>A value of "auto" (the default) is specific to Group's use of
* blendMode and indicates that the underlying blendMode should be
* <code>BlendMode.NORMAL</code> except when <code>alpha</code> is not
* equal to either 0 or 1, when it is set to <code>BlendMode.LAYER</code>.
* This behavior ensures that groups have correct
* compositing of their graphic objects when the group is translucent.</p>
*
* @default "auto"
*
* @see flash.display.DisplayObject#blendMode
* @see flash.display.BlendMode
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
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;
// SDK-29631: Use super.$blendMode instead of super.blendMode
// since Group completely overrides blendMode and we
// want to bypass the extra logic in UIComponent which
// has its own override.
// TODO (egeorgie): figure out whether we can share some
// of that logic in the future.
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();
}
}
}
/**
* override setting of children
*/
override protected function addMXMLChildren(comps:Array):void
{
mxmlContent = comps;
}
//----------------------------------
// mxmlContent
//----------------------------------
private var mxmlContentChanged:Boolean = false;
private var _mxmlContent:Array;
[ArrayElementType("mx.core.IVisualElement")]
/**
* The visual content children for this Group.
*
* This method is used internally by Flex and is not intended for direct
* use by developers.
*
* <p>The content items should only be IVisualElement objects.
* An <code>mxmlContent</code> Array should not be shared between multiple
* Group containers because visual elements can only live in one container
* at a time.</p>
*
* <p>If the content is an Array, do not modify the Array
* directly. Use the methods defined by the Group class instead.</p>
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function set mxmlContent(value:Array):void
{
if (createChildrenCalled)
{
setMXMLContent(value);
}
else
{
mxmlContentChanged = true;
_mxmlContent = value;
// we will validate this in createChildren();
}
}
/**
* @private
*/
mx_internal function getMXMLContent():Array
{
if (_mxmlContent)
return _mxmlContent.concat();
else
return null;
}
/**
* @private
* Adds the elements in <code>mxmlContent</code> to the Group.
* Flex calls this method automatically; you do not call it directly.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
private function setMXMLContent(value:Array):void
{
var i:int;
// if there's old content and it's different than what
// we're trying to set it to, then let's remove all the old
// elements first.
if (_mxmlContent != null && _mxmlContent != value)
{
for (i = _mxmlContent.length - 1; i >= 0; i--)
{
elementRemoved(_mxmlContent[i], i);
}
}
_mxmlContent = (value) ? value.concat() : null; // defensive copy
if (_mxmlContent != null)
{
var n:int = _mxmlContent.length;
for (i = 0; i < n; i++)
{
var elt:IVisualElement = _mxmlContent[i];
// A common mistake is to bind the viewport property of a Scroller
// to a group that was defined in the MXML file with a different parent
if (elt.parent && (elt.parent != this))
throw new Error(resourceManager.getString("components", "mxmlElementNoMultipleParents", [elt]));
elementAdded(elt, i);
}
}
}
//--------------------------------------------------------------------------
//
// Properties: ScaleGrid
//
//--------------------------------------------------------------------------
private var scaleGridChanged:Boolean = false;
// store the scaleGrid into a rectangle to save space (top, left, bottom, right);
private var scaleGridStorageVariable:Rectangle;
//----------------------------------
// scale9Grid
//----------------------------------
/**
* @private
*/
override public function set scale9Grid(value:Rectangle):void
{
if (value != null)
{
scaleGridTop = value.top;
scaleGridBottom = value.bottom;
scaleGridLeft = value.left;
scaleGridRight = value.right;
}
else
{
scaleGridTop = NaN;
scaleGridBottom = NaN;
scaleGridLeft = NaN;
scaleGridRight = NaN;
}
}
//----------------------------------
// scaleGridBottom
//----------------------------------
[Inspectable(category="General")]
/**
* Specifies the bottom coordinate of the scale grid.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get scaleGridBottom():Number
{
if (scaleGridStorageVariable)
return scaleGridStorageVariable.height;
return NaN;
}
public function set scaleGridBottom(value:Number):void
{
if (!scaleGridStorageVariable)
scaleGridStorageVariable = new Rectangle(NaN, NaN, NaN, NaN);
if (value != scaleGridStorageVariable.height)
{
scaleGridStorageVariable.height = value;
scaleGridChanged = true;
invalidateProperties();
invalidateDisplayList();
}
}
//----------------------------------
// scaleGridLeft
//----------------------------------
[Inspectable(category="General")]
/**
* Specifies the left coordinate of the scale grid.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get scaleGridLeft():Number
{
if (scaleGridStorageVariable)
return scaleGridStorageVariable.x;
return NaN;
}
public function set scaleGridLeft(value:Number):void
{
if (!scaleGridStorageVariable)
scaleGridStorageVariable = new Rectangle(NaN, NaN, NaN, NaN);
if (value != scaleGridStorageVariable.x)
{
scaleGridStorageVariable.x = value;
scaleGridChanged = true;
invalidateProperties();
invalidateDisplayList();
}
}
//----------------------------------
// scaleGridRight
//----------------------------------
[Inspectable(category="General")]
/**
* Specifies the right coordinate of the scale grid.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get scaleGridRight():Number
{
if (scaleGridStorageVariable)
return scaleGridStorageVariable.width;
return NaN;
}
public function set scaleGridRight(value:Number):void
{
if (!scaleGridStorageVariable)
scaleGridStorageVariable = new Rectangle(NaN, NaN, NaN, NaN);
if (value != scaleGridStorageVariable.width)
{
scaleGridStorageVariable.width = value;
scaleGridChanged = true;
invalidateProperties();
invalidateDisplayList();
}
}
//----------------------------------
// scaleGridTop
//----------------------------------
[Inspectable(category="General")]
/**
* Specifies the top coordinate of the scale grid.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get scaleGridTop():Number
{
if (scaleGridStorageVariable)
return scaleGridStorageVariable.y;
return NaN;
}
public function set scaleGridTop(value:Number):void
{
if (!scaleGridStorageVariable)
scaleGridStorageVariable = new Rectangle(NaN, NaN, NaN, NaN);
if (value != scaleGridStorageVariable.y)
{
scaleGridStorageVariable.y = value;
scaleGridChanged = true;
invalidateProperties();
invalidateDisplayList();
}
}
private function isValidScaleGrid():Boolean
{
return !isNaN(scaleGridLeft) &&
!isNaN(scaleGridTop) &&
!isNaN(scaleGridRight) &&
!isNaN(scaleGridBottom);
}
//--------------------------------------------------------------------------
//
// Overridden methods: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
* Defer adding IAdvancedStyleClients until createChildren() time. The AdvancedStyleClient's
* styleName might be a component's show inclusion in the IVisualElement hierarchy was also
* deferred.
*/
override public function addStyleClient(styleClient:IAdvancedStyleClient):void
{
if (!createChildrenCalled)
{
if (!deferredStyleClients)
deferredStyleClients = new Dictionary(true);
deferredStyleClients[styleClient] = true;
}
else
{
super.addStyleClient(styleClient);
}
}
/**
* @private
*/
override public function removeStyleClient(styleClient:IAdvancedStyleClient):void
{
if (deferredStyleClients && !createChildrenCalled)
delete deferredStyleClients[styleClient];
else
super.removeStyleClient(styleClient);
}
/**
* @private
* Whether createChildren() has been called or not.
* We use this in the setter for mxmlContent to know
* whether to validate the value immediately, or just
* wait to let createChildren() do it.
*/
private var createChildrenCalled:Boolean = false;
/**
* @private
*/
override protected function createChildren():void
{
super.createChildren();
createChildrenCalled = true;
if (mxmlContentChanged)
{
mxmlContentChanged = false;
setMXMLContent(_mxmlContent);
}
if (deferredStyleClients)
{
for (var obj:Object in deferredStyleClients)
{
var styleClient:IAdvancedStyleClient = obj as IAdvancedStyleClient;
if (styleClient)
super.addStyleClient(styleClient);
}
deferredStyleClients = null;
}
}
/**
* @private
*/
override public function validateProperties():void
{
super.validateProperties();
// Property validation happens top-down, so now let's
// validate graphic element properties after
// calling super.validateProperties()
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();
}
}
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
invalidatePropertiesFlag = false;
if (blendModeChanged)
{
blendModeChanged = false;
// Figure out the correct blendMode value
// to set.
// SDK-29631: Use super.$blendMode instead of super.blendMode
// since Group completely overrides blendMode and we
// want to bypass the extra logic in UIComponent which
// has its own override.
// TODO (egeorgie): figure out whether we can share some
// of that logic in the future.
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;
}
}
}
}
// Due to dependent properties alpha and blendMode there may be a need
// for a second pass at committing properties (to ensure our new
// blendMode or blendShader is assigned to our underlying display
// object).
if (invalidatePropertiesFlag)
{
super.commitProperties();
invalidatePropertiesFlag = false;
}
if (needsDisplayObjectAssignment)
{
needsDisplayObjectAssignment = false;
assignDisplayObjects();
}
if (scaleGridChanged)
{
// Don't reset scaleGridChanged since we also check it in updateDisplayList
if (isValidScaleGrid())
resizeMode = ResizeMode.SCALE; // Force the resizeMode to scale
}
}
/**
* @private
*/
override public function validateSize(recursive:Boolean = false):void
{
// Since IGraphicElement is not ILayoutManagerClient, we need to make sure we
// validate sizes of the elements, even in cases where recursive==false.
// Size validation happens bottom-up, so now let's
// validate graphic element size before
// calling super.validateSize()
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.validateSize();
}
}
super.validateSize(recursive);
}
/**
* @private
*/
override public function setActualSize(w:Number, h:Number):void
{
if (_width != w || _height != h)
{
if (mouseEnabledWhereTransparent && hasMouseListeners)
{
// Re-render our mouse event fill if necessary.
redrawRequested = true;
super.$invalidateDisplayList();
}
}
super.setActualSize(w, h);
}
/**
* @private
*/
override public function validateDisplayList():void
{
// call super.validateDisplayList() and let updateDisplayList() run
super.validateDisplayList();
// If the DisplayObject assignment is still not completed, then postpone validation
// of the GraphicElements. invalidateDisplayList() will be called during the next
// commitProperties() call since needsDisplayObjectAssignment=true,
// so we will be re-running validateDisplayList() anyways
if (needsDisplayObjectAssignment && invalidatePropertiesFlag)
return;
// DisplayList validation happens top-down, so we should
// validate graphic element DisplayList after
// calling super.validateDisplayList(). This is
// gets tricky because of graphic-element sharing. We clear
// Group's graphic's object in updateDisplayList() and handle the
// rest of the DisplayList validation in here.
// Iterate through the graphic elements. If an element has a displayObject that has been
// invalidated, then validate all graphic elements that draw to this displayObject.
// The algorithm assumes that all of the elements that share a displayObject are in between
// the element with the shared displayObject and the next element that has a displayObject.
var sharedDisplayObject:ISharedDisplayObject = this;
if (numGraphicElements > 0)
{
var length:int = numElements;
for (var i:int = 0; i < length; i++)
{
var element:IGraphicElement = getElementAt(i) as IGraphicElement;
if (!element)
continue;
// Do a special check for layer, we may stumble upon an element with layer != 0
// before we're done with the current shared sequence and we don't want to mark
// the sequence as valid, until we reach the next sequence.
if (element.depth == 0)
{
// Is this the start of a new shared sequence?
if (element.displayObjectSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT)
{
// We have finished redrawing the previous sequence
if (sharedDisplayObject)
sharedDisplayObject.redrawRequested = false;
// Start the new sequence
sharedDisplayObject = element.displayObject as ISharedDisplayObject;
}
if (!sharedDisplayObject || sharedDisplayObject.redrawRequested)
element.validateDisplayList();
}
else
{
// If we have layering, we don't share the display objects.
// Don't update the current sharedDisplayObject
var elementDisplayObject:ISharedDisplayObject = element.displayObject as ISharedDisplayObject;
if (!elementDisplayObject || elementDisplayObject.redrawRequested)
{
element.validateDisplayList();
if (elementDisplayObject)
elementDisplayObject.redrawRequested = false;
}
}
}
}
// Mark the last shared displayObject valid
if (sharedDisplayObject)
sharedDisplayObject.redrawRequested = false;
}
/**
* @private
*/
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
// let user's code (layout) run first before dealing with graphic element
// sharing because that's when redraws can be requested
super.updateDisplayList(unscaledWidth, unscaledHeight);
// Clear the group's graphic because graphic elements might be drawing to it
// This isn't needed for DataGroup because there's no DisplayObject sharing
// This code exists in updateDisplayList() as opposed to validateDisplayList()
// because of compatibility issues since most of this code was
// refactored from updateDisplayList() and in to validateDisplayList(). User's code
// already assumed that they could call super.updateDisplayList() and then be able to draw
// into the Group's graphics object. Because of that, the graphics.clear() call is left
// in updateDisplayList() instead of in validateDisplayList() with the rest of the graphic
// element sharing code.
var sharedDisplayObject:ISharedDisplayObject = this;
if (sharedDisplayObject.redrawRequested)
{
// clear the graphics here. The pattern is usually to call graphics.clear()
// before calling super.updateDisplayList() so what happens in super.updateDisplayList()
// isn't erased. However, in this case, what happens in super.updateDisplayList() isn't
// much, and we want to make sure super.updateDisplayList() runs first since the layout
// is what actually triggers the the shareDisplayObject to request to be redrawn.
graphics.clear();
drawBackground();
// If a scaleGrid is set, make sure the extent of the groups bounds are filled so
// the player will scale our contents as expected.
if (isValidScaleGrid() && resizeMode == ResizeMode.SCALE)
{
graphics.lineStyle();
graphics.beginFill(0, 0);
graphics.drawRect(0, 0, 1, 1);
graphics.drawRect(measuredWidth - 1, measuredHeight - 1, 1, 1);
graphics.endFill();
}
}
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
* TODO (rfrishbe): Most of this code is a duplicate of UIComponent::notifyStyleChangeInChildren,
* refactor as appropriate to avoid code duplication once we have a common
* child iterator between UIComponent and Group.
*/
override public function notifyStyleChangeInChildren(
styleProp:String, recursive:Boolean):void
{
if (mxmlContentChanged || !recursive)
return;
var n:int = numElements;
for (var i:int = 0; i < n; i++)
{
var child:ISimpleStyleClient = getElementAt(i) as ISimpleStyleClient;
if (child)
{
child.styleChanged(styleProp);
if (child is IStyleClient)
IStyleClient(child).notifyStyleChangeInChildren(styleProp, recursive);
}
}
if (advanceStyleClientChildren != null)
{
for (var styleClient:Object in advanceStyleClientChildren)
{
var iAdvanceStyleClientChild:IAdvancedStyleClient = styleClient
as IAdvancedStyleClient;
if (iAdvanceStyleClientChild)
{
iAdvanceStyleClientChild.styleChanged(styleProp);
}
}
}
}
/**
* @private
* TODO (rfrishbe): Most of this code is a duplicate of UIComponent::regenerateStyleCache,
* refactor as appropriate to avoid code duplication once we have a common
* child iterator between UIComponent and Group.
*/
override public function regenerateStyleCache(recursive:Boolean):void
{
// Regenerate the proto chain for this object
initProtoChain();
// Recursively call this method on each child.
var n:int = numElements;
for (var i:int = 0; i < n; i++)
{
var child:IVisualElement = getElementAt(i);
if (child is IStyleClient)
{
// Does this object already have a proto chain?
// If not, there's no need to regenerate a new one.
if (IStyleClient(child).inheritingStyles !=
StyleProtoChain.STYLE_UNINITIALIZED)
{
IStyleClient(child).regenerateStyleCache(recursive);
}
}
else if (child is IUITextField)
{
// Does this object already have a proto chain?
// If not, there's no need to regenerate a new one.
if (IUITextField(child).inheritingStyles)
StyleProtoChain.initTextField(IUITextField(child));
}
}
// Call this method on each non-visual StyleClient
if (advanceStyleClientChildren != null)
{
for (var styleClient:Object in advanceStyleClientChildren)
{
var iAdvanceStyleClientChild:IAdvancedStyleClient = styleClient
as IAdvancedStyleClient;
if (iAdvanceStyleClientChild &&
iAdvanceStyleClientChild.inheritingStyles !=
StyleProtoChain.STYLE_UNINITIALIZED)
{
iAdvanceStyleClientChild.regenerateStyleCache(recursive);
}
}
}
}
/**
*
* @private
* Spark components get their children created earlier than createChildren
* but not parented until createChildren
*/
override public function parentChanged(p:DisplayObjectContainer):void
{
super.parentChanged(p);
var children:Array = this.MXMLDescriptor;
if (children && !processedMXMLDescriptors)
{
generateMXMLInstances(document, children);
processedMXMLDescriptors = true;
}
}
//--------------------------------------------------------------------------
//
// Content management
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function get numElements():int
{
if (_mxmlContent == null)
return 0;
return _mxmlContent.length;
}
/**
* @private
*/
override public function getElementAt(index:int):IVisualElement
{
// check for RangeError:
checkForRangeError(index);
return _mxmlContent[index];
}
/**
* @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
*/
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;
}
/**
* @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 (!mxmlContentChanged)
elementAdded(element, index);
scaleGridChanged = true;
return element;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function removeElement(element:IVisualElement):IVisualElement
{
return removeElementAt(getElementIndex(element));
}
/**
* @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 (!mxmlContentChanged)
elementRemoved(element, index);
_mxmlContent.splice(index, 1);
return element;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function removeAllElements():void
{
for (var i:int = numElements - 1; i >= 0; i--)
{
removeElementAt(i);
}
}
/**
* @private
*/
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
*/
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);
if (getElementIndex(element) == index)
return;
removeElement(element);
addElementAt(element, index);
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function swapElements(element1:IVisualElement, element2:IVisualElement):void
{
swapElementsAt(getElementIndex(element1), getElementIndex(element2));
}
/**
* @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);
// 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;
}
else if (index1 == index2)
return;
var element1:IVisualElement = _mxmlContent[index1];
var element2:IVisualElement = _mxmlContent[index2];
// Make sure we do the proper invalidations, but don't dispatch events
if (!mxmlContentChanged)
{
elementRemoved(element1, index1, false /*notifyListeners*/);
elementRemoved(element2, index2, false /*notifyListeners*/);
}
// 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 (!mxmlContentChanged)
{
elementAdded(element2, index1, false /*notifyListeners*/);
elementAdded(element1, index2, false /*notifyListeners*/);
}
}
//--------------------------------------------------------------------------
//
// Content management (internal)
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function invalidateLayering():void
{
if (layeringMode == ITEM_ORDERED_LAYERING)
layeringMode = SPARSE_LAYERING;
invalidateDisplayObjectOrdering();
}
/**
* 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)
{
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
{
addDisplayObjectToDisplayList(DisplayObject(element), index);
}
}
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))
{
numGraphicElements--;
removingGraphicElementChild(element as IGraphicElement);
}
else if (childDO && childDO.parent == this)
{
super.removeChild(childDO);
}
invalidateDisplayObjectOrdering();
invalidateSize();
invalidateDisplayList();
if (layout)
layout.elementRemoved(index);
}
/**
* @private
*/
mx_internal function addingGraphicElementChild(child:IGraphicElement):void
{
// Special case (defensive coding) - if the element was previously
// a child of this Group, and it didn't release its previously assigned
// DisplayObject when its parent changed, and there's the possibility that
// the Group may assign it the same DisplayObject and the same sharing mode,
// we still need to invalidate and redraw as the displayObject likely has
// been redrawn while the element was not a child of this Group.
if (child.displayObject && child.displayObjectSharingMode == DisplayObjectSharingMode.USES_SHARED_OBJECT)
invalidateGraphicElementDisplayList(child);
child.parentChanged(this);
// Sets up the inheritingStyles and nonInheritingStyles objects
// and their proto chains so that getStyle() works.
// If this object already has some children,
// then reinitialize the children's proto chains.
if (child is IStyleClient)
IStyleClient(child).regenerateStyleCache(true);
if (child is ISimpleStyleClient)
ISimpleStyleClient(child).styleChanged(null);
if (child is IStyleClient)
IStyleClient(child).notifyStyleChangeInChildren(null, true);
}
/**
* @private
*/
mx_internal function removingGraphicElementChild(child:IGraphicElement):void
{
// First discard the displayObject, as child may decide to destroy it
// when parent changes.
discardDisplayObject(child);
child.parentChanged(null);
}
/**
* Removes the element's <code>DisplayObject</code> from this <code>Group's</code>
* display list.
*
* The <code>Group</code> also ensures any elements that share the
* <code>DisplayObject</code> is redrawn.
*
* <p>This method doesn't necessarily trigger new <code>DisplayObject</code>
* reassignment for the passed in <code>element</code>.
* To request new display object reassignment, call the
* <code>invalidateGraphicElementSharing()</code> method.</p>
*
* @param element The graphic element whose display object is discarded.
* @see #invalidateGraphicElementSharing
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
mx_internal function discardDisplayObject(element:IGraphicElement):void
{
var oldDisplayObject:DisplayObject = element.displayObject;
if (!oldDisplayObject)
return;
// If the element created the display object
if (element.displayObjectSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT &&
oldDisplayObject.parent == this)
{
super.removeChild(oldDisplayObject);
// Redo the shared sequences.
invalidateDisplayObjectOrdering();
}
else if (oldDisplayObject is ISharedDisplayObject)
{
// Redraw the shared sequence
ISharedDisplayObject(oldDisplayObject).redrawRequested = true;
// 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
*
* Returns true if the Group's display object can be shared with graphic elements
* inside the group
*/
private function get canShareDisplayObject():Boolean
{
// Work-around for Flash Player bug: if GraphicElements share
// the Group's Display Object and cacheAsBitmap is true, the
// scrollRect won't function correctly.
// We can't even check the cacheAsBitmap property since that will
// cause a back buffer allocation, which is another FP bug.
if (scrollRect)
return false;
// We can't share ourselves if we're in blendMode != normal, or we have
// to deal with any layering. The reason is because we handle layer = 0 first
// in our implementation, and we don't want those to use our display object to
// draw into because there could be something further down the line that has
// layer < 0. The cases where we are in blendMode != normal is when blendMode
// has been explicitly set or blendMode is auto and there is a fractional alpha.
return (_blendMode == "normal" || _blendMode == "auto" && (alpha == 0 || alpha == 1))
&& (layeringMode == ITEM_ORDERED_LAYERING);
}
/**
* @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.
*/
private function invalidateDisplayObjectOrdering():Boolean
{
if (layeringMode == SPARSE_LAYERING || numGraphicElements > 0)
{
needsDisplayObjectAssignment = true;
invalidateProperties();
return true;
}
return false;
}
/**
* @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 = getElementAt(i);
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)
{
super.setChildIndex(curElement as DisplayObject, insertIndex++);
}
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;
}
/**
* @private
* Performs invalidation of the old and new shared sequenences.
* If necesary, removes the old display object from the list.
*/
private function invalidateAfterAssignment(element:IGraphicElement,
oldSharingMode:String,
oldDisplayObject:DisplayObject):void
{
// Make sure we remove or mark for redraw the old displayObject
var displayObject:DisplayObject = element.displayObject;
var sharingMode:String = element.displayObjectSharingMode;
if (oldDisplayObject == displayObject && sharingMode == oldSharingMode)
return;
// Make sure we redraw the display object
if (displayObject is ISharedDisplayObject)
ISharedDisplayObject(displayObject).redrawRequested = true;
// Old display object also needs to be redrawn, in case any other GE still uses it.
if (oldDisplayObject is ISharedDisplayObject)
ISharedDisplayObject(oldDisplayObject).redrawRequested = true;
// Make sure we remove the old display object, if needed
if (oldDisplayObject && oldDisplayObject.parent == this &&
oldDisplayObject != displayObject && oldSharingMode != DisplayObjectSharingMode.USES_SHARED_OBJECT)
super.removeChild(oldDisplayObject);
}
/**
* @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);
}
/**
* Notify the host component that an element has changed and needs to be redrawn.
* Group calls the <code>validateDisplayList()</code> method on the IGraphicElement
* to give it a chance to redraw.
*
* @param element The element that has changed.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function invalidateGraphicElementDisplayList(element:IGraphicElement):void
{
if (element.displayObject is ISharedDisplayObject)
ISharedDisplayObject(element.displayObject).redrawRequested = true;
// Invalidate display list only, no need to run the layout.
super.$invalidateDisplayList();
}
/**
* Notify the host component that an element changed and needs to validate properties.
* Group calls the <code>validateProperties()</code> method on the IGraphicElement
* to give it a chance to commit its properties.
*
* @param element The element that has changed.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function invalidateGraphicElementProperties(element:IGraphicElement):void
{
invalidateProperties();
}
/**
* Notify the host component that an element size has changed.
* Group calls the <code>validateSize()</code> method on the IGraphicElement
* to give it a chance to validate its size.
*
* @param element The element that has changed size.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function invalidateGraphicElementSize(element:IGraphicElement):void
{
// Invalidate the size only, no need to run the layout.
// Later on, if the size changes, then a layout pass will be triggered.
super.$invalidateSize();
}
/**
* Notify the host that an element layer has changed.
* Group re-evaluates the sequences of elements with shared DisplayObjects
* and may re-assign the DisplayObjects and redraw the sequences as a result.
*
* @param element The element that has changed size.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function invalidateGraphicElementSharing(element:IGraphicElement):void
{
// One of our children have told us they might need a displayObject
invalidateDisplayObjectOrdering();
}
/**
* @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 to ensure we set redrawRequested when appropriate.
*/
override public function set mouseEnabledWhereTransparent(value:Boolean):void
{
if (value == mouseEnabledWhereTransparent)
return;
super.mouseEnabledWhereTransparent = value;
redrawRequested = true;
}
//--------------------------------------------------------------------------
//
// 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 or the background for the container
* needs to be redrawn.
* 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;
}
}
}