| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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 spark.components.supportClasses |
| { |
| |
| import flash.display.DisplayObject; |
| import flash.events.Event; |
| import flash.events.KeyboardEvent; |
| import flash.events.MouseEvent; |
| import flash.events.TimerEvent; |
| import flash.geom.Point; |
| import flash.ui.Keyboard; |
| import flash.utils.Timer; |
| |
| import mx.core.IDataRenderer; |
| import mx.core.IFactory; |
| import mx.core.UIComponent; |
| import mx.core.mx_internal; |
| import mx.events.FlexEvent; |
| import mx.managers.IFocusManagerComponent; |
| |
| import spark.effects.animation.Animation; |
| import spark.effects.animation.MotionPath; |
| import spark.effects.animation.SimpleMotionPath; |
| import spark.effects.easing.Sine; |
| import spark.events.TrackBaseEvent; |
| import spark.formatters.NumberFormatter; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Styles |
| //-------------------------------------- |
| |
| include "../../styles/metadata/BasicInheritingTextStyles.as" |
| |
| /** |
| * The alpha of the focus ring for this component. |
| * |
| * @default 0.55 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="focusAlpha", type="Number", inherit="no", theme="spark, mobile", minValue="0.0", maxValue="1.0")] |
| |
| /** |
| * @copy spark.components.supportClasses.GroupBase#focusColor |
| * |
| * @default 0xFFFFFF |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="focusColor", type="uint", format="Color", inherit="yes", theme="spark, mobile")] |
| |
| /** |
| * When <code>true</code>, the thumb's value is |
| * committed as it is dragged along the track instead |
| * of when the thumb button is released. |
| * |
| * @default true |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="liveDragging", type="Boolean", inherit="no")] |
| |
| //-------------------------------------- |
| // Excluded APIs |
| //-------------------------------------- |
| |
| [Exclude(name="color", kind="style")] |
| [Exclude(name="fontSize", kind="style")] |
| [Exclude(name="fontWeight", kind="style")] |
| [Exclude(name="textAlign", kind="style")] |
| |
| //-------------------------------------- |
| // Other metadata |
| //-------------------------------------- |
| |
| [AccessibilityClass(implementation="spark.accessibility.SliderBaseAccImpl")] |
| |
| /** |
| * The SliderBase class lets 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 SliderBase class is a base class for HSlider and VSlider. |
| * |
| * @mxml |
| * |
| * <p>The <code><s:SliderBase></code> tag inherits all of the tag |
| * attributes of its superclass and adds the following tag attributes:</p> |
| * |
| * <pre> |
| * <s:SliderBase |
| * <strong>Properties</strong> |
| * dataTipFormatFunction="20" |
| * dataTipPrecision="2" |
| * maximum="10" |
| * showDataTip="true" |
| * maxDragRate=30 |
| * |
| * <strong>Styles</strong> |
| * alignmentBaseline="USE_DOMINANT_BASELINE" |
| * baselineShift="0.0" |
| * cffHinting="HORIZONTAL_STEM" |
| * color="0" |
| * digitCase="DEFAULT" |
| * digitWidth="DEFAULT" |
| * direction="LTR" |
| * dominantBaseline="AUTO" |
| * focusAlph="0.55" |
| * focusColor="0xFFFFFF" |
| * fontFamily="Arial" |
| * fontLookup="DEVICE" |
| * fontSize="12" |
| * fontStyle="NORMAL" |
| * fontWeight="NORMAL" |
| * justificationRule="AUTO" |
| * justificationStyle="AUTO" |
| * kerning="AUTO" |
| * ligatureLevel="COMMON" |
| * lineHeight="120%" |
| * lineThrough="false" |
| * liveDragging="true" |
| * local="en" |
| * renderingMode="CFF" |
| * textAlignLast="START" |
| * textAlpha="1" |
| * textDecoration="NONE" |
| * textJustify="INTER_WORD" |
| * trackingLeft="0" |
| * trackingRight="0" |
| * typographicCase="DEFAULT" |
| * /> |
| * </pre> |
| * |
| * @see spark.components.HSlider |
| * @see spark.components.VSlider |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public class SliderBase extends TrackBase implements IFocusManagerComponent |
| { |
| include "../../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class mixins |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Placeholder for mixin by SliderBaseAccImpl. |
| */ |
| mx_internal static var createAccessibilityImplementation:Function; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function SliderBase():void |
| { |
| super(); |
| |
| maximum = 10; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Skin parts |
| // |
| //-------------------------------------------------------------------------- |
| |
| [SkinPart(required="false", type="mx.core.IDataRenderer")] |
| |
| /** |
| * A skin part that defines a dataTip that displays a formatted version of |
| * the current value. The dataTip appears while the thumb is being dragged. |
| * This is a dynamic skin part and must be of type IFactory. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public var dataTip:IFactory; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Maximum number of times per second we will change the slider position |
| * and update the display while dragging. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| protected var maxDragRate:Number = 30; |
| |
| /** |
| * @private |
| */ |
| private var dataFormatter:NumberFormatter; |
| |
| /** |
| * @private |
| */ |
| private var animator:Animation = null; |
| |
| /** |
| * @private |
| */ |
| private var dataTipInitialPosition:Point; |
| |
| /** |
| * @private |
| */ |
| private var dataTipInstance:IDataRenderer; |
| |
| /** |
| * @private |
| */ |
| private var slideToValue:Number; |
| |
| /** |
| * @private |
| */ |
| private var isKeyDown:Boolean = false; |
| |
| /** |
| * @private |
| * Location of the mouse down event on the thumb, relative to the thumb's origin. |
| * Used to update the value property when the mouse is dragged. |
| */ |
| private var clickOffset:Point; |
| |
| /** |
| * @private |
| * Local coordinates of most recent mouse move. |
| */ |
| private var mostRecentMousePoint:Point; |
| |
| /** |
| * @private |
| * Timer used to do drag scrolling. |
| */ |
| private var dragTimer:Timer = null; |
| |
| /** |
| * @private |
| * True when there's a pending mouse move (stored in mostRecentMousePoint) |
| * that needs to be handled in the dragTimer handler. |
| */ |
| private var dragPending:Boolean = false; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //--------------------------------- |
| // maximum |
| //--------------------------------- |
| |
| [Inspectable(category="General", defaultValue="10.0")] |
| |
| /** |
| * Number which represents the maximum value possible for |
| * <code>value</code>. If the values for either |
| * <code>minimum</code> or <code>value</code> are greater |
| * than <code>maximum</code>, they will be changed to |
| * reflect the new <code>maximum</code> |
| * |
| * @default 10 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| override public function get maximum():Number |
| { |
| return super.maximum; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //--------------------------------- |
| // 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):Object |
| * </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 SliderBase Control named 'slide': </p> |
| * |
| * <pre> |
| * import mx.formatters.NumberBase; |
| * function myDataTipFormatter(value:Number):Object { |
| * var dataFormatter:NumberBase = new NumberBase(".", ",", ".", ""); |
| * return "$ " + dataFormatter.formatPrecision(String(value), slide.dataTipPrecision); |
| * } |
| * </pre> |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get dataTipFormatFunction():Function |
| { |
| return _dataTipFormatFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set dataTipFormatFunction(value:Function):void |
| { |
| _dataTipFormatFunction = value; |
| } |
| |
| //--------------------------------- |
| // dataTipPrecision |
| //--------------------------------- |
| |
| [Inspectable(defaultValue="2", minValue="0")] |
| |
| /** |
| * Number of decimal places to use for the data tip text. |
| * A value of 0 means to round all values to an integer. |
| * This value is ignored if <code>dataTipFormatFunction</code> is defined. |
| * |
| * @default 2 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public var dataTipPrecision:int = 2; |
| |
| //---------------------------------- |
| // pendingValue |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _pendingValue:Number = 0; |
| |
| /** |
| * The value the slider will have when the mouse button is released. This property |
| * also holds the temporary values set during an animation of the thumb if |
| * the <code>liveDragging</code> style is true; the real value is only set |
| * when the animation ends. |
| * |
| * <p>If the <code>liveDragging</code> style is false, then the slider's value is only set |
| * when the mouse button is released. The value is not updated while the slider thumb is |
| * being dragged.</p> |
| * |
| * <p>This property is updated when the slider thumb moves, even if |
| * <code>liveDragging</code> is false.</p> |
| * |
| * @default 0 |
| * @return The value implied by the thumb position. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| protected function get pendingValue():Number |
| { |
| return _pendingValue; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function set pendingValue(value:Number):void |
| { |
| if (value == _pendingValue) |
| return; |
| _pendingValue = value; |
| invalidateDisplayList(); |
| } |
| |
| //--------------------------------- |
| // showDataTip |
| //--------------------------------- |
| |
| /** |
| * If set to <code>true</code>, shows a data tip during user interaction |
| * containing the current value of the slider. In addition, the skinPart, |
| * <code>dataTip</code>, must be defined in the skin in order to |
| * display a data tip. |
| * @default true |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public var showDataTip:Boolean = true; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| override protected function initializeAccessibility():void |
| { |
| if (SliderBase.createAccessibilityImplementation != null) |
| SliderBase.createAccessibilityImplementation(this); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function partAdded(partName:String, instance:Object):void |
| { |
| super.partAdded(partName, instance); |
| |
| // Prevent focus on our children so that focus remains with the SliderBase |
| if (instance == thumb) |
| thumb.focusEnabled = false; |
| else if (instance == track) |
| track.focusEnabled = false; |
| } |
| |
| /** |
| * @private |
| */ |
| override public function drawFocus(isFocused:Boolean):void |
| { |
| // if there's a thumb, just draw focus on the thumb; |
| // otherwise, draw it on the whole component |
| if (thumb) |
| { |
| thumb.drawFocusAnyway = true; |
| thumb.drawFocus(isFocused); |
| } |
| else |
| { |
| super.drawFocus(isFocused); |
| } |
| } |
| |
| /** |
| * @private |
| * Keep the pendingValue in sync with the actual value so that updateSkinDisplayList() |
| * overrides can just use pendingValue. |
| */ |
| override protected function setValue(value:Number):void |
| { |
| _pendingValue = value; |
| |
| super.setValue(value); |
| } |
| |
| /** |
| * @private |
| */ |
| override mx_internal function updateErrorSkin():void |
| { |
| // Don't draw the error skin |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Used to position the data tip when it is visible. Subclasses must implement |
| * this function. |
| * |
| * @param dataTipInstance The <code>dataTip</code> instance to update and position |
| * @param initialPosition The initial position of the <code>dataTip</code> in the skin |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| protected function updateDataTip(dataTipInstance:IDataRenderer, initialPosition:Point):void |
| { |
| // Override in the subclasses |
| } |
| |
| /** |
| * @private |
| * Returns a formatted version of the value |
| */ |
| private function formatDataTipText(value:Number):Object |
| { |
| var formattedValue:Object; |
| |
| if (dataTipFormatFunction != null) |
| { |
| formattedValue = dataTipFormatFunction(value); |
| } |
| else |
| { |
| if (dataFormatter == null) |
| { |
| dataFormatter = new NumberFormatter(); |
| addStyleClient(dataFormatter); |
| } |
| |
| dataFormatter.fractionalDigits = dataTipPrecision; |
| dataFormatter.trailingZeros = true; |
| |
| formattedValue = dataFormatter.format(value); |
| } |
| |
| return formattedValue; |
| } |
| |
| /** |
| * @private |
| * Handles events from the Animation that runs the animated slide. |
| * We just call setValue() with the current animated value |
| */ |
| private function animationUpdateHandler(animation:Animation):void |
| { |
| pendingValue = animation.currentValue["value"]; |
| } |
| |
| /** |
| * @private |
| * Handles end event from the Animation that runs the animated slide. |
| * We dispatch the "changeEnd" event at this time, after the animation |
| * is done since each animation occurs after a user interaction. |
| */ |
| private function animationEndHandler(animation:Animation):void |
| { |
| setValue(slideToValue); |
| |
| dispatchEvent(new Event(Event.CHANGE)); |
| dispatchEvent(new FlexEvent(FlexEvent.CHANGE_END)); |
| } |
| |
| /** |
| * @private |
| * Stops a running animation prematurely and sets the value |
| * of the slider to the current pendingValue. We also dispatch |
| * a "changeEnd" event since the user has started another interaction. |
| */ |
| private function stopAnimation():void |
| { |
| animator.stop(); |
| |
| setValue(nearestValidValue(pendingValue, snapInterval)); |
| |
| dispatchEvent(new Event(Event.CHANGE)); |
| dispatchEvent(new FlexEvent(FlexEvent.CHANGE_END)); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| override protected function thumb_mouseDownHandler(event:MouseEvent):void |
| { |
| // finish previous animation |
| if (animator && animator.isPlaying) |
| stopAnimation(); |
| |
| super.thumb_mouseDownHandler(event); |
| clickOffset = thumb.globalToLocal(new Point(event.stageX, event.stageY)); |
| |
| // Popup a dataTip only if we have a SkinPart and the boolean flag is true |
| if (dataTip && showDataTip && enabled) |
| { |
| dataTipInstance = IDataRenderer(createDynamicPartInstance("dataTip")); |
| |
| dataTipInstance.data = formatDataTipText( |
| nearestValidValue(pendingValue, snapInterval)); |
| |
| var tipAsUIComponent:UIComponent = dataTipInstance as UIComponent; |
| |
| // Allow styles to be inherited from SliderBase. |
| if (tipAsUIComponent) |
| { |
| tipAsUIComponent.owner = this; |
| tipAsUIComponent.isPopUp = true; |
| } |
| |
| systemManager.toolTipChildren.addChild(DisplayObject(dataTipInstance)); |
| |
| // Force the dataTip to render so that we have the correct size since |
| // updateDataTip might need the size |
| if (tipAsUIComponent) |
| { |
| tipAsUIComponent.validateNow(); |
| tipAsUIComponent.setActualSize(tipAsUIComponent.getExplicitOrMeasuredWidth(), |
| tipAsUIComponent.getExplicitOrMeasuredHeight()); |
| } |
| |
| dataTipInitialPosition = new Point(DisplayObject(dataTipInstance).x, |
| DisplayObject(dataTipInstance).y); |
| updateDataTip(dataTipInstance, dataTipInitialPosition); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function handleMousePoint(p:Point):void |
| { |
| var newValue:Number = pointToValue(p.x - clickOffset.x, p.y - clickOffset.y); |
| newValue = nearestValidValue(newValue, snapInterval); |
| |
| if (newValue != pendingValue) |
| { |
| dispatchEvent(new TrackBaseEvent(TrackBaseEvent.THUMB_DRAG)); |
| if (getStyle("liveDragging") === true) |
| { |
| setValue(newValue); |
| dispatchEvent(new Event(Event.CHANGE)); |
| } |
| else |
| { |
| pendingValue = newValue; |
| } |
| } |
| |
| if (dataTipInstance && showDataTip) |
| { |
| // If showing the dataTip, we need to validate to |
| // make sure the thumb is in the right position. |
| //validateNow(); |
| |
| dataTipInstance.data = formatDataTipText(pendingValue); |
| |
| // Force the dataTip to render so that we have the correct size since |
| // updateDataTip might need the size |
| var tipAsUIComponent:UIComponent = dataTipInstance as UIComponent; |
| if (tipAsUIComponent) |
| { |
| tipAsUIComponent.validateNow(); |
| tipAsUIComponent.setActualSize(tipAsUIComponent.getExplicitOrMeasuredWidth(),tipAsUIComponent.getExplicitOrMeasuredHeight()); |
| } |
| |
| updateDataTip(dataTipInstance, dataTipInitialPosition); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function system_mouseMoveHandler(event:MouseEvent):void |
| { |
| if (!track) |
| return; |
| |
| mostRecentMousePoint = track.globalToLocal(new Point(event.stageX, event.stageY)); |
| if (!dragTimer) |
| { |
| dragTimer = new Timer(1000/maxDragRate, 0); |
| dragTimer.addEventListener(TimerEvent.TIMER, dragTimerHandler); |
| } |
| |
| if (!dragTimer.running) |
| { |
| // This changes the slider value and invalidates the display list. |
| handleMousePoint(mostRecentMousePoint); |
| event.updateAfterEvent(); |
| |
| // Start the periodic timer that will do subsequent drag |
| // scrolling if necessary. |
| dragTimer.start(); |
| |
| // No additional mouse events received yet, so no scrolling pending. |
| dragPending = false; |
| } |
| else |
| { |
| dragPending = true; |
| } |
| } |
| |
| /** |
| * @private |
| * Used to periodically change the slider value during a drag gesture. |
| */ |
| private function dragTimerHandler(event:TimerEvent):void |
| { |
| if (dragPending) |
| { |
| // This changes the slider value and invalidates the display list. |
| handleMousePoint(mostRecentMousePoint); |
| |
| // Call updateAfterEvent() to make sure it looks smooth |
| event.updateAfterEvent(); |
| |
| // No scroll is pending now. |
| dragPending = false; |
| } |
| else |
| { |
| // The timer elapsed with no mouse events, so we'll |
| // just turn the timer off for now. It will get turned |
| // back on if another mouse event comes in. |
| dragTimer.stop(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function system_mouseUpHandler(event:Event):void |
| { |
| if (dragTimer) |
| { |
| if (dragPending) |
| { |
| // This changes the slider value and invalidates the display list. |
| handleMousePoint(mostRecentMousePoint); |
| |
| // Call updateAfterEvent() to make sure it looks smooth |
| if (event is MouseEvent) |
| MouseEvent(event).updateAfterEvent(); |
| } |
| // The drag gesture is over, so we no longer need the timer. |
| dragTimer.stop(); |
| dragTimer.removeEventListener(TimerEvent.TIMER, dragTimerHandler); |
| dragTimer = null; |
| } |
| |
| if ((getStyle("liveDragging") === false) && (value != pendingValue)) |
| { |
| setValue(pendingValue); |
| dispatchEvent(new Event(Event.CHANGE)); |
| } |
| |
| if (dataTipInstance) |
| { |
| removeDynamicPartInstance("dataTip", dataTipInstance); |
| systemManager.toolTipChildren.removeChild(DisplayObject(dataTipInstance)); |
| dataTipInstance = null; |
| } |
| |
| super.system_mouseUpHandler(event); |
| } |
| |
| /** |
| * @private |
| * Handle keyboard events. Left/Down decreases the value |
| * decreases the value by stepSize. The opposite for |
| * Right/Up arrows. The Home and End keys set the value |
| * to the min and max respectively. |
| * |
| * We dispatch changing events when the keystroke |
| * may both repeat and alter the value. |
| */ |
| override protected function keyDownHandler(event:KeyboardEvent):void |
| { |
| super.keyDownHandler(event); |
| |
| if (event.isDefaultPrevented()) |
| return; |
| |
| if (animator && animator.isPlaying) |
| stopAnimation(); |
| |
| var prevValue:Number = this.value; |
| var newValue:Number; |
| |
| // If rtl layout, need to swap LEFT/UP and RIGHT/DOWN so correct action |
| // is done. |
| var keyCode:uint = mapKeycodeForLayoutDirection(event, true); |
| |
| switch (keyCode) |
| { |
| case Keyboard.DOWN: |
| case Keyboard.LEFT: |
| { |
| newValue = nearestValidValue(pendingValue - stepSize, snapInterval); |
| |
| if (prevValue != newValue) |
| { |
| if (!isKeyDown) |
| { |
| dispatchEvent(new FlexEvent(FlexEvent.CHANGE_START)); |
| isKeyDown = true; |
| } |
| setValue(newValue); |
| dispatchEvent(new Event(Event.CHANGE)); |
| } |
| event.preventDefault(); |
| break; |
| } |
| |
| case Keyboard.UP: |
| case Keyboard.RIGHT: |
| { |
| newValue = nearestValidValue(pendingValue + stepSize, snapInterval); |
| |
| if (prevValue != newValue) |
| { |
| if (!isKeyDown) |
| { |
| dispatchEvent(new FlexEvent(FlexEvent.CHANGE_START)); |
| isKeyDown = true; |
| } |
| setValue(newValue); |
| dispatchEvent(new Event(Event.CHANGE)); |
| } |
| event.preventDefault(); |
| break; |
| } |
| |
| case Keyboard.HOME: |
| { |
| value = minimum; |
| if (value != prevValue) |
| dispatchEvent(new Event(Event.CHANGE)); |
| event.preventDefault(); |
| break; |
| } |
| |
| case Keyboard.END: |
| { |
| value = maximum; |
| if (value != prevValue) |
| dispatchEvent(new Event(Event.CHANGE)); |
| event.preventDefault(); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Handle keyboard release events. Allows us to send out changeEnd |
| * event. |
| */ |
| override protected function keyUpHandler(event:KeyboardEvent) : void |
| { |
| switch (event.keyCode) |
| { |
| case Keyboard.DOWN: |
| case Keyboard.LEFT: |
| case Keyboard.UP: |
| case Keyboard.RIGHT: |
| { |
| if (isKeyDown) |
| { |
| // Dispatch "change" event only after a repeat occurs. |
| dispatchEvent(new FlexEvent(FlexEvent.CHANGE_END)); |
| isKeyDown = false; |
| } |
| event.preventDefault(); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Handle mouse-down events for the slider track. We |
| * calculate the value based on the new position and then |
| * move the thumb to the correct location as well as |
| * commit the value. |
| */ |
| override protected function track_mouseDownHandler(event:MouseEvent):void |
| { |
| if (!enabled) |
| return; |
| |
| // Offset the track-relative coordinates of this event so that |
| // the thumb will end up centered over the mouse down location. |
| var thumbW:Number = (thumb) ? thumb.width : 0; |
| var thumbH:Number = (thumb) ? thumb.height : 0; |
| var offsetX:Number = event.stageX - (thumbW / 2); |
| var offsetY:Number = event.stageY - (thumbH / 2); |
| var p:Point = track.globalToLocal(new Point(offsetX, offsetY)); |
| |
| var newValue:Number = pointToValue(p.x, p.y); |
| newValue = nearestValidValue(newValue, snapInterval); |
| |
| if (newValue != pendingValue) |
| { |
| var slideDuration:Number = getStyle("slideDuration"); |
| if (slideDuration != 0) |
| { |
| if (!animator) |
| { |
| animator = new Animation(); |
| var animTarget:AnimationTarget = new AnimationTarget(animationUpdateHandler); |
| animTarget.endFunction = animationEndHandler; |
| animator.animationTarget = animTarget; |
| // TODO (chaase): hard-coding easer for now - how to style it? |
| animator.easer = new Sine(0); |
| } |
| |
| // Finish any current animation before we start the next one. |
| if (animator.isPlaying) |
| stopAnimation(); |
| |
| // holds the final value to be set when animation ends |
| slideToValue = newValue; |
| animator.duration = slideDuration * |
| (Math.abs(pendingValue - slideToValue) / (maximum - minimum)); |
| animator.motionPaths = new <MotionPath>[ |
| new SimpleMotionPath("value", pendingValue, slideToValue)]; |
| |
| dispatchEvent(new FlexEvent(FlexEvent.CHANGE_START)); |
| animator.play(); |
| } |
| else |
| { |
| setValue(newValue); |
| dispatchEvent(new Event(Event.CHANGE)); |
| } |
| } |
| |
| event.updateAfterEvent(); |
| } |
| } |
| |
| } |