blob: 0953233df8785558e855d14dd5fb2faeee7702d0 [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 mx.core
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Graphics;
import flash.display.InteractiveObject;
import flash.display.Loader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.system.ApplicationDomain;
import flash.text.TextField;
import flash.text.TextLineMetrics;
import flash.ui.Keyboard;
import flash.utils.getDefinitionByName;
import mx.binding.BindingManager;
import mx.containers.utilityClasses.PostScaleAdapter;
import mx.controls.HScrollBar;
import mx.controls.VScrollBar;
import mx.controls.listClasses.IListItemRenderer;
import mx.controls.scrollClasses.ScrollBar;
import mx.core.IUITextField;
import mx.events.ChildExistenceChangedEvent;
import mx.events.FlexEvent;
import mx.events.IndexChangedEvent;
import mx.events.ScrollEvent;
import mx.events.ScrollEventDetail;
import mx.events.ScrollEventDirection;
import mx.geom.RoundedRectangle;
import mx.managers.IFocusManager;
import mx.managers.IFocusManagerContainer;
import mx.managers.ILayoutManagerClient;
import mx.managers.ISystemManager;
import mx.styles.CSSStyleDeclaration;
import mx.styles.ISimpleStyleClient;
import mx.styles.IStyleClient;
import mx.styles.StyleProtoChain;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched after a child has been added to a container.
*
* <p>The childAdd event is dispatched when the <code>addChild()</code>
* or <code>addChildAt()</code> method is called.
* When a container is first created, the <code>addChild()</code>
* method is automatically called for each child component declared
* in the MXML file.
* The <code>addChildAt()</code> method is automatically called
* whenever a Repeater object adds or removes child objects.
* The application developer may also manually call these
* methods to add new children.</p>
*
* <p>At the time when this event is sent, the child object has been
* initialized, but its width and height have not yet been calculated,
* and the child has not been drawn on the screen.
* If you want to be notified when the child has been fully initialized
* and rendered, then register as a listener for the child's
* <code>creationComplete</code> event.</p>
*
* @eventType mx.events.ChildExistenceChangedEvent.CHILD_ADD
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="childAdd", type="mx.events.ChildExistenceChangedEvent")]
/**
* Dispatched after the index (among the container children)
* of a container child changes.
* This event is only dispatched for the child specified as the argument to
* the <code>setChildIndex()</code> method; it is not dispatched
* for any other child whose index changes as a side effect of the call
* to the <code>setChildIndex()</code> method.
*
* <p>The child's index is changed when the
* <code>setChildIndex()</code> method is called.</p>
*
* @eventType mx.events.IndexChangedEvent.CHILD_INDEX_CHANGE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="childIndexChange", type="mx.events.IndexChangedEvent")]
/**
* Dispatched before a child of a container is removed.
*
* <p>This event is delivered when any of the following methods is called:
* <code>removeChild()</code>, <code>removeChildAt()</code>,
* or <code>removeAllChildren()</code>.</p>
*
* @eventType mx.events.ChildExistenceChangedEvent.CHILD_REMOVE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="childRemove", type="mx.events.ChildExistenceChangedEvent")]
/**
* Dispatched when the <code>data</code> property changes.
*
* <p>When a container is used as a renderer in a List or other components,
* the <code>data</code> property is used pass to the container
* the data to display.</p>
*
* @eventType mx.events.FlexEvent.DATA_CHANGE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="dataChange", type="mx.events.FlexEvent")]
/**
* Dispatched when the user manually scrolls the container.
*
* <p>The event is dispatched when the scroll position is changed using
* either the mouse (e.g. clicking on the scrollbar's "down" button)
* or the keyboard (e.g., clicking on the down-arrow key).
* However, this event is not dispatched if the scroll position
* is changed programatically (e.g., setting the value of the
* <code>horizontalScrollPosition</code> property).
* The <code>viewChanged</code> event is delivered whenever the
* scroll position is changed, either manually or programatically.</p>
*
* <p>At the time when this event is dispatched, the scrollbar has
* been updated to the new position, but the container's child objects
* have not been shifted to reflect the new scroll position.</p>
*
* @eventType mx.events.ScrollEvent.SCROLL
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="scroll", type="mx.events.ScrollEvent")]
//--------------------------------------
// Styles
//--------------------------------------
include "../styles/metadata/BarColorStyle.as"
include "../styles/metadata/BorderStyles.as"
include "../styles/metadata/ContainerBackgroundStyles.as"
include "../styles/metadata/PaddingStyles.as"
include "../styles/metadata/TextStyles.as"
/**
* Accent color used by component skins. The default button skin uses this color
* to tint the background. Slider track highlighting uses this color.
*
* @default #0099FF
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="accentColor", type="uint", format="Color", inherit="yes", theme="spark")]
/**
* If a background image is specified, this style specifies
* whether it is fixed with regard to the viewport (<code>"fixed"</code>)
* or scrolls along with the content (<code>"scroll"</code>).
*
* @default "scroll"
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="backgroundAttachment", type="String", inherit="no")]
/**
* The alpha of the content background for this component.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="contentBackgroundAlpha", type="Number", inherit="yes", theme="spark")]
/**
* Color of the content area of the component.
*
* @default 0xFFFFFF
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="contentBackgroundColor", type="uint", format="Color", inherit="yes", theme="spark")]
/**
* Radius of component corners.
* The default value depends on the component class;
* if not overridden for the class, the default value
* is 0.
* The default value for ApplicationControlBar is 5.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="cornerRadius", type="Number", format="Length", inherit="no", theme="halo, spark")]
/**
* The alpha value for the overlay that is placed on top of the
* container when it is disabled.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="disabledOverlayAlpha", type="Number", inherit="no")]
/**
* Color of focus ring when the component is in focus
*
* @default 0x70B2EE
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="focusColor", type="uint", format="Color", inherit="yes", theme="spark")]
/**
* The name of the horizontal scrollbar style.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="horizontalScrollBarStyleName", type="String", inherit="no")]
/**
* The name of the vertical scrollbar style.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="verticalScrollBarStyleName", type="String", inherit="no")]
/**
* Number of pixels between the container's bottom border
* and the bottom of its content area.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingBottom", type="Number", format="Length", inherit="no")]
/**
* Number of pixels between the container's top border
* and the top of its content area.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingTop", type="Number", format="Length", inherit="no")]
/**
* Color of any symbol of a component. Examples include the check mark of a CheckBox or
* the arrow of a ScrollBar button.
*
* @default 0x000000
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="symbolColor", type="uint", format="Color", inherit="yes", theme="spark")]
[ResourceBundle("core")]
/**
* The Container class is an abstract base class for components that
* controls the layout characteristics of child components.
* You do not create an instance of Container in an application.
* Instead, you create an instance of one of Container's subclasses,
* such as Canvas or HBox.
*
* <p>The Container class contains the logic for scrolling, clipping,
* and dynamic instantiation.
* It contains methods for adding and removing children.
* It also contains the <code>getChildAt()</code> method, and the logic
* for drawing the background and borders of containers.</p>
*
* @mxml
*
* Flex Framework containers inherit the following attributes from the Container
* class:</p>
*
* <pre>
* &lt;mx:<i>tagname</i>
* <strong>Properties</strong>
* autoLayout="true|false"
* clipContent="true|false"
* creationIndex="undefined"
* creationPolicy="auto|all|queued|none"
* defaultButton="<i>No default</i>"
* horizontalLineScrollSize="5"
* horizontalPageScrollSize="0"
* horizontalScrollBar="null"
* horizontalScrollPolicy="auto|on|off"
* horizontalScrollPosition="0"
* icon="undefined"
* label=""
* verticalLineScrollSize="5"
* verticalPageScrollSize="0"
* verticalScrollBar="null"
* verticalScrollPolicy="auto|on|off"
* verticalScrollPosition="0"
*
* <strong>Styles</strong>
* backgroundAlpha="1.0"
* backgroundAttachment="scroll"
* backgroundColor="undefined"
* backgroundDisabledColor="undefined"
* backgroundImage="undefined"
* backgroundSize="auto"
* <i> For the Application container only,</i> backgroundSize="100%"
* barColor="undefined"
* borderColor="0xAAB3B3"
* borderSides="left top right bottom"
* borderSkin="mx.skins.halo.HaloBorder"
* borderStyle="inset"
* borderThickness="1"
* color="0x0B333C"
* cornerRadius="0"
* disabledColor="0xAAB3B3"
* disbledOverlayAlpha="undefined"
* dropShadowColor="0x000000"
* dropShadowEnabled="false"
* fontAntiAliasType="advanced"
* fontfamily="Verdana"
* fontGridFitType="pixel"
* fontSharpness="0""
* fontSize="10"
* fontStyle="normal"
* fontThickness="0"
* fontWeight="normal"
* horizontalScrollBarStyleName="undefined"
* paddingBottom="0"
* paddingLeft="0"
* paddingRight="0"
* paddingTop="0"
* shadowDirection="center"
* shadowDistance="2"
* textAlign="left"
* textDecoration="none|underline"
* textIndent="0"
* verticalScrollBarStyleName="undefined"
*
* <strong>Events</strong>
* childAdd="<i>No default</i>"
* childIndexChange="<i>No default</i>"
* childRemove="<i>No default</i>"
* dataChange="<i>No default</i>"
* scroll="<i>No default</i>"
* &gt;
* ...
* <i>child tags</i>
* ...
* &lt;/mx:<i>tagname</i>&gt;
* </pre>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class Container extends UIComponent
implements IContainer, IDataRenderer,
IFocusManagerContainer, IListItemRenderer,
IRawChildrenContainer, IChildList, IVisualElementContainer,
INavigatorContent
{
include "../core/Version.as"
//--------------------------------------------------------------------------
//
// Notes: Child management
//
//--------------------------------------------------------------------------
/*
Although at the level of a Flash DisplayObjectContainer, all
children are equal, in a Flex Container some children are "more
equal than others". (George Orwell, "Animal Farm")
In particular, Flex distinguishes between content children and
non-content (or "chrome") children. Content children are the kind
that can be specified in MXML. If you put several controls
into a VBox, those are its content children. Non-content children
are the other ones that you get automatically, such as a
background/border, scrollbars, the titlebar of a Panel,
AccordionHeaders, etc.
Most application developers are uninterested in non-content children,
so Container overrides APIs such as numChildren and getChildAt()
to deal only with content children. For example, Container, keeps
its own _numChildren counter.
Container assumes that content children are contiguous, and that
non-content children come before or after the content children.
In order words, Container partitions DisplayObjectContainer's
index range into three parts:
A B C D E F G H I
0 1 2 3 4 5 6 7 8 <- index for all children
0 1 2 3 <- index for content children
The content partition contains the content children D E F G.
The pre-content partition contains the non-content children
A B C that always stay before the content children.
The post-content partition contains the non-content children
H I that always stay after the content children.
Container maintains two state variables, _firstChildIndex
and _numChildren, which keep track of the partitioning.
In this example, _firstChildIndex would be 3 and _numChildren
would be 4.
*/
//--------------------------------------------------------------------------
//
// Class constants
//
//--------------------------------------------------------------------------
/**
* @private
* See changedStyles, below
*/
private static const MULTIPLE_PROPERTIES:String = "<MULTIPLE>";
//--------------------------------------------------------------------------
//
// Class methods
//
//--------------------------------------------------------------------------
mx_internal function getLayoutChildAt(index:int):IUIComponent
{
return PostScaleAdapter.getCompatibleIUIComponent(getChildAt(index));
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function Container()
{
super();
tabEnabled = false;
tabFocusEnabled = false;
showInAutomationHierarchy = false;
// If available, get soft-link to the RichEditableText class
// to use in keyDownHandler().
if (ApplicationDomain.currentDomain.hasDefinition(
"spark.components.RichEditableText"))
{
richEditableTextClass =
Class(ApplicationDomain.currentDomain.getDefinition(
"spark.components.RichEditableText"));
}
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
//----------------------------------
// Child creation vars
//----------------------------------
/**
* The creation policy of this container.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var actualCreationPolicy:String;
/**
* @private
*/
private var numChildrenBefore:int;
/**
* @private
*/
private var recursionFlag:Boolean = true;
//----------------------------------
// Layout vars
//----------------------------------
/**
* @private
* Remember when a child has been added or removed.
* When that occurs, we want to run the LayoutManager
* (even if autoLayout is false).
*/
private var forceLayout:Boolean = false;
/**
* @private
*/
mx_internal var doingLayout:Boolean = false;
//----------------------------------
// Style vars
//----------------------------------
/**
* @private
* If this value is non-null, then we need to recursively notify children
* that a style property has changed. If one style property has changed,
* this field holds the name of the style that changed. If multiple style
* properties have changed, then the value of this field is
* Container.MULTIPLE_PROPERTIES.
*/
private var changedStyles:String = null;
//----------------------------------
// Scrolling vars
//----------------------------------
/**
* @private
*/
private var _creatingContentPane:Boolean = false;
/**
* Containers use an internal content pane to control scrolling.
* The <code>creatingContentPane</code> is <code>true</code> while the container is creating
* the content pane so that some events can be ignored or blocked.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get creatingContentPane():Boolean
{
return _creatingContentPane;
}
public function set creatingContentPane(value:Boolean):void
{
_creatingContentPane = value;
}
/**
* @private
* A box that takes up space in the lower right corner,
* between the horizontal and vertical scrollbars.
*/
protected var whiteBox:Shape;
/**
* @private
*/
mx_internal var contentPane:Sprite = null;
/**
* @private
* Flags that remember what work to do during the next updateDisplayList().
*/
private var scrollPropertiesChanged:Boolean = false;
private var scrollPositionChanged:Boolean = true;
private var horizontalScrollPositionPending:Number;
private var verticalScrollPositionPending:Number;
/**
* @private
* Cached values describing the total size of the content being scrolled
* and the size of the area in which the scrolled content is displayed.
*/
private var scrollableWidth:Number = 0;
private var scrollableHeight:Number = 0;
private var viewableWidth:Number = 0;
private var viewableHeight:Number = 0;
//----------------------------------
// Other vars
//----------------------------------
/**
* @private
* The border/background object.
*/
mx_internal var border:IFlexDisplayObject;
/**
* @private
* Sprite used to block user input when the container is disabled.
*/
mx_internal var blocker:Sprite;
/**
* @private
* Keeps track of the number of mouse events we are listening for
*/
private var mouseEventReferenceCount:int = 0;
/**
* @private
* Soft-link to RichEditableText class object, if available.
*/
private var richEditableTextClass:Class;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// baselinePosition
//----------------------------------
/**
* @private
* The baselinePosition of a Container is calculated
* as if there was a UITextField using the Container's styles
* whose top is at viewMetrics.top.
*/
override public function get baselinePosition():Number
{
if (!validateBaselinePosition())
return NaN;
// Unless the height is very small, the baselinePosition
// of a generic Container is calculated as if there was
// a UITextField using the Container's styles
// whose top is at viewMetrics.top.
// If the height is small, the baselinePosition is calculated
// as if there were text within whose ascent the Container
// is vertically centered.
// At the crossover height, these two calculations
// produce the same result.
var lineMetrics:TextLineMetrics = measureText("Wj");
if (height < 2 * viewMetrics.top + 4 + lineMetrics.ascent)
return int(height + (lineMetrics.ascent - height) / 2);
return viewMetrics.top + 2 + lineMetrics.ascent;
}
//----------------------------------
// contentMouseX
//----------------------------------
/**
* @copy mx.core.UIComponent#contentMouseX
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get contentMouseX():Number
{
if (contentPane)
return contentPane.mouseX;
return super.contentMouseX;
}
//----------------------------------
// contentMouseY
//----------------------------------
/**
* @copy mx.core.UIComponent#contentMouseY
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get contentMouseY():Number
{
if (contentPane)
return contentPane.mouseY;
return super.contentMouseY;
}
//----------------------------------
// doubleClickEnabled
//----------------------------------
/**
* @private
* Propagate to children.
*/
override public function set doubleClickEnabled(value:Boolean):void
{
super.doubleClickEnabled = value;
if (contentPane)
{
var n:int = contentPane.numChildren;
for (var i:int = 0; i < n; i++)
{
var child:InteractiveObject =
contentPane.getChildAt(i) as InteractiveObject;
if (child)
child.doubleClickEnabled = value;
}
}
}
//----------------------------------
// enabled
//----------------------------------
[Inspectable(category="General", enumeration="true,false", defaultValue="true")]
/**
* @private
*/
override public function set enabled(value:Boolean):void
{
super.enabled = value;
// Scrollbars must be enabled/disabled when this container is.
if (horizontalScrollBar)
horizontalScrollBar.enabled = value;
if (verticalScrollBar)
verticalScrollBar.enabled = value;
invalidateProperties();
if (border && border is IInvalidating)
IInvalidating(border).invalidateDisplayList();
}
//----------------------------------
// focusPane
//----------------------------------
/**
* @private
* Storage for the focusPane property.
*/
private var _focusPane:Sprite;
/**
* @private
* Focus pane associated with this object.
* An object has a focus pane when one of its children has got focus.
*/
override public function get focusPane():Sprite
{
return _focusPane;
}
/**
* @private
*/
override public function set focusPane(o:Sprite):void
{
// The addition or removal of the focus sprite should not trigger
// a measurement/layout pass. Temporarily set the invalidation flags,
// so that calls to invalidateSize() and invalidateDisplayList() have
// no effect.
var oldInvalidateSizeFlag:Boolean = invalidateSizeFlag;
var oldInvalidateDisplayListFlag:Boolean = invalidateDisplayListFlag;
invalidateSizeFlag = true;
invalidateDisplayListFlag = true;
if (o)
{
rawChildren.addChild(o);
o.x = 0;
o.y = 0;
o.scrollRect = null;
_focusPane = o;
}
else
{
rawChildren.removeChild(_focusPane);
_focusPane = null;
}
if (o && contentPane)
{
o.x = contentPane.x;
o.y = contentPane.y;
o.scrollRect = contentPane.scrollRect;
}
invalidateSizeFlag = oldInvalidateSizeFlag;
invalidateDisplayListFlag = oldInvalidateDisplayListFlag;
}
//----------------------------------
// 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");
}
//----------------------------------
// $numChildren
//----------------------------------
/**
* @private
* This property allows access to the Player's native implementation
* of the numChildren property, which can be useful since components
* can override numChildren and thereby hide the native implementation.
* Note that this "base property" is final and cannot be overridden,
* so you can count on it to reflect what is happening at the player level.
*/
mx_internal final function get $numChildren():int
{
return super.numChildren;
}
//----------------------------------
// numChildren
//----------------------------------
/**
* @private
* Storage for the numChildren property.
*/
mx_internal var _numChildren:int = 0;
/**
* Number of child components in this container.
*
* <p>The number of children is initially equal
* to the number of children declared in MXML.
* At runtime, new children may be added by calling
* <code>addChild()</code> or <code>addChildAt()</code>,
* and existing children may be removed by calling
* <code>removeChild()</code>, <code>removeChildAt()</code>,
* or <code>removeAllChildren()</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function get numChildren():int
{
return contentPane ? contentPane.numChildren : _numChildren;
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// autoLayout
//----------------------------------
/**
* @private
* Storage for the autoLayout property.
*/
private var _autoLayout:Boolean = true;
[Inspectable(defaultValue="true")]
/**
* If <code>true</code>, measurement and layout are done
* when the position or size of a child is changed.
* If <code>false</code>, measurement and layout are done only once,
* when children are added to or removed from the container.
*
* <p>When using the Move effect, the layout around the component that
* is moving does not readjust to fit that the Move effect animates.
* Setting a container's <code>autoLayout</code> property to
* <code>true</code> has no effect on this behavior.</p>
*
* <p>The Zoom effect does not work when the <code>autoLayout</code>
* property is <code>false</code>.</p>
*
* <p>The <code>autoLayout</code> property does not apply to
* Accordion or ViewStack containers.</p>
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get autoLayout():Boolean
{
return _autoLayout;
}
/**
* @private
*/
public function set autoLayout(value:Boolean):void
{
_autoLayout = value;
// If layout is being turned back on, trigger a layout to occur now.
if (value)
{
invalidateSize();
invalidateDisplayList();
var p:IInvalidating = parent as IInvalidating;
if (p)
{
p.invalidateSize();
p.invalidateDisplayList();
}
}
}
//----------------------------------
// borderMetrics
//----------------------------------
/**
* Returns an EdgeMetrics object that has four properties:
* <code>left</code>, <code>top</code>, <code>right</code>,
* and <code>bottom</code>.
* The value of each property is equal to the thickness of one side
* of the border, expressed in pixels.
*
* <p>Unlike <code>viewMetrics</code>, this property is not
* overridden by subclasses of Container.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get borderMetrics():EdgeMetrics
{
return border && border is IRectangularBorder ?
IRectangularBorder(border).borderMetrics :
EdgeMetrics.EMPTY;
}
//----------------------------------
// childDescriptors
//----------------------------------
/**
* @private
* Storage for the childDescriptors property.
* This variable is initialized in the construct() method
* using the childDescriptors in the initObj, which is autogenerated.
* If this Container was not created by createComponentFromDescriptor(),
* its childDescriptors property is null.
*/
private var _childDescriptors:Array /* of UIComponentDescriptor */;
/**
* Array of UIComponentDescriptor objects produced by the MXML compiler.
*
* <p>Each UIComponentDescriptor object contains the information
* specified in one child MXML tag of the container's MXML tag.
* The order of the UIComponentDescriptor objects in the Array
* is the same as the order of the child tags.
* During initialization, the child descriptors are used to create
* the container's child UIComponent objects and its Repeater objects,
* and to give them the initial property values, event handlers, effects,
* and so on, that were specified in MXML.</p>
*
* @see mx.core.UIComponentDescriptor
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get childDescriptors():Array /* of UIComponentDescriptor */
{
return _childDescriptors;
}
//----------------------------------
// childRepeaters
//----------------------------------
/**
* @private
* Storage for the childRepeaters property.
*/
private var _childRepeaters:Array;
/**
* @private
* An array of the Repeater objects found within this container.
*/
mx_internal function get childRepeaters():Array
{
return _childRepeaters;
}
/**
* @private
*/
mx_internal function set childRepeaters(value:Array):void
{
_childRepeaters = value;
}
//----------------------------------
// clipContent
//----------------------------------
/**
* @private
* Storage for the clipContent property.
*/
private var _clipContent:Boolean = true;
[Inspectable(defaultValue="true")]
/**
* Whether to apply a clip mask if the positions and/or sizes
* of this container's children extend outside the borders of
* this container.
* If <code>false</code>, the children of this container
* remain visible when they are moved or sized outside the
* borders of this container.
* If <code>true</code>, the children of this container are clipped.
*
* <p>If <code>clipContent</code> is <code>false</code>, then scrolling
* is disabled for this container and scrollbars will not appear.
* If <code>clipContent</code> is true, then scrollbars will usually
* appear when the container's children extend outside the border of
* the container.
* For additional control over the appearance of scrollbars,
* see <code>horizontalScrollPolicy</code> and <code>verticalScrollPolicy</code>.</p>
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get clipContent():Boolean
{
return _clipContent;
}
/**
* @private
*/
public function set clipContent(value:Boolean):void
{
if (_clipContent != value)
{
_clipContent = value;
invalidateDisplayList();
}
}
//----------------------------------
// createdComponents
//----------------------------------
/**
* @private
* Internal variable used to keep track of the components created
* by this Container. This is different than the list maintained
* by DisplayObjectContainer, because it includes Repeaters.
*/
private var _createdComponents:Array;
/**
* @private
* An array of all components created by this container including
* Repeater components.
*/
mx_internal function get createdComponents():Array
{
return _createdComponents;
}
/**
* @private
*/
mx_internal function set createdComponents(value:Array):void
{
_createdComponents = value;
}
//----------------------------------
// creationIndex
//----------------------------------
/**
* @private
* Storage for the creationIndex property.
*/
private var _creationIndex:int = -1;
[Inspectable(defaultValue="undefined")]
/**
* Specifies the order to instantiate and draw the children
* of the container.
*
* <p>This property can only be used when the <code>creationPolicy</code>
* property is set to <code>ContainerCreationPolicy.QUEUED</code>.
* Otherwise, it is ignored.</p>
*
* @default -1
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Deprecated]
public function get creationIndex():int
{
return _creationIndex;
}
/**
* @private
*/
public function set creationIndex(value:int):void
{
_creationIndex = value;
}
//----------------------------------
// creationPolicy
//----------------------------------
// Internal flag used when creationPolicy="none".
// When set, the value of the backing store _creationPolicy
// style is "auto" so descendants inherit the correct value.
private var creationPolicyNone:Boolean = false;
[Inspectable(enumeration="all,auto,none")]
/**
* The child creation policy for this MX Container.
* ActionScript values can be <code>ContainerCreationPolicy.AUTO</code>,
* <code>ContainerCreationPolicy.ALL</code>,
* or <code>ContainerCreationPolicy.NONE</code>.
* MXML values can be <code>auto</code>, <code>all</code>,
* or <code>none</code>.
*
* <p>If no <code>creationPolicy</code> is specified for a container,
* that container inherits its parent's <code>creationPolicy</code>.
* If no <code>creationPolicy</code> is specified for the Application,
* it defaults to <code>ContainerCreationPolicy.AUTO</code>.</p>
*
* <p>A <code>creationPolicy</code> of <code>ContainerCreationPolicy.AUTO</code> means
* that the container delays creating some or all descendants
* until they are needed, a process which is known as <i>deferred
* instantiation</i>.
* This policy produces the best startup time because fewer
* UIComponents are created initially.
* However, this introduces navigation delays when a user navigates
* to other parts of the application for the first time.
* Navigator containers such as Accordion, TabNavigator, and ViewStack
* implement the <code>ContainerCreationPolicy.AUTO</code> policy by creating all their
* children immediately, but wait to create the deeper descendants
* of a child until it becomes the selected child of the navigator
* container.</p>
*
* <p>A <code>creationPolicy</code> of <code>ContainerCreationPolicy.ALL</code> means
* that the navigator containers immediately create deeper descendants
* for each child, rather than waiting until that child is
* selected. For single-view containers such as a VBox container,
* there is no difference between the <code>ContainerCreationPolicy.AUTO</code> and
* <code>ContainerCreationPolicy.ALL</code> policies.</p>
*
* <p>A <code>creationPolicy</code> of <code>ContainerCreationPolicy.NONE</code> means
* that the container creates none of its children.
* In that case, it is the responsibility of the MXML author
* to create the children by calling the
* <code>createComponentsFromDescriptors()</code> method.</p>
*
* @default ContainerCreationPolicy.AUTO
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get creationPolicy():String
{
// Use an inheriting style as the backing storage for this property.
// This allows the property to be inherited by either mx or spark
// containers, and also to correctly cascade through containers that
// don't have this property (ie Group).
// This style is an implementation detail and should be considered
// private. Do not set it from CSS.
if (creationPolicyNone)
return ContainerCreationPolicy.NONE;
return getStyle("_creationPolicy");
}
/**
* @private
*/
public function set creationPolicy(value:String):void
{
var styleValue:String = value;
if (value == ContainerCreationPolicy.NONE)
{
// creationPolicy of none is not inherited by descendants.
// In this case, set the style to "auto" and set a local
// flag for subsequent access to the creationPolicy property.
creationPolicyNone = true;
styleValue = ContainerCreationPolicy.AUTO;
}
else
{
creationPolicyNone = false;
}
setStyle("_creationPolicy", styleValue);
setActualCreationPolicies(value);
}
//----------------------------------
// defaultButton
//----------------------------------
/**
* @private
* Storage for the defaultButton property.
*/
private var _defaultButton:IFlexDisplayObject;
[Inspectable(category="General")]
/**
* The Button control designated as the default button
* for the container.
* When controls in the container have focus, pressing the
* Enter key is the same as clicking this Button control.
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get defaultButton():IFlexDisplayObject
{
return _defaultButton;
}
/**
* @private
*/
public function set defaultButton(value:IFlexDisplayObject):void
{
_defaultButton = value;
ContainerGlobals.focusedContainer = null;
}
//----------------------------------
// deferredContentCreated
//----------------------------------
/**
* IDeferredContentOwner equivalent of processedDescriptors
*
* @see UIComponent#processedDescriptors
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get deferredContentCreated():Boolean
{
return processedDescriptors;
}
//----------------------------------
// data
//----------------------------------
/**
* @private
* Storage for the data property.
*/
private var _data:Object;
[Bindable("dataChange")]
[Inspectable(environment="none")]
/**
* The <code>data</code> property lets you pass a value
* to the component when you use it in an item renderer or item editor.
* You typically use data binding to bind a field of the <code>data</code>
* property to a property of this component.
*
* <p>You do not set this property in MXML.</p>
*
* @default null
* @see mx.core.IDataRenderer
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get data():Object
{
return _data;
}
/**
* @private
*/
public function set data(value:Object):void
{
_data = value;
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
invalidateDisplayList();
}
//----------------------------------
// firstChildIndex
//----------------------------------
/**
* @private
* Storage for the firstChildIndex property.
*/
private var _firstChildIndex:int = 0;
/**
* @private
* The index of the first content child,
* when dealing with both content and non-content children.
*/
mx_internal function get firstChildIndex():int
{
return _firstChildIndex;
}
//----------------------------------
// horizontalLineScrollSize
//----------------------------------
/**
* @private
* Storage for the horizontalLineScrollSize property.
*/
private var _horizontalLineScrollSize:Number = 5;
[Bindable("horizontalLineScrollSizeChanged")]
[Inspectable(defaultValue="5")]
/**
* Number of pixels to move when the left- or right-arrow
* button in the horizontal scroll bar is pressed.
*
* @default 5
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get horizontalLineScrollSize():Number
{
return _horizontalLineScrollSize;
}
/**
* @private
*/
public function set horizontalLineScrollSize(value:Number):void
{
scrollPropertiesChanged = true;
_horizontalLineScrollSize = value;
invalidateDisplayList();
dispatchEvent(new Event("horizontalLineScrollSizeChanged"));
}
//----------------------------------
// horizontalPageScrollSize
//----------------------------------
/**
* @private
* Storage for the horizontalPageScrollSize property.
*/
private var _horizontalPageScrollSize:Number = 0;
[Bindable("horizontalPageScrollSizeChanged")]
[Inspectable(defaultValue="0")]
/**
* Number of pixels to move when the track in the
* horizontal scroll bar is pressed.
* A value of 0 means that the page size
* will be calculated to be a full screen.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get horizontalPageScrollSize():Number
{
return _horizontalPageScrollSize;
}
/**
* @private
*/
public function set horizontalPageScrollSize(value:Number):void
{
scrollPropertiesChanged = true;
_horizontalPageScrollSize = value;
invalidateDisplayList();
dispatchEvent(new Event("horizontalPageScrollSizeChanged"));
}
//----------------------------------
// horizontalScrollBar
//----------------------------------
/**
* @private
* The horizontal scrollbar (null if not present).
*/
private var _horizontalScrollBar:ScrollBar;
/**
* The horizontal scrollbar used in this container.
* This property is null if no horizontal scroll bar
* is currently displayed.
* In general you do not access this property directly.
* Manipulation of the <code>horizontalScrollPolicy</code>
* and <code>horizontalScrollPosition</code>
* properties should provide sufficient control over the scroll bar.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get horizontalScrollBar():ScrollBar
{
return _horizontalScrollBar;
}
/**
* @private
*/
public function set horizontalScrollBar(value:ScrollBar):void
{
_horizontalScrollBar = value;
}
//----------------------------------
// horizontalScrollPosition
//----------------------------------
/**
* @private
* Storage for the horizontalScrollPosition property.
*/
private var _horizontalScrollPosition:Number = 0;
[Bindable("scroll")]
[Bindable("viewChanged")]
[Inspectable(defaultValue="0")]
/**
* The current position of the horizontal scroll bar.
* This is equal to the distance in pixels between the left edge
* of the scrollable surface and the leftmost piece of the surface
* that is currently visible.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get horizontalScrollPosition():Number
{
if (!isNaN(horizontalScrollPositionPending))
return horizontalScrollPositionPending;
return _horizontalScrollPosition;
}
/**
* @private
*/
public function set horizontalScrollPosition(value:Number):void
{
if (_horizontalScrollPosition == value)
return;
// Note: We can't use maxHorizontalScrollPosition to clamp the value here.
// The horizontalScrollBar may not exist yet,
// or its maxPos might change during layout.
// (For example, you could set the horizontalScrollPosition of a childless container,
// then add a child which causes it to have a scrollbar.)
// The horizontalScrollPosition gets clamped to the range 0 through maxHorizontalScrollPosition
// late, in the updateDisplayList() method, just before the scrollPosition
// of the horizontalScrollBar is set.
_horizontalScrollPosition = value;
scrollPositionChanged = true;
if (!initialized)
horizontalScrollPositionPending = value;
invalidateDisplayList();
dispatchEvent(new Event("viewChanged"));
}
//----------------------------------
// horizontalScrollPolicy
//----------------------------------
/**
* @private
* Storage for the horizontalScrollPolicy property.
*/
mx_internal var _horizontalScrollPolicy:String = ScrollPolicy.AUTO;
[Bindable("horizontalScrollPolicyChanged")]
[Inspectable(category="General", enumeration="off,on,auto", defaultValue="auto")]
/**
* Specifies whether the horizontal scroll bar is always present,
* always absent, or automatically added when needed.
* ActionScript values can be <code>ScrollPolicy.ON</code>, <code>ScrollPolicy.OFF</code>,
* and <code>ScrollPolicy.AUTO</code>.
* MXML values can be <code>"on"</code>, <code>"off"</code>,
* and <code>"auto"</code>.
*
* <p>Setting this property to <code>ScrollPolicy.OFF</code> also prevents the
* <code>horizontalScrollPosition</code> property from having an effect.</p>
*
* <p>Note: This property does not apply to the ControlBar container.</p>
*
* <p>If the <code>horizontalScrollPolicy</code> is <code>ScrollPolicy.AUTO</code>,
* the horizontal scroll bar appears when all of the following
* are true:</p>
* <ul>
* <li>One of the container's children extends beyond the left
* edge or right edge of the container.</li>
* <li>The <code>clipContent</code> property is <code>true</code>.</li>
* <li>The width and height of the container are large enough to
* reasonably accommodate a scroll bar.</li>
* </ul>
*
* @default ScrollPolicy.AUTO
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get horizontalScrollPolicy():String
{
return _horizontalScrollPolicy;
}
/**
* @private
*/
public function set horizontalScrollPolicy(value:String):void
{
if (_horizontalScrollPolicy != value)
{
_horizontalScrollPolicy = value;
invalidateDisplayList();
dispatchEvent(new Event("horizontalScrollPolicyChanged"));
}
}
//----------------------------------
// icon
//----------------------------------
/**
* @private
* Storage for the icon property.
*/
private var _icon:Class = null;
[Bindable("iconChanged")]
[Inspectable(category="General", defaultValue="", format="EmbeddedFile")]
/**
* The Class of the icon displayed by some navigator
* containers to represent this Container.
*
* <p>For example, if this Container is a child of a TabNavigator,
* this icon appears in the corresponding tab.
* If this Container is a child of an Accordion,
* this icon appears in the corresponding header.</p>
*
* <p>To embed the icon in the SWF file, use the &#64;Embed()
* MXML compiler directive:</p>
*
* <pre>
* icon="&#64;Embed('filepath')"
* </pre>
*
* <p>The image can be a JPEG, GIF, PNG, SVG, or SWF file.</p>
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get icon():Class
{
return _icon;
}
/**
* @private
*/
public function set icon(value:Class):void
{
_icon = value;
dispatchEvent(new Event("iconChanged"));
}
//----------------------------------
// label
//----------------------------------
/**
* @private
* Storage for the label property.
*/
private var _label:String = "";
[Bindable("labelChanged")]
[Inspectable(category="General", defaultValue="")]
/**
* The text displayed by some navigator containers to represent
* this Container.
*
* <p>For example, if this Container is a child of a TabNavigator,
* this string appears in the corresponding tab.
* If this Container is a child of an Accordion,
* this string appears in the corresponding header.</p>
*
* @default ""
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get label():String
{
return _label;
}
/**
* @private
*/
public function set label(value:String):void
{
_label = value;
dispatchEvent(new Event("labelChanged"));
}
//----------------------------------
// maxHorizontalScrollPosition
//----------------------------------
/**
* The largest possible value for the
* <code>horizontalScrollPosition</code> property.
* Defaults to 0 if the horizontal scrollbar is not present.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maxHorizontalScrollPosition():Number
{
return horizontalScrollBar ?
horizontalScrollBar.maxScrollPosition :
Math.max(scrollableWidth - viewableWidth, 0);
}
//----------------------------------
// maxVerticalScrollPosition
//----------------------------------
/**
* The largest possible value for the
* <code>verticalScrollPosition</code> property.
* Defaults to 0 if the vertical scrollbar is not present.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maxVerticalScrollPosition():Number
{
return verticalScrollBar ?
verticalScrollBar.maxScrollPosition :
Math.max(scrollableHeight - viewableHeight, 0);
}
//----------------------------------
// numChildrenCreated
//----------------------------------
/**
* @private
*/
private var _numChildrenCreated:int = -1;
/**
* @private
* The number of children created inside this container.
* The default value is 0.
*/
mx_internal function get numChildrenCreated():int
{
return _numChildrenCreated;
}
/**
* @private
*/
mx_internal function set numChildrenCreated(value:int):void
{
_numChildrenCreated = value;
}
//----------------------------------
// numRepeaters
//----------------------------------
/**
* @private
* The number of Repeaters in this Container.
*
* <p>This number includes Repeaters that are immediate children of this
* container and Repeaters that are nested inside other Repeaters.
* Consider the following example:</p>
*
* <pre>
* &lt;mx:HBox&gt;
* &lt;mx:Repeater dataProvider="[1, 2]"&gt;
* &lt;mx:Repeater dataProvider="..."&gt;
* &lt;mx:Button/&gt;
* &lt;/mx:Repeater&gt;
* &lt;/mx:Repeater&gt;
* &lt;mx:HBox&gt;
* </pre>
*
* <p>In this example, the <code>numRepeaters</code> property
* for the HBox would be set equal to 3 -- one outer Repeater
* and two inner repeaters.</p>
*
* <p>The <code>numRepeaters</code> property does not include Repeaters
* that are nested inside other containers.
* Consider this example:</p>
*
* <pre>
* &lt;mx:HBox&gt;
* &lt;mx:Repeater dataProvider="[1, 2]"&gt;
* &lt;mx:VBox&gt;
* &lt;mx:Repeater dataProvider="..."&gt;
* &lt;mx:Button/&gt;
* &lt;/mx:Repeater&gt;
* &lt;/mx:VBox&gt;
* &lt;/mx:Repeater&gt;
* &lt;mx:HBox&gt;
* </pre>
*
* <p>In this example, the <code>numRepeaters</code> property
* for the outer HBox would be set equal to 1 -- just the outer repeater.
* The two inner VBox containers would also have a
* <code>numRepeaters</code> property equal to 1 -- one Repeater
* per VBox.</p>
*/
mx_internal function get numRepeaters():int
{
return childRepeaters ? childRepeaters.length : 0;
}
//----------------------------------
// rawChildren
//----------------------------------
/**
* @private
* The single IChildList object that's always returned
* from the rawChildren property, below.
*/
private var _rawChildren:ContainerRawChildrenList;
/**
* A container typically contains child components, which can be enumerated
* using the <code>Container.getChildAt()</code> method and
* <code>Container.numChildren</code> property. In addition, the container
* may contain style elements and skins, such as the border and background.
* Flash Player and AIR do not draw any distinction between child components
* and skins. They are all accessible using the player's
* <code>getChildAt()</code> method and
* <code>numChildren</code> property.
* However, the Container class overrides the <code>getChildAt()</code> method
* and <code>numChildren</code> property (and several other methods)
* to create the illusion that
* the container's children are the only child components.
*
* <p>If you need to access all of the children of the container (both the
* content children and the skins), then use the methods and properties
* on the <code>rawChildren</code> property instead of the regular Container methods.
* For example, use the <code>Container.rawChildren.getChildAt())</code> method.
* However, if a container creates a ContentPane Sprite object for its children,
* the <code>rawChildren</code> property value only counts the ContentPane, not the
* container's children.
* It is not always possible to determine when a container will have a ContentPane.</p>
*
* <p><b>Note:</b>If you call the <code>addChild</code> or
* <code>addChildAt</code> method of the <code>rawChildren</code> object,
* set <code>tabFocusEnabled = false</code> on the component that you have added.
* Doing so prevents users from tabbing to the visual-only component
* that you have added.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get rawChildren():IChildList
{
if (!_rawChildren)
_rawChildren = new ContainerRawChildrenList(this);
return _rawChildren;
}
//----------------------------------
// usePadding
//----------------------------------
/**
* @private
*/
mx_internal function get usePadding():Boolean
{
// Containers, by default, always use padding.
return true;
}
//----------------------------------
// verticalLineScrollSize
//----------------------------------
/**
* @private
* Storage for the verticalLineScrollSize property.
*/
private var _verticalLineScrollSize:Number = 5;
[Bindable("verticalLineScrollSizeChanged")]
[Inspectable(defaultValue="5")]
/**
* Number of pixels to scroll when the up- or down-arrow
* button in the vertical scroll bar is pressed,
* or when you scroll by using the mouse wheel.
*
* @default 5
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get verticalLineScrollSize():Number
{
return _verticalLineScrollSize;
}
/**
* @private
*/
public function set verticalLineScrollSize(value:Number):void
{
scrollPropertiesChanged = true;
_verticalLineScrollSize = value;
invalidateDisplayList();
dispatchEvent(new Event("verticalLineScrollSizeChanged"));
}
//----------------------------------
// verticalPageScrollSize
//----------------------------------
/**
* @private
* Storage for the verticalPageScrollSize property.
*/
private var _verticalPageScrollSize:Number = 0;
[Bindable("verticalPageScrollSizeChanged")]
[Inspectable(defaultValue="0")]
/**
* Number of pixels to scroll when the track
* in the vertical scroll bar is pressed.
* A value of 0 means that the page size
* will be calculated to be a full screen.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get verticalPageScrollSize():Number
{
return _verticalPageScrollSize;
}
/**
* @private
*/
public function set verticalPageScrollSize(value:Number):void
{
scrollPropertiesChanged = true;
_verticalPageScrollSize = value;
invalidateDisplayList();
dispatchEvent(new Event("verticalPageScrollSizeChanged"));
}
//----------------------------------
// verticalScrollBar
//----------------------------------
/**
* @private
* The vertical scrollbar (null if not present).
*/
private var _verticalScrollBar:ScrollBar;
/**
* The vertical scrollbar used in this container.
* This property is null if no vertical scroll bar
* is currently displayed.
* In general you do not access this property directly.
* Manipulation of the <code>verticalScrollPolicy</code>
* and <code>verticalScrollPosition</code>
* properties should provide sufficient control over the scroll bar.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get verticalScrollBar():ScrollBar
{
return _verticalScrollBar;
}
/**
* @private
*/
public function set verticalScrollBar(value:ScrollBar):void
{
_verticalScrollBar = value;
}
//----------------------------------
// verticalScrollPosition
//----------------------------------
/**
* @private
* Storage for the verticalScrollPosition property.
*/
private var _verticalScrollPosition:Number = 0;
[Bindable("scroll")]
[Bindable("viewChanged")]
[Inspectable(defaultValue="0")]
/**
* The current position of the vertical scroll bar.
* This is equal to the distance in pixels between the top edge
* of the scrollable surface and the topmost piece of the surface
* that is currently visible.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get verticalScrollPosition():Number
{
if (!isNaN(verticalScrollPositionPending))
return verticalScrollPositionPending;
return _verticalScrollPosition;
}
/**
* @private
*/
public function set verticalScrollPosition(value:Number):void
{
if (_verticalScrollPosition == value)
return;
// Note: We can't use maxVerticalScrollPosition to clamp the value here.
// The verticalScrollBar may not exist yet,
// or its maxPos might change during layout.
// (For example, you could set the verticalScrollPosition of a childless container,
// then add a child which causes it to have a scrollbar.)
// The verticalScrollPosition gets clamped to the range 0 through maxVerticalScrollPosition
// late, in the updateDisplayList() method, just before the scrollPosition
// of the verticalScrollBar is set.
_verticalScrollPosition = value;
scrollPositionChanged = true;
if (!initialized)
verticalScrollPositionPending = value;
invalidateDisplayList();
dispatchEvent(new Event("viewChanged"));
}
//----------------------------------
// verticalScrollPolicy
//----------------------------------
/**
* @private
* Storage for the verticalScrollPolicy property.
*/
mx_internal var _verticalScrollPolicy:String = ScrollPolicy.AUTO;
[Bindable("verticalScrollPolicyChanged")]
[Inspectable(category="General", enumeration="off,on,auto", defaultValue="auto")]
/**
* Specifies whether the vertical scroll bar is always present,
* always absent, or automatically added when needed.
* Possible values are <code>ScrollPolicy.ON</code>, <code>ScrollPolicy.OFF</code>,
* and <code>ScrollPolicy.AUTO</code>.
* MXML values can be <code>"on"</code>, <code>"off"</code>,
* and <code>"auto"</code>.
*
* <p>Setting this property to <code>ScrollPolicy.OFF</code> also prevents the
* <code>verticalScrollPosition</code> property from having an effect.</p>
*
* <p>Note: This property does not apply to the ControlBar container.</p>
*
* <p>If the <code>verticalScrollPolicy</code> is <code>ScrollPolicy.AUTO</code>,
* the vertical scroll bar appears when all of the following
* are true:</p>
* <ul>
* <li>One of the container's children extends beyond the top
* edge or bottom edge of the container.</li>
* <li>The <code>clipContent</code> property is <code>true</code>.</li>
* <li>The width and height of the container are large enough to
* reasonably accommodate a scroll bar.</li>
* </ul>
*
* @default ScrollPolicy.AUTO
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get verticalScrollPolicy():String
{
return _verticalScrollPolicy;
}
/**
* @private
*/
public function set verticalScrollPolicy(value:String):void
{
if (_verticalScrollPolicy != value)
{
_verticalScrollPolicy = value;
invalidateDisplayList();
dispatchEvent(new Event("verticalScrollPolicyChanged"));
}
}
//----------------------------------
// viewMetrics
//----------------------------------
/**
* @private
* Offsets including borders and scrollbars
*/
private var _viewMetrics:EdgeMetrics;
/**
* Returns an object that has four properties: <code>left</code>,
* <code>top</code>, <code>right</code>, and <code>bottom</code>.
* The value of each property equals the thickness of the chrome
* (visual elements) around the edge of the container.
*
* <p>The chrome includes the border thickness.
* If the <code>horizontalScrollPolicy</code> or <code>verticalScrollPolicy</code>
* property value is <code>ScrollPolicy.ON</code>, the
* chrome also includes the thickness of the corresponding
* scroll bar. If a scroll policy is <code>ScrollPolicy.AUTO</code>,
* the chrome measurement does not include the scroll bar thickness,
* even if a scroll bar is displayed.</p>
*
* <p>Subclasses of Container should override this method, so that
* they include other chrome to be taken into account when positioning
* the Container's children.
* For example, the <code>viewMetrics</code> property for the
* Panel class should return an object whose <code>top</code> property
* includes the thickness of the Panel container's title bar.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get viewMetrics():EdgeMetrics
{
var bm:EdgeMetrics = borderMetrics;
// If scrollPolicy is ScrollPolicy.ON, then the scrollbars are accounted for
// during both measurement and layout.
//
// If scrollPolicy is ScrollPolicy.AUTO, then scrollbars are ignored during
// measurement. Otherwise, the entire layout of the app could change
// everytime that the scrollbars turn on or off.
//
// However, we do take the width of scrollbars into account when laying
// out our children. That way, children that have a percentage width or
// percentage height will only expand to consume space that's left over
// after leaving room for the scrollbars.
var verticalScrollBarIncluded:Boolean =
verticalScrollBar != null &&
(doingLayout || verticalScrollPolicy == ScrollPolicy.ON);
var horizontalScrollBarIncluded:Boolean =
horizontalScrollBar != null &&
(doingLayout || horizontalScrollPolicy == ScrollPolicy.ON);
if (!verticalScrollBarIncluded && !horizontalScrollBarIncluded)
return bm;
// The viewMetrics property needs to return its own object.
// Rather than allocating a new one each time, we'll allocate one once
// and then hold a reference to it.
if (!_viewMetrics)
{
_viewMetrics = bm.clone();
}
else
{
_viewMetrics.left = bm.left;
_viewMetrics.right = bm.right;
_viewMetrics.top = bm.top;
_viewMetrics.bottom = bm.bottom;
}
if (verticalScrollBarIncluded)
_viewMetrics.right += verticalScrollBar.minWidth;
if (horizontalScrollBarIncluded)
_viewMetrics.bottom += horizontalScrollBar.minHeight;
return _viewMetrics;
}
//----------------------------------
// viewMetricsAndPadding
//----------------------------------
/**
* @private
* Cached value containing the view metrics plus the object's margins.
*/
private var _viewMetricsAndPadding:EdgeMetrics;
/**
* Returns an object that has four properties: <code>left</code>,
* <code>top</code>, <code>right</code>, and <code>bottom</code>.
* The value of each property is equal to the thickness of the chrome
* (visual elements)
* around the edge of the container plus the thickness of the object's margins.
*
* <p>The chrome includes the border thickness.
* If the <code>horizontalScrollPolicy</code> or <code>verticalScrollPolicy</code>
* property value is <code>ScrollPolicy.ON</code>, the
* chrome also includes the thickness of the corresponding
* scroll bar. If a scroll policy is <code>ScrollPolicy.AUTO</code>,
* the chrome measurement does not include the scroll bar thickness,
* even if a scroll bar is displayed.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get viewMetricsAndPadding():EdgeMetrics
{
// If this object has scrollbars, and if the verticalScrollPolicy
// is not ScrollPolicy.ON, then the view metrics change
// depending on whether we're doing layout or not.
// In that case, we can't use a cached value.
// In all other cases, use the cached value if it exists.
if (_viewMetricsAndPadding &&
(!horizontalScrollBar ||
horizontalScrollPolicy == ScrollPolicy.ON) &&
(!verticalScrollBar ||
verticalScrollPolicy == ScrollPolicy.ON))
{
return _viewMetricsAndPadding;
}
if (!_viewMetricsAndPadding)
_viewMetricsAndPadding = new EdgeMetrics();
var o:EdgeMetrics = _viewMetricsAndPadding;
var vm:EdgeMetrics = viewMetrics;
o.left = vm.left + getStyle("paddingLeft");
o.right = vm.right + getStyle("paddingRight");
o.top = vm.top + getStyle("paddingTop");
o.bottom = vm.bottom + getStyle("paddingBottom");
return o;
}
//--------------------------------------------------------------------------
//
// Overridden methods: EventDispatcher
//
//--------------------------------------------------------------------------
/**
* @private
* If we add a mouse event, then we need to add a mouse shield
* to us and to all our children
* The mouseShield style is a non-inheriting style
* that is used by the view.
* The mouseShieldChildren style is an inherting style
* that is used by the children views.
*/
override public function addEventListener(
type:String, listener:Function,
useCapture:Boolean = false,
priority:int = 0,
useWeakReference:Boolean = false):void
{
super.addEventListener(type, listener, useCapture,
priority, useWeakReference);
// If we are a mouse event, then create a mouse shield.
if (type == MouseEvent.CLICK ||
type == MouseEvent.DOUBLE_CLICK ||
type == MouseEvent.MOUSE_DOWN ||
type == MouseEvent.MOUSE_MOVE ||
type == MouseEvent.MOUSE_OVER ||
type == MouseEvent.MOUSE_OUT ||
type == MouseEvent.MOUSE_UP ||
type == MouseEvent.MOUSE_WHEEL)
{
if (mouseEventReferenceCount < 0x7FFFFFFF /* int_max */ &&
mouseEventReferenceCount++ == 0)
{
setStyle("mouseShield", true);
setStyle("mouseShieldChildren", true);
}
}
}
/**
* @private
* We're doing special behavior on addEventListener to make sure that
* we successfully capture mouse events, even when there's no background.
* However, this means adding an event listener changes the behavior
* a little, and this can be troublesome for overlapping components
* that now don't get any mouse events. This is acceptable normally;
* however, automation adds certain events to the Container, and
* it'd be better if automation support didn't modify the behavior of
* the component. For this reason, especially, we have an mx_internal
* $addEventListener to add event listeners without affecting the behavior
* of the component.
*/
mx_internal function $addEventListener(
type:String, listener:Function,
useCapture:Boolean = false,
priority:int = 0,
useWeakReference:Boolean = false):void
{
super.addEventListener(type, listener, useCapture,
priority, useWeakReference);
}
/**
* @private
* Remove the mouse shield if we no longer listen to any mouse events
*/
override public function removeEventListener(
type:String, listener:Function,
useCapture:Boolean = false):void
{
super.removeEventListener(type, listener, useCapture);
// If we are a mouse event,
// then decrement the mouse shield reference count.
if (type == MouseEvent.CLICK ||
type == MouseEvent.DOUBLE_CLICK ||
type == MouseEvent.MOUSE_DOWN ||
type == MouseEvent.MOUSE_MOVE ||
type == MouseEvent.MOUSE_OVER ||
type == MouseEvent.MOUSE_OUT ||
type == MouseEvent.MOUSE_UP ||
type == MouseEvent.MOUSE_WHEEL)
{
if (mouseEventReferenceCount > 0 &&
--mouseEventReferenceCount == 0)
{
setStyle("mouseShield", false);
setStyle("mouseShieldChildren", false);
}
}
}
/**
* @private
* We're doing special behavior on removeEventListener to make sure that
* we successfully capture mouse events, even when there's no background.
* However, this means removing an event listener changes the behavior
* a little, and this can be troublesome for overlapping components
* that now don't get any mouse events. This is acceptable normally;
* however, automation adds certain events to the Container, and
* it'd be better if automation support didn't modify the behavior of
* the component. For this reason, especially, we have an mx_internal
* $removeEventListener to remove event listeners without affecting the behavior
* of the component.
*/
mx_internal function $removeEventListener(
type:String, listener:Function,
useCapture:Boolean = false):void
{
super.removeEventListener(type, listener, useCapture);
}
//--------------------------------------------------------------------------
//
// Overridden methods: DisplayObjectContainer
//
//--------------------------------------------------------------------------
/**
* Adds a child DisplayObject to this Container.
* The child is added after other existing children,
* so that the first child added has index 0,
* the next has index 1, an so on.
*
* <p><b>Note: </b>While the <code>child</code> argument to the method
* is specified as of type DisplayObject, the argument must implement
* the IUIComponent interface to be added as a child of a container.
* All Flex components implement this interface.</p>
*
* <p>Children are layered from back to front.
* In other words, if children overlap, the one with index 0
* is farthest to the back, and the one with index
* <code>numChildren - 1</code> is frontmost.
* This means the newly added children are layered
* in front of existing children.</p>
*
* @param child The DisplayObject to add as a child of this Container.
* It must implement the IUIComponent interface.
*
* @return The added child as an object of type DisplayObject.
* You typically cast the return value to UIComponent,
* or to the type of the added component.
*
* @see mx.core.IUIComponent
*
* @tiptext Adds a child object to this container.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function addChild(child:DisplayObject):DisplayObject
{
return addChildAt(child, numChildren);
/* Application and Panel are depending on addChild calling addChildAt */
/*
addingChild(child);
if (contentPane)
contentPane.addChild(child);
else
$addChild(child);
childAdded(child);
return child;
*/
}
/**
* Adds a child DisplayObject to this Container.
* The child is added at the index specified.
*
* <p><b>Note: </b>While the <code>child</code> argument to the method
* is specified as of type DisplayObject, the argument must implement
* the IUIComponent interface to be added as a child of a container.
* All Flex components implement this interface.</p>
*
* <p>Children are layered from back to front.
* In other words, if children overlap, the one with index 0
* is farthest to the back, and the one with index
* <code>numChildren - 1</code> is frontmost.
* This means the newly added children are layered
* in front of existing children.</p>
*
* <p>When you add a new child at an index that is already occupied
* by an old child, it doesn't replace the old child; instead the
* old child and the ones after it "slide over" and have their index
* incremented by one.
* For example, suppose a Container contains the children
* (A, B, C) and you add D at index 1.
* Then the container will contain (A, D, B, C).
* If you want to replace an old child, you must first remove it
* before adding the new one.</p>
*
* @param child The DisplayObject to add as a child of this Container.
* It must implement the IUIComponent interface.
*
* @param index The index to add the child at.
*
* @return The added child as an object of type DisplayObject.
* You typically cast the return value to UIComponent,
* or to the type of the added component.
*
* @see mx.core.IUIComponent
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function addChildAt(child:DisplayObject,
index:int):DisplayObject
{
var formerParent:DisplayObjectContainer = child.parent;
if (formerParent && !(formerParent is Loader))
{
// Adjust index if necessary when former parent happens
// to be the same container.
if (formerParent == this)
index = (index == numChildren) ? index - 1 : index;
formerParent.removeChild(child);
}
addingChild(child);
// Add the child to either this container or its contentPane.
// The player will dispatch an "added" event from the child
// after it has been added, so all "added" handlers execute here.
if (contentPane)
contentPane.addChildAt(child, index);
else
$addChildAt(child, _firstChildIndex + index);
childAdded(child);
if ((child is UIComponent) && UIComponent(child).isDocument)
BindingManager.setEnabled(child, true);
return child;
}
/**
* Removes a child DisplayObject from the child list of this Container.
* The removed child will have its <code>parent</code>
* property set to null.
* The child will still exist unless explicitly destroyed.
* If you add it to another container,
* it will retain its last known state.
*
* @param child The DisplayObject to remove.
*
* @return The removed child as an object of type DisplayObject.
* You typically cast the return value to UIComponent,
* or to the type of the removed component.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function removeChild(child:DisplayObject):DisplayObject
{
if (child is IDeferredInstantiationUIComponent &&
IDeferredInstantiationUIComponent(child).descriptor)
{
// if child's descriptor is present, it means child was created
// with MXML. Need to go through and remove component in
// createdComponents so there is no memory leak by keeping
// a reference to the removed child (SDK-12506)
if (createdComponents)
{
var n:int = createdComponents.length;
for (var i:int = 0; i < n; i++)
{
if (createdComponents[i] === child)
{
// delete this reference
createdComponents.splice(i, 1);
}
}
}
}
removingChild(child);
if ((child is UIComponent) && UIComponent(child).isDocument)
BindingManager.setEnabled(child, false);
// Remove the child from either this container or its contentPane.
// The player will dispatch a "removed" event from the child
// before it is removed, so all "removed" handlers execute here.
if (contentPane)
contentPane.removeChild(child);
else
$removeChild(child);
childRemoved(child);
return child;
}
/**
* Removes a child DisplayObject from the child list of this Container
* at the specified index.
* The removed child will have its <code>parent</code>
* property set to null.
* The child will still exist unless explicitly destroyed.
* If you add it to another container,
* it will retain its last known state.
*
* @param index The child index of the DisplayObject to remove.
*
* @return The removed child as an object of type DisplayObject.
* You typically cast the return value to UIComponent,
* or to the type of the removed component.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function removeChildAt(index:int):DisplayObject
{
return removeChild(getChildAt(index));
/*
Shouldn't implement removeChildAt() in terms of removeChild().
If we change this ViewStack IList, Application, and Panel are depending on it
*/
}
/**
* Gets the <i>n</i>th child component object.
*
* <p>The children returned from this method include children that are
* declared in MXML and children that are added using the
* <code>addChild()</code> or <code>addChildAt()</code> method.</p>
*
* @param childIndex Number from 0 to (numChildren - 1).
*
* @return Reference to the child as an object of type DisplayObject.
* You typically cast the return value to UIComponent,
* or to the type of a specific Flex control, such as ComboBox or TextArea.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function getChildAt(index:int):DisplayObject
{
if (contentPane)
{
return contentPane.getChildAt(index);
}
else
{
// The DisplayObjectContainer implementation of getChildAt()
// in the Player throws this error if the index is bad,
// so we should too.
// if (index < 0 || index >= _numChildren)
// throw new RangeError("The supplied index is out of bounds");
return super.getChildAt(_firstChildIndex + index);
}
}
/**
* Returns the child whose <code>name</code> property is the specified String.
*
* @param name The identifier of the child.
*
* @return The DisplayObject representing the child as an object of type DisplayObject.
* You typically cast the return value to UIComponent,
* or to the type of a specific Flex control, such as ComboBox or TextArea.
* Throws a run-time error if the child of the specified name does not exist.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function getChildByName(name:String):DisplayObject
{
if (contentPane)
{
return contentPane.getChildByName(name);
}
else
{
var child:DisplayObject = super.getChildByName(name);
if (!child)
return null;
// Check if the child is in the index range for content children.
var index:int = super.getChildIndex(child) - _firstChildIndex;
if (index < 0 || index >= _numChildren)
return null;
return child;
}
}
/**
* Gets the zero-based index of a specific child.
*
* <p>The first child of the container (i.e.: the first child tag
* that appears in the MXML declaration) has an index of 0,
* the second child has an index of 1, and so on.
* The indexes of a container's children determine
* the order in which they get laid out.
* For example, in a VBox the child with index 0 is at the top,
* the child with index 1 is below it, etc.</p>
*
* <p>If you add a child by calling the <code>addChild()</code> method,
* the new child's index is equal to the largest index among existing
* children plus one.
* You can insert a child at a specified index by using the
* <code>addChildAt()</code> method; in that case the indices of the
* child previously at that index, and the children at higher indices,
* all have their index increased by 1 so that all indices fall in the
* range from 0 to <code>(numChildren - 1)</code>.</p>
*
* <p>If you remove a child by calling <code>removeChild()</code>
* or <code>removeChildAt()</code> method, then the indices of the
* remaining children are adjusted so that all indices fall in the
* range from 0 to <code>(numChildren - 1)</code>.</p>
*
* <p>If <code>myView.getChildIndex(myChild)</code> returns 5,
* then <code>myView.getChildAt(5)</code> returns myChild.</p>
*
* <p>The index of a child may be changed by calling the
* <code>setChildIndex()</code> method.</p>
*
* @param child Reference to child whose index to get.
*
* @return Number between 0 and (numChildren - 1).
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function getChildIndex(child:DisplayObject):int
{
if (contentPane)
{
return contentPane.getChildIndex(child);
}
else
{
var index:int = super.getChildIndex(child) - _firstChildIndex;
// The DisplayObjectContainer implementation of getChildIndex()
// in the Player throws this error if the child isn't a child,
// so we should too.
// if (index < 0 || index >= _numChildren)
// throw new ArgumentError("The DisplayObject supplied must be a child of the caller.");
return index;
}
}
/**
* Sets the index of a particular child.
* See the <code>getChildIndex()</code> method for a
* description of the child's index.
*
* @param child Reference to child whose index to set.
*
* @param newIndex Number that indicates the new index.
* Must be an integer between 0 and (numChildren - 1).
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function setChildIndex(child:DisplayObject, newIndex:int):void
{
var oldIndex:int;
var eventOldIndex:int = oldIndex;
var eventNewIndex:int = newIndex;
if (contentPane)
{
contentPane.setChildIndex(child, newIndex);
if (_autoLayout || forceLayout)
invalidateDisplayList();
}
else
{
oldIndex = super.getChildIndex(child);
// Offset the index, to leave room for skins before the list of children
newIndex += _firstChildIndex;
if (newIndex == oldIndex)
return;
// Change the child's index, shifting around other children to make room
super.setChildIndex(child, newIndex);
invalidateDisplayList();
eventOldIndex = oldIndex - _firstChildIndex;
eventNewIndex = newIndex - _firstChildIndex;
}
// Notify others that the child index has changed
var event:IndexChangedEvent = new IndexChangedEvent(IndexChangedEvent.CHILD_INDEX_CHANGE);
event.relatedObject = child;
event.oldIndex = eventOldIndex;
event.newIndex = eventNewIndex;
dispatchEvent(event);
dispatchEvent(new Event("childrenChanged"));
}
/**
* @private
*/
override public function contains(child:DisplayObject):Boolean
{
if (contentPane)
return contentPane.contains(child);
else
return super.contains(child);
}
//--------------------------------------------------------------------------
//
// Methods: IVisualElementContainer
//
//--------------------------------------------------------------------------
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get numElements():int
{
return numChildren;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function getElementAt(index:int):IVisualElement
{
return getChildAt(index) as IVisualElement;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function getElementIndex(element:IVisualElement):int
{
if (! (element is DisplayObject) )
throw ArgumentError(element + " is not found in this Container");
return getChildIndex(element as DisplayObject);
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function addElement(element:IVisualElement):IVisualElement
{
if (! (element is DisplayObject) )
throw ArgumentError(element + " is not supported in this Container");
return addChild(element as DisplayObject) as IVisualElement;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function addElementAt(element:IVisualElement, index:int):IVisualElement
{
if (! (element is DisplayObject) )
throw ArgumentError(element + " is not supported in this Container");
return addChildAt(element as DisplayObject, index) as IVisualElement;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function removeElement(element:IVisualElement):IVisualElement
{
if (! (element is DisplayObject) )
throw ArgumentError(element + " is not found in this Container");
return removeChild(element as DisplayObject) as IVisualElement;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function removeElementAt(index:int):IVisualElement
{
return removeChildAt(index) as IVisualElement;
}
/**
* @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 setElementIndex(element:IVisualElement, index:int):void
{
if (! (element is DisplayObject) )
throw ArgumentError(element + " is not found in this Container");
return setChildIndex(element as DisplayObject, index);
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function swapElements(element1:IVisualElement, element2:IVisualElement):void
{
if (! (element1 is DisplayObject) )
throw ArgumentError(element1 + " is not found in this Container");
if (! (element2 is DisplayObject) )
throw ArgumentError(element2 + " is not found in this Container");
swapChildren(element1 as DisplayObject, element2 as DisplayObject);
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function swapElementsAt(index1:int, index2:int):void
{
swapChildrenAt(index1, index2);
}
//--------------------------------------------------------------------------
//
// Overridden methods: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function initialize():void
{
// Until component templating is implemented, the childDescriptors
// come either from the top-level descriptor in the component itself
// (i.e., the defined children) or from a descriptor for an instance
// of that component in some other component or app (i.e., the
// instance children). At this point _childDescriptors already
// contains the instance children if there are any; but if the
// document defines any children, we have to use them instead.
if (documentDescriptor && !processedDescriptors)
{
// NOTE: documentDescriptor.properties is a potentially
// expensive function call, so do it only once.
var props:* = documentDescriptor.properties;
if (props && props.childDescriptors)
{
if (_childDescriptors)
{
var message:String = resourceManager.getString(
"core", "multipleChildSets_ClassAndInstance");
throw new Error(message);
}
else
{
_childDescriptors = props.childDescriptors;
}
}
}
super.initialize();
}
/**
* @private
* Create components that are children of this Container.
*/
override protected function createChildren():void
{
super.createChildren();
// Create the border/background object.
createBorder();
// To save ourselves an extra layout pass, check to see
// if the scrollbars will definitely be needed.
// If so, create them now.
createOrDestroyScrollbars(
horizontalScrollPolicy == ScrollPolicy.ON,
verticalScrollPolicy == ScrollPolicy.ON,
horizontalScrollPolicy == ScrollPolicy.ON ||
verticalScrollPolicy == ScrollPolicy.ON);
// Determine the child-creation policy (ContainerCreationPolicy.AUTO,
// ContainerCreationPolicy.ALL, or ContainerCreationPolicy.NONE).
// If the author has specified a policy, use it.
// Otherwise, use the parent's policy.
// This must be set before createChildren() gets called.
if (actualCreationPolicy == null)
{
if (creationPolicy != null)
actualCreationPolicy = creationPolicy;
if (actualCreationPolicy == ContainerCreationPolicy.QUEUED)
actualCreationPolicy = ContainerCreationPolicy.AUTO;
}
// It is ok for actualCreationPolicy to be null. Popups require it.
if (actualCreationPolicy == ContainerCreationPolicy.NONE)
{
actualCreationPolicy = ContainerCreationPolicy.AUTO;
}
else if (actualCreationPolicy == ContainerCreationPolicy.QUEUED)
{
var mainApp:* = parentApplication ?
parentApplication :
FlexGlobals.topLevelApplication;
if ("addToCreationQueue" in mainApp)
mainApp.addToCreationQueue(this, _creationIndex, null, this);
else // If the app doesn't support queued creation, create our components now
createComponentsFromDescriptors();
}
else if (recursionFlag)
{
// Create whatever children are appropriate. If any were
// previously created, they don't get re-created.
createComponentsFromDescriptors();
}
// If autoLayout is initially false, we still want to do
// measurement once (even if we don't have any children)
if (autoLayout == false)
forceLayout = true;
// weak references
UIComponentGlobals.layoutManager.addEventListener(
FlexEvent.UPDATE_COMPLETE, layoutCompleteHandler, false, 0, true);
}
/**
* @private
* Override to NOT set precessedDescriptors.
*/
override protected function initializationComplete():void
{
// Don't call super.initializationComplete().
}
/**
* @private
*/
override public function invalidateLayoutDirection():void
{
super.invalidateLayoutDirection();
// We have to deal with non-styled raw children here.
if (_rawChildren)
{
const rawNumChildren:int = _rawChildren.numChildren;
for (var i:int = 0; i < rawNumChildren; i++)
{
var child:DisplayObject = _rawChildren.getChildAt(i);
if (!(child is IStyleClient) && child is ILayoutDirectionElement)
ILayoutDirectionElement(child).invalidateLayoutDirection();
}
}
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
if (changedStyles)
{
// If multiple properties have changed, set styleProp to null.
// Otherwise, set it to the name of the style that has changed.
var styleProp:String = changedStyles == MULTIPLE_PROPERTIES ?
null :
changedStyles;
super.notifyStyleChangeInChildren(styleProp, true);
changedStyles = null;
}
createOrDestroyBlocker();
}
/**
* @private
*/
override public function validateSize(recursive:Boolean = false):void
{
// If autoLayout is turned off and we haven't recently created
// or destroyed any children, then we're not doing any
// measurement or layout.
// Return false indicating that the measurements haven't changed.
if (autoLayout == false && forceLayout == false)
{
if (recursive)
{
var n:int = super.numChildren;
for (var i:int = 0; i < n; i++)
{
var child:DisplayObject = super.getChildAt(i);
if (child is ILayoutManagerClient )
ILayoutManagerClient (child).validateSize(true);
}
}
adjustSizesForScaleChanges();
}
else
{
super.validateSize(recursive);
}
}
/**
* @private
*/
override public function validateDisplayList():void
{
// trace(">>Container validateLayoutPhase " + this);
var vm:EdgeMetrics;
// If autoLayout is turned off and we haven't recently created or
// destroyed any children, then don't do any layout
if (_autoLayout || forceLayout)
{
doingLayout = true;
super.validateDisplayList();
doingLayout = false;
}
else
{
// Layout borders, Panel headers, and other border chrome.
layoutChrome(unscaledWidth, unscaledHeight);
}
// Set this to block requeuing when sizing children.
invalidateDisplayListFlag = true;
// Based on the positions of the children, determine
// whether a clip mask and scrollbars are needed.
if (createContentPaneAndScrollbarsIfNeeded())
{
// Redo layout if scrollbars just got created or destroyed (because
// now we may have more or less space).
if (_autoLayout || forceLayout)
{
doingLayout = true;
super.validateDisplayList();
doingLayout = false;
}
// If a scrollbar was created, that may precipitate the need
// for a second scrollbar, so run it a second time.
createContentPaneAndScrollbarsIfNeeded();
}
// The relayout performed by the above calls
// to super.validateDisplayList() may result
// in new max scroll positions that are less
// than previously-set scroll positions.
// For example, when a maximally-scrolled container
// is resized to be larger, the new max scroll positions
// are reduced and the current scroll positions
// will be invalid unless we clamp them.
if (clampScrollPositions())
scrollChildren();
if (contentPane)
{
vm = viewMetrics;
// Set the position and size of the overlay .
if (effectOverlay)
{
effectOverlay.x = 0;
effectOverlay.y = 0;
effectOverlay.width = unscaledWidth;
effectOverlay.height = unscaledHeight;
}
// Set the positions and sizes of the scrollbars.
if (horizontalScrollBar || verticalScrollBar)
{
// Get the view metrics and remove the thickness
// of the scrollbars from the view metrics.
// We can't simply get the border metrics,
// because some subclass (e.g.: Window)
// might add to the metrics.
if (verticalScrollBar &&
verticalScrollPolicy == ScrollPolicy.ON)
{
vm.right -= verticalScrollBar.minWidth;
}
if (horizontalScrollBar &&
horizontalScrollPolicy == ScrollPolicy.ON)
{
vm.bottom -= horizontalScrollBar.minHeight;
}
if (horizontalScrollBar)
{
var w:Number = unscaledWidth - vm.left - vm.right;
if (verticalScrollBar)
w -= verticalScrollBar.minWidth;
horizontalScrollBar.setActualSize(
w, horizontalScrollBar.minHeight);
horizontalScrollBar.move(vm.left,
unscaledHeight - vm.bottom -
horizontalScrollBar.minHeight);
}
if (verticalScrollBar)
{
var h:Number = unscaledHeight - vm.top - vm.bottom;
if (horizontalScrollBar)
h -= horizontalScrollBar.minHeight;
verticalScrollBar.setActualSize(
verticalScrollBar.minWidth, h);
verticalScrollBar.move(unscaledWidth - vm.right -
verticalScrollBar.minWidth,
vm.top);
}
// Set the position of the box
// that covers the gap between the scroll bars.
if (whiteBox)
{
whiteBox.x = verticalScrollBar.x;
whiteBox.y = horizontalScrollBar.y;
}
}
contentPane.x = vm.left;
contentPane.y = vm.top;
if (focusPane)
{
focusPane.x = vm.left
focusPane.y = vm.top;
}
scrollChildren();
}
invalidateDisplayListFlag = false;
// that blocks UI input as well as draws an alpha overlay.
// Make sure the blocker is correctly positioned and sized here.
if (blocker)
{
vm = viewMetrics;
if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0)
vm = EdgeMetrics.EMPTY;
var bgColor:Object = enabled ?
null :
getStyle("backgroundDisabledColor");
if (bgColor === null || isNaN(Number(bgColor)))
bgColor = getStyle("backgroundColor");
if (bgColor === null || isNaN(Number(bgColor)))
bgColor = 0xFFFFFF;
var blockerAlpha:Number = getStyle("disabledOverlayAlpha");
if (isNaN(blockerAlpha))
blockerAlpha = 0.6;
blocker.x = vm.left;
blocker.y = vm.top;
var widthToBlock:Number = unscaledWidth - (vm.left + vm.right);
var heightToBlock:Number = unscaledHeight - (vm.top + vm.bottom);
blocker.graphics.clear();
blocker.graphics.beginFill(uint(bgColor), blockerAlpha);
blocker.graphics.drawRect(0, 0, widthToBlock, heightToBlock);
blocker.graphics.endFill();
// Blocker must be in front of everything
rawChildren.setChildIndex(blocker, rawChildren.numChildren - 1);
}
// trace("<<Container internalValidateDisplayList " + this);
}
/**
* Respond to size changes by setting the positions and sizes
* of this container's children.
*
* <p>See the <code>UIComponent.updateDisplayList()</code> method for more information
* about the <code>updateDisplayList()</code> method.</p>
*
* <p>The <code>Container.updateDisplayList()</code> method sets the position
* and size of the Container container's border.
* In every subclass of Container, the subclass's <code>updateDisplayList()</code>
* method should call the <code>super.updateDisplayList()</code> method,
* so that the border is positioned properly.</p>
*
* @param unscaledWidth Specifies the width of the component, in pixels,
* in the component's coordinates, regardless of the value of the
* <code>scaleX</code> property of the component.
*
* @param unscaledHeight Specifies the height of the component, in pixels,
* in the component's coordinates, regardless of the value of the
* <code>scaleY</code> property of the component.
*
* @see mx.core.UIComponent
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
layoutChrome(unscaledWidth, unscaledHeight);
if (scrollPositionChanged)
{
clampScrollPositions();
scrollChildren();
scrollPositionChanged = false;
}
if (scrollPropertiesChanged)
{
if (horizontalScrollBar)
{
horizontalScrollBar.lineScrollSize = horizontalLineScrollSize;
horizontalScrollBar.pageScrollSize = horizontalPageScrollSize;
}
if (verticalScrollBar)
{
verticalScrollBar.lineScrollSize = verticalLineScrollSize;
verticalScrollBar.pageScrollSize = verticalPageScrollSize;
}
scrollPropertiesChanged = false;
}
if (contentPane && contentPane.scrollRect)
{
// Draw content pane
var backgroundColor:Object = enabled ?
null :
getStyle("backgroundDisabledColor");
if (backgroundColor === null || isNaN(Number(backgroundColor)))
backgroundColor = getStyle("backgroundColor");
var backgroundAlpha:Number = getStyle("backgroundAlpha");
if (!_clipContent ||
isNaN(Number(backgroundColor)) ||
backgroundColor === "" ||
(!(horizontalScrollBar || verticalScrollBar) && !cacheAsBitmap))
{
backgroundColor = null;
}
// If there's a backgroundImage or background, unset
// opaqueBackground.
else if (getStyle("backgroundImage") ||
getStyle("background"))
{
backgroundColor = null;
}
// If the background is not opaque, unset opaqueBackground.
else if (backgroundAlpha != 1)
{
backgroundColor = null;
}
contentPane.opaqueBackground = backgroundColor;
// Set cacheAsBitmap only if opaqueBackground is also set (to avoid
// text anti-aliasing issue with device text on Windows).
contentPane.cacheAsBitmap = (backgroundColor != null);
}
}
/**
* @copy mx.core.UIComponent#contentToGlobal()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function contentToGlobal(point:Point):Point
{
if (contentPane)
return contentPane.localToGlobal(point);
return localToGlobal(point);
}
/**
* @copy mx.core.UIComponent#globalToContent()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function globalToContent(point:Point):Point
{
if (contentPane)
return contentPane.globalToLocal(point);
return globalToLocal(point);
}
/**
* @copy mx.core.UIComponent#contentToLocal()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function contentToLocal(point:Point):Point
{
if (!contentPane)
return point;
point = contentToGlobal(point);
return globalToLocal(point);
}
/**
* @copy mx.core.UIComponent#localToContent()
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function localToContent(point:Point):Point
{
if (!contentPane)
return point;
point = localToGlobal(point);
return globalToContent(point);
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
var allStyles:Boolean = styleProp == null || styleProp == "styleName";
// Check to see if this is one of the style properties that is known
// to affect page layout.
if (allStyles || styleManager.isSizeInvalidatingStyle(styleProp))
{
// Some styles, such as horizontalAlign and verticalAlign,
// affect the layout of this object's children without changing the
// view's size. This function forces the view to be remeasured
// and layed out.
invalidateDisplayList();
}
// Replace the borderSkin
if (allStyles || styleProp == "borderSkin")
{
if (border)
{
rawChildren.removeChild(DisplayObject(border));
border = null;
createBorder();
}
}
// Create a border object, if none previously existed and
// one is needed now.
if (allStyles ||
styleProp == "borderStyle" ||
styleProp == "backgroundColor" ||
styleProp == "backgroundImage" ||
styleProp == "mouseShield" ||
styleProp == "mouseShieldChildren")
{
createBorder();
}
super.styleChanged(styleProp);
// Check to see if this is one of the style properties that is known.
// to affect page layout.
if (allStyles ||
styleManager.isSizeInvalidatingStyle(styleProp))
{
invalidateViewMetricsAndPadding();
}
if (allStyles || styleProp == "horizontalScrollBarStyleName")
{
if (horizontalScrollBar && horizontalScrollBar is ISimpleStyleClient)
{
var horizontalScrollBarStyleName:String =
getStyle("horizontalScrollBarStyleName");
ISimpleStyleClient(horizontalScrollBar).styleName =
horizontalScrollBarStyleName;
}
}
if (allStyles || styleProp == "verticalScrollBarStyleName")
{
if (verticalScrollBar && verticalScrollBar is ISimpleStyleClient)
{
var verticalScrollBarStyleName:String =
getStyle("verticalScrollBarStyleName");
ISimpleStyleClient(verticalScrollBar).styleName =
verticalScrollBarStyleName;
}
}
}
/**
* @private
* Call the styleChanged method on children of this container
*
* Notify chrome children immediately, and recursively call this
* function for all descendants of the chrome children. We recurse
* regardless of the recursive flag because one of the descendants
* might have a styleName property that points to this object.
*
* If recursive is true, then also notify content children ... but
* do it later. Notification is deferred so that multiple calls to
* setStyle can be batched up into one traversal.
*/
override public function notifyStyleChangeInChildren(
styleProp:String, recursive:Boolean):void
{
// Notify chrome children immediately, recursively calling this
// this function
var n:int = super.numChildren;
for (var i:int = 0; i < n; i++)
{
// Is this a chrome child?
if (contentPane ||
i < _firstChildIndex ||
i >= _firstChildIndex + _numChildren)
{
var child:ISimpleStyleClient = super.getChildAt(i) as ISimpleStyleClient;
if (child)
{
child.styleChanged(styleProp);
if (child is IStyleClient)
IStyleClient(child).notifyStyleChangeInChildren(styleProp, recursive);
}
}
}
// If recursive, then remember to notify the content children later
if (recursive)
{
// If multiple styleProps have changed, set changedStyles to
// MULTIPLE_PROPERTIES. Otherwise, set it to the name of the
// changed property.
changedStyles = (changedStyles != null || styleProp == null) ?
MULTIPLE_PROPERTIES : styleProp;
invalidateProperties();
}
}
/**
* @private
*/
override public function regenerateStyleCache(recursive:Boolean):void
{
super.regenerateStyleCache(recursive);
if (contentPane)
{
// Do the same thing as UIComponent, but don't check the child's index to
// ascertain that it's a content child (we already know that here).
var n:int = contentPane.numChildren;
for (var i:int = 0; i < n; i++)
{
var child:DisplayObject = getChildAt(i);
if (child is UIComponent)
{
// Does this object already have a proto chain? If not,
// there's no need to regenerate a new one.
if (UIComponent(child).inheritingStyles != StyleProtoChain.STYLE_UNINITIALIZED)
UIComponent(child).regenerateStyleCache(recursive);
}
else if (child is IUITextField && IUITextField(child).inheritingStyles)
{
StyleProtoChain.initTextField(IUITextField(child));
}
}
}
}
/**
* Used internally by the Dissolve Effect to add the overlay to the chrome of a container.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override protected function attachOverlay():void
{
rawChildren_addChild(effectOverlay);
}
/**
* Fill an overlay object which is always the topmost child in the container.
* This method is used
* by the Dissolve effect; never call it directly. It is called
* internally by the <code>addOverlay()</code> method.
*
* The Container fills the overlay object so it covers the viewable area returned
* by the <code>viewMetrics</code> property and uses the <code>cornerRadius</code> style.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override mx_internal function fillOverlay(overlay:UIComponent, color:uint,
targetArea:RoundedRectangle = null):void
{
var vm:EdgeMetrics = viewMetrics;
var cornerRadius:Number = 0; //getStyle("cornerRadius");
if (!targetArea)
{
targetArea = new RoundedRectangle(
vm.left, vm.top,
unscaledWidth - vm.right - vm.left,
unscaledHeight - vm.bottom - vm.top,cornerRadius);
}
if (isNaN(targetArea.x) || isNaN(targetArea.y) ||
isNaN(targetArea.width) || isNaN(targetArea.height) ||
isNaN(targetArea.cornerRadius))
return;
var g:Graphics = overlay.graphics;
g.clear();
g.beginFill(color);
g.drawRoundRect(targetArea.x, targetArea.y,
targetArea.width, targetArea.height,
targetArea.cornerRadius * 2,
targetArea.cornerRadius * 2);
g.endFill();
}
/**
* Executes all the data bindings on this Container. Flex calls this method
* automatically once a Container has been created to cause any data bindings that
* have destinations inside of it to execute.
*
* Workaround for MXML container/bindings problem (177074):
* override Container.executeBindings() to prefer descriptor.document over parentDocument in the
* call to BindingManager.executeBindings().
*
* This should always provide the correct behavior for instances created by descriptor, and will
* provide the original behavior for procedurally-created instances. (The bug may or may not appear
* in the latter case.)
*
* A more complete fix, guaranteeing correct behavior in both non-DI and reparented-component
* scenarios, is anticipated for updater 1.
*
* @param recurse If <code>false</code>, then only execute the bindings
* on this Container.
* If <code>true</code>, then also execute the bindings on this
* container's children, grandchildren,
* great-grandchildren, and so on.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function executeBindings(recurse:Boolean = false):void
{
var bindingsHost:Object = descriptor && descriptor.document ? descriptor.document : parentDocument;
BindingManager.executeBindings(bindingsHost, id, this);
if (recurse)
executeChildBindings(recurse);
}
/**
* @private
* Prepare the Object for printing
*
* @see mx.printing.FlexPrintJob
*/
override public function prepareToPrint(target:IFlexDisplayObject):Object
{
var rect:Rectangle = (contentPane && contentPane.scrollRect) ? contentPane.scrollRect : null;
if (rect)
contentPane.scrollRect = null;
super.prepareToPrint(target);
return rect;
}
/**
* @private
* After printing is done
*
* @see mx.printing.FlexPrintJob
*/
override public function finishPrint(obj:Object, target:IFlexDisplayObject):void
{
if (obj)
contentPane.scrollRect = Rectangle(obj);
super.finishPrint(obj,target);
}
//--------------------------------------------------------------------------
//
// Methods: Child management
//
//--------------------------------------------------------------------------
/**
* @private
*/
override mx_internal function addingChild(child:DisplayObject):void
{
// Throw an RTE if child is not an IUIComponent.
var uiChild:IUIComponent = IUIComponent(child);
// Set the child's virtual parent, nestLevel, document, etc.
super.addingChild(child);
invalidateSize();
invalidateDisplayList();
if (!contentPane)
{
// If this is the first content child, then any chrome
// that already exists is positioned in front of it.
// If other content children already existed, then set the
// depth of this object to be just behind the existing
// content children.
if (_numChildren == 0)
_firstChildIndex = super.numChildren;
// Increment the number of content children.
_numChildren++;
}
if (contentPane && !autoLayout)
{
forceLayout = true;
// weak reference
UIComponentGlobals.layoutManager.addEventListener(
FlexEvent.UPDATE_COMPLETE, layoutCompleteHandler, false, 0, true);
}
}
/**
* @private
*/
override mx_internal function childAdded(child:DisplayObject):void
{
if (hasEventListener("childrenChanged"))
dispatchEvent(new Event("childrenChanged"));
if (hasEventListener(ChildExistenceChangedEvent.CHILD_ADD))
{
var event:ChildExistenceChangedEvent =
new ChildExistenceChangedEvent(
ChildExistenceChangedEvent.CHILD_ADD);
event.relatedObject = child;
dispatchEvent(event);
}
if (child.hasEventListener(FlexEvent.ADD))
child.dispatchEvent(new FlexEvent(FlexEvent.ADD));
super.childAdded(child); // calls createChildren()
}
/**
* @private
*/
override mx_internal function removingChild(child:DisplayObject):void
{
super.removingChild(child);
if (child.hasEventListener(FlexEvent.REMOVE))
child.dispatchEvent(new FlexEvent(FlexEvent.REMOVE));
if (hasEventListener(ChildExistenceChangedEvent.CHILD_REMOVE))
{
var event:ChildExistenceChangedEvent =
new ChildExistenceChangedEvent(
ChildExistenceChangedEvent.CHILD_REMOVE);
event.relatedObject = child;
dispatchEvent(event);
}
}
/**
* @private
*/
override mx_internal function childRemoved(child:DisplayObject):void
{
super.childRemoved(child);
invalidateSize();
invalidateDisplayList();
if (!contentPane)
{
_numChildren--;
if (_numChildren == 0)
_firstChildIndex = super.numChildren;
}
if (contentPane && !autoLayout)
{
forceLayout = true;
// weak reference
UIComponentGlobals.layoutManager.addEventListener(
FlexEvent.UPDATE_COMPLETE, layoutCompleteHandler, false, 0, true);
}
if (hasEventListener("childrenChanged"))
dispatchEvent(new Event("childrenChanged"));
}
[Bindable("childrenChanged")]
/**
* Returns an Array of DisplayObject objects consisting of the content children
* of the container.
* This array does <b>not</b> include the DisplayObjects that implement
* the container's display elements, such as its border and
* the background image.
*
* @return Array of DisplayObject objects consisting of the content children
* of the container.
*
* @see #rawChildren
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function getChildren():Array
{
var results:Array = [];
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
results.push(getChildAt(i));
}
return results;
}
/**
* Removes all children from the child list of this container.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function removeAllChildren():void
{
while (numChildren > 0)
{
removeChildAt(0);
}
}
/**
* @private
*/
override public function set nestLevel(value:int):void
{
var oldValue:int = super.nestLevel;
super.nestLevel = value;
if (contentPane)
{
// If my parent hasn't been attached to the display list, then its nestLevel
// will be zero. If it tries to set my nestLevel to 1, ignore it. We'll
// update nest levels again after the parent is added to the display list.
if (value == 1)
return;
// Also punt if the new value for nestLevel is the same as my current value.
// TODO: (aharui) add early exit if nestLevel isn't changing
if (value > 1 && oldValue != value)
value ++;
else if (value == 0)
value = 0;
else
value ++;
var n:int = contentPane.numChildren;
for (var i:int = 0; i < n; i++)
{
var ui:ILayoutManagerClient = contentPane.getChildAt(i) as ILayoutManagerClient;
if (ui)
{
ui.nestLevel = value;
}
else
{
var textField:IUITextField = contentPane.getChildAt(i) as IUITextField;
if (textField)
textField.nestLevel = value;
}
}
}
}
//--------------------------------------------------------------------------
//
// Methods: Deferred instantiation
//
//--------------------------------------------------------------------------
/**
* @private
* For containers, we need to ensure that at most one set of children
* has been specified for the component.
* There are two ways to specify multiple sets of children:
* a) the component itself, as well as an instance of the component,
* might specify children;
* b) both a base and derived component might specify children.
* Case (a) is handled in initialize(), above.
* Case (b) is handled here.
* This method is called in overrides of initialize()
* that are generated for MXML components.
*/
mx_internal function setDocumentDescriptor(desc:UIComponentDescriptor):void
{
if (processedDescriptors)
return;
if (_documentDescriptor && _documentDescriptor.properties.childDescriptors)
{
if (desc.properties.childDescriptors)
{
var message:String = resourceManager.getString(
"core", "multipleChildSets_ClassAndSubclass");
throw new Error(message);
}
}
else
{
_documentDescriptor = desc;
_documentDescriptor.document = this;
}
}
/**
* @private
* Used by subclasses, so must be public.
*/
mx_internal function setActualCreationPolicies(policy:String):void
{
actualCreationPolicy = policy;
// Recursively set the actualCreationPolicy of all descendant
// containers which have an undefined creationPolicy.
var childPolicy:String = policy;
if (policy == ContainerCreationPolicy.QUEUED)
childPolicy = ContainerCreationPolicy.AUTO;
//trace("setActualCreationPolicies policy", policy, "childPolicy", childPolicy);
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
var child:IFlexDisplayObject = IFlexDisplayObject(getChildAt(i));
if (child is Container)
{
var childContainer:Container = Container(child);
if (childContainer.creationPolicy == null)
childContainer.setActualCreationPolicies(childPolicy);
}
}
}
/**
* Iterate through the Array of <code>childDescriptors</code>,
* and call the <code>createComponentFromDescriptor()</code> method for each one.
*
* <p>If the value of the container's <code>creationPolicy</code> property is
* <code>ContainerCreationPolicy.ALL</code>, then this method is called
* automatically during the initialization sequence.</p>
*
* <p>If the value of the container's <code>creationPolicy</code> is
* <code>ContainerCreationPolicy.AUTO</code>,
* then this method is called automatically when the
* container's children are about to become visible.</p>
*
* <p>If the value of the container's <code>creationPolicy</code> property is
* <code>ContainerCreationPolicy.NONE</code>,
* then you should call this function
* when you want to create this container's children.</p>
*
* @param recurse If <code>true</code>, recursively
* create components.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function createComponentsFromDescriptors(
recurse:Boolean = true):void
{
numChildrenBefore = numChildren;
createdComponents = [];
var n:int = childDescriptors ? childDescriptors.length : 0;
for (var i:int = 0; i < n; i++)
{
var component:IFlexDisplayObject =
createComponentFromDescriptor(childDescriptors[i], recurse);
createdComponents.push(component);
}
if (creationPolicy == ContainerCreationPolicy.QUEUED ||
creationPolicy == ContainerCreationPolicy.NONE)
{
UIComponentGlobals.layoutManager.usePhasedInstantiation = false;
}
numChildrenCreated = numChildren - numChildrenBefore;
processedDescriptors = true;
dispatchEvent(new FlexEvent(FlexEvent.CONTENT_CREATION_COMPLETE));
}
/**
* Performs the equivalent action of calling
* the <code>createComponentsFromDescriptors(true)</code> method for containers
* that implement the IDeferredContentOwner interface to support deferred instantiation.
*
* @see #createComponentsFromDescriptors()
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function createDeferredContent():void
{
var children:Array = this.MXMLDescriptor;
if (children)
generateMXMLInstances(document, children);
else
createComponentsFromDescriptors(true);
}
/**
* Given a single UIComponentDescriptor, create the corresponding
* component and add the component as a child of this Container.
*
* <p>This method instantiates the new object but does not add it to the display list, so the object does not
* appear on the screen by default. To add the new object to the display list, call the <code>validateNow()</code>
* method on the container after calling the <code>createComponentFromDescriptor()</code> method,
* as the following example shows:
* <pre>
* myVBox.createComponentFromDescriptor(myVBox.childDescriptors[0],false);
* myVBox.validateNow();
* </pre>
* </p>
*
* <p>Alternatively, you can call the <code>createComponentsFromDescriptors()</code> method on the
* container to create all components at one time. You are not required to call the <code>validateNow()</code>
* method after calling the <code>createComponentsFromDescriptors()</code> method.</p>
*
*
* @param descriptorOrIndex The UIComponentDescriptor for the
* component to be created. This argument is either a
* UIComponentDescriptor object or the index of one of the container's
* children (an integer between 0 and n-1, where n is the total
* number of children of this container).
*
* @param recurse If <code>false</code>, create this component
* but none of its children.
* If <code>true</code>, after creating the component, Flex calls
* the <code>createComponentsFromDescriptors()</code> method to create all or some
* of its children, based on the value of the component's <code>creationPolicy</code> property.
*
* @return The created component.
*
* @see mx.core.UIComponentDescriptor
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function createComponentFromDescriptor(
descriptor:ComponentDescriptor,
recurse:Boolean):IFlexDisplayObject
{
// If recurse is 'false', we create this component but none
// of its children.
// If recurse is 'true', after creating the component we call
// createComponentsFromDescriptors() to create all or some
// of its children, based on the component's
// actualContainerCreationPolicy.
var childDescriptor:UIComponentDescriptor =
UIComponentDescriptor(descriptor);
var childProperties:Object = childDescriptor.properties;
// This function could be asked to create the same child component
// twice. That's fine if the child component is inside a repeater.
// In other cases, though, we want to avoid creating the same child
// twice.
//
// The hasChildMatchingDescriptor function is a bit expensive, so
// we try to avoid calling it if we know we're inside the first call
// to createComponents.
if ((numChildrenBefore != 0 || numChildrenCreated != -1) &&
childDescriptor.instanceIndices == null &&
hasChildMatchingDescriptor(childDescriptor))
{
return null;
}
// Turn on the three-frame instantiation scheme.
UIComponentGlobals.layoutManager.usePhasedInstantiation = true;
// Create the new child object and give it a unique name
var childType:Class = childDescriptor.type;
var child:IDeferredInstantiationUIComponent = new childType();
child.id = childDescriptor.id;
if (child.id && child.id != "")
child.name = child.id;
// Copy property values from the descriptor
// to the newly created component.
child.descriptor = childDescriptor;
if (childProperties.childDescriptors && child is Container)
{
Container(child)._childDescriptors =
childProperties.childDescriptors;
delete childProperties.childDescriptors;
}
for (var p:String in childProperties)
{
child[p] = childProperties[p];
}
// Set a flag indicating whether we should call
// this function recursively.
if (child is Container)
Container(child).recursionFlag = recurse;
if (childDescriptor.instanceIndices)
{
if (child is IRepeaterClient)
{
var rChild:IRepeaterClient = IRepeaterClient(child);
rChild.instanceIndices = childDescriptor.instanceIndices;
rChild.repeaters = childDescriptor.repeaters;
rChild.repeaterIndices = childDescriptor.repeaterIndices;
}
}
if (child is IStyleClient)
{
var scChild:IStyleClient = IStyleClient(child);
// Initialize the CSSStyleDeclaration.
// It is used by initProtoChain(), which is called by addChild().
if (childDescriptor.stylesFactory != null)
{
if (!scChild.styleDeclaration)
scChild.styleDeclaration = new CSSStyleDeclaration(null, styleManager);
scChild.styleDeclaration.factory =
childDescriptor.stylesFactory;
}
}
// For each event, register the handle method, which is specified in
// the descriptor by name, as one of the child's event listeners.
var childEvents:Object = childDescriptor.events;
if (childEvents)
{
for (var eventName:String in childEvents)
{
var eventHandler:String = childEvents[eventName];
child.addEventListener(eventName,
childDescriptor.document[eventHandler]);
}
}
// For each effect, register the EffectManager as an event listener
var childEffects:Array = childDescriptor.effects;
if (childEffects)
child.registerEffects(childEffects);
if (child is IRepeaterClient)
IRepeaterClient(child).initializeRepeaterArrays(this);
// If an MXML id was specified, create a property with this name on
// the MXML document object whose value references the child.
// This should be the last step in initializing the child, so that
// it can't be referenced until initialization is complete.
// However, it must be done before executing executeBindings().
child.createReferenceOnParentDocument(
IFlexDisplayObject(childDescriptor.document));
if (!child.document)
child.document = childDescriptor.document;
// Repeaters don't get added as children of the Container,
// so they have their own initialization sequence.
if (child is IRepeater)
{
// Add this repeater to the list maintained by the parent
// container
if (!childRepeaters)
childRepeaters = [];
childRepeaters.push(child);
// The Binding Manager may have some data that it wants to bind to
// various properties of the newly created repeater.
child.executeBindings();
IRepeater(child).initializeRepeater(this, recurse);
}
else
{
// This needs to run before child.executeBindings(), because
// executeBindings() depends on the parent being set.
addChild(DisplayObject(child));
child.executeBindings();
if (creationPolicy == ContainerCreationPolicy.QUEUED ||
creationPolicy == ContainerCreationPolicy.NONE)
{
child.addEventListener(FlexEvent.CREATION_COMPLETE,
creationCompleteHandler);
}
}
// Return a reference to the child UIComponent that was just created.
return child;
}
/**
* @private
*/
private function hasChildMatchingDescriptor(
descriptor:UIComponentDescriptor):Boolean
{
// Optimization: If the descriptor has an id but no such id
// reference exists on the document, then there are no children
// in this container with that descriptor.
// (On the other hand, if there IS an id reference on the document,
// we can't be sure that it is for a child of this container. It
// could be an indexed reference in which some instances are
// in other containers. This happens when you have
// <Repeater>
// <VBox>
// <Button>
var id:String = descriptor.id;
if (id != null && (id in document) && document[id] == null)
return false;
var n:int = numChildren;
var i:int;
// Iterate over this container's children, looking for one
// that matches this descriptor
for (i = 0; i < n; i++)
{
var child:IUIComponent = IUIComponent(getChildAt(i));
if (child is IDeferredInstantiationUIComponent &&
IDeferredInstantiationUIComponent(child)
.descriptor == descriptor)
{
return true;
}
}
// Also check this container's Repeaters, if there are any.
if (childRepeaters)
{
n = childRepeaters.length;
for (i = 0; i < n; i++)
{
if (IDeferredInstantiationUIComponent(childRepeaters[i])
.descriptor == descriptor)
{
return true;
}
}
}
return false;
}
//--------------------------------------------------------------------------
//
// Methods: Support for rawChildren access
//
//--------------------------------------------------------------------------
/**
* @private
* This class overrides addChild() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_addChild(child:DisplayObject):DisplayObject
{
// This method is only used to implement rawChildren.addChild(),
// so the child being added is assumed to be a non-content child.
// (You would use just addChild() to add a content child.)
// If there are no content children, the new child is placed
// in the pre-content partition.
// If there are content children, the new child is placed
// in the post-content partition.
if (_numChildren == 0)
_firstChildIndex++;
super.addingChild(child);
$addChild(child);
super.childAdded(child);
dispatchEvent(new Event("childrenChanged"));
return child;
}
/**
* @private
* This class overrides addChildAt() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_addChildAt(child:DisplayObject,
index:int):DisplayObject
{
if (_firstChildIndex < index &&
index < _firstChildIndex + _numChildren + 1)
{
_numChildren++;
}
else if (index <= _firstChildIndex)
{
_firstChildIndex++;
}
super.addingChild(child);
$addChildAt(child, index);
super.childAdded(child);
dispatchEvent(new Event("childrenChanged"));
return child;
}
/**
* @private
* This class overrides removeChild() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_removeChild(
child:DisplayObject):DisplayObject
{
var index:int = rawChildren_getChildIndex(child);
return rawChildren_removeChildAt(index);
}
/**
* @private
* This class overrides removeChildAt() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_removeChildAt(index:int):DisplayObject
{
var child:DisplayObject = super.getChildAt(index);
super.removingChild(child);
$removeChildAt(index);
super.childRemoved(child);
if (_firstChildIndex < index &&
index < _firstChildIndex + _numChildren)
{
_numChildren--;
}
else if (_numChildren == 0 || index < _firstChildIndex)
{
_firstChildIndex--;
}
invalidateSize();
invalidateDisplayList();
dispatchEvent(new Event("childrenChanged"));
return child;
}
/**
* @private
* This class overrides getChildAt() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_getChildAt(index:int):DisplayObject
{
return super.getChildAt(index);
}
/**
* @private
* This class overrides getChildByName() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_getChildByName(name:String):DisplayObject
{
return super.getChildByName(name);
}
/**
* @private
* This class overrides getChildIndex() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_getChildIndex(child:DisplayObject):int
{
return super.getChildIndex(child);
}
/**
* @private
* This class overrides setChildIndex() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_setChildIndex(child:DisplayObject,
newIndex:int):void
{
var oldIndex:int = super.getChildIndex(child);
super.setChildIndex(child, newIndex);
// Is this a piece of chrome that was previously before
// the content children and is now after them in the list?
if (oldIndex < _firstChildIndex && newIndex >= _firstChildIndex)
{
_firstChildIndex--;
}
// Is this a piece of chrome that was previously after
// the content children and is now before them in the list?
else if (oldIndex >= _firstChildIndex && newIndex <= _firstChildIndex)
{
_firstChildIndex++
}
dispatchEvent(new Event("childrenChanged"));
}
/**
* @private
* This class overrides getObjectsUnderPoint() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_getObjectsUnderPoint(pt:Point):Array
{
return super.getObjectsUnderPoint(pt);
}
/**
* @private
* This class overrides contains() to deal with only content children,
* so in order to implement the rawChildren property we need
* a parallel method that deals with all children.
*/
mx_internal function rawChildren_contains(child:DisplayObject):Boolean
{
return super.contains(child);
}
//--------------------------------------------------------------------------
//
// Methods: Chrome management
//
//--------------------------------------------------------------------------
/**
* Respond to size changes by setting the positions and sizes
* of this container's borders.
* This is an advanced method that you might override
* when creating a subclass of Container.
*
* <p>Flex calls the <code>layoutChrome()</code> method when the
* container is added to a parent container using the <code>addChild()</code> method,
* and when the container's <code>invalidateDisplayList()</code> method is called.</p>
*
* <p>The <code>Container.layoutChrome()</code> method is called regardless of the
* value of the <code>autoLayout</code> property.</p>
*
* <p>The <code>Container.layoutChrome()</code> method sets the
* position and size of the Container container's border.
* In every subclass of Container, the subclass's <code>layoutChrome()</code>
* method should call the <code>super.layoutChrome()</code> method,
* so that the border is positioned properly.</p>
*
* @param unscaledWidth Specifies the width of the component, in pixels,
* in the component's coordinates, regardless of the value of the
* <code>scaleX</code> property of the component.
*
* @param unscaledHeight Specifies the height of the component, in pixels,
* in the component's coordinates, regardless of the value of the
* <code>scaleY</code> property of the component.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function layoutChrome(unscaledWidth:Number,
unscaledHeight:Number):void
{
// Border covers the whole thing.
if (border)
{
updateBackgroundImageRect();
border.move(0, 0);
border.setActualSize(unscaledWidth, unscaledHeight);
}
}
/**
* Creates the container's border skin
* if it is needed and does not already exist.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function createBorder():void
{
if (!border && isBorderNeeded())
{
var borderClass:Class = getStyle("borderSkin");
if (borderClass != null)
{
border = new borderClass();
border.name = "border";
if (border is IUIComponent)
IUIComponent(border).enabled = enabled;
if (border is ISimpleStyleClient)
ISimpleStyleClient(border).styleName = this;
// Add the border behind all the children.
rawChildren.addChildAt(DisplayObject(border), 0);
invalidateDisplayList();
}
}
}
/**
* @private
* Store references to the default border classes here so we don't have to look them up
* every time.
*/
private static var haloBorder:Class;
private static var sparkBorder:Class;
private static var sparkContainerBorder:Class;
private static var didLookup:Boolean = false;
private static function getDefinition(name:String):Class
{
var result:Object = null;
try
{
result = getDefinitionByName(name);
}
catch(e:Error)
{
}
return result as Class;
}
/**
* @private
*/
private function isBorderNeeded():Boolean
{
//trace("isBorderNeeded",this,"ms",getStyle("mouseShield"),"borderStyle",getStyle("borderStyle"));
// If the borderSkin is a custom class, always assume the border is needed.
var c:Class = getStyle("borderSkin");
if (!didLookup)
{
// Lookup the default border classes by name to avoid a linkage dependency.
// Note: this code assumes either HaloBorder or spark BorderSkin is the default border skin.
// If this is changed in defaults.css, it must also be changed here.
haloBorder = getDefinition("mx.skins.halo::HaloBorder");
sparkBorder = getDefinition("mx.skins.spark::BorderSkin");
sparkContainerBorder = getDefinition("mx.skins.spark::ContainerBorderSkin");
didLookup = true;
}
if (!(c == haloBorder || c == sparkBorder || c == sparkContainerBorder))
return true;
var v:Object = getStyle("borderStyle");
if (v)
{
// If borderStyle is "none", then only create a border if the mouseShield style is true
// (meaning that there is a mouse event listener on this view). We don't create a border
// if our parent's mouseShieldChildren style is true.
if ((v != "none") || (v == "none" && getStyle("mouseShield")))
{
return true;
}
}
v = getStyle("contentBackgroundColor");
if (c == sparkBorder && v !== null)
return true;
v = getStyle("backgroundColor");
if (v !== null && v !== "")
return true;
v = getStyle("backgroundImage");
return v != null && v != "";
}
/**
* @private
*/
mx_internal function invalidateViewMetricsAndPadding():void
{
_viewMetricsAndPadding = null;
}
/**
* @private
*/
private function createOrDestroyBlocker():void
{
// If this container is being enabled and a blocker exists,
// remove it. If this container is being disabled and a
// blocker doesn't exist, create it.
if (enabled)
{
if (blocker)
{
rawChildren.removeChild(blocker);
blocker = null;
}
}
else
{
if (!blocker)
{
blocker = new FlexSprite();
blocker.name = "blocker";
blocker.mouseEnabled = true;
rawChildren.addChild(blocker);
blocker.addEventListener(MouseEvent.CLICK,
blocker_clickHandler);
// If the focus is a child of ours, we clear it here.
var o:DisplayObject =
focusManager ?
DisplayObject(focusManager.getFocus()) :
null;
while (o)
{
if (o == this)
{
var sm:ISystemManager = systemManager;
if (sm && sm.stage)
sm.stage.focus = null;
break;
}
o = o.parent;
}
}
}
}
/**
* @private
*/
private function updateBackgroundImageRect():void
{
var rectBorder:IRectangularBorder = border as IRectangularBorder;
if (!rectBorder)
return;
// If viewableWidth and viewableHeight are 0, we don't have any
// scrollbars or clipped content.
if (viewableWidth == 0 && viewableHeight == 0)
{
rectBorder.backgroundImageBounds = null;
return;
}
var vm:EdgeMetrics = viewMetrics;
var bkWidth:Number = viewableWidth ? viewableWidth :
unscaledWidth - vm.left - vm.right;
var bkHeight:Number = viewableHeight ? viewableHeight :
unscaledHeight - vm.top - vm.bottom;
if (getStyle("backgroundAttachment") == "fixed")
{
rectBorder.backgroundImageBounds = new Rectangle(vm.left, vm.top,
bkWidth, bkHeight);
}
else
{
rectBorder.backgroundImageBounds = new Rectangle(vm.left, vm.top,
Math.max(scrollableWidth, bkWidth),
Math.max(scrollableHeight, bkHeight));
}
}
//--------------------------------------------------------------------------
//
// Methods: Scrolling
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function createContentPaneAndScrollbarsIfNeeded():Boolean
{
var bounds:Rectangle;
var changed:Boolean;
// No mask is needed if clipping isn't active
if (_clipContent)
{
// Get the new scrollable width, which is the equal to the right
// edge of the rightmost child. Also get the new scrollable height.
bounds = getScrollableRect();
// Create or destroy scrollbars if necessary, and update the
// properties of the scrollbars.
changed = createScrollbarsIfNeeded(bounds);
if (border)
updateBackgroundImageRect();
return changed;
}
else
{
changed = createOrDestroyScrollbars(false, false, false);
// Get scrollableWidth and scrollableHeight for scrollChildren()
bounds = getScrollableRect();
scrollableWidth = bounds.right;
scrollableHeight = bounds.bottom;
if (changed && border)
updateBackgroundImageRect();
return changed;
}
}
/**
* @private
*/
mx_internal function getScrollableRect():Rectangle
{
var left:Number = 0;
var top:Number = 0;
var right:Number = 0;
var bottom:Number = 0;
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
var x:Number;
var y:Number;
var width:Number;
var height:Number;
var child:DisplayObject = getChildAt(i);
if (child is IUIComponent)
{
if (!IUIComponent(child).includeInLayout)
continue;
var uic:IUIComponent = PostScaleAdapter.getCompatibleIUIComponent(child);
width = uic.width;
height = uic.height;
x = uic.x;
y = uic.y;
}
else
{
width = child.width;
height = child.height;
x = child.x;
y = child.y;
}
left = Math.min(left, x);
top = Math.min(top, y);
// width/height can be NaN if using percentages and
// hasn't been layed out yet.
if (!isNaN(width))
right = Math.max(right, x + width);
if (!isNaN(height))
bottom = Math.max(bottom, y + height);
}
// Add in the right/bottom margins and view metrics.
var vm:EdgeMetrics = viewMetrics;
var bounds:Rectangle = new Rectangle();
bounds.left = left;
bounds.top = top;
bounds.right = right;
bounds.bottom = bottom;
if (usePadding)
{
bounds.right += getStyle("paddingRight");
bounds.bottom += getStyle("paddingBottom");
}
return bounds;
}
/**
* @private
*/
private function createScrollbarsIfNeeded(bounds:Rectangle):Boolean
{
var newScrollableWidth:Number = bounds.right;
var newScrollableHeight:Number = bounds.bottom;
var newViewableWidth:Number = unscaledWidth;
var newViewableHeight:Number = unscaledHeight;
var hasNegativeCoords:Boolean = bounds.left < 0 || bounds.top < 0;
var vm:EdgeMetrics = viewMetrics;
// Several of the layout managers round floating-point numbers
// down, using Math.floor().
// The rounded-down width value is passed to UIComponent.setActualSize,
// which does the following:
//
// unscaledWidth = w / scaleX
//
// Suppose "w" was originally 91.9 but the layout manager
// rounded it down to 91. Suppose scaleX is 0.01.
// Then unscaledWidth is 91/0.01 = 9100, but it would have been
// 91.9/0.01 = 9190 if it weren't for the rounding.
// To undo the effect of the rounding, we'll add a fudge factor to
// newViewableWidth. That way, we don't display unwanted scrollbars.
if (scaleX != 1.0)
newViewableWidth += 1.0 / Math.abs(scaleX);
if (scaleY != 1.0)
newViewableHeight += 1.0 / Math.abs(scaleY);
newViewableWidth = Math.floor(newViewableWidth);
newViewableHeight = Math.floor(newViewableHeight);
newScrollableWidth = Math.floor(newScrollableWidth);
newScrollableHeight = Math.floor(newScrollableHeight);
if (horizontalScrollBar && horizontalScrollPolicy != ScrollPolicy.ON)
newViewableHeight -= horizontalScrollBar.minHeight;
if (verticalScrollBar && verticalScrollPolicy != ScrollPolicy.ON)
newViewableWidth -= verticalScrollBar.minWidth;
newViewableWidth -= (vm.left + vm.right);
newViewableHeight -= (vm.top + vm.bottom);
var needHorizontal:Boolean =
horizontalScrollPolicy == ScrollPolicy.ON;
var needVertical:Boolean =
verticalScrollPolicy == ScrollPolicy.ON;
var needContentPane:Boolean =
needHorizontal ||
needVertical ||
hasNegativeCoords ||
effectOverlay != null ||
vm.left > 0 ||
vm.top > 0;
// These "if" statements are tuned for the most common case,
// which is that the Container does not need scrollbars.
if (newViewableWidth < newScrollableWidth)
{
needContentPane = true;
// Don't display scrollbars if the Container is so small
// that scrollbars would occlude everything else
// or the scrollbar buttons would overlap.
if (horizontalScrollPolicy == ScrollPolicy.AUTO &&
unscaledHeight - vm.top - vm.bottom >= 18 &&
unscaledWidth - vm.left - vm.right >= 32)
{
needHorizontal = true;
}
}
if (newViewableHeight < newScrollableHeight)
{
needContentPane = true;
if (verticalScrollPolicy == ScrollPolicy.AUTO &&
unscaledWidth - vm.left - vm.right >= 18 &&
unscaledHeight - vm.top - vm.bottom >= 32)
{
needVertical = true;
}
}
// Fix for 106095. The logic here says "if removing the scrollbars
// would make enough room to display the view's children, then remove
// the scrollbars".
if (needHorizontal &&
needVertical &&
horizontalScrollPolicy == ScrollPolicy.AUTO &&
verticalScrollPolicy == ScrollPolicy.AUTO &&
horizontalScrollBar &&
verticalScrollBar &&
newViewableWidth + verticalScrollBar.minWidth >= newScrollableWidth &&
newViewableHeight + horizontalScrollBar.minHeight >= newScrollableHeight)
{
needHorizontal = needVertical = false;
}
// If the vertical scrollbar is going to be removed anyway, and
// removing it would also free up enough space for the contents to fit
// horizontally, then there's no need for the horizontal scrollbar
// either.
else if (needHorizontal &&
!needVertical &&
verticalScrollBar &&
horizontalScrollPolicy == ScrollPolicy.AUTO &&
newViewableWidth + verticalScrollBar.minWidth >= newScrollableWidth)
{
needHorizontal = false;
}
var changed:Boolean = createOrDestroyScrollbars(
needHorizontal, needVertical, needContentPane);
if ((scrollableWidth != newScrollableWidth ||
viewableWidth != newViewableWidth) ||
changed)
{
if (horizontalScrollBar)
{
horizontalScrollBar.setScrollProperties(
newViewableWidth, 0,
newScrollableWidth - newViewableWidth, horizontalPageScrollSize);
scrollPositionChanged = true;
}
viewableWidth = newViewableWidth;
scrollableWidth = newScrollableWidth;
}
if ((scrollableHeight != newScrollableHeight ||
viewableHeight != newViewableHeight) ||
changed)
{
if (verticalScrollBar)
{
verticalScrollBar.setScrollProperties(
newViewableHeight, 0,
newScrollableHeight-newViewableHeight, verticalPageScrollSize);
scrollPositionChanged = true;
}
viewableHeight = newViewableHeight;
scrollableHeight = newScrollableHeight;
}
return changed;
}
/**
* @private
*/
private function createOrDestroyScrollbars(
needHorizontal:Boolean,
needVertical:Boolean,
needContentPane:Boolean):Boolean
{
var changed:Boolean = false;
var fm:IFocusManager;
if (needHorizontal || needVertical || needContentPane)
createContentPane();
// Create or destroy horizontal scrollbar.
if (needHorizontal)
{
if (!horizontalScrollBar)
{
horizontalScrollBar = new HScrollBar();
horizontalScrollBar.name = "horizontalScrollBar";
var horizontalScrollBarStyleName:String =
getStyle("horizontalScrollBarStyleName");
if (horizontalScrollBarStyleName && horizontalScrollBar is ISimpleStyleClient)
ISimpleStyleClient(horizontalScrollBar).styleName = horizontalScrollBarStyleName;
rawChildren.addChild(DisplayObject(horizontalScrollBar));
horizontalScrollBar.lineScrollSize = horizontalLineScrollSize;
horizontalScrollBar.pageScrollSize = horizontalPageScrollSize;
horizontalScrollBar.addEventListener(ScrollEvent.SCROLL, horizontalScrollBar_scrollHandler);
horizontalScrollBar.enabled = enabled;
if (horizontalScrollBar is IInvalidating)
IInvalidating(horizontalScrollBar).validateNow();
invalidateDisplayList();
invalidateViewMetricsAndPadding();
changed = true;
if (!verticalScrollBar)
addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
}
}
else
{
if (horizontalScrollBar)
{
horizontalScrollBar.removeEventListener(
ScrollEvent.SCROLL,
horizontalScrollBar_scrollHandler);
rawChildren.removeChild(DisplayObject(horizontalScrollBar));
horizontalScrollBar = null;
viewableWidth = scrollableWidth = 0;
if (_horizontalScrollPosition != 0)
{
_horizontalScrollPosition = 0;
scrollPositionChanged = true;
}
invalidateDisplayList();
invalidateViewMetricsAndPadding();
changed = true;
fm = focusManager;
if (!verticalScrollBar && (!fm || fm.getFocus() != this))
removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
}
}
// Create or destroy vertical scrollbar.
if (needVertical)
{
if (!verticalScrollBar)
{
verticalScrollBar = new VScrollBar();
verticalScrollBar.name = "verticalScrollBar";
var verticalScrollBarStyleName:String =
getStyle("verticalScrollBarStyleName");
if (verticalScrollBarStyleName && verticalScrollBar is ISimpleStyleClient)
ISimpleStyleClient(verticalScrollBar).styleName = verticalScrollBarStyleName;
rawChildren.addChild(DisplayObject(verticalScrollBar));
verticalScrollBar.lineScrollSize = verticalLineScrollSize;
verticalScrollBar.pageScrollSize = verticalPageScrollSize;
verticalScrollBar.addEventListener(ScrollEvent.SCROLL, verticalScrollBar_scrollHandler);
verticalScrollBar.enabled = enabled;
if (verticalScrollBar is IInvalidating)
IInvalidating(verticalScrollBar).validateNow();
invalidateDisplayList();
invalidateViewMetricsAndPadding();
changed = true;
if (!horizontalScrollBar)
addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
// Listen for "mouseWheel" events on myself or any of my children
addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
}
}
else
{
if (verticalScrollBar)
{
verticalScrollBar.removeEventListener(ScrollEvent.SCROLL, verticalScrollBar_scrollHandler);
rawChildren.removeChild(DisplayObject(verticalScrollBar));
verticalScrollBar = null;
viewableHeight = scrollableHeight = 0;
if (_verticalScrollPosition != 0)
{
_verticalScrollPosition = 0;
scrollPositionChanged = true;
}
invalidateDisplayList();
invalidateViewMetricsAndPadding();
changed = true;
fm = focusManager;
if (!horizontalScrollBar && (!fm || fm.getFocus() != this))
removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
}
}
// Create or destroy the whiteBox.
// If both scrollBars are active, there's an empty space
// between the two scrollBars in the lower right corner.
// The whiteBox fills that space, so that the container's
// children aren't visible when they scroll underneath.
if (horizontalScrollBar && verticalScrollBar)
{
if (!whiteBox)
{
whiteBox = new FlexShape();
whiteBox.name = "whiteBox";
var g:Graphics = whiteBox.graphics;
g.beginFill(0xFFFFFF);
g.drawRect(0, 0, verticalScrollBar.minWidth, horizontalScrollBar.minHeight);
g.endFill()
rawChildren.addChild(whiteBox);
}
}
else
{
if (whiteBox)
{
rawChildren.removeChild(whiteBox);
whiteBox = null;
}
}
return changed;
}
/**
* @private
* Ensures that horizontalScrollPosition is in the range
* from 0 through maxHorizontalScrollPosition and that
* verticalScrollPosition is in the range from 0 through
* maxVerticalScrollPosition.
* Returns true if either horizontalScrollPosition or
* verticalScrollPosition was changed to ensure this.
*/
private function clampScrollPositions():Boolean
{
var changed:Boolean = false;
// Clamp horizontalScrollPosition to the range
// 0 through maxHorizontalScrollPosition.
// If horizontalScrollBar doesn't exist,
// maxHorizontalScrollPosition will be 0.
if (_horizontalScrollPosition < 0)
{
_horizontalScrollPosition = 0;
changed = true;
}
else if (_horizontalScrollPosition > maxHorizontalScrollPosition)
{
_horizontalScrollPosition = maxHorizontalScrollPosition;
changed = true;
}
// Set the position of the horizontal scrollbar's thumb.
if (horizontalScrollBar &&
horizontalScrollBar.scrollPosition != _horizontalScrollPosition)
{
horizontalScrollBar.scrollPosition = _horizontalScrollPosition;
}
// Clamp verticalScrollPosition to the range
// 0 through maxVerticalScrollPosition.
// If verticalScrollBar doesn't exist,
// maxVerticalScrollPosition will be 0.
if (_verticalScrollPosition < 0)
{
_verticalScrollPosition = 0;
changed = true;
}
else if (_verticalScrollPosition > maxVerticalScrollPosition)
{
_verticalScrollPosition = maxVerticalScrollPosition;
changed = true;
}
// Set the position of the vertical scrollbar's thumb.
if (verticalScrollBar &&
verticalScrollBar.scrollPosition != _verticalScrollPosition)
{
verticalScrollBar.scrollPosition = _verticalScrollPosition;
}
return changed;
}
/**
* @private
*/
mx_internal function createContentPane():void
{
if (contentPane)
return;
creatingContentPane = true;
// Reparent the children. Get the number before we create contentPane
// because that changes logic of how many children we have
var n:int = numChildren;
var newPane:Sprite = new FlexSprite();
newPane.name = "contentPane";
// Place content pane above border and background image but below
// all other chrome.
var childIndex:int;
if (border)
{
childIndex = rawChildren.getChildIndex(DisplayObject(border)) + 1;
if (border is IRectangularBorder && IRectangularBorder(border).hasBackgroundImage)
childIndex++;
}
else
{
childIndex = 0;
}
rawChildren.addChildAt(newPane, childIndex);
for (var i:int = 0; i < n; i++)
{
// use super because contentPane now exists and messes up getChildAt();
var child:IUIComponent =
IUIComponent(super.getChildAt(_firstChildIndex));
newPane.addChild(DisplayObject(child));
child.parentChanged(newPane);
_numChildren--; // required
}
contentPane = newPane;
creatingContentPane = false
// UIComponent sets $visible to false. If we don't make it true here,
// nothing shows up. Making this true should be harmless, as the
// container itself should be false, and so should all its children.
contentPane.visible = true;
}
/**
* Positions the container's content area relative to the viewable area
* based on the horizontalScrollPosition and verticalScrollPosition properties.
* Content that doesn't appear in the viewable area gets clipped.
* This method should be overridden by subclasses that have scrollable
* chrome in the content area.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function scrollChildren():void
{
if (!contentPane)
return;
var vm:EdgeMetrics = viewMetrics;
var x:Number = 0;
var y:Number = 0;
var w:Number = unscaledWidth - vm.left - vm.right;
var h:Number = unscaledHeight - vm.top - vm.bottom;
if (_clipContent)
{
x += _horizontalScrollPosition;
if (horizontalScrollBar)
w = viewableWidth;
y += _verticalScrollPosition;
if (verticalScrollBar)
h = viewableHeight;
}
else
{
w = scrollableWidth;
h = scrollableHeight;
}
// If we have enough space to display everything, don't set
// scrollRect.
var sr:Rectangle = getScrollableRect();
if (x == 0 && y == 0 // Not scrolled
&& w >= sr.right && h >= sr.bottom && // Vertical content visible
sr.left >= 0 && sr.top >= 0 && _forceClippingCount <= 0) // No negative coordinates
{
contentPane.scrollRect = null;
contentPane.opaqueBackground = null;
contentPane.cacheAsBitmap = false;
}
else
{
contentPane.scrollRect = new Rectangle(x, y, w, h);
}
if (focusPane)
focusPane.scrollRect = contentPane.scrollRect;
if (border && border is IRectangularBorder &&
IRectangularBorder(border).hasBackgroundImage)
{
IRectangularBorder(border).layoutBackgroundImage();
}
}
/**
* @private
*/
private function dispatchScrollEvent(direction:String,
oldPosition:Number,
newPosition:Number,
detail:String):void
{
var event:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL);
event.direction = direction;
event.position = newPosition;
event.delta = newPosition - oldPosition;
event.detail = detail;
dispatchEvent(event);
}
/**
* @private
* Used by a child component to force clipping during a Move effect
*/
private var _forceClippingCount:int;
mx_internal function set forceClipping(value:Boolean):void
{
if (_clipContent) // Can only force clipping if clipContent == true
{
if (value)
_forceClippingCount++
else
_forceClippingCount--;
createContentPane();
scrollChildren();
}
}
//--------------------------------------------------------------------------
//
// Methods: Binding
//
//--------------------------------------------------------------------------
/**
* Executes the bindings into this Container's child UIComponent objects.
* Flex calls this method automatically once a Container has been created.
*
* @param recurse If <code>false</code>, then only execute the bindings
* on the immediate children of this Container.
* If <code>true</code>, then also execute the bindings on this
* container's grandchildren,
* great-grandchildren, and so on.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function executeChildBindings(recurse:Boolean):void
{
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
var child:IUIComponent = IUIComponent(getChildAt(i));
if (child is IDeferredInstantiationUIComponent)
{
IDeferredInstantiationUIComponent(child).
executeBindings(recurse);
}
}
}
//--------------------------------------------------------------------------
//
// Overridden event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function keyDownHandler(event:KeyboardEvent):void
{
// If a text field currently has focus, it is handling all arrow keys.
// We shouldn't also scroll this Container.
var focusObj:Object = getFocus();
if ((focusObj is TextField) ||
(richEditableTextClass && focusObj is richEditableTextClass))
{
return;
}
// If the KeyBoardEvent can be canceled and a descendant has done so,
// don't process it at all.
if (event.isDefaultPrevented())
return;
var direction:String;
var oldPos:Number;
if (verticalScrollBar)
{
direction = ScrollEventDirection.VERTICAL;
oldPos = verticalScrollPosition;
switch (event.keyCode)
{
case Keyboard.DOWN:
{
verticalScrollPosition += verticalLineScrollSize;
dispatchScrollEvent(direction, oldPos,
verticalScrollPosition,
ScrollEventDetail.LINE_DOWN);
event.stopPropagation();
break;
}
case Keyboard.UP:
{
verticalScrollPosition -= verticalLineScrollSize;
dispatchScrollEvent(direction, oldPos,
verticalScrollPosition,
ScrollEventDetail.LINE_UP);
event.stopPropagation();
break;
}
case Keyboard.PAGE_UP:
{
verticalScrollPosition -= verticalPageScrollSize;
dispatchScrollEvent(direction, oldPos,
verticalScrollPosition,
ScrollEventDetail.PAGE_UP);
event.stopPropagation();
break;
}
case Keyboard.PAGE_DOWN:
{
verticalScrollPosition += verticalPageScrollSize;
dispatchScrollEvent(direction, oldPos,
verticalScrollPosition,
ScrollEventDetail.PAGE_DOWN);
event.stopPropagation();
break;
}
case Keyboard.HOME:
{
verticalScrollPosition =
verticalScrollBar.minScrollPosition;
dispatchScrollEvent(direction, oldPos,
verticalScrollPosition,
ScrollEventDetail.AT_TOP);
event.stopPropagation();
break;
}
case Keyboard.END:
{
verticalScrollPosition =
verticalScrollBar.maxScrollPosition;
dispatchScrollEvent(direction, oldPos,
verticalScrollPosition,
ScrollEventDetail.AT_BOTTOM);
event.stopPropagation();
break;
}
}
}
if (horizontalScrollBar)
{
direction = ScrollEventDirection.HORIZONTAL;
oldPos = horizontalScrollPosition;
// If rtl layout, need to swap LEFT and RIGHT so correct action
// is done.
var keyCode:uint = mapKeycodeForLayoutDirection(event);
switch (keyCode)
{
case Keyboard.LEFT:
{
horizontalScrollPosition -= horizontalLineScrollSize;
dispatchScrollEvent(direction, oldPos,
horizontalScrollPosition,
ScrollEventDetail.LINE_LEFT);
event.stopPropagation();
break;
}
case Keyboard.RIGHT:
{
horizontalScrollPosition += horizontalLineScrollSize;
dispatchScrollEvent(direction, oldPos,
horizontalScrollPosition,
ScrollEventDetail.LINE_RIGHT);
event.stopPropagation();
break;
}
}
}
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
* This method copied verbatim from mx.core.ScrollControlBase.
*/
private function mouseWheelHandler(event:MouseEvent):void
{
// If this Container has a vertical scrollbar, then handle the event
// and prevent further bubbling
if (verticalScrollBar && !event.isDefaultPrevented())
{
var scrollDirection:int = event.delta <= 0 ? 1 : -1;
var lineScrollSize:int = verticalScrollBar ?
verticalScrollBar.lineScrollSize :
1;
// Make sure we scroll by at least one line
var scrollAmount:Number =
Math.max(Math.abs(event.delta), lineScrollSize);
// Multiply by 3 to make scrolling a little faster
var oldPosition:Number = verticalScrollPosition;
verticalScrollPosition += 3 * scrollAmount * scrollDirection;
dispatchScrollEvent(ScrollEventDirection.VERTICAL,
oldPosition, verticalScrollPosition,
event.delta <= 0 ?
ScrollEventDetail.LINE_UP :
ScrollEventDetail.LINE_DOWN);
event.preventDefault();
}
}
/**
* @private
* This function is called when the LayoutManager finishes running.
* Clear the forceLayout flag that was set earlier.
*/
private function layoutCompleteHandler(event:FlexEvent):void
{
UIComponentGlobals.layoutManager.removeEventListener(
FlexEvent.UPDATE_COMPLETE, layoutCompleteHandler);
forceLayout = false;
var needToScrollChildren:Boolean = false;
if (!isNaN(horizontalScrollPositionPending))
{
if (horizontalScrollPositionPending < 0)
horizontalScrollPositionPending = 0;
else if (horizontalScrollPositionPending > maxHorizontalScrollPosition)
horizontalScrollPositionPending = maxHorizontalScrollPosition;
// Set the position of the horizontal scrollbar's thumb.
if (horizontalScrollBar &&
horizontalScrollBar.scrollPosition !=
horizontalScrollPositionPending)
{
_horizontalScrollPosition = horizontalScrollPositionPending;
horizontalScrollBar.scrollPosition =
horizontalScrollPositionPending;
needToScrollChildren = true;
}
horizontalScrollPositionPending = NaN;
}
if (!isNaN(verticalScrollPositionPending))
{
// Clamp verticalScrollPosition to the range 0 through maxVerticalScrollPosition.
// If verticalScrollBar doesn't exist, maxVerticalScrollPosition will be 0.
if (verticalScrollPositionPending < 0)
verticalScrollPositionPending = 0;
else if (verticalScrollPositionPending > maxVerticalScrollPosition)
verticalScrollPositionPending = maxVerticalScrollPosition;
// Set the position of the vertical scrollbar's thumb.
if (verticalScrollBar && verticalScrollBar.scrollPosition != verticalScrollPositionPending)
{
_verticalScrollPosition = verticalScrollPositionPending;
verticalScrollBar.scrollPosition = verticalScrollPositionPending;
needToScrollChildren = true;
}
verticalScrollPositionPending = NaN;
}
if (needToScrollChildren)
scrollChildren();
}
/**
* @private
*/
private function creationCompleteHandler(event:FlexEvent):void
{
numChildrenCreated--;
if (numChildrenCreated <= 0)
dispatchEvent(new FlexEvent("childrenCreationComplete"));
}
/**
* @private
* This method is called if the user interactively moves
* the horizontal scrollbar thumb.
*/
private function horizontalScrollBar_scrollHandler(event:Event):void
{
// TextField.scroll bubbles so you might see it here
if (event is ScrollEvent)
{
var oldPos:Number = horizontalScrollPosition;
horizontalScrollPosition = horizontalScrollBar.scrollPosition;
dispatchScrollEvent(ScrollEventDirection.HORIZONTAL,
oldPos,
horizontalScrollPosition,
ScrollEvent(event).detail);
}
}
/**
* @private
* This method is called if the user interactively moves
* the vertical scrollbar thumb.
*/
private function verticalScrollBar_scrollHandler(event:Event):void
{
// TextField.scroll bubbles so you might see it here
if (event is ScrollEvent)
{
var oldPos:Number = verticalScrollPosition;
verticalScrollPosition = verticalScrollBar.scrollPosition;
dispatchScrollEvent(ScrollEventDirection.VERTICAL,
oldPos,
verticalScrollPosition,
ScrollEvent(event).detail);
}
}
/**
* @private
*/
private function blocker_clickHandler(event:Event):void
{
// Swallow click events from blocker.
event.stopPropagation();
}
}
}