blob: c621781fc6e6ef87372f7632246bb6f10c4047cd [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.sliderClasses
{
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Point;
import mx.core.IFlexDisplayObject;
import mx.core.IUIComponent;
import mx.core.LayoutDirection;
import mx.core.UIComponent;
import mx.core.mx_internal;
import mx.effects.Tween;
import mx.events.FlexEvent;
import mx.events.SliderEvent;
import mx.events.SliderEventClickTarget;
import mx.formatters.NumberFormatter;
import mx.styles.ISimpleStyleClient;
import mx.styles.StyleProxy;
use namespace mx_internal;
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched when the slider changes value due to mouse or keyboard interaction.
*
* <p>If the <code>liveDragging</code> property is <code>true</code>,
* the event is dispatched continuously as the user moves the thumb.
* If <code>liveDragging</code> is <code>false</code>,
* the event is dispatched when the user releases the slider thumb.</p>
*
* @eventType mx.events.SliderEvent.CHANGE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="change", type="mx.events.SliderEvent")]
/**
* Dispatched when the slider's thumb is pressed and then moved by the mouse.
* This event is always preceded by a <code>thumbPress</code> event.
* @eventType mx.events.SliderEvent.THUMB_DRAG
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="thumbDrag", type="mx.events.SliderEvent")]
/**
* Dispatched when the slider's thumb is pressed, meaning
* the user presses the mouse button over the thumb.
*
* @eventType mx.events.SliderEvent.THUMB_PRESS
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="thumbPress", type="mx.events.SliderEvent")]
/**
* Dispatched when the slider's thumb is released,
* meaning the user releases the mouse button after
* a <code>thumbPress</code> event.
*
* @eventType mx.events.SliderEvent.THUMB_RELEASE
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Event(name="thumbRelease", type="mx.events.SliderEvent")]
//--------------------------------------
// Styles
//--------------------------------------
include "../../styles/metadata/FillStyles.as";
/**
* The color of the black section of the border.
*
* @default 0x919999
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="borderColor", type="uint", format="Color", inherit="no", theme="halo")]
/**
* Color of the track highlight.
*
* @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")]
/**
* 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")]
/**
* Invert the direction of the thumbs.
* If <code>true</code>, the thumbs will be flipped.
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="invertThumbDirection", type="Boolean", inherit="no")]
/**
* The y-position offset (if direction is horizontal)
* or x-position offset (if direction is vertical)
* of the labels relative to the track.
*
* @default -10
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="labelOffset", type="Number", format="Length", inherit="no")]
/**
* The name of the style to use for the slider label.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="labelStyleName", type="String", inherit="no")]
/**
* Duration in milliseconds for the sliding animation
* when you click on the track to move a thumb.
*
* @default 300
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="slideDuration", type="Number", format="Time", inherit="no")]
/**
* Tweening function used by the sliding animation
* when you click on the track to move a thumb.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="slideEasingFunction", type="Function", inherit="no")]
/**
* The y-position offset (if direction is horizontal)
* or x-position offset (if direction is vertical)
* of the thumb relative to the track.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="thumbOffset", type="Number", format="Length", inherit="no")]
/**
* The color of the tick marks.
* Can be a hex color value or the string name of a known color.
*
* @default 0x6F7777.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="tickColor", type="uint", format="Color", inherit="no")]
/**
* The length in pixels of the tick marks.
* If <code>direction</code> is <code>Direction.HORIZONTAL</code>,
* then adjust the height of the tick marks.
* If <code>direction</code> is <code>Direction.VERTICAL</code>,
* then adjust the width.
*
* @default 3
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="tickLength", type="Number", format="Length", inherit="no")]
/**
* The y-position offset (if direction is horizontal)
* or x-position offset (if direction is vertical)
* of the tick marks relative to the track.
*
* @default -6
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="tickOffset", type="Number", format="Length", inherit="no")]
/**
* The thickness in pixels of the tick marks.
* If direction is horizontal,
* then adjust the width of the tick marks.
* If direction is vertical,
* then adjust the height.
*
* @default 1
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="tickThickness", type="Number", format="Length", inherit="no")]
/**
* The colors of the track, as an array of two colors.
* You can use the same color twice for a solid track color.
*
* <p>You use this property along with the <code>fillAlphas</code>
* property. Typically you set <code>fillAlphas</code> to [ 1.0, 1.0 ]
* when setting <code>trackColors</code>.</p>
*
* @default [ 0xE7E7E7, 0xE7E7E7 ]
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="trackColors", type="Array", arrayType="uint", format="Color", inherit="no", theme="halo")]
/**
* Specifies whether to enable track highlighting between thumbs
* (or a single thumb and the beginning of the track).
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="showTrackHighlight", type="Boolean", inherit="no")]
/**
* The size of the track margins, in pixels.
* If <code>undefined</code>, then the track margins will be determined
* by the length of the first and last labels.
* If given a value, Flex attempts to fit the labels in the available space.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="trackMargin", type="Number", format="Length", inherit="no")]
/**
* The name of the style declaration to use for the data tip.
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="dataTipStyleName", type="String", inherit="no")]
/**
* The offset, in pixels, of the data tip relative to the thumb.
* Used in combination with the <code>dataTipPlacement</code>
* style property of the HSlider and VSlider controls.
*
* @default 16
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="dataTipOffset", type="Number", format="Length", inherit="no")]
/**
* Number of decimal places to use for the data tip text.
* A value of 0 means to round all values to an integer.
*
* @default 2
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="dataTipPrecision", type="int", inherit="no")]
/**
* The default skin for the slider thumb.
*
* @default SliderThumbSkin
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="thumbSkin", type="Class", inherit="no", states="up, over, down, disabled")]
/**
* The skin for the slider thumb up state.
*
* @default SliderThumbSkin
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="thumbUpSkin", type="Class", inherit="no")]
/**
* The skin for the slider thumb over state.
*
* @default SliderThumbSkin
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="thumbOverSkin", type="Class", inherit="no")]
/**
* The skin for the slider thumb down state.
*
* @default SliderThumbSkin
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="thumbDownSkin", type="Class", inherit="no")]
/**
* The skin for the slider thumb disabled state.
*
* @default SliderThumbSkin
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="thumbDisabledSkin", type="Class", inherit="no")]
/**
* The skin for the slider track when it is selected.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="trackHighlightSkin", type="Class", inherit="no")]
/**
* The skin for the slider track.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="trackSkin", type="Class", inherit="no")]
//--------------------------------------
// Other metadata
//--------------------------------------
[AccessibilityClass(implementation="mx.accessibility.SliderAccImpl")]
[ResourceBundle("SharedResources")]
/**
* The Slider class is the base class for the Flex slider controls.
* The slider controls let users select a value by moving a slider thumb
* between the end points of the slider
* track. The current value of the slider is determined by
* the relative location of the thumb between the
* end points of the slider, corresponding to the slider's minimum and maximum values.
* The Slider class is subclassed by HSlider and VSlider.
*
* @mxml
*
* <p>The Slider class cannot be used as an MXML tag. Use the <code>&lt;mx:HSlider&gt;</code>
* and <code>&lt;mx:VSlider&gt;</code> tags instead. However, the Slider class does define tag
* attributes used by the <code>&lt;mx:HSlider&gt;</code> and <code>&lt;mx:VSlider&gt;</code> tags. </p>
*
* <p>The Slider class inherits all of the tag attributes
* of its superclass, and adds the following tag attributes:</p>
*
* <pre>
* &lt;mx:<i>tagname</i>
* <strong>Properties</strong>
* allowThumbOverlap="false|true"
* allowTrackClick="true|false"
* dataTipFormatFunction="undefined"
* direction="horizontal|vertical"
* labels="undefined"
* liveDragging="false|true"
* maximum="10"
* minimum="0"
* showDataTip="true|false"
* sliderDataTipClass="sliderDataTip"
* sliderThumbClass="SliderThumb"
* snapInterval="0"
* thumbCount="1"
* tickInterval="0"
* tickValues="undefined"
* value="<i>The value of the minimum property.</i>"
*
* <strong>Styles</strong>
* borderColor="0x919999"
* dataTipOffset="16"
* dataTipPrecision="2"
* dataTipStyleName="undefined"
* fillAlphas="[0.6, 0.4, 0.75, 0.65]"
* fillColors="[0xFFFFFF, 0xCCCCCC, 0xFFFFFF, 0xEEEEEE;]"
* labelOffset="-10"
* labelStyleName="undefined"
* showTrackHighlight="false"
* slideDuration="300"
* slideEasingFunction="undefined"
* thumbDisabledSkin="SliderThumbSkin"
* thumbDownSkin="SliderThumbSkin"
* thumbOffset="0"
* thumbOverSkin="SliderThumbSkin"
* thumbUpSkin="SliderThumbSkin"
* tickColor="0x6F7777"
* tickLength="3"
* tickOffset="-6"
* tickThickness="1"
* trackColors="[ 0xEEEEEE, 0xFFFFFF ]"
* tracHighlightSkin="SliderHighlightSkin"
* trackMargin="undefined"
* trackSkin="SliderTrackSkin"
*
* <strong>Events</strong>
* change="<i>No default</i>"
* thumbDrag="<i>No default</i>"
* thumbPress="<i>No default</i>"
* thumbRelease="<i>No default</i>"
* /&gt;
* </pre>
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class Slider extends UIComponent
{
include "../../core/Version.as";
//--------------------------------------------------------------------------
//
// Class mixins
//
//--------------------------------------------------------------------------
/**
* @private
* Placeholder for mixin by SliderAccImpl.
*/
mx_internal static var createAccessibilityImplementation:Function;
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function Slider()
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var track:IFlexDisplayObject;
/**
* @private
*/
private var thumbs:UIComponent;
/**
* @private
*/
private var thumbsChanged:Boolean = true;
/**
* @private
*/
private var ticks:UIComponent;
/**
* @private
*/
private var ticksChanged:Boolean = false;
/**
* @private
*/
private var labelObjects:UIComponent;
/**
* @private
*/
private var highlightTrack:IFlexDisplayObject;
/**
* @private
*/
mx_internal var innerSlider:UIComponent;
/**
* @private
*/
private var trackHitArea:UIComponent;
/**
* @private
*/
mx_internal var dataTip:SliderDataTip;
/**
* @private
*/
private var trackHighlightChanged:Boolean = true;
/**
* @private
*/
private var initValues:Boolean = true; // Always initValues at startup
/**
* @private
*/
private var dataFormatter:NumberFormatter;
/**
* @private
*/
private var interactionClickTarget:String;
/**
* @private
*/
private var labelStyleChanged:Boolean = false;
/**
* @private
* is the last interaction from the keyboard?
*/
mx_internal var keyInteraction:Boolean = false;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// baselinePosition
//----------------------------------
/**
* @private
*/
override public function get baselinePosition():Number
{
if (!validateBaselinePosition())
return NaN;
return int(0.75 * height);
}
//----------------------------------
// enabled
//----------------------------------
/**
* @private
*/
private var _enabled:Boolean;
/**
* @private
*/
private var enabledChanged:Boolean = false;
[Inspectable(category="General", enumeration="true,false", defaultValue="true")]
/**
* @private
*/
override public function get enabled():Boolean
{
return _enabled;
}
/**
* @private
*/
override public function set enabled(value:Boolean):void
{
_enabled = value;
enabledChanged = true;
invalidateProperties();
}
/**
* @private
*/
private var _tabIndex:Number;
/**
* @private
*/
private var tabIndexChanged:Boolean;
/**
* @private
*/
override public function set tabIndex(value:int):void
{
super.tabIndex = value;
_tabIndex = value;
tabIndexChanged = true;
invalidateProperties();
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// allowThumbOverlap
//----------------------------------
[Inspectable(defaultValue="false")]
/**
* If set to <code>false</code>, then each thumb can only be moved to the edge of
* the adjacent thumb.
* If <code>true</code>, then each thumb can be moved to any position on the track.
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var allowThumbOverlap:Boolean = false;
//----------------------------------
// allowTrackClick
//----------------------------------
[Inspectable(defaultValue="true")]
/**
* Specifies whether clicking on the track will move the slider thumb.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var allowTrackClick:Boolean = true;
//----------------------------------
// dataTipFormatFunction
//----------------------------------
/**
* @private
*/
private var _dataTipFormatFunction:Function;
/**
* Callback function that formats the data tip text.
* The function takes a single Number as an argument
* and returns a formatted String.
*
* <p>The function has the following signature:</p>
* <pre>
* funcName(value:Number):String
* </pre>
*
* <p>The following example prefixes the data tip text with a dollar sign and
* formats the text using the <code>dataTipPrecision</code>
* of a Slider Control named 'slide': </p>
*
* <pre>
* import mx.formatters.NumberBase;
* function myDataTipFormatter(value:Number):String {
* var dataFormatter:NumberBase = new NumberBase(".", ",", ".", "");
* return "$ " + dataFormatter.formatPrecision(String(value), slide.getStyle("dataTipPrecision"));
* }
* </pre>
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get dataTipFormatFunction():Function
{
return _dataTipFormatFunction;
}
/**
* @private
*/
public function set dataTipFormatFunction(value:Function):void
{
_dataTipFormatFunction = value;
}
//----------------------------------
// direction
//----------------------------------
/**
* @private
*/
private var _direction:String = SliderDirection.HORIZONTAL;
/**
* @private
*/
private var directionChanged:Boolean = false;
[Inspectable(defaultValue="horizontal")]
/**
* The orientation of the slider control.
* Valid values in MXML are <code>"horizontal"</code> or <code>"vertical"</code>.
*
* <p>In ActionScript, you use the following constants
* to set this property:
* <code>SliderDirection.VERTICAL</code> and
* <code>SliderDirection.HORIZONTAL</code>.</p>
*
* The HSlider and VSlider controls set this property for you;
* do not set it when using those controls.
*
* @default SliderDirection.HORIZONTAL
* @see mx.controls.sliderClasses.SliderDirection
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get direction():String
{
return _direction;
}
/**
* @private
*/
public function set direction(value:String):void
{
_direction = value;
directionChanged = true;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
}
//----------------------------------
// labels
//----------------------------------
/**
* @private
*/
private var _labels:Array = [];
/**
* @private
*/
private var labelsChanged:Boolean = false;
[Inspectable(category="General", arrayType="String", defaultValue="undefined")]
/**
* An array of strings used for the slider labels.
* Flex positions the labels at the beginning of the track,
* and spaces them evenly between the beginning of the track
* and the end of the track.
*
* <p>For example, if the array contains three items,
* the first item is placed at the beginning of the track,
* the second item in the middle, and the last item
* at the end of the track.</p>
*
* <p>If only one label is specified, it is placed at the
* beginning of the track.
* By default, labels are placed above the tick marks
* (if present) or above the track.
* To align the labels with the tick marks, make sure that
* the number of tick marks is equal to the number of labels.</p>
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get labels():Array
{
return _labels;
}
/**
* @private
*/
public function set labels(value:Array):void
{
_labels = value;
labelsChanged = true;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
}
//----------------------------------
// liveDragging
//----------------------------------
[Inspectable(category="General", defaultValue="false")]
/**
* Specifies whether live dragging is enabled for the slider.
* If <code>false</code>, Flex sets the <code>value</code> and
* <code>values</code> properties and dispatches the <code>change</code>
* event when the user stops dragging the slider thumb.
* If <code>true</code>, Flex sets the <code>value</code> and
* <code>values</code> properties and dispatches the <code>change</code>
* event continuously as the user moves the thumb.
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var liveDragging:Boolean = false;
//----------------------------------
// maximum
//----------------------------------
/**
* @private
* Storage for the maximum property.
*/
private var _maximum:Number = 10;
[Inspectable(category="General", defaultValue="10")]
/**
* The maximum allowed value on the slider.
*
* @default 10
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get maximum():Number
{
return _maximum;
}
/**
* @private
*/
public function set maximum(value:Number):void
{
_maximum = value;
ticksChanged = true;
if (!initValues)
valuesChanged = true;
invalidateProperties();
invalidateDisplayList();
}
//----------------------------------
// minimum
//----------------------------------
/**
* @private
* Storage for the minimum property.
*/
private var _minimum:Number = 0;
/**
* @private
*/
private var minimumSet:Boolean = false;
[Inspectable(category="General", defaultValue="0")]
/**
* The minimum allowed value on the slider control.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get minimum():Number
{
return _minimum;
}
/**
* @private
*/
public function set minimum(value:Number):void
{
_minimum = value;
ticksChanged = true;
if (!initValues)
valuesChanged = true;
invalidateProperties();
invalidateDisplayList();
}
//----------------------------------
// showDataTip
//----------------------------------
[Inspectable(category="General", defaultValue="true")]
/**
* If set to <code>true</code>, show a data tip during user interaction
* containing the current value of the slider.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var showDataTip:Boolean = true;
//----------------------------------
// sliderThumbClass
//----------------------------------
/**
* @private
*/
private var _thumbClass:Class = SliderThumb;
/**
* A reference to the class to use for each thumb.
*
* @default SliderThumb
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get sliderThumbClass():Class
{
return _thumbClass;
}
/**
* @private
*/
public function set sliderThumbClass(value:Class):void
{
_thumbClass = value;
thumbsChanged = true;
invalidateProperties();
invalidateDisplayList();
}
//----------------------------------
// sliderDataTipClass
//----------------------------------
/**
* @private
*/
private var _sliderDataTipClass:Class = SliderDataTip;
/**
* A reference to the class to use for the data tip.
*
* @default SliderDataTip
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get sliderDataTipClass():Class
{
return _sliderDataTipClass;
}
/**
* @private
*/
public function set sliderDataTipClass(value:Class):void
{
_sliderDataTipClass = value;
invalidateProperties();
}
//----------------------------------
// snapInterval
//----------------------------------
/**
* @private
*/
private var _snapInterval:Number = 0;
/**
* @private
*/
private var snapIntervalPrecision:int = -1;
/**
* @private
*/
private var snapIntervalChanged:Boolean = false;
[Inspectable(category="General", defaultValue="0")]
/**
* Specifies the increment value of the slider thumb
* as the user moves the thumb.
* For example, if <code>snapInterval</code> is 2,
* the <code>minimum</code> value is 0,
* and the <code>maximum</code> value is 10,
* the thumb snaps to the values 0, 2, 4, 6, 8, and 10
* as the user move the thumb.
* A value of 0, means that the slider moves continuously
* between the <code>minimum</code> and <code>maximum</code> values.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get snapInterval():Number
{
return _snapInterval;
}
/**
* @private
*/
public function set snapInterval(value:Number):void
{
_snapInterval = value;
var parts:Array = (new String(1 + value)).split(".");
if (parts.length == 2)
snapIntervalPrecision = parts[1].length;
else
snapIntervalPrecision = -1;
if (!isNaN(value) && value != 0)
{
snapIntervalChanged = true;
invalidateProperties();
invalidateDisplayList();
}
}
//----------------------------------
// thumbCount
//----------------------------------
/**
* @private
* Storage for the thumbCount property.
*/
private var _thumbCount:int = 1;
[Inspectable(category="General", defaultValue="1")]
/**
* The number of thumbs allowed on the slider.
* Possible values are 1 or 2.
* If set to 1, then the <code>value</code> property contains
* the current value of the slider.
* If set to 2, then the <code>values</code> property contains
* an array of values representing the value for each thumb.
*
* @default 1
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get thumbCount():int
{
return _thumbCount;
}
/**
* @private
*/
public function set thumbCount(value:int):void
{
var numThumbs:int = (value > 2) ? 2 : value;
numThumbs = value < 1 ? 1 : value;
if (numThumbs != _thumbCount)
{
_thumbCount = numThumbs;
thumbsChanged = true;
initValues = true;
invalidateProperties();
invalidateDisplayList();
}
}
//----------------------------------
// thumbStyleFilters
//----------------------------------
/**
* Set of styles to pass from the Slider to the thumbs.
* @see mx.styles.StyleProxy
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function get thumbStyleFilters():Object
{
return null;
}
//----------------------------------
// tickInterval
//----------------------------------
/**
* @private
*/
private var _tickInterval:Number = 0;
[Inspectable(category="General", defaultValue="0")]
/**
* The spacing of the tick marks relative to the <code>maximum</code> value
* of the control.
* Flex displays tick marks whenever you set the <code>tickInterval</code>
* property to a nonzero value.
*
* <p>For example, if <code>tickInterval</code> is 1 and
* <code>maximum</code> is 10, then a tick mark is placed at each
* 1/10th interval along the slider.
* A value of 0 shows no tick marks. If the <code>tickValues</code> property
* is set to a non-empty Array, then this property is ignored.</p>
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get tickInterval():Number
{
return _tickInterval;
}
/**
* @private
*/
public function set tickInterval(value:Number):void
{
_tickInterval = value;
ticksChanged = true;
invalidateProperties();
invalidateDisplayList();
}
//----------------------------------
// tickValues
//----------------------------------
/**
* @private
*/
private var _tickValues:Array = [];
[Inspectable(category="General", defaultValue="undefined", arrayType="Number")]
/**
* The positions of the tick marks on the slider. The positions correspond
* to the values on the slider and should be between
* the <code>minimum</code> and <code>maximum</code> values.
* For example, if the <code>tickValues</code> property
* is [0, 2.5, 7.5, 10] and <code>maximum</code> is 10, then a tick mark is placed
* in the following positions along the slider: the beginning of the slider,
* 1/4 of the way in from the left,
* 3/4 of the way in from the left, and at the end of the slider.
*
* <p>If this property is set to a non-empty Array, then the <code>tickInterval</code> property
* is ignored.</p>
*
* @default undefined
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get tickValues():Array
{
return _tickValues;
}
/**
* @private
*/
public function set tickValues(value:Array):void
{
_tickValues = value;
ticksChanged = true;
invalidateProperties();
invalidateDisplayList();
}
//----------------------------------
// value
//----------------------------------
[Bindable("change")]
[Bindable("valueCommit")]
[Inspectable(category="General", defaultValue="undefined")]
/**
* Contains the position of the thumb, and is a number between the
* <code>minimum</code> and <code>maximum</code> properties.
* Use the <code>value</code> property when <code>thumbCount</code> is 1.
* When <code>thumbCount</code> is greater than 1, use the
* <code>values</code> property instead.
* The default value is equal to the minimum property.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get value():Number
{
return _values[0];
}
/**
* @private
*/
public function set value(val:Number):void
{
setValueAt(val, 0, true);
valuesChanged = true;
minimumSet = true;
invalidateProperties();
invalidateDisplayList();
dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
}
//----------------------------------
// values
//----------------------------------
/**
* @private
*/
private var _values:Array = [ 0, 0 ];
/**
* @private
*/
private var valuesChanged:Boolean = false;
[Bindable("change")]
[Inspectable(category="General", arrayType="Number")]
/**
* An array of values for each thumb when <code>thumbCount</code>
* is greater than 1.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get values():Array
{
return _values;
}
/**
* @private
*/
public function set values(value:Array):void
{
_values = value;
valuesChanged = true;
minimumSet = true;
invalidateProperties();
invalidateDisplayList();
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function initializeAccessibility():void
{
if (Slider.createAccessibilityImplementation != null)
Slider.createAccessibilityImplementation(this);
}
/**
* @private
*/
override protected function createChildren():void
{
super.createChildren();
if (!innerSlider)
{
innerSlider = new UIComponent();
UIComponent(innerSlider).tabChildren = true;
addChild(innerSlider);
}
createBackgroundTrack();
if (!trackHitArea)
{
trackHitArea = new UIComponent();
innerSlider.addChild(trackHitArea); // trackHitArea should always be on top
trackHitArea.addEventListener(MouseEvent.MOUSE_DOWN,
track_mouseDownHandler);
}
invalidateProperties(); // Force commitProperties to be called on instantiation
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
var anyStyle:Boolean = styleProp == null || styleProp == "styleName";
super.styleChanged(styleProp);
if (styleProp == "showTrackHighlight" || anyStyle)
{
trackHighlightChanged = true;
invalidateProperties();
}
if (styleProp == "trackHighlightSkin" || anyStyle)
{
if (innerSlider && highlightTrack)
{
innerSlider.removeChild(DisplayObject(highlightTrack));
highlightTrack = null;
}
trackHighlightChanged = true;
invalidateProperties();
}
if (styleProp == "labelStyleName" || anyStyle)
{
labelStyleChanged = true;
invalidateProperties();
}
if (styleProp == "trackMargin" || anyStyle)
{
invalidateSize();
}
if (styleProp == "trackSkin" || anyStyle)
{
if (track)
{
innerSlider.removeChild(DisplayObject(track));
track = null;
createBackgroundTrack();
}
}
invalidateDisplayList();
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
var n:int;
var i:int;
if (trackHighlightChanged)
{
trackHighlightChanged = false;
if (getStyle("showTrackHighlight"))
{
createHighlightTrack();
}
else if (highlightTrack)
{
innerSlider.removeChild(DisplayObject(highlightTrack));
highlightTrack = null;
}
}
if (directionChanged)
{
directionChanged = false;
var isHorizontal:Boolean = _direction == SliderDirection.HORIZONTAL;
if (isHorizontal)
{
DisplayObject(innerSlider).rotation = 0;
}
else
{
DisplayObject(innerSlider).rotation = -90;
innerSlider.y = unscaledHeight;
}
if (labelObjects)
{
for (var labelIndex:int = labelObjects.numChildren - 1; labelIndex >= 0; labelIndex--)
{
var labelObj:SliderLabel = SliderLabel(
labelObjects.getChildAt(labelIndex));
labelObj.rotation = isHorizontal ? 0 : 90;
}
}
}
if (labelStyleChanged && !labelsChanged)
{
labelStyleChanged = false;
if (labelObjects)
{
var labelStyleName:String = getStyle("labelStyleName");
n = labelObjects.numChildren;
for (i = 0; i < n; i++)
{
ISimpleStyleClient(labelObjects.getChildAt(i)).styleName = labelStyleName;
}
}
}
if (ticksChanged)
{
ticksChanged = false;
createTicks();
}
if (labelsChanged)
{
labelsChanged = false;
createLabels();
}
if (thumbsChanged)
{
thumbsChanged = false;
createThumbs();
}
if (initValues)
{
initValues = false;
if (!valuesChanged)
{
var val:Number = minimum;
n = _thumbCount;
for (i = 0; i < n; i++)
{
_values[i] = val;
setValueAt(val, i);
if (_snapInterval && _snapInterval != 0)
val += snapInterval;
else
val++;
}
snapIntervalChanged = false;
}
}
if (snapIntervalChanged)
{
snapIntervalChanged = false;
if (!valuesChanged)
{
n = thumbs.numChildren;
for (i = 0; i < n; i++)
{
setValueAt(getValueFromX(SliderThumb(thumbs.getChildAt(i)).xPosition), i);
}
}
}
if (valuesChanged)
{
valuesChanged = false;
n = _thumbCount;
for (i = 0; i < n; i++)
{
setValueAt(getValueFromX(getXFromValue(Math.min(Math.max(values[i], minimum), maximum))), i);
}
}
if (enabledChanged)
{
enabledChanged = false;
n = thumbs.numChildren;
for (i = 0; i < n; i++)
{
SliderThumb(thumbs.getChildAt(i)).enabled = enabled;
}
n = labelObjects ? labelObjects.numChildren : 0;
for (i = 0; i < n; i++)
{
SliderLabel(labelObjects.getChildAt(i)).enabled = enabled;
}
if (track is IUIComponent)
IUIComponent(track).enabled = enabled;
if (highlightTrack && highlightTrack is IUIComponent)
IUIComponent(highlightTrack).enabled = enabled;
}
if (tabIndexChanged)
{
tabIndexChanged = false;
n = thumbs.numChildren;
for (i = 0; i < n; i++)
{
SliderThumb(thumbs.getChildAt(i)).tabIndex = _tabIndex;
}
}
}
/**
* Calculates the amount of space that the component takes up.
* A horizontal slider control calculates its height by examining
* the position of its labels, tick marks, and thumbs
* relative to the track.
* The height of the control is equivalent to the position
* of the bottom of the lowest element subtracted
* from the position of the top of the highest element.
* The width of a horizontal slider control defaults to 250 pixels.
* For a vertical slider control, the width and the length
* measurements are reversed.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override protected function measure():void
{
super.measure();
var isHorizontal:Boolean = (direction == SliderDirection.HORIZONTAL);
var numLabels:int = labelObjects ? labelObjects.numChildren : 0;
var trackMargin:Number = getStyle("trackMargin");
var length:Number = DEFAULT_MEASURED_WIDTH;
if (!isNaN(trackMargin))
{
if (numLabels > 0)
{
length += (isHorizontal ?
SliderLabel(labelObjects.getChildAt(0)).getExplicitOrMeasuredWidth() / 2 :
SliderLabel(labelObjects.getChildAt(0)).getExplicitOrMeasuredHeight() / 2);
}
if (numLabels > 1)
{
length += (isHorizontal ?
SliderLabel(labelObjects.getChildAt(numLabels - 1)).getExplicitOrMeasuredWidth() / 2 :
SliderLabel(labelObjects.getChildAt(numLabels - 1)).getExplicitOrMeasuredHeight() / 2);
}
//length += track.width;
}
var bounds:Object = getComponentBounds();
var thickness:Number = bounds.lower - bounds.upper;
measuredMinWidth = measuredWidth = isHorizontal ? length : thickness;
measuredMinHeight = measuredHeight = isHorizontal ? thickness : length;
}
/**
* Positions the elements of the control.
* The track, thumbs, labels, and tick marks are all positioned
* and sized by this method.
* The track is sized based on the length of the labels and on the track margin.
* If you specify a <code>trackMargin</code>, then the size of the track
* is equal to the available width minus the <code>trackMargin</code> times 2.
*
* <p>Tick marks are spaced at even intervals along the track starting from the beginning of the track. An additional tick mark is placed
* at the end of the track if one doesn't already exist (if the tick interval isn't a factor of the maximum value). The tick mark
* y-position is based on the <code>tickOffset</code>. An offset of 0 places the bottom of the tick at the top of the track. Negative offsets
* move the ticks upwards while positive offsets move them downward through the track.</p>
*
* <p>Labels are positioned at even intervals along the track. The labels are always horizontally centered above their
* interval position unless the <code>trackMargin</code> setting is too small. If you specify a <code>trackMargin</code>, then the first and last labels will
* position themselves at the left and right borders of the control. Labels will not crop or resize themselves if they overlap,
* so be sure to allow enough space for them to fit on the track. The y-position is based on the <code>labelOffset</code> property. An offset of 0
* places the bottom of the label at the top of the track. Unlike tick marks, the labels can not be positioned to overlap the track.
* If the offset is a positive number, then the top of the label will be positioned below the bottom of the track.</p>
*
* <p>The thumbs are positioned to overlap the track. Their x-position is determined by their value. The y-position is
* controlled by the <code>thumbOffset</code> property. An offset of 0 places the center of the thumb at the center of the track. A negative
* offset moves the thumbs upwards while a positive offset moves the thumbs downwards.</p>
*
* <p>The placement of the tick marks, labels and thumbs are all independent from each other. They will not attempt to reposition
* themselves if they overlap.</p>
*
* <p>For a vertical slider control, the same rules apply. In the above description, substitute width for height, height for width,
* left for up or top, right for down or bottom, x-position for y-position, and y-position for x-position.</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);
// graphics.beginFill(0xEEEEEE);
// graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
// graphics.endFill();
var isHorizontal:Boolean = (_direction == SliderDirection.HORIZONTAL);
var numLabels:int = labelObjects ? labelObjects.numChildren : 0;
var trackMargin:Number = getStyle("trackMargin");
var firstThumbWidth:Number = 6;
var firstThumbHeight:Number = 0;
var firstThumb:SliderThumb;
if (thumbs && thumbs.numChildren > 0)
firstThumb = SliderThumb(thumbs.getChildAt(0));
if (thumbs && firstThumb) {
firstThumbWidth = firstThumb.getExplicitOrMeasuredWidth();
firstThumbHeight = firstThumb.getExplicitOrMeasuredHeight();
}
var trackLeftOffset:Number = firstThumbWidth / 2; // Enough space for the thumb to rest at the edges
var trackRightOffset:Number = trackLeftOffset;
var availSpace:Number;
var firstLabelSize:Number = 0;
if (numLabels > 0)
{
var firstLabel:SliderLabel =
SliderLabel(labelObjects.getChildAt(0));
firstLabelSize = isHorizontal ?
firstLabel.getExplicitOrMeasuredWidth() :
firstLabel.getExplicitOrMeasuredHeight();
}
var lastLabelSize:Number = 0;
if (numLabels > 1)
{
var lastLabel:SliderLabel =
SliderLabel(labelObjects.getChildAt(numLabels - 1));
lastLabelSize = isHorizontal ?
lastLabel.getExplicitOrMeasuredWidth():
lastLabel.getExplicitOrMeasuredHeight();
}
if (!isNaN(trackMargin))
availSpace = trackMargin;
else
availSpace = (firstLabelSize + lastLabelSize) / 2;
if (numLabels > 0)
{
if (!isNaN(trackMargin))
{
trackLeftOffset = Math.max(trackLeftOffset,
availSpace / (numLabels > 1 ? 2 : 1));
}
else
{
trackLeftOffset = Math.max(trackLeftOffset, firstLabelSize / 2);
}
}
else
{
trackLeftOffset = Math.max(trackLeftOffset, availSpace / 2);
}
var bounds:Object = getComponentBounds();
//track.x = Math.round(trackLeftOffset);
var trackY:Number = (((isHorizontal ? unscaledHeight : unscaledWidth) -
(Number(bounds.lower) - Number(bounds.upper))) / 2) - Number(bounds.upper);
track.move(Math.round(trackLeftOffset), Math.round(trackY));
track.setActualSize((isHorizontal ? unscaledWidth: unscaledHeight) - (trackLeftOffset * 2), track.height);
// Layout the thumbs' y positions.
var tY:Number = track.y +
(track.height - firstThumbHeight) / 2 +
getStyle("thumbOffset");
var n:int = _thumbCount;
for (var i:int = 0; i < n; i++)
{
var currentThumb:SliderThumb = SliderThumb(thumbs.getChildAt(i));
currentThumb.move(currentThumb.x, tY);
currentThumb.visible = true;
currentThumb.setActualSize(currentThumb.getExplicitOrMeasuredWidth(),
currentThumb.getExplicitOrMeasuredHeight());
}
var g:Graphics = trackHitArea.graphics;
var tLength:Number = 0
if (_tickInterval > 0 || (_tickValues && _tickValues.length > 0))
tLength = getStyle("tickLength");
g.clear();
g.beginFill(0,0);
g.drawRect(track.x,
track.y - firstThumbHeight/2 - tLength,
track.width,
track.height + firstThumbHeight + tLength);
g.endFill();
if (_direction != SliderDirection.HORIZONTAL)
innerSlider.y = unscaledHeight;
layoutTicks();
layoutLabels();
setPosFromValue(); // use the value to position the thumb's x
drawTrackHighlight();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function createBackgroundTrack():void
{
if (!track)
{
var trackSkinClass:Class = getStyle("trackSkin");
track = new trackSkinClass();
if (track is ISimpleStyleClient)
ISimpleStyleClient(track).styleName = this;
if (track is IUIComponent)
IUIComponent(track).enabled = enabled;
innerSlider.addChildAt(DisplayObject(track),0);
}
}
/**
* @private
*/
private function createHighlightTrack():void
{
var showTrackHighlight:Boolean = getStyle("showTrackHighlight");
if (!highlightTrack && showTrackHighlight)
{
var trackHighlightClass:Class =
getStyle("trackHighlightSkin");
highlightTrack = new trackHighlightClass();
if (highlightTrack is ISimpleStyleClient)
ISimpleStyleClient(highlightTrack).styleName = this;
if (highlightTrack is IUIComponent)
IUIComponent(highlightTrack).enabled = enabled;
innerSlider.addChildAt(DisplayObject(highlightTrack),
innerSlider.getChildIndex(DisplayObject(track)) + 1);
}
}
/**
* @private
*/
private function createThumbs():void
{
var n:int;
var i:int;
// Delete all thumb children
if (thumbs)
{
n = thumbs.numChildren;
for (i = n - 1; i >= 0; i--)
{
// we don't need to bother to remove the event listeners here
// they will be removed by the garbage collector automatically
thumbs.removeChildAt(i);
}
}
else
{
thumbs = new UIComponent();
thumbs.tabChildren = true;
thumbs.tabEnabled = false;
thumbs.tabFocusEnabled = false;
innerSlider.addChild(thumbs);
}
var thumb:SliderThumb; // We want to force the thumb to be a subclass of SliderThumb
n = _thumbCount;
for (i = 0; i < n; i++)
{
thumb = SliderThumb(new _thumbClass());
thumb.owner = this;
thumb.styleName = new StyleProxy(this, thumbStyleFilters);
thumb.thumbIndex = i;
thumb.visible = true;
thumb.enabled = enabled;
thumb.upSkinName = "thumbUpSkin";
thumb.downSkinName = "thumbDownSkin";
thumb.disabledSkinName = "thumbDisabledSkin";
thumb.overSkinName = "thumbOverSkin";
thumb.skinName = "thumbSkin";
thumbs.addChild(thumb);
thumb.addEventListener(FocusEvent.FOCUS_IN,
thumb_focusInHandler);
thumb.addEventListener(FocusEvent.FOCUS_OUT,
thumb_focusOutHandler);
}
}
/**
* @private
*/
private function createLabels():void
{
var labelObj:SliderLabel;
if (labelObjects)
{
for (var i:int = labelObjects.numChildren - 1; i >= 0; i--)
{
labelObjects.removeChildAt(i);
}
}
else
{
labelObjects = new UIComponent();
innerSlider.addChildAt(labelObjects, innerSlider.getChildIndex(trackHitArea));
}
if (_labels)
{
var numLabels:int = _labels.length;
for (var j:int = 0; j < numLabels; j++)
{
labelObj = new SliderLabel();
labelObj.text = _labels[j] is String ?
_labels[j] :
_labels[j].toString();
if (_direction != SliderDirection.HORIZONTAL)
labelObj.rotation = 90;
var labelStyleName:String = getStyle("labelStyleName");
if (labelStyleName)
labelObj.styleName = labelStyleName;
labelObjects.addChild(labelObj);
}
}
}
/**
* @private
*/
private function createTicks():void
{
if (!ticks)
{
ticks = new UIComponent();
innerSlider.addChild(ticks);
}
}
/**
* @private
*/
private function getComponentBounds():Object
{
var isHorizontal:Boolean = (direction == SliderDirection.HORIZONTAL);
var numLabels:int = labelObjects ? labelObjects.numChildren : 0;
var labelY:Number;
var labelSize:Number = 0;
var thumbY:Number;
var upperBound:Number = 0;
var lowerBound:Number = track.height;
if (numLabels > 0)
{
var sliderLabel:SliderLabel =
SliderLabel(labelObjects.getChildAt(0));
if (isHorizontal)
{
labelSize = sliderLabel.getExplicitOrMeasuredHeight();
}
else
{
for (var i:int = 0; i < numLabels; i++)
{
sliderLabel = SliderLabel(labelObjects.getChildAt(i));
labelSize = Math.max(labelSize,
sliderLabel.getExplicitOrMeasuredWidth());
}
}
var labOffset:Number = getStyle("labelOffset");
labelY = labOffset - (labOffset > 0 ? 0 : labelSize);
upperBound = Math.min(upperBound, labelY);
lowerBound = Math.max(lowerBound, labOffset + (labOffset > 0 ? labelSize : 0));
}
if (ticks)
{
var tLen:Number = getStyle("tickLength");
var tOff:Number = getStyle("tickOffset");
upperBound = Math.min(upperBound, tOff - tLen);
lowerBound = Math.max(lowerBound, tOff);
}
if (thumbs && thumbs.numChildren > 0)
{
thumbY = (track.height - SliderThumb(thumbs.getChildAt(0)).getExplicitOrMeasuredHeight()) / 2 +
getStyle("thumbOffset");
upperBound = Math.min(upperBound, thumbY);
lowerBound = Math.max(lowerBound, thumbY + SliderThumb(thumbs.getChildAt(0)).getExplicitOrMeasuredHeight());
}
return { lower: lowerBound, upper: upperBound };
}
/**
* @private
*/
private function layoutTicks():void
{
if (ticks)
{
var g:Graphics = ticks.graphics;
var tLength:Number = getStyle("tickLength");
var tOffset:Number = getStyle("tickOffset");
var tickWidth:Number = getStyle("tickThickness");
var xPos:Number;
var tColor:Number = getStyle("tickColor");
var usePositions:Boolean = _tickValues && _tickValues.length > 0 ? true : false;
var positionIndex:int = 0;
var val:Number = usePositions ? _tickValues[positionIndex++] : minimum;
g.clear();
if (_tickInterval > 0 || usePositions)
{
g.lineStyle(tickWidth,tColor,100);
do
{
xPos = Math.round(getXFromValue(val));
g.moveTo(xPos, tLength);
g.lineTo(xPos, 0);
val = usePositions ? (positionIndex < _tickValues.length ? _tickValues[positionIndex++] : NaN) : _tickInterval + val;
} while (val < maximum || (usePositions && positionIndex < _tickValues.length))
// draw the last tick
if (!usePositions || val == maximum)
{
xPos = track.x + track.width - 1;
g.moveTo(xPos, tLength);
g.lineTo(xPos, 0);
}
ticks.y = Math.round(track.y + tOffset - tLength);
}
}
}
/**
* @private
*/
private function layoutLabels():void
{
var numLabels:Number = labelObjects ? labelObjects.numChildren : 0;
var availSpace:Number;
if (numLabels > 0)
{
var labelInterval:Number = track.width / (numLabels - 1);
// The amount of space we have available for the labels to hang past the track
availSpace = Math.max((_direction == SliderDirection.HORIZONTAL ?
unscaledWidth :
unscaledHeight) - track.width,
SliderThumb(thumbs.getChildAt(0)).getExplicitOrMeasuredWidth());
var labelPos:Number;
var left:Number = track.x;
var curLabel:Object;
for (var i:int = 0; i < numLabels; i++)
{
curLabel = labelObjects.getChildAt(i);
curLabel.setActualSize(curLabel.getExplicitOrMeasuredWidth(), curLabel.getExplicitOrMeasuredHeight());
var yPos:Number = track.y - curLabel.height + getStyle("labelOffset");
if (_direction == SliderDirection.HORIZONTAL)
{
labelPos = curLabel.getExplicitOrMeasuredWidth() / 2;
if (i == 0)
labelPos = Math.min(labelPos, availSpace / (numLabels > Number(1) ? Number(2) : Number(1)));
else if (i == (numLabels - 1))
labelPos = Math.max(labelPos, curLabel.getExplicitOrMeasuredWidth() - availSpace / 2);
curLabel.move(left - labelPos,yPos);
}
else
{
var labelOff:Number = getStyle("labelOffset");
labelPos = curLabel.getExplicitOrMeasuredHeight() / 2;
if (i == 0)
labelPos = Math.max(labelPos, curLabel.getExplicitOrMeasuredHeight() - availSpace / (numLabels > Number(1) ? Number(2) : Number(1)));
else if (i == (numLabels-1))
labelPos = Math.min(labelPos,availSpace / 2);
curLabel.move(left + labelPos,track.y + labelOff +
(labelOff > 0 ? 0 : -curLabel.getExplicitOrMeasuredWidth()));
}
left += labelInterval;
}
}
}
/**
* @private
*/
mx_internal function drawTrackHighlight():void
{
if (highlightTrack)
{
var xPos:Number;
var tWidth:Number;
var firstThumb:SliderThumb = SliderThumb(thumbs.getChildAt(0));
if (_thumbCount > 1)
{
xPos = firstThumb.xPosition;
var secondThumb:SliderThumb = SliderThumb(thumbs.getChildAt(1));
tWidth = secondThumb.xPosition - firstThumb.xPosition;
}
else
{
xPos = track.x;
tWidth = firstThumb.xPosition - xPos;
}
highlightTrack.move(xPos, track.y + 1);
highlightTrack.setActualSize(tWidth > 0 ? tWidth : 0, highlightTrack.height);
}
}
/**
* @private
* Helper function that starts the dataTip and dispatches the press event.
*/
mx_internal function onThumbPress(thumb:Object):void
{
if (showDataTip)
{
// Setup number formatter
dataFormatter = new NumberFormatter();
dataFormatter.precision = getStyle("dataTipPrecision");
if (!dataTip)
{
dataTip = SliderDataTip(new sliderDataTipClass());
systemManager.toolTipChildren.addChild(dataTip);
var dataTipStyleName:String = getStyle("dataTipStyleName");
if (dataTipStyleName)
{
dataTip.styleName = dataTipStyleName;
}
}
var formattedVal:String;
if (_dataTipFormatFunction != null)
{
formattedVal = this._dataTipFormatFunction(
getValueFromX(thumb.xPosition));
}
else
{
formattedVal = dataFormatter.format(getValueFromX(thumb.xPosition));
}
dataTip.text = formattedVal;
// Tool tip has been freshly created and new text assigned to it.
// Hence force a validation so that we can set the
// size required to show the text completely.
dataTip.validateNow();
dataTip.setActualSize(dataTip.getExplicitOrMeasuredWidth(),dataTip.getExplicitOrMeasuredHeight());
positionDataTip(thumb);
}
keyInteraction = false;
var event:SliderEvent = new SliderEvent(SliderEvent.THUMB_PRESS);
event.value = getValueFromX(thumb.xPosition);;
event.thumbIndex = thumb.thumbIndex;
dispatchEvent(event);
}
/**
* @private
*/
mx_internal function onThumbRelease(thumb:Object):void
{
interactionClickTarget = SliderEventClickTarget.THUMB;
destroyDataTip();
setValueFromPos(thumb.thumbIndex);
dataFormatter = null;
var event:SliderEvent = new SliderEvent(SliderEvent.THUMB_RELEASE);
event.value = getValueFromX(thumb.xPosition);;
event.thumbIndex = thumb.thumbIndex;
dispatchEvent(event);
}
/**
* @private
*/
mx_internal function onThumbMove(thumb:Object):void
{
var value:Number = getValueFromX(thumb.xPosition);
if (showDataTip)
{
dataTip.text = _dataTipFormatFunction != null ?
_dataTipFormatFunction(value) :
dataFormatter.format(value);
dataTip.setActualSize(dataTip.getExplicitOrMeasuredWidth(),
dataTip.getExplicitOrMeasuredHeight());
positionDataTip(thumb);
}
if (liveDragging)
{
interactionClickTarget = SliderEventClickTarget.THUMB;
setValueAt(value, thumb.thumbIndex);
}
var event:SliderEvent = new SliderEvent(SliderEvent.THUMB_DRAG);
event.value = value;
event.thumbIndex = thumb.thumbIndex;
dispatchEvent(event);
}
/**
* @private
*/
private function positionDataTip(thumb:Object):void
{
var relX:Number;
var relY:Number;
var tX:Number = thumb.x;
var tY:Number = thumb.y;
var tPlacement:String = getStyle("dataTipPlacement");
var tOffset:Number = getStyle("dataTipOffset");
// If the layoutDirection is rtl, swap left and right placement
if (layoutDirection == LayoutDirection.RTL)
{
if (tPlacement == "left")
tPlacement = "right";
else if (tPlacement == "right")
tPlacement = "left";
}
// Need to special case tooltip position because the tooltip movieclip
// resides in the root movie clip, instead of the Slider movieclip
if (_direction == SliderDirection.HORIZONTAL)
{
relX = tX;
relY = tY;
if (tPlacement == "left")
{
relX -= tOffset + dataTip.width;
relY += (thumb.height - dataTip.height) / 2;
}
else if (tPlacement == "right")
{
relX += tOffset + thumb.width;
relY += (thumb.height - dataTip.height) / 2;
}
else if (tPlacement == "top")
{
relY -= tOffset + dataTip.height;
relX -= (dataTip.width - thumb.width) / 2;
}
else if (tPlacement == "bottom")
{
relY += tOffset + thumb.height;
relX -= (dataTip.width - thumb.width) / 2;
}
}
else
{
relX = tY;
relY = unscaledHeight - tX - (dataTip.height + thumb.width) / 2;
if (tPlacement == "left")
{
relX -= tOffset + dataTip.width;
}
else if (tPlacement == "right")
{
relX += tOffset + thumb.height;
}
else if (tPlacement == "top")
{
relY -= tOffset + (dataTip.height + thumb.width) / 2;
relX -= (dataTip.width - thumb.height) / 2;
}
else if (tPlacement == "bottom")
{
relY += tOffset + (dataTip.height + thumb.width) / 2;
relX -= (dataTip.width - thumb.height) / 2;
}
}
if (layoutDirection == LayoutDirection.RTL)
relX += dataTip.width;
var o:Point = new Point(relX, relY);
var r:Point = localToGlobal(o);
r = dataTip.parent.globalToLocal(r);
dataTip.x = r.x < 0 ? 0 : r.x;
dataTip.y = r.y < 0 ? 0 : r.y;
}
/**
* @private
*/
private function destroyDataTip():void
{
if (dataTip)
{
systemManager.toolTipChildren.removeChild(dataTip);
dataTip = null;
}
}
/**
* @private
* Utility for finding the x position which corresponds
* to the given value.
*/
mx_internal function getXFromValue(v:Number):Number
{
var val:Number;
if (v == minimum)
val = track.x;
else if (v == maximum)
val = track.x + track.width;
else
val = track.x + (v - minimum) * (track.width) / (maximum - minimum);
return val;
}
/**
* @private
*/
mx_internal function getXBounds(selectedThumbIndex:int):Object
{
var maxX:Number = track.x + track.width;
var minX:Number = track.x;
if (allowThumbOverlap)
{
return { max: maxX, min: minX };
}
var minBound:Number = NaN;
var maxBound:Number = NaN;
var prevThumb:SliderThumb =
selectedThumbIndex > 0 ?
SliderThumb(thumbs.getChildAt(selectedThumbIndex - 1)) :
null;
var nextThumb:SliderThumb =
selectedThumbIndex + 1 < thumbs.numChildren ?
SliderThumb(thumbs.getChildAt(selectedThumbIndex + 1)) :
null;
if (prevThumb)
minBound = prevThumb.xPosition + prevThumb.width / 2;
if (nextThumb)
maxBound = nextThumb.xPosition - nextThumb.width / 2;
if (isNaN(minBound))
minBound = minX;
else
minBound = Math.min(Math.max(minX,minBound),maxX);
if (isNaN(maxBound))
maxBound = maxX;
else
maxBound = Math.max(Math.min(maxX, maxBound),minX);
return { max: maxBound, min: minBound };
}
/**
* @private
* Utility for positioning the thumb(s) from the current value.
*/
private function setPosFromValue():void
{
var n:int = _thumbCount;
for (var i:int = 0; i < n; i++)
{
var thumb:SliderThumb = SliderThumb(thumbs.getChildAt(i));
thumb.xPosition = getXFromValue(values[i]);
}
}
/**
* @private
* Utility for getting a value corresponding to a given x.
*/
mx_internal function getValueFromX(xPos:Number):Number
{
var v:Number = (xPos - track.x) *
(maximum - minimum) /
(track.width) + minimum;
var threshold:Number = (maximum - minimum) / 1000;
// kill rounding error at the edges.
if (v - minimum <= threshold)
{
v = minimum;
}
else if (maximum - v <= threshold)
{
v = maximum;
}
else if (!isNaN(_snapInterval) && _snapInterval != 0)
{
v = Math.round((v - minimum) / _snapInterval) *
_snapInterval + minimum;
}
return v;
}
/**
* @private
* Utility for committing a value of a given thumb.
*/
private function setValueFromPos(thumbIndex:int):void
{
var thumb:SliderThumb = SliderThumb(thumbs.getChildAt(thumbIndex));
setValueAt(getValueFromX(thumb.xPosition), thumbIndex);
}
/**
* @private
*/
mx_internal function getSnapValue(value:Number, thumb:SliderThumb = null):Number
{
if (!isNaN(_snapInterval) && _snapInterval != 0)
{
var val:Number = getValueFromX(value);
if (thumb && (thumbs.numChildren > 1) && !allowThumbOverlap)
{
var check_bounds:Boolean = true;
var bounds:Object
bounds = getXBounds(thumb.thumbIndex);
var prevThumb:SliderThumb =
thumb.thumbIndex > 0 ?
SliderThumb(thumbs.getChildAt(thumb.thumbIndex- 1)) :
null;
var nextThumb:SliderThumb =
thumb.thumbIndex + 1 < thumbs.numChildren ?
SliderThumb(thumbs.getChildAt(thumb.thumbIndex + 1)) :
null;
if (prevThumb)
{
bounds.min -= (prevThumb.width / 2);
// check if thumb is at minimum, if not we can ignore the bounds logic
if (val == minimum)
if (getValueFromX((prevThumb.xPosition - prevThumb.width/2)) != minimum)
check_bounds = false;
}
else
{
if (val == minimum)
check_bounds = false;
}
if (nextThumb)
{
bounds.max += (nextThumb.width / 2);
// check if thumb is at maximum, if not we can ignore the bounds logic
if (val == maximum)
if (getValueFromX((nextThumb.xPosition + nextThumb.width/2)) != maximum)
check_bounds = false;
}
else
{
if (val == maximum)
check_bounds = false;
}
if (check_bounds)
val = Math.min(Math.max(val, getValueFromX(Math.round(bounds.min)) + _snapInterval),
getValueFromX(Math.round(bounds.max)) - _snapInterval);
}
return getXFromValue(val);
}
return value;
}
/**
* @private Accessed by the Thumb to find out the snap interval
*/
mx_internal function getSnapIntervalWidth():Number
{
return _snapInterval * track.width / (maximum - minimum);
}
/**
* @private
*/
mx_internal function updateThumbValue(thumbIndex:int):void
{
setValueFromPos(thumbIndex);
}
/**
* Returns the thumb object at the given index. Use this method to
* style and customize individual thumbs in a slider control.
*
* @param index The zero-based index number of the thumb.
*
* @return A reference to the SliderThumb object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function getThumbAt(index:int):SliderThumb
{
return index >= 0 && index < thumbs.numChildren ?
SliderThumb(thumbs.getChildAt(index)) :
null;
}
/**
* This method sets the value of a slider thumb, and updates the display.
*
* @param index The zero-based index number of the thumb to set
* the value of, where a value of 0 corresponds to the first thumb.
*
* @param value The value to set the thumb to
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function setThumbValueAt(index:int, value:Number):void
{
setValueAt(value, index, true);
valuesChanged = true;
invalidateProperties();
invalidateDisplayList();
}
/**
* @private
*/
private function setValueAt(value:Number, index:int,
isProgrammatic:Boolean = false):void
{
var oldValue:Number = _values[index];
var threshold:Number = (maximum - minimum) / 1000;
// we need to do the round of (to remove the floating point error)
// if the stepSize had a fractional value
if (snapIntervalPrecision != -1)
{
var scale:Number = Math.pow(10, snapIntervalPrecision);
value = Math.round(value * scale) / scale;
}
_values[index] = value;
if (!isProgrammatic)
{
var event:SliderEvent = new SliderEvent(SliderEvent.CHANGE);
event.value = value;
event.thumbIndex = index;
event.clickTarget = interactionClickTarget;
//set the triggerEvent correctly
if (keyInteraction)
{
event.triggerEvent = new KeyboardEvent(KeyboardEvent.KEY_DOWN);
//reset to mouse default
keyInteraction = false;
}
else
{
event.triggerEvent = new MouseEvent(MouseEvent.CLICK);
}
if (!isNaN(oldValue))
{
if (Math.abs(oldValue - value) > threshold)
dispatchEvent(event)
}
// Handle case of changing from NaN to a valid value
else if (!isNaN(value))
{
dispatchEvent(event);
}
}
invalidateDisplayList();
}
/**
* @private
*/
mx_internal function registerMouseMove(listener:Function):void
{
innerSlider.addEventListener(MouseEvent.MOUSE_MOVE, listener);
}
/**
* @private
*/
mx_internal function unRegisterMouseMove(listener:Function):void
{
innerSlider.removeEventListener(MouseEvent.MOUSE_MOVE, listener);
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
* Move the thumb when pressed.
*/
private function track_mouseDownHandler(event:MouseEvent):void
{
if (event.target != trackHitArea && event.target != ticks)
return;
if (enabled && allowTrackClick)
{
interactionClickTarget = SliderEventClickTarget.TRACK;
//this is a mouse event
keyInteraction = false;
var pt:Point = new Point(event.localX, event.localY);
var xM:Number = pt.x;
var minIndex:Number = 0;
var minDistance:Number = 10000000;
// find the nearest thumb
var n:int = _thumbCount;
for (var i:int = 0; i < n; i++)
{
var d:Number = Math.abs(SliderThumb(thumbs.getChildAt(i)).xPosition - xM);
if (d < minDistance)
{
minIndex = i;
minDistance = d;
}
}
var thumb:SliderThumb = SliderThumb(thumbs.getChildAt(minIndex));
if (!isNaN(_snapInterval) && _snapInterval != 0)
xM = getXFromValue(getValueFromX(xM));
var duration:Number = getStyle("slideDuration");
var t:Tween = new Tween(thumb, thumb.xPosition, xM, duration);
var easingFunction:Function = getStyle("slideEasingFunction") as Function;
if (easingFunction != null)
t.easingFunction = easingFunction;
drawTrackHighlight();
}
}
/**
* @private
*/
private function thumb_focusInHandler(event:FocusEvent):void
{
dispatchEvent(event);
}
/**
* @private
*/
private function thumb_focusOutHandler(event:FocusEvent):void
{
dispatchEvent(event);
}
/**
* @private
*/
mx_internal function getTrackHitArea():UIComponent
{
return trackHitArea;
}
}
}