blob: 3c188d38612311b2ab7a657e11b9e59d7e2ffad6 [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.controls
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.text.TextField;
import flash.text.TextLineMetrics;
import flash.ui.Keyboard;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.FlexVersion;
import mx.core.IDataRenderer;
import mx.core.IFlexDisplayObject;
import mx.core.IIMESupport;
import mx.core.ITextInput;
import mx.core.UIComponent;
import mx.core.UITextField;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import mx.events.NumericStepperEvent;
import mx.managers.IFocusManager;
import mx.managers.IFocusManagerComponent;
import mx.styles.StyleProxy;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched when the value of the NumericStepper control changes
* as a result of user interaction.
*
* @eventType mx.events.NumericStepperEvent.CHANGE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="change", type="mx.events.NumericStepperEvent")]
/**
* Dispatched when the <code>data</code> property changes.
*
* <p>When you use a component as an item renderer,
* the <code>data</code> property contains the data to display.
* You can listen for this event and update the component
* when the <code>data</code> property changes.</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")]
//--------------------------------------
// Styles
//--------------------------------------
include "../styles/metadata/BackgroundStyles.as"
include "../styles/metadata/BorderStyles.as"
include "../styles/metadata/FocusStyles.as"
include "../styles/metadata/IconColorStyles.as"
include "../styles/metadata/LeadingStyle.as"
include "../styles/metadata/PaddingStyles.as"
include "../styles/metadata/TextStyles.as"
/**
* 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
* for the Halo theme is 5 and for the Spark theme is 2.
*
* @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")]
/**
* Name of the class to use as the default skin for the down arrow.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperDownSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperDecrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="downArrowSkin", type="Class", inherit="no", states="up, over, down, disabled")]
/**
* Name of the class to use as the skin for the Down arrow
* when the arrow is disabled.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperDownSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperDecrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="downArrowDisabledSkin", type="Class", inherit="no")]
/**
* Name of the class to use as the skin for the Down arrow
* when the arrow is enabled and a user presses the mouse button over the arrow.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperDownSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperDecrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="downArrowDownSkin", type="Class", inherit="no")]
/**
* Name of the class to use as the skin for the Down arrow
* when the arrow is enabled and the mouse pointer is over the arrow.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperDownSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperDecrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="downArrowOverSkin", type="Class", inherit="no")]
/**
* Name of the class to use as the skin for the Down arrow
* when the arrow is enabled and the mouse pointer is not on the arrow.
* There is no default.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="downArrowUpSkin", type="Class", 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")]
/**
* Alphas used for the highlight fill of controls.
*
* @default [ 0.3, 0.0 ]
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="highlightAlphas", type="Array", arrayType="Number", inherit="no", theme="halo")]
/**
* 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")]
/**
* The class implementing ITextInput that is used by this component
* to input text.
*
* <p>It can be set to either the mx.core.TextInput class
* (to use the classic Halo TextInput control)
* or the mx.controls.MXFTETextInput class
* (to use the Spark TextInput component based on the Text Layout Framework
* to get improved text rendering, including bidirectional layout).</p>
*
* @default mx.controls.TextInput
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
[Style(name="textInputClass", type="Class", inherit="no")]
/**
* Name of the class to use as the default skin for the up arrow.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperUpSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperIncrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="upArrowSkin", type="Class", inherit="no", states="up, over, down, disabled")]
/**
* Name of the class to use as the skin for the Up arrow
* when the arrow is disabled.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperUpSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperIncrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="upArrowDisabledSkin", type="Class", inherit="no")]
/**
* Name of the class to use as the skin for the Up arrow
* when the arrow is enabled and a user presses the mouse button over the arrow.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperUpSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperIncrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="upArrowDownSkin", type="Class", inherit="no")]
/**
* Name of the class to use as the skin for the Up arrow
* when the arrow is enabled and the mouse pointer is over the arrow.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperUpSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperIncrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="upArrowOverSkin", type="Class", inherit="no")]
/**
* Name of the class to use as the skin for the Up arrow
* when the arrow is enabled and the mouse pointer is not on the arrow.
*
* <p>The default skin class is based on the theme. For example, with the Halo theme,
* the default skin class is <code>mx.skins.halo.NumericStepperUpSkin</code>. For the Spark theme, the default skin
* class is <code>mx.skins.spark.StepperIncrButtonSkin</code>.</p>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="upArrowUpSkin", type="Class", inherit="no")]
//--------------------------------------
// Other metadata
//--------------------------------------
[IconFile("NumericStepper.png")]
[DefaultBindingProperty(source="value", destination="value")]
[DefaultTriggerEvent("change")]
[Alternative(replacement="spark.components.NumericStepper", since="4.0")]
/**
* The NumericStepper control lets the user select
* a number from an ordered set.
* The NumericStepper control consists of a single-line
* input text field and a pair of arrow buttons
* for stepping through the possible values.
* The Up Arrow and Down Arrow keys also cycle through the values.
*
* <p>The NumericStepper control has the following default characteristics:</p>
* <table class="innertable">
* <tr>
* <th>Characteristic</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>Default size</td>
* <td>Wide enough to display the maximum number of digits used by the control</td>
* </tr>
* <tr>
* <td>Minimum size</td>
* <td>Based on the size of the text.</td>
* </tr>
* <tr>
* <td>Maximum size</td>
* <td>Undefined</td>
* </tr>
* </table>
*
* @mxml
*
* The <code>&lt;mx:NumericStepper&gt;</code> tag inherits all of the tag
* attributes of its superclass, and adds the following tag attributes:
*
* <pre>
* &lt;mx:NumericStepper
* <strong>Properties</strong>
* imeMode="null"
* maxChars="10"
* maximum="10"
* minimum="0"
* stepSize="1"
* value="0"
*
* <strong>Styles</strong>
* backgroundAlpha="1.0"
* backgroundColor="undefined"
* backgroundImage="undefined"
* backgroundSize="auto"
* borderColor="0xAAB3B3"
* borderSides="left top right bottom"
* borderSkin="HaloBorder"
* borderStyle="inset"
* borderThickness="1"
* color="0x0B333C"
* cornerRadius="0"
* disabledColor="0xAAB3B3"
* disabledIconColor="0x999999"
* downArrowDisabledSkin="NumericStepperDownSkin"
* downArrowDownSkin="NumericStepperDownSkin"
* downArrowOverSkin="NumericStepperDownSkin"
* downArrowUpSkin="NumericStepperDownSkin"
* dropShadowEnabled="false"
* dropShadowColor="0x000000"
* focusAlpha="0.5"
* focusRoundedCorners="tl tr bl br"
* fontAntiAliasType="advanced"
* fontFamily="Verdana"
* fontGridFitType="pixel"
* fontSharpness="0"
* fontSize="10"
* fontStyle="normal|italic"
* fontThickness="0"
* fontWeight="normal|bold"
* highlightAlphas="[0.3,0.0]"
* iconColor="0x111111"
* leading="2"
* paddingLeft="0"
* paddingRight="0"
* shadowDirection="center"
* shadowDistance="2"
* textAlign="left|center|right"
* textDecoration="none|underline"
* textIndent="0"
* upArrowDisabledSkin="NumericStepperUpSkin"
* upArrowDownSkin="NumericStepperUpSkin"
* upArrowOverSkin="NumericStepperUpSkin"
* upArrowUpSkin="NumericStepperUpSkin"
*
* <strong>Events</strong>
* change="<i>No default</i>"
* dataChange="<i>No default</i>"
* /&gt;
* </pre>
*
* @includeExample examples/NumericStepperExample.mxml
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class NumericStepper extends UIComponent
implements IDataRenderer, IDropInListItemRenderer,
IFocusManagerComponent, IIMESupport,
IListItemRenderer
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function NumericStepper()
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
mx_internal var inputField:ITextInput;
/**
* @private
*/
mx_internal var nextButton:Button;
/**
* @private
*/
mx_internal var prevButton:Button;
/**
* @private
* Flag that will block default data/listData behavior
*/
private var valueSet:Boolean;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// baselinePosition
//----------------------------------
/**
* @private
* The baselinePosition of a NumericStepper is calculated
* for its inputField.
*/
override public function get baselinePosition():Number
{
if (!validateBaselinePosition())
return NaN;
return inputField.y + inputField.baselinePosition;
}
//----------------------------------
// enabled
//----------------------------------
/**
* @private
*/
private var enabledChanged:Boolean = false;
[Inspectable(category="General", enumeration="true,false", defaultValue="true")]
/**
* @private
*/
override public function set enabled(value:Boolean):void
{
super.enabled = value;
enabledChanged = true;
invalidateProperties();
}
/**
* @private
*/
override public function get enabled():Boolean
{
return super.enabled;
}
//----------------------------------
// tabIndex
//----------------------------------
/**
* @private
* Storage for the tabIndex property.
*/
private var _tabIndex:int = -1;
/**
* @private
*/
private var tabIndexChanged:Boolean = false;
/**
* @private
* Tab order in which the control receives the focus when navigating
* with the Tab key.
*
* @default -1
* @tiptext tabIndex of the component
* @helpid 3198
*/
override public function get tabIndex():int
{
return _tabIndex;
}
/**
* @private
*/
override public function set tabIndex(value:int):void
{
if (value == _tabIndex)
return;
_tabIndex = value;
tabIndexChanged = true;
invalidateProperties();
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// 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>When you use the control as a drop-in item renderer or drop-in
* item editor, Flex automatically writes the current value of the item
* to the <code>value</code> property of this control.</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
{
if (!_listData)
data = this.value;
return _data;
}
/**
* @private
*/
public function set data(value:Object):void
{
_data = value;
if (!valueSet)
{
this.value = _listData ? parseFloat(_listData.label) : Number(_data);
valueSet = false;
}
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
//----------------------------------
// downArrowStyleFilters
//----------------------------------
/**
* Set of styles to pass from the NumericStepper to the down arrow.
* @see mx.styles.StyleProxy
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function get downArrowStyleFilters():Object
{
return _downArrowStyleFilters;
}
private static var _downArrowStyleFilters:Object =
{
"borderColor" : "borderColor",
"cornerRadius" : "cornerRadius",
"highlightAlphas" : "highlightAlphas",
"downArrowUpSkin" : "downArrowUpSkin",
"downArrowOverSkin" : "downArrowOverSkin",
"downArrowDownSkin" : "downArrowDownSkin",
"downArrowDisabledSkin" : "downArrowDisabledSkin",
"downArrowSkin" : "downArrowSkin",
"repeatDelay" : "repeatDelay",
"repeatInterval" : "repeatInterval"
};
//----------------------------------
// enableIME
//----------------------------------
/**
* A flag that indicates whether the IME should
* be enabled when the component receives focus.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function get enableIME():Boolean
{
return true;
}
//----------------------------------
// imeMode
//----------------------------------
/**
* @private
*/
private var _imeMode:String = null;
[Inspectable(defaultValue="")]
/**
* Specifies the IME (Input Method Editor) mode.
* The IME enables users to enter text in Chinese, Japanese, and Korean.
* Flex sets the specified IME mode when the control gets the focus
* and sets it back to previous value when the control loses the focus.
*
* <p>The flash.system.IMEConversionMode class defines constants for the
* valid values for this property.
* You can also specify <code>null</code> to specify no IME.</p>
*
* @see flash.system.IMEConversionMode
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get imeMode():String
{
return _imeMode;
}
/**
* @private
*/
public function set imeMode(value:String):void
{
_imeMode = value;
if (inputField)
inputField.imeMode = _imeMode;
}
//----------------------------------
// inputFieldStyleFilters
//----------------------------------
/**
* Set of styles to pass from the NumericStepper to the input field.
* @see mx.styles.StyleProxy
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function get inputFieldStyleFilters():Object
{
return _inputFieldStyleFilters;
}
private static var _inputFieldStyleFilters:Object =
{
"backgroundAlpha" : "backgroundAlpha",
"backgroundColor" : "backgroundColor",
"backgroundImage" : "backgroundImage",
"backgroundDisabledColor" : "backgroundDisabledColor",
"backgroundSize" : "backgroundSize",
"borderAlpha" : "borderAlpha",
"borderColor" : "borderColor",
"borderSides" : "borderSides",
"borderSkin" : "borderSkin",
"borderStyle" : "borderStyle",
"borderThickness" : "borderThickness",
"borderVisible" : "borderVisible",
"dropShadowColor" : "dropShadowColor",
"dropShadowEnabled" : "dropShadowEnabled",
"embedFonts" : "embedFonts",
"focusAlpha" : "focusAlpha",
"focusBlendMode" : "focusBlendMode",
"focusRoundedCorners" : "focusRoundedCorners",
"focusThickness" : "focusThickness",
"paddingLeft" : "paddingLeft",
"paddingRight" : "paddingRight",
"shadowDirection" : "shadowDirection",
"shadowDistance" : "shadowDistance",
"textDecoration" : "textDecoration"
};
//----------------------------------
// listData
//----------------------------------
/**
* @private
* Storage for the listData property.
*/
private var _listData:BaseListData;
[Bindable("dataChange")]
[Inspectable(environment="none")]
/**
* When a component is used as a drop-in item renderer or drop-in
* item editor, Flex initializes the <code>listData</code> property
* of the component with the appropriate data from the List control.
* The component can then use the <code>listData</code> property
* to initialize the <code>data</code> property of the drop-in
* item renderer or drop-in item editor.
*
* <p>You do not set this property in MXML or ActionScript;
* Flex sets it when the component is used as a drop-in item renderer
* or drop-in item editor.</p>
*
* @default null
* @see mx.controls.listClasses.IDropInListItemRenderer
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get listData():BaseListData
{
return _listData;
}
/**
* @private
*/
public function set listData(value:BaseListData):void
{
_listData = value;
}
//----------------------------------
// maxChars
//----------------------------------
/**
* @private
* Storage for the maxChars property.
*/
private var _maxChars:int = 0;
/**
* @private
*/
private var maxCharsChanged:Boolean = false;
[Bindable("maxCharsChanged")]
/**
* The maximum number of characters that can be entered in the field.
* A value of 0 means that any number of characters can be entered.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maxChars():int
{
return _maxChars;
}
public function set maxChars(value:int):void
{
if (value == _maxChars)
return;
_maxChars = value;
maxCharsChanged = true;
invalidateProperties();
dispatchEvent(new Event("maxCharsChanged"));
}
//----------------------------------
// maximum
//----------------------------------
/**
* @private
* Storage for maximum property.
*/
private var _maximum:Number = 10;
[Bindable("maximumChanged")]
[Inspectable(category="General", defaultValue="10")]
/**
* Maximum value of the NumericStepper.
* The maximum can be any number, including a fractional value.
*
* @default 10
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maximum():Number
{
return _maximum;
}
public function set maximum(value:Number):void
{
_maximum = value;
// To validate the value as min/max/stepsize has changed.
if (!valueChanged)
{
this.value = this.value;
valueSet = false;
}
dispatchEvent(new Event("maximumChanged"));
}
//----------------------------------
// minimum
//----------------------------------
/**
* @private
* Storage for minimum property.
*/
private var _minimum:Number = 0;
[Bindable("minimumChanged")]
[Inspectable(category="General", defaultValue="0")]
/**
* Minimum value of the NumericStepper.
* The minimum can be any number, including a fractional value.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get minimum():Number
{
return _minimum;
}
public function set minimum(value:Number):void
{
_minimum = value;
// To validate the value as min/max/stepsize has changed.
if (!valueChanged)
{
this.value = this.value;
valueSet = false;
}
dispatchEvent(new Event("minimumChanged"));
}
//----------------------------------
// nextValue
//----------------------------------
/**
* @private
* Storage for the nextValue property.
*/
private var _nextValue:Number = 0;
/**
* The value that is one step larger than the current <code>value</code>
* property and not greater than the <code>maximum</code> property value.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get nextValue():Number
{
if (checkRange(value + stepSize))
_nextValue = value + stepSize;
return _nextValue;
}
//----------------------------------
// previousValue
//----------------------------------
/**
* @private
* Storage for the previousValue property.
*/
private var _previousValue:Number = 0;
/**
* The value that is one step smaller than the current <code>value</code>
* property and not smaller than the <code>maximum</code> property value.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get previousValue():Number
{
if (checkRange(_value - stepSize))
_previousValue = value - stepSize;
return _previousValue;
}
//----------------------------------
// stepSize
//----------------------------------
/**
* @private
* Storage for the stepSize property.
*/
private var _stepSize:Number = 1;
[Bindable("stepSizeChanged")]
[Inspectable(category="General", defaultValue="1")]
/**
* Non-zero unit of change between values.
* The <code>value</code> property must be a multiple of this number.
*
* @default 1
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get stepSize():Number
{
return _stepSize;
}
/**
* @private
*/
public function set stepSize(value:Number):void
{
_stepSize = value;
// To validate the value as min/max/stepsize has changed.
if (!valueChanged)
{
this.value = this.value;
valueSet = false;
}
dispatchEvent(new Event("stepSizeChanged"));
}
//----------------------------------
// upArrowStyleFilters
//----------------------------------
/**
* Set of styles to pass from the NumericStepper to the up arrow.
* @see mx.styles.StyleProxy
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function get upArrowStyleFilters():Object
{
return _upArrowStyleFilters;
}
private static var _upArrowStyleFilters:Object =
{
"borderColor" : "borderColor",
"cornerRadius" : "cornerRadius",
"highlightAlphas" : "highlightAlphas",
"upArrowUpSkin" : "upArrowUpSkin",
"upArrowOverSkin" : "upArrowOverSkin",
"upArrowDownSkin" : "upArrowDownSkin",
"upArrowDisabledSkin" : "upArrowDisabledSkin",
"upArrowSkin" : "upArrowSkin",
"repeatDelay" : "repeatDelay",
"repeatInterval" : "repeatInterval"
};
//----------------------------------
// value
//----------------------------------
/**
* @private
* Storage for the value property.
*/
private var _value:Number = 0;
/**
* @private
* last value we send CHANGE for.
* _value will hold uncommitted values as well
*/
private var lastValue:Number = 0;
/**
* @private
*/
private var lastField:String = "";
/**
* @private
* Holds the value of the value property
* until it is committed in commitProperties().
*/
private var proposedValue:Number = 0;
/**
* @private
* Keeps track of whether we need to update
* the value in commitProperties().
*/
private var valueChanged:Boolean = false;
[Bindable("change")]
[Bindable("valueCommit")]
[Inspectable(category="General", defaultValue="0")]
/**
* Current value displayed in the text area of the NumericStepper control.
* If a user enters number that is not a multiple of the
* <code>stepSize</code> property or is not in the range
* between the <code>maximum</code> and <code>minimum</code> properties,
* this property is set to the closest valid value.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get value():Number
{
return valueChanged ? proposedValue : _value;
}
/**
* @private
*/
public function set value(value:Number):void
{
valueSet = true;
proposedValue = value;
valueChanged = true;
invalidateProperties();
invalidateSize();
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function createChildren():void
{
super.createChildren();
if (!inputField)
{
// Mechanism to use MXFTETextInput.
var textInputClass:Class = getStyle("textInputClass");
if (!textInputClass ||
FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0)
{
inputField = new TextInput();
}
else
{
inputField = new textInputClass();
}
inputField.styleName = new StyleProxy(this, inputFieldStyleFilters);
inputField.focusEnabled = false;
// restrict to numbers - dashes - commas - decimals
inputField.restrict = "0-9\\-\\.\\,";
inputField.maxChars = _maxChars;
inputField.text = String(_value);
inputField.parentDrawsFocus = true;
inputField.imeMode = _imeMode;
inputField.addEventListener(FocusEvent.FOCUS_IN, inputField_focusInHandler);
inputField.addEventListener(FocusEvent.FOCUS_OUT, inputField_focusOutHandler);
inputField.addEventListener(KeyboardEvent.KEY_DOWN, inputField_keyDownHandler);
inputField.addEventListener(Event.CHANGE, inputField_changeHandler);
addChild(DisplayObject(inputField));
}
if (!nextButton)
{
nextButton = new Button();
nextButton.styleName = new StyleProxy(this, upArrowStyleFilters);
nextButton.upSkinName = "upArrowUpSkin";
nextButton.overSkinName = "upArrowOverSkin";
nextButton.downSkinName = "upArrowDownSkin";
nextButton.disabledSkinName = "upArrowDisabledSkin";
nextButton.skinName = "upArrowSkin";
nextButton.upIconName = "";
nextButton.overIconName = "";
nextButton.downIconName = "";
nextButton.disabledIconName = "";
nextButton.focusEnabled = false;
nextButton.tabEnabled = false;
nextButton.autoRepeat = true;
nextButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
nextButton.addEventListener(FlexEvent.BUTTON_DOWN, buttonDownHandler);
addChild(nextButton);
}
if (!prevButton)
{
prevButton = new Button();
prevButton.styleName = new StyleProxy(this, downArrowStyleFilters);
prevButton.upSkinName = "downArrowUpSkin";
prevButton.overSkinName = "downArrowOverSkin";
prevButton.downSkinName = "downArrowDownSkin";
prevButton.disabledSkinName = "downArrowDisabledSkin";
prevButton.skinName = "downArrowSkin";
prevButton.upIconName = "";
prevButton.overIconName = "";
prevButton.downIconName = "";
prevButton.disabledIconName = "";
prevButton.focusEnabled = false;
prevButton.tabEnabled = false;
prevButton.autoRepeat = true;
prevButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
prevButton.addEventListener(FlexEvent.BUTTON_DOWN, buttonDownHandler);
addChild(prevButton);
}
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
if (maxCharsChanged)
{
maxCharsChanged = false;
inputField.maxChars = _maxChars;
}
if (valueChanged)
{
valueChanged = false;
setValue(isNaN(proposedValue) ? 0 : proposedValue, false);
}
if (enabledChanged)
{
enabledChanged = false;
prevButton.enabled = enabled;
nextButton.enabled = enabled;
inputField.enabled = enabled;
}
if (tabIndexChanged)
{
inputField.tabIndex = _tabIndex;
tabIndexChanged = false;
}
}
/**
* @private
* Return the preferred sizes of the stepper.
*/
override protected function measure():void
{
super.measure();
var widestNumber:Number = minimum.toString().length >
maximum.toString().length ?
minimum :
maximum;
widestNumber += stepSize;
var lineMetrics:TextLineMetrics = measureText(checkValidValue(widestNumber).toString());
var textHeight:Number = inputField.getExplicitOrMeasuredHeight();
var buttonHeight:Number = prevButton.getExplicitOrMeasuredHeight() +
nextButton.getExplicitOrMeasuredHeight();
var h:Number = Math.max(textHeight, buttonHeight);
h = Math.max(DEFAULT_MEASURED_MIN_HEIGHT, h);
var textWidth:Number = lineMetrics.width + UITextField.TEXT_WIDTH_PADDING;
var buttonWidth:Number = Math.max(prevButton.getExplicitOrMeasuredWidth(),
nextButton.getExplicitOrMeasuredWidth());
var w:Number = textWidth + buttonWidth + 20;
w = Math.max(DEFAULT_MEASURED_MIN_WIDTH, w);
measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH;
measuredMinHeight = DEFAULT_MEASURED_MIN_HEIGHT;
measuredWidth = w;
measuredHeight = h;
}
/**
* @private
* Place the buttons to the right of the text field.
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
var w:Number = nextButton.getExplicitOrMeasuredWidth();
var h:Number = Math.round(unscaledHeight / 2);
var h2:Number = unscaledHeight - h;
nextButton.x = unscaledWidth - w;
nextButton.y = 0;
nextButton.setActualSize(w, h);
prevButton.x = unscaledWidth - w;
prevButton.y = h;
prevButton.setActualSize(w, h2);
inputField.setActualSize(unscaledWidth - w, unscaledHeight);
}
/**
* @private
* Update the text field.
*/
override public function setFocus():void
{
inputField.setFocus();
}
/**
* @private
*/
override protected function isOurFocus(target:DisplayObject):Boolean
{
return target == inputField || super.isOurFocus(target);
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Verify that the value is within range.
*/
private function checkRange(v:Number):Boolean
{
return v >= minimum && v <= maximum;
}
/**
* @private
*/
private function checkValidValue(value:Number):Number
{
if (isNaN(value))
return this.value;
var closest:Number = stepSize * Math.round(value / stepSize);
// The following logic has been implemented to fix bug 135045.
// It assumes that the above line of code which rounds off the
// value is not removed ! (That is, the precision of the value is
// never expected to be greater than the step size.
// ex : value = 1.11111 stepSize = 0.01)
// Use precision of the step to round of the value.
// When the stepSize is very small the system tends to put it in
// exponential format.(ex : 1E-7) The following string split logic
// cannot work with exponential notation. Hence we add 1 to the stepSize
// to make it get represented in the decimal format.
// We are only interested in the number of digits towards the right
// of the decimal place so it doesnot affect anything else.
var parts:Array = (new String(1 + stepSize)).split(".");
// we need to do the round of (to remove the floating point error)
// if the stepSize had a fractional value
if (parts.length == 2)
{
var scale:Number = Math.pow(10, parts[1].length);
closest = Math.round(closest * scale) / scale;
}
if (closest > maximum)
return maximum;
else if (closest < minimum)
return minimum;
else
return closest;
}
/**
* @private
*/
private function setValue(value:Number,
sendEvent:Boolean = true,
trigger:Event = null):void
{
var v:Number = checkValidValue(value);
if (v == lastValue)
return;
lastValue = _value = v;
var numStr:String = v.toString();
if (numStr.indexOf("e") >= 0) {
var parts:Array = (new String(1 + stepSize)).split(".");
if (parts.length == 2)
numStr = v.toFixed(parts[1].length).toString();
}
inputField.text = numStr;
lastField = numStr;
if (sendEvent)
{
var event:NumericStepperEvent =
new NumericStepperEvent(NumericStepperEvent.CHANGE);
event.value = _value;
event.triggerEvent = trigger;
dispatchEvent(event);
}
dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
}
/**
* @private
* Checks the value in the text field. If it is valid value
* and different from the current value, it is taken as new value.
*/
private function takeValueFromTextField(trigger:Event = null):void
{
var inputValue:Number = Number(inputField.text);
if ((inputValue != lastValue &&
(Math.abs(inputValue - lastValue) >= 0.000001 || isNaN(inputValue)))
|| inputField.text == ""
|| (inputField.text && inputField.text.length != lastField.length))
{
var newValue:Number = checkValidValue(Number(inputField.text));
inputField.text = newValue.toString();
setValue(newValue, trigger != null, trigger);
}
}
/**
* @private
* Increase/decrease the current value.
*/
private function buttonPress(button:Button, trigger:Event = null):void
{
if (enabled)
{
// we may get a buttonPress message before focusOut event for
// the text field. Hence we need to check the value in
// inputField.
takeValueFromTextField();
var oldValue:Number = lastValue;
setValue(button == nextButton ?
lastValue + stepSize :
lastValue - stepSize, true, trigger);
if (oldValue != lastValue)
inputField.selectRange(0,0);
}
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
* Remove the focus from the text field.
*/
override protected function focusInHandler(event:FocusEvent):void
{
super.focusInHandler(event);
var fm:IFocusManager = focusManager;
if (fm)
fm.defaultButtonEnabled = false;
}
/**
* @private
* Remove the focus from the text field.
*/
override protected function focusOutHandler(event:FocusEvent):void
{
var fm:IFocusManager = focusManager;
if (fm && event.target == this)
fm.defaultButtonEnabled = true;
super.focusOutHandler(event);
takeValueFromTextField(event);
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function buttonDownHandler(event:FlexEvent):void
{
buttonPress(Button(event.currentTarget), event);
}
/**
* @private
*/
private function buttonClickHandler(event:MouseEvent):void
{
inputField.setFocus();
inputField.selectRange(0, 0);
}
/**
* @private
*/
private function inputField_focusInHandler(event:FocusEvent):void
{
focusInHandler(event);
// Send out a new FocusEvent because the TextInput eats the event
// Make sure that it does not bubble.
dispatchEvent(new FocusEvent(event.type, false, false,
event.relatedObject,
event.shiftKey, event.keyCode));
}
/**
* @private
*/
private function inputField_focusOutHandler(event:FocusEvent):void
{
focusOutHandler(event);
// Send out a new FocusEvent because the TextInput eats the event
// Make sure that it does not bubble
dispatchEvent(new FocusEvent(event.type, false, false,
event.relatedObject,
event.shiftKey,event.keyCode));
}
/**
* @private
*/
private function inputField_keyDownHandler(event:KeyboardEvent):void
{
var tmpV:Number;
switch (event.keyCode)
{
case Keyboard.DOWN:
{
tmpV = value - stepSize;
setValue(tmpV, true);
break;
}
case Keyboard.UP:
{
tmpV = stepSize + value;
setValue(tmpV, true);
break;
}
case Keyboard.HOME:
{
inputField.text = minimum.toString();
setValue(minimum, true);
break;
}
case Keyboard.END:
{
inputField.text = maximum.toString();
setValue(maximum, true);
break;
}
case Keyboard.ENTER:
case Keyboard.TAB:
{
var inputValue:Number = Number(inputField.text);
if (inputValue != lastValue &&
(Math.abs(inputValue - lastValue) >= 0.000001 ||
isNaN(inputValue)) || (inputField.text &&
inputField.text.length != lastField.length))
{
var newValue:Number = checkValidValue(Number(inputField.text));
// When the TextInput receives our key, it will ignore
// the programmatically set text, unless we push it through
// here.
inputField.text = newValue.toString();
inputField.validateNow();
setValue(newValue, true);
}
// Prevent the defaultButton from firing
event.stopImmediatePropagation();
break;
}
}
// Act as a proxy because the TextInput stops propogation
dispatchEvent(event);
}
/**
* @private
*/
private function inputField_changeHandler(event:Event):void
{
// Stop the event from bubbling up.
event.stopImmediatePropagation();
var inputValue:Number = Number(inputField.text);
if ((inputValue != value &&
(Math.abs(inputValue - value) >= 0.000001 || isNaN(inputValue))) ||
inputField.text == "")
{
_value = checkValidValue(inputValue);
}
}
}
}