blob: dc0b76b219cbd3a53d921293c6c71622244aaf38 [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.containers
{
import flash.display.DisplayObject;
import mx.containers.utilityClasses.BoxLayout;
import mx.controls.Label;
import mx.core.Container;
import mx.core.IFlexModuleFactory;
import mx.core.IInvalidating;
import mx.core.IUIComponent;
import mx.core.mx_internal;
import mx.styles.IStyleManager2;
import mx.styles.StyleManager;
use namespace mx_internal;
include "../styles/metadata/GapStyles.as";
//--------------------------------------
// Styles
//--------------------------------------
/**
* Number of pixels between the label and child components.
* The default value is 14.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="indicatorGap", type="Number", format="Length", inherit="yes")]
/**
* Width of the form labels.
* The default is the length of the longest label in the form.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="labelWidth", type="Number", format="Length", inherit="yes")]
/**
* Number of pixels between the container's bottom border
* and the bottom edge of its content area.
* The default value is 16.
*
* @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 edge of its content area.
* The default value is 16.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingTop", type="Number", format="Length", inherit="no")]
//--------------------------------------
// Excluded APIs
//--------------------------------------
[Exclude(name="focusIn", kind="event")]
[Exclude(name="focusOut", kind="event")]
[Exclude(name="focusBlendMode", kind="style")]
[Exclude(name="focusSkin", kind="style")]
[Exclude(name="focusThickness", kind="style")]
[Exclude(name="focusInEffect", kind="effect")]
[Exclude(name="focusOutEffect", kind="effect")]
//--------------------------------------
// Other metadata
//--------------------------------------
[IconFile("Form.png")]
[Alternative(replacement="spark.components.Form", since="4.5")]
/**
* The Form container lets you control the layout of a form,
* mark form fields as required or optional, handle error messages,
* and bind your form data to the Flex data model to perform
* data checking and validation.
* It also lets you use style sheets to configure the appearance
* of your forms.
*
* <p>The following table describes the components you use to create forms in Flex:</p>
* <table class="innertable">
* <tr>
* <th>Component</th>
* <th>Tag</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>Form</td>
* <td><code>&lt;mx:Form&gt;</code></td>
* <td>Defines the container for the entire form, including the overall form layout.
* Use the FormHeading control and FormItem container to define content.
* You can also insert other types of components in a Form container.</td>
* </tr>
* <tr>
* <td>FormHeading</td>
* <td><code>&lt;mx:FormHeading&gt;</code></td>
* <td>Defines a heading within your form. You can have multiple FormHeading controls within a single Form container.</td>
* </tr>
* <tr>
* <td>FormItem</td>
* <td><code>&lt;mx:FormItem&gt;</code></td>
* <td>Contains one or more form children arranged horizontally or vertically. Children can be controls or other containers.
* A single Form container can hold multiple FormItem containers.</td>
* </tr>
* </table>
*
* @mxml
*
* <p>The <code>&lt;mx:Form&gt;</code> tag inherits all the tag
* attributes of its superclass and adds the following tag attributes:</p>
*
* <pre>
* &lt;mx:Form
* <strong>Styles</strong>
* horizontalGap="8"
* indicatorGap="14"
* labelWidth="<i>Calculated</i>"
* paddingBottom="16"
* paddingTop="16"
* verticalGap="6"
* &gt;
* ...
* <i>child tags</i>
* ...
* &lt;/mx:Form&gt;
* </pre>
*
* @includeExample examples/FormExample.mxml
*
* @see mx.containers.FormHeading
* @see mx.containers.FormItem
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class Form extends Container
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function Form()
{
super();
showInAutomationHierarchy = true;
layoutObject.target = this;
layoutObject.direction = BoxDirection.VERTICAL;
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
mx_internal var layoutObject:BoxLayout = new BoxLayout();
/**
* @private
*/
private var measuredLabelWidth:Number;
//--------------------------------------------------------------------------
//
// Overridden Properties
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function set moduleFactory(moduleFactory:IFlexModuleFactory):void
{
super.moduleFactory = moduleFactory;
styleManager.registerInheritingStyle("labelWidth");
styleManager.registerSizeInvalidatingStyle("labelWidth");
styleManager.registerInheritingStyle("indicatorGap");
styleManager.registerSizeInvalidatingStyle("indicatorGap");
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// maxLabelWidth
//----------------------------------
[Bindable("updateComplete")]
/**
* The maximum width, in pixels, of the labels of the FormItems containers in this Form.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maxLabelWidth():Number
{
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
var child:DisplayObject = getChildAt(i);
if (child is FormItem)
{
var itemLabel:Label = FormItem(child).itemLabel;
if (itemLabel)
return itemLabel.width;
}
}
return 0;
}
//--------------------------------------------------------------------------
//
// Overridden methods: DisplayObjectContainer
//
//--------------------------------------------------------------------------
/**
* @private
* Discard the cached measuredLabelWidth if a child
* is added or removed.
*/
override public function addChild(child:DisplayObject):DisplayObject
{
invalidateLabelWidth();
return super.addChild(child);
}
/**
* @private
*/
override public function addChildAt(child:DisplayObject,
index:int):DisplayObject
{
invalidateLabelWidth();
return super.addChildAt(child, index);
}
/**
* @private
*/
override public function removeChild(child:DisplayObject):DisplayObject
{
invalidateLabelWidth();
return super.removeChild(child);
}
/**
* @private
*/
override public function removeChildAt(index:int):DisplayObject
{
invalidateLabelWidth();
return super.removeChildAt(index);
}
//--------------------------------------------------------------------------
//
// Overridden methods: UIComponent
//
//--------------------------------------------------------------------------
/**
* Calculates the preferred, minimum and maximum sizes of the Form.
* For more information about the <code>measure</code> method,
* see the <code>UIComponent.measure()</code> method.
* <p>The <code>Form.measure()</code> method sets the
* <code>measuredWidth</code> property to the width of the
* largest child, plus the values of the <code>paddingLeft</code>
* and <code>paddingRight</code> style properties and the
* width of the border.</p>
*
* <p>The <code>measuredHeight</code> property is set to the sum
* of the <code>measuredHeight</code>S of all children,
* plus <code>verticalGap</code> space between each child.
* The <code>paddingTop</code> and <code>paddingBottom</code>
* style properties and the height of the border are also added.</p>
*
* <p>The <code>measuredMinWidth</code> property is set to the largest
* minimum width of the children.
* If the child has a percentage value for <code>width</code>,
* the <code>minWidth</code> property is used, otherwise the
* <code>measuredWidth</code> property is used.
* The values of the <code>paddingLeft</code> and
* <code>paddingRight</code> style properties and the width
* of the border are also added.</p>
*
* <p>The <code>measuredMinHeight</code> property is set to the same value
* as that of the <code>measuredHeight</code> property.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override protected function measure():void
{
super.measure();
layoutObject.measure();
calculateLabelWidth();
}
/**
* Responds to size changes by setting the positions
* and sizes of this container's children.
* For more information about the <code>updateDisplayList()</code> method,
* see the <code>UIComponent.updateDisplayList()</code> method.
*
* <p>The <code>Form.updateDisplayList()</code> method
* positions the children in a vertical column,
* spaced by the <code>verticalGap</code> style property.
* The <code>paddingLeft</code>, <code>paddingRight</code>,
* <code>paddingTop</code> and <code>paddingBottom</code>
* style properties are applied.</p>
*
* <p>If a child has a percentage width,
* it is stretched horizontally to the specified
* percentage of the Form container; otherwise, it is set
* to its <code>measuredWidth</code> property.
* Each child is set to its <code>measuredHeight</code> property.</p>
*
* <p>This method calls the <code>super.updateDisplayList()</code>
* method before doing anything else.</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
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
layoutObject.updateDisplayList(unscaledWidth, unscaledHeight);
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
// Check to see if this is one of the style properties
// that is known to affect layout.
if (!styleProp ||
styleProp == "styleName" ||
styleManager.isSizeInvalidatingStyle(styleProp))
{
invalidateLabelWidth();
}
super.styleChanged(styleProp);
}
/**
* @private
* */
override public function invalidateSize():void
{
super.invalidateSize();
invalidateLabelWidth();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
internal function invalidateLabelWidth():void
{
// We only need to invalidate the label width
// after we've been initialized.
if (!isNaN(measuredLabelWidth) && initialized)
{
measuredLabelWidth = NaN;
// Need to invalidate the size of all children
// to make sure they respond to the label width change.
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
var child:IUIComponent = IUIComponent(getChildAt(i));
if (child is IInvalidating)
IInvalidating(child).invalidateSize();
}
}
}
/**
* @private
*/
internal function calculateLabelWidth():Number
{
// See if we've already calculated it.
if (!isNaN(measuredLabelWidth))
return measuredLabelWidth;
var labelWidth:Number = 0;
var labelWidthSet:Boolean = false;
// Determine best label width.
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
var child:DisplayObject = getChildAt(i);
if (child is FormItem && FormItem(child).includeInLayout)
{
labelWidth = Math.max(labelWidth,
FormItem(child).getPreferredLabelWidth());
// only set measuredLabelWidth yet if we have at least one FormItem child
labelWidthSet = true;
}
}
if (labelWidthSet)
measuredLabelWidth = labelWidth;
return labelWidth;
}
}
}