//////////////////////////////////////////////////////////////////////////////// // // ADOBE SYSTEMS INCORPORATED // Copyright 2004-2007 Adobe Systems Incorporated // All Rights Reserved. // // NOTICE: Adobe permits you to use, modify, and distribute this file // in accordance with the terms of the license agreement accompanying it. // //////////////////////////////////////////////////////////////////////////////// package flexlib.baseClasses { import flexlib.controls.sliderClasses.SliderThumb; import flash.display.DisplayObject; import flash.display.Graphics; import flash.display.Sprite; import flash.events.Event; import flash.events.FocusEvent; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.geom.Point; import flash.utils.getTimer; import mx.controls.sliderClasses.SliderDataTip; import mx.controls.sliderClasses.SliderDirection; import mx.controls.sliderClasses.SliderLabel; import mx.core.FlexVersion; import mx.core.IFlexDisplayObject; import mx.core.mx_internal; import mx.core.UIComponent; import mx.effects.Tween; import mx.events.FlexEvent; import mx.events.SliderEvent; import mx.events.SliderEventClickTarget; import mx.formatters.NumberFormatter; import mx.managers.ISystemManager; import mx.managers.SystemManager; 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. * *

If the liveDragging property is true, * the event is dispatched continuously as the user moves the thumb. * If liveDragging is false, * the event is dispatched when the user releases the slider thumb.

* * @eventType mx.events.SliderEvent.CHANGE */ [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 thumbPress event. * @eventType mx.events.SliderEvent.THUMB_DRAG */ [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 */ [Event(name="thumbPress", type="mx.events.SliderEvent")] /** * Dispatched when the slider's thumb is released, * meaning the user releases the mouse button after * a thumbPress event. * * @eventType mx.events.SliderEvent.THUMB_RELEASE */ [Event(name="thumbRelease", type="mx.events.SliderEvent")] //-------------------------------------- // Styles //-------------------------------------- //include "../../styles/metadata/FillStyles.as"; /** * The color of the black section of the border. * * @default 0x919999 */ [Style(name="borderColor", type="uint", format="Color", inherit="no")] /** * Invert the direction of the thumbs. * If true, the thumbs will be flipped. * * @default false */ [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 */ [Style(name="labelOffset", type="Number", format="Length", inherit="no")] /** * The name of the style to use for the slider label. * * @default undefined */ [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 */ [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 */ [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 */ [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. */ [Style(name="tickColor", type="uint", format="Color", inherit="no")] /** * The length in pixels of the tick marks. * If direction is Direction.HORIZONTAL, * then adjust the height of the tick marks. * If direction is Direction.VERTICAL, * then adjust the width. * * @default 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 */ [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 */ [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. * *

You use this property along with the fillAlphas * property. Typically you set fillAlphas to [ 1.0, 1.0 ] * when setting trackColors.

* * @default [ 0xE7E7E7, 0xE7E7E7 ] */ [Style(name="trackColors", type="Array", arrayType="uint", format="Color", inherit="no")] /** * Specifies whether to enable track highlighting between thumbs * (or a single thumb and the beginning of the track). * * @default false */ [Style(name="showTrackHighlight", type="Boolean", inherit="no")] /** * The size of the track margins, in pixels. * If undefined, 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 */ [Style(name="trackMargin", type="Number", format="Length", inherit="no")] /** * The name of the style declaration to use for the data tip. * * @default undefined */ [Style(name="dataTipStyleName", type="String", inherit="no")] /** * The offset, in pixels, of the data tip relative to the thumb. * Used in combination with the dataTipPlacement * style property of the HSlider and VSlider controls. * * @default 16 */ [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 */ [Style(name="dataTipPrecision", type="int", inherit="no")] /** * The default skin for the slider thumb. * * @default SliderThumbSkin */ [Style(name="thumbSkin", type="Class", inherit="no", states="up, over, down, disabled")] /** * The skin for the slider thumb up state. * * @default SliderThumbSkin */ [Style(name="thumbUpSkin", type="Class", inherit="no")] /** * The skin for the slider thumb over state. * * @default SliderThumbSkin */ [Style(name="thumbOverSkin", type="Class", inherit="no")] /** * The skin for the slider thumb down state. * * @default SliderThumbSkin */ [Style(name="thumbDownSkin", type="Class", inherit="no")] /** * The skin for the slider thumb disabled state. * * @default SliderThumbSkin */ [Style(name="thumbDisabledSkin", type="Class", inherit="no")] /** * The skin for the slider track when it is selected. */ [Style(name="trackHighlightSkin", type="Class", inherit="no")] /** * The skin for the slider track. */ [Style(name="trackSkin", type="Class", inherit="no")] //-------------------------------------- // Other metadata //-------------------------------------- [AccessibilityClass(implementation="mx.accessibility.SliderAccImpl")] [ResourceBundle("SharedResources")] /** * SliderBase is a copy/paste version of the original Slider class in the Flex framework. * *

The only modifications made to this class were to change some properties and * methods from private to protected so we can override them in a subclass.

* * 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 * *

The Slider class cannot be used as an MXML tag. Use the <mx:HSlider> * and <mx:VSlider> tags instead. However, the Slider class does define tag * attributes used by the <mx:HSlider> and <mx:VSlider> tags.

* *

The Slider class inherits all of the tag attributes * of its superclass, and adds the following tag attributes:

* *
 *  <mx:tagname
 *    Properties
 *    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="The value of the minimum property."
 * 
 *    Styles
 *    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"
 *  
 *    Events
 *    change="No default"
 *    thumbDrag="No default"
 *    thumbPress="No default"
 *    thumbRelease="No default"
 *  />
 *  
*/ public class SliderBase extends UIComponent { //include "../../core/Version.as"; //-------------------------------------------------------------------------- // // Class mixins // //-------------------------------------------------------------------------- /** * @private * Placeholder for mixin by SliderAccImpl. */ mx_internal static var createAccessibilityImplementation:Function; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. */ public function SliderBase() { super(); tabChildren = true; } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private */ protected var track:IFlexDisplayObject; /** * @private */ protected 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 */ protected 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 */ protected var dataFormatter:NumberFormatter; /** * @private */ protected 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 (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0) return super.baselinePosition; 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 false, then each thumb can only be moved to the edge of * the adjacent thumb. * If true, then each thumb can be moved to any position on the track. * * @default false */ public var allowThumbOverlap:Boolean = false; //---------------------------------- // allowTrackClick //---------------------------------- [Inspectable(defaultValue="true")] /** * Specifies whether clicking on the track will move the slider thumb. * * @default true */ public var allowTrackClick:Boolean = true; //---------------------------------- // dataTipFormatFunction //---------------------------------- /** * @private */ protected 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. * *

The function has the following signature:

*
     *  funcName(value:Number):String
     *  
* *

The following example prefixes the data tip text with a dollar sign and * formats the text using the dataTipPrecision * of a Slider Control named 'slide':

* *
     *  import mx.formatters.NumberBase;
     *  function myDataTipFormatter(value:Number):String { 
     *      var dataFormatter:NumberBase = new NumberBase(".", ",", ".", ""); 
     *      return   "$ " + dataFormatter.formatPrecision(String(value), slide.getStyle("dataTipPrecision")); 
     *  }
     *  
* * @default undefined */ public function get dataTipFormatFunction():Function { return _dataTipFormatFunction; } /** * @private */ public function set dataTipFormatFunction(value:Function):void { _dataTipFormatFunction = value; } //---------------------------------- // direction //---------------------------------- /** * @private */ protected 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 "horizontal" or "vertical". * *

In ActionScript, you use the following constants * to set this property: * SliderDirection.VERTICAL and * SliderDirection.HORIZONTAL.

* * 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 */ 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. * *

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.

* *

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.

* * @default undefined */ 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 false, Flex sets the value and * values properties and dispatches the change * event when the user stops dragging the slider thumb. * If true, Flex sets the value and * values properties and dispatches the change * event continuously as the user moves the thumb. * * @default false */ 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 */ 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 */ 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 true, show a data tip during user interaction * containing the current value of the slider. * * @default true */ public var showDataTip:Boolean = true; //---------------------------------- // sliderThumbClass //---------------------------------- /** * @private */ private var _thumbClass:Class = SliderThumb; /** * A reference to the class to use for each thumb. * * @default SliderThumb */ 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 */ 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 snapInterval is 2, * the minimum value is 0, * and the maximum 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 minimum and maximum values. * * @default 0 */ 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 value property contains * the current value of the slider. * If set to 2, then the values property contains * an array of values representing the value for each thumb. * * @default 1 */ 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 */ 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 maximum value * of the control. * Flex displays tick marks whenever you set the tickInterval * property to a nonzero value. * *

For example, if tickInterval is 1 and * maximum 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 tickValues property * is set to a non-empty Array, then this property is ignored.

* * @default 0 */ 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 minimum and maximum values. * For example, if the tickValues property * is [0, 2.5, 7.5, 10] and maximum 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. * *

If this property is set to a non-empty Array, then the tickInterval property * is ignored.

* * @default undefined */ 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 * minimum and maximum properties. * Use the value property when thumbCount is 1. * When thumbCount is greater than 1, use the * values property instead. * The default value is equal to the minimum property. */ 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 thumbCount * is greater than 1. */ 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 (SliderBase.createAccessibilityImplementation != null) SliderBase.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 (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. */ 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 trackMargin, then the size of the track * is equal to the available width minus the trackMargin times 2. * *

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 multiple of the maximum value). The tick mark * y-position is based on the tickOffset. 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.

* *

Labels are positioned at even intervals along the track. The labels are always horizontally centered above their * interval position unless the trackMargin setting is too small. If you specify a trackMargin, 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 labelOffset 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.

* *

The thumbs are positioned to overlap the track. Their x-position is determined by their value. The y-position is * controlled by the thumbOffset 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.

* *

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.

* *

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.

* * @param unscaledWidth Specifies the width of the component, in pixels, * in the component's coordinates, regardless of the value of the * scaleX 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 * scaleY property of the component. */ 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 numThumbs:int = thumbs ? thumbs.numChildren : 0; var trackMargin:Number = getStyle("trackMargin"); var widestThumb:Number = 6; if(thumbs == null) { return; } var firstThumb:SliderThumb = SliderThumb(thumbs.getChildAt(0)); if (thumbs && firstThumb) widestThumb = firstThumb.getExplicitOrMeasuredWidth(); var trackLeftOffset:Number = widestThumb / 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 - firstThumb.getExplicitOrMeasuredHeight()) / 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); var fullThumbHeight:Number = firstThumb.getExplicitOrMeasuredHeight(); var halfThumbHeight:Number = (!fullThumbHeight) ? 0 : (fullThumbHeight / 2); g.drawRect(track.x, track.y - halfThumbHeight - tLength, track.width, track.height + fullThumbHeight + 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; 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; 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; 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.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 xOffset:Number = tickWidth / 2; 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) - xOffset); 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 - xOffset; 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 */ protected 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"); // 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; } } 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; // kill rounding error at the edges. if (v - minimum <= 0.002) { v = minimum; } else if (maximum - v <= 0.002) { 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. */ 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 */ public function setThumbValueAt(index:int, value:Number):void { setValueAt(value, index, true); valuesChanged = true; invalidateProperties(); invalidateDisplayList(); } /** * @private */ protected function setValueAt(value:Number, index:int, isProgrammatic:Boolean = false):void { var oldValue:Number = _values[index]; // 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) && Math.abs(oldValue - value) > 0.002) 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; } } }