scaffold.libs_as.feathers.controls.SimpleScrollBar.as Maven / Gradle / Ivy
/*
Feathers
Copyright 2012-2015 Bowler Hat LLC. All Rights Reserved.
This program is free software. You can redistribute and/or modify it in
accordance with the terms of the accompanying license agreement.
*/
package feathers.controls
{
import feathers.core.FeathersControl;
import feathers.core.IFeathersControl;
import feathers.core.IMeasureDisplayObject;
import feathers.core.IValidating;
import feathers.core.PropertyProxy;
import feathers.events.FeathersEventType;
import feathers.layout.Direction;
import feathers.skins.IStyleProvider;
import feathers.utils.math.clamp;
import feathers.utils.math.roundToNearest;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.utils.Timer;
import starling.display.DisplayObject;
import starling.display.Quad;
import starling.events.Event;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
/**
* Dispatched when the scroll bar's value changes.
*
* The properties of the event object have the following values:
*
* Property Value
* bubbles
false
* currentTarget
The Object that defines the
* event listener that handles the event. For example, if you use
* myButton.addEventListener()
to register an event listener,
* myButton is the value of the currentTarget
.
* data
null
* target
The Object that dispatched the event;
* it is not always the Object listening for the event. Use the
* currentTarget
property to always access the Object
* listening for the event.
*
*
* @eventType starling.events.Event.CHANGE
*/
[Event(name="change",type="starling.events.Event")]
/**
* Dispatched when the user starts dragging the scroll bar's thumb.
*
* The properties of the event object have the following values:
*
* Property Value
* bubbles
false
* currentTarget
The Object that defines the
* event listener that handles the event. For example, if you use
* myButton.addEventListener()
to register an event listener,
* myButton is the value of the currentTarget
.
* data
null
* target
The Object that dispatched the event;
* it is not always the Object listening for the event. Use the
* currentTarget
property to always access the Object
* listening for the event.
*
*
* @eventType feathers.events.FeathersEventType.BEGIN_INTERACTION
*/
[Event(name="beginInteraction",type="starling.events.Event")]
/**
* Dispatched when the user stops dragging the scroll bar's thumb.
*
* The properties of the event object have the following values:
*
* Property Value
* bubbles
false
* currentTarget
The Object that defines the
* event listener that handles the event. For example, if you use
* myButton.addEventListener()
to register an event listener,
* myButton is the value of the currentTarget
.
* data
null
* target
The Object that dispatched the event;
* it is not always the Object listening for the event. Use the
* currentTarget
property to always access the Object
* listening for the event.
*
*
* @eventType feathers.events.FeathersEventType.END_INTERACTION
*/
[Event(name="endInteraction",type="starling.events.Event")]
/**
* Select a value between a minimum and a maximum by dragging a thumb over
* a physical range. This type of scroll bar does not have a visible track,
* and it does not have increment and decrement buttons. It is ideal for
* mobile applications where the scroll bar is often simply a visual element
* to indicate the scroll position. For a more feature-rich scroll bar,
* see the ScrollBar
component.
*
* The following example updates a list to use simple scroll bars:
*
*
* list.horizontalScrollBarFactory = function():IScrollBar
* {
* var scrollBar:SimpleScrollBar = new SimpleScrollBar();
* scrollBar.direction = Direction.HORIZONTAL;
* return scrollBar;
* };
* list.verticalScrollBarFactory = function():IScrollBar
* {
* var scrollBar:SimpleScrollBar = new SimpleScrollBar();
* scrollBar.direction = Direction.VERTICAL;
* return scrollBar;
* };
*
* @see ../../../help/simple-scroll-bar.html How to use the Feathers SimpleScrollBar component
* @see feathers.controls.ScrollBar
*/
public class SimpleScrollBar extends FeathersControl implements IDirectionalScrollBar
{
/**
* @private
*/
private static const HELPER_POINT:Point = new Point();
/**
* @private
*/
protected static const INVALIDATION_FLAG_THUMB_FACTORY:String = "thumbFactory";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.Direction.HORIZONTAL
.
*
* DEPRECATION WARNING: This constant is deprecated
* starting with Feathers 3.0. It will be removed in a future version of
* Feathers according to the standard
* Feathers deprecation policy.
*/
public static const DIRECTION_HORIZONTAL:String = "horizontal";
/**
* @private
* DEPRECATED: Replaced by feathers.layout.Direction.VERTICAL
.
*
* DEPRECATION WARNING: This constant is deprecated
* starting with Feathers 3.0. It will be removed in a future version of
* Feathers according to the standard
* Feathers deprecation policy.
*/
public static const DIRECTION_VERTICAL:String = "vertical";
/**
* The default value added to the styleNameList
of the thumb.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_THUMB:String = "feathers-simple-scroll-bar-thumb";
/**
* The default IStyleProvider
for all SimpleScrollBar
* components.
*
* @default null
* @see feathers.core.FeathersControl#styleProvider
*/
public static var globalStyleProvider:IStyleProvider;
/**
* @private
*/
protected static function defaultThumbFactory():Button
{
return new Button();
}
/**
* Constructor.
*/
public function SimpleScrollBar()
{
super();
this.addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
}
/**
* The value added to the styleNameList
of the thumb. This
* variable is protected
so that sub-classes can customize
* the thumb style name in their constructors instead of using the
* default style name defined by DEFAULT_CHILD_STYLE_NAME_THUMB
.
*
* To customize the thumb style name without subclassing, see
* customThumbStyleName
.
*
* @see #customThumbStyleName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var thumbStyleName:String = DEFAULT_CHILD_STYLE_NAME_THUMB;
/**
* @private
*/
protected var thumbOriginalWidth:Number = NaN;
/**
* @private
*/
protected var thumbOriginalHeight:Number = NaN;
/**
* The thumb sub-component.
*
* For internal use in subclasses.
*
* @see #thumbFactory
* @see #createThumb()
*/
protected var thumb:DisplayObject;
/**
* @private
*/
protected var track:Quad;
/**
* @private
*/
override protected function get defaultStyleProvider():IStyleProvider
{
return SimpleScrollBar.globalStyleProvider;
}
/**
* @private
*/
protected var _direction:String = Direction.HORIZONTAL;
[Inspectable(type="String",enumeration="horizontal,vertical")]
/**
* Determines if the scroll bar's thumb can be dragged horizontally or
* vertically. When this value changes, the scroll bar's width and
* height values do not change automatically.
*
* In the following example, the direction is changed to vertical:
*
*
* scrollBar.direction = Direction.VERTICAL;
*
* @default feathers.layout.Direction.HORIZONTAL
*
* @see feathers.layout.Direction#HORIZONTAL
* @see feathers.layout.Direction#VERTICAL
*/
public function get direction():String
{
return this._direction;
}
/**
* @private
*/
public function set direction(value:String):void
{
if(this._direction == value)
{
return;
}
this._direction = value;
this.invalidate(INVALIDATION_FLAG_DATA);
this.invalidate(INVALIDATION_FLAG_THUMB_FACTORY);
}
/**
* Determines if the value should be clamped to the range between the
* minimum and maximum. If false
and the value is outside of the range,
* the thumb will shrink as if the range were increasing.
*
* In the following example, the clamping behavior is updated:
*
*
* scrollBar.clampToRange = true;
*
* @default false
*/
public var clampToRange:Boolean = false;
/**
* @private
*/
protected var _value:Number = 0;
/**
* @inheritDoc
*
* @default 0
*
* @see #maximum
* @see #minimum
* @see #step
* @see #page
* @see #event:change
*/
public function get value():Number
{
return this._value;
}
/**
* @private
*/
public function set value(newValue:Number):void
{
if(this.clampToRange)
{
newValue = clamp(newValue, this._minimum, this._maximum);
}
if(this._value == newValue)
{
return;
}
this._value = newValue;
this.invalidate(INVALIDATION_FLAG_DATA);
if(this.liveDragging || !this.isDragging)
{
this.dispatchEventWith(Event.CHANGE);
}
}
/**
* @private
*/
protected var _minimum:Number = 0;
/**
* @inheritDoc
*
* @default 0
*
* @see #value
* @see #maximum
*/
public function get minimum():Number
{
return this._minimum;
}
/**
* @private
*/
public function set minimum(value:Number):void
{
if(this._minimum == value)
{
return;
}
this._minimum = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _maximum:Number = 0;
/**
* @inheritDoc
*
* @default 0
*
* @see #value
* @see #minimum
*/
public function get maximum():Number
{
return this._maximum;
}
/**
* @private
*/
public function set maximum(value:Number):void
{
if(this._maximum == value)
{
return;
}
this._maximum = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _step:Number = 0;
/**
* @inheritDoc
*
* @default 0
*
* @see #value
* @see #page
*/
public function get step():Number
{
return this._step;
}
/**
* @private
*/
public function set step(value:Number):void
{
this._step = value;
}
/**
* @private
*/
protected var _page:Number = 0;
/**
* @inheritDoc
*
* @default 0
*
* @see #value
* @see #step
*/
public function get page():Number
{
return this._page;
}
/**
* @private
*/
public function set page(value:Number):void
{
if(this._page == value)
{
return;
}
this._page = value;
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* Quickly sets all padding properties to the same value. The
* padding
getter always returns the value of
* paddingTop
, but the other padding values may be
* different.
*
* In the following example, the padding is changed to 20 pixels:
*
*
* scrollBar.padding = 20;
*
* @default 0
*
* @see #paddingTop
* @see #paddingRight
* @see #paddingBottom
* @see #paddingLeft
*/
public function get padding():Number
{
return this._paddingTop;
}
/**
* @private
*/
public function set padding(value:Number):void
{
this.paddingTop = value;
this.paddingRight = value;
this.paddingBottom = value;
this.paddingLeft = value;
}
/**
* @private
*/
protected var _paddingTop:Number = 0;
/**
* The minimum space, in pixels, above the thumb.
*
* In the following example, the top padding is changed to 20 pixels:
*
*
* scrollBar.paddingTop = 20;
*
* @default 0
*/
public function get paddingTop():Number
{
return this._paddingTop;
}
/**
* @private
*/
public function set paddingTop(value:Number):void
{
if(this._paddingTop == value)
{
return;
}
this._paddingTop = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _paddingRight:Number = 0;
/**
* The minimum space, in pixels, to the right of the thumb.
*
* In the following example, the right padding is changed to 20 pixels:
*
*
* scrollBar.paddingRight = 20;
*
* @default 0
*/
public function get paddingRight():Number
{
return this._paddingRight;
}
/**
* @private
*/
public function set paddingRight(value:Number):void
{
if(this._paddingRight == value)
{
return;
}
this._paddingRight = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _paddingBottom:Number = 0;
/**
* The minimum space, in pixels, below the thumb.
*
* In the following example, the bottom padding is changed to 20 pixels:
*
*
* scrollBar.paddingBottom = 20;
*
* @default 0
*/
public function get paddingBottom():Number
{
return this._paddingBottom;
}
/**
* @private
*/
public function set paddingBottom(value:Number):void
{
if(this._paddingBottom == value)
{
return;
}
this._paddingBottom = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _paddingLeft:Number = 0;
/**
* The minimum space, in pixels, to the left of the thumb.
*
* In the following example, the left padding is changed to 20 pixels:
*
*
* scrollBar.paddingLeft = 20;
*
* @default 0
*/
public function get paddingLeft():Number
{
return this._paddingLeft;
}
/**
* @private
*/
public function set paddingLeft(value:Number):void
{
if(this._paddingLeft == value)
{
return;
}
this._paddingLeft = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var currentRepeatAction:Function;
/**
* @private
*/
protected var _repeatTimer:Timer;
/**
* @private
*/
protected var _repeatDelay:Number = 0.05;
/**
* The time, in seconds, before actions are repeated. The first repeat
* happens after a delay that is five times longer than the following
* repeats.
*
* In the following example, the repeat delay is changed to 500 milliseconds:
*
*
* scrollBar.repeatDelay = 0.5;
*
* @default 0.05
*/
public function get repeatDelay():Number
{
return this._repeatDelay;
}
/**
* @private
*/
public function set repeatDelay(value:Number):void
{
if(this._repeatDelay == value)
{
return;
}
this._repeatDelay = value;
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var isDragging:Boolean = false;
/**
* Determines if the scroll bar dispatches the Event.CHANGE
* event every time the thumb moves, or only once it stops moving.
*
* In the following example, live dragging is disabled:
*
*
* scrollBar.liveDragging = false;
*
* @default true
*/
public var liveDragging:Boolean = true;
/**
* @private
*/
protected var _thumbFactory:Function;
/**
* A function used to generate the scroll bar's thumb sub-component.
* The thumb must be an instance of Button
. This factory
* can be used to change properties on the thumb when it is first
* created. For instance, if you are skinning Feathers components
* without a theme, you might use this factory to set skins and other
* styles on the thumb.
*
* The function should have the following signature:
* function():Button
*
* In the following example, a custom thumb factory is passed
* to the scroll bar:
*
*
* scrollBar.thumbFactory = function():Button
* {
* var thumb:Button = new Button();
* thumb.defaultSkin = new Image( upTexture );
* thumb.downSkin = new Image( downTexture );
* return thumb;
* };
*
* @default null
*
* @see feathers.controls.Button
* @see #thumbProperties
*/
public function get thumbFactory():Function
{
return this._thumbFactory;
}
/**
* @private
*/
public function set thumbFactory(value:Function):void
{
if(this._thumbFactory == value)
{
return;
}
this._thumbFactory = value;
this.invalidate(INVALIDATION_FLAG_THUMB_FACTORY);
}
/**
* @private
*/
protected var _customThumbStyleName:String;
/**
* A style name to add to the scroll bar's thumb sub-component.
* Typically used by a theme to provide different styles to different
* scroll bars.
*
* In the following example, a custom thumb style name is passed
* to the scroll bar:
*
*
* scrollBar.customThumbStyleName = "my-custom-thumb";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( Button ).setFunctionForStyleName( "my-custom-thumb", setCustomThumbStyles );
*
* @default null
*
* @see #DEFAULT_CHILD_STYLE_NAME_THUMB
* @see feathers.core.FeathersControl#styleNameList
* @see #thumbFactory
* @see #thumbProperties
*/
public function get customThumbStyleName():String
{
return this._customThumbStyleName;
}
/**
* @private
*/
public function set customThumbStyleName(value:String):void
{
if(this._customThumbStyleName == value)
{
return;
}
this._customThumbStyleName = value;
this.invalidate(INVALIDATION_FLAG_THUMB_FACTORY);
}
/**
* @private
*/
protected var _thumbProperties:PropertyProxy;
/**
* An object that stores properties for the scroll bar's thumb, and the
* properties will be passed down to the thumb when the scroll bar
* validates. For a list of available properties, refer to
* feathers.controls.Button
.
*
* If the subcomponent has its own subcomponents, their properties
* can be set too, using attribute @
notation. For example,
* to set the skin on the thumb which is in a SimpleScrollBar
,
* which is in a List
, you can use the following syntax:
* list.verticalScrollBarProperties.@thumbProperties.defaultSkin = new Image(texture);
*
* Setting properties in a thumbFactory
function instead
* of using thumbProperties
will result in better
* performance.
*
* In the following example, the scroll bar's thumb properties
* are updated:
*
*
* scrollBar.thumbProperties.defaultSkin = new Image( upTexture );
* scrollBar.thumbProperties.downSkin = new Image( downTexture );
*
* @default null
*
* @see #thumbFactory
* @see feathers.controls.Button
*/
public function get thumbProperties():Object
{
if(!this._thumbProperties)
{
this._thumbProperties = new PropertyProxy(thumbProperties_onChange);
}
return this._thumbProperties;
}
/**
* @private
*/
public function set thumbProperties(value:Object):void
{
if(this._thumbProperties == value)
{
return;
}
if(!value)
{
value = new PropertyProxy();
}
if(!(value is PropertyProxy))
{
var newValue:PropertyProxy = new PropertyProxy();
for(var propertyName:String in value)
{
newValue[propertyName] = value[propertyName];
}
value = newValue;
}
if(this._thumbProperties)
{
this._thumbProperties.removeOnChangeCallback(thumbProperties_onChange);
}
this._thumbProperties = PropertyProxy(value);
if(this._thumbProperties)
{
this._thumbProperties.addOnChangeCallback(thumbProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _touchPointID:int = -1;
/**
* @private
*/
protected var _touchStartX:Number = NaN;
/**
* @private
*/
protected var _touchStartY:Number = NaN;
/**
* @private
*/
protected var _thumbStartX:Number = NaN;
/**
* @private
*/
protected var _thumbStartY:Number = NaN;
/**
* @private
*/
protected var _touchValue:Number;
/**
* @private
*/
override protected function initialize():void
{
if(!this.track)
{
this.track = new Quad(10, 10, 0xff00ff);
this.track.alpha = 0;
this.track.addEventListener(TouchEvent.TOUCH, track_touchHandler);
this.addChild(this.track);
}
if(this._value < this._minimum)
{
this.value = this._minimum;
}
else if(this._value > this._maximum)
{
this.value = this._maximum;
}
}
/**
* @private
*/
override protected function draw():void
{
var dataInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_DATA)
var stylesInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_STYLES);
var sizeInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_SIZE);
var stateInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_STATE);
var thumbFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_THUMB_FACTORY);
if(thumbFactoryInvalid)
{
this.createThumb();
}
if(thumbFactoryInvalid || stylesInvalid)
{
this.refreshThumbStyles();
}
if(dataInvalid || thumbFactoryInvalid || stateInvalid)
{
this.refreshEnabled();
}
sizeInvalid = this.autoSizeIfNeeded() || sizeInvalid;
this.layout();
}
/**
* If the component's dimensions have not been set explicitly, it will
* measure its content and determine an ideal size for itself. If the
* explicitWidth
or explicitHeight
member
* variables are set, those value will be used without additional
* measurement. If one is set, but not the other, the dimension with the
* explicit value will not be measured, but the other non-explicit
* dimension will still need measurement.
*
* Calls setSizeInternal()
to set up the
* actualWidth
and actualHeight
member
* variables used for layout.
*
* Meant for internal use, and subclasses may override this function
* with a custom implementation.
*/
protected function autoSizeIfNeeded():Boolean
{
if(this.thumbOriginalWidth !== this.thumbOriginalWidth ||
this.thumbOriginalHeight !== this.thumbOriginalHeight) //isNaN
{
if(this.thumb is IValidating)
{
IValidating(this.thumb).validate();
}
this.thumbOriginalWidth = this.thumb.width;
this.thumbOriginalHeight = this.thumb.height;
}
var needsWidth:Boolean = this._explicitWidth !== this._explicitWidth; //isNaN
var needsHeight:Boolean = this._explicitHeight !== this._explicitHeight; //isNaN
if(!needsWidth && !needsHeight)
{
return false;
}
var range:Number = this._maximum - this._minimum;
var adjustedPage:Number = this._page;
if(adjustedPage === 0)
{
//fall back to using step!
adjustedPage = this._step;
}
if(adjustedPage > range)
{
adjustedPage = range;
}
var newWidth:Number = this._explicitWidth;
var newHeight:Number = this._explicitHeight;
if(needsWidth)
{
if(this._direction == Direction.VERTICAL)
{
newWidth = this.thumbOriginalWidth;
}
else //horizontal
{
if(adjustedPage === 0)
{
newWidth = this.thumbOriginalWidth;
}
else
{
newWidth = this.thumbOriginalWidth * range / adjustedPage;
if(newWidth < this.thumbOriginalWidth)
{
newWidth = this.thumbOriginalWidth;
}
}
}
newWidth += this._paddingLeft + this._paddingRight;
}
if(needsHeight)
{
if(this._direction == Direction.VERTICAL)
{
if(adjustedPage === 0)
{
newHeight = this.thumbOriginalHeight;
}
else
{
newHeight = this.thumbOriginalHeight * range / adjustedPage;
if(newHeight < this.thumbOriginalHeight)
{
newHeight = this.thumbOriginalHeight;
}
}
}
else //horizontal
{
newHeight = this.thumbOriginalHeight;
}
newHeight += this._paddingTop + this._paddingBottom;
}
return this.setSizeInternal(newWidth, newHeight, false);
}
/**
* Creates and adds the thumb
sub-component and
* removes the old instance, if one exists.
*
* Meant for internal use, and subclasses may override this function
* with a custom implementation.
*
* @see #thumb
* @see #thumbFactory
* @see #customThumbStyleName
*/
protected function createThumb():void
{
if(this.thumb)
{
this.thumb.removeFromParent(true);
this.thumb = null;
}
var factory:Function = this._thumbFactory != null ? this._thumbFactory : defaultThumbFactory;
var thumbStyleName:String = this._customThumbStyleName != null ? this._customThumbStyleName : this.thumbStyleName;
var thumb:Button = Button(factory());
thumb.styleNameList.add(thumbStyleName);
thumb.isFocusEnabled = false;
thumb.keepDownStateOnRollOut = true;
thumb.addEventListener(TouchEvent.TOUCH, thumb_touchHandler);
this.addChild(thumb);
this.thumb = thumb;
}
/**
* @private
*/
protected function refreshThumbStyles():void
{
for(var propertyName:String in this._thumbProperties)
{
var propertyValue:Object = this._thumbProperties[propertyName];
this.thumb[propertyName] = propertyValue;
}
}
/**
* @private
*/
protected function refreshEnabled():void
{
if(this.thumb is IFeathersControl)
{
IFeathersControl(this.thumb).isEnabled = this._isEnabled && this._maximum > this._minimum;
}
}
/**
* @private
*/
protected function layout():void
{
this.track.width = this.actualWidth;
this.track.height = this.actualHeight;
var range:Number = this._maximum - this._minimum;
this.thumb.visible = range > 0;
if(!this.thumb.visible)
{
return;
}
//this will auto-size the thumb, if needed
if(this.thumb is IValidating)
{
IValidating(this.thumb).validate();
}
var contentWidth:Number = this.actualWidth - this._paddingLeft - this._paddingRight;
var contentHeight:Number = this.actualHeight - this._paddingTop - this._paddingBottom;
var adjustedPage:Number = this._page;
if(this._page == 0)
{
adjustedPage = this._step;
}
else if(adjustedPage > range)
{
adjustedPage = range;
}
var valueOffset:Number = 0;
if(this._value < this._minimum)
{
valueOffset = (this._minimum - this._value);
}
if(this._value > this._maximum)
{
valueOffset = (this._value - this._maximum);
}
if(this._direction == Direction.VERTICAL)
{
this.thumb.width = this.thumbOriginalWidth;
var thumbMinHeight:Number = this.thumbOriginalHeight;
if(this.thumb is IMeasureDisplayObject)
{
thumbMinHeight = IMeasureDisplayObject(this.thumb).minHeight;
}
var thumbHeight:Number = contentHeight * adjustedPage / range;
var heightOffset:Number = contentHeight - thumbHeight;
if(heightOffset > thumbHeight)
{
heightOffset = thumbHeight;
}
heightOffset *= valueOffset / (range * thumbHeight / contentHeight);
thumbHeight -= heightOffset;
if(thumbHeight < thumbMinHeight)
{
thumbHeight = thumbMinHeight;
}
this.thumb.height = thumbHeight;
this.thumb.x = this._paddingLeft + (this.actualWidth - this._paddingLeft - this._paddingRight - this.thumb.width) / 2;
var trackScrollableHeight:Number = contentHeight - this.thumb.height;
var thumbY:Number = trackScrollableHeight * (this._value - this._minimum) / range;
if(thumbY > trackScrollableHeight)
{
thumbY = trackScrollableHeight;
}
else if(thumbY < 0)
{
thumbY = 0;
}
this.thumb.y = this._paddingTop + thumbY;
}
else //horizontal
{
var thumbMinWidth:Number = this.thumbOriginalWidth;
if(this.thumb is IMeasureDisplayObject)
{
thumbMinWidth = IMeasureDisplayObject(this.thumb).minWidth;
}
var thumbWidth:Number = contentWidth * adjustedPage / range;
var widthOffset:Number = contentWidth - thumbWidth;
if(widthOffset > thumbWidth)
{
widthOffset = thumbWidth;
}
widthOffset *= valueOffset / (range * thumbWidth / contentWidth);
thumbWidth -= widthOffset;
if(thumbWidth < thumbMinWidth)
{
thumbWidth = thumbMinWidth;
}
this.thumb.width = thumbWidth;
this.thumb.height = this.thumbOriginalHeight;
var trackScrollableWidth:Number = contentWidth - this.thumb.width;
var thumbX:Number = trackScrollableWidth * (this._value - this._minimum) / range;
if(thumbX > trackScrollableWidth)
{
thumbX = trackScrollableWidth;
}
else if(thumbX < 0)
{
thumbX = 0;
}
this.thumb.x = this._paddingLeft + thumbX;
this.thumb.y = this._paddingTop + (this.actualHeight - this._paddingTop - this._paddingBottom - this.thumb.height) / 2;
}
//final validation to avoid juggler next frame issues
if(this.thumb is IValidating)
{
IValidating(this.thumb).validate();
}
}
/**
* @private
*/
protected function locationToValue(location:Point):Number
{
var percentage:Number = 0;
if(this._direction == Direction.VERTICAL)
{
var trackScrollableHeight:Number = this.actualHeight - this.thumb.height - this._paddingTop - this._paddingBottom;
if(trackScrollableHeight > 0)
{
var yOffset:Number = location.y - this._touchStartY - this._paddingTop;
var yPosition:Number = Math.min(Math.max(0, this._thumbStartY + yOffset), trackScrollableHeight);
percentage = yPosition / trackScrollableHeight;
}
}
else //horizontal
{
var trackScrollableWidth:Number = this.actualWidth - this.thumb.width - this._paddingLeft - this._paddingRight;
if(trackScrollableWidth > 0)
{
var xOffset:Number = location.x - this._touchStartX - this._paddingLeft;
var xPosition:Number = Math.min(Math.max(0, this._thumbStartX + xOffset), trackScrollableWidth);
percentage = xPosition / trackScrollableWidth;
}
}
return this._minimum + percentage * (this._maximum - this._minimum);
}
/**
* @private
*/
protected function adjustPage():void
{
var range:Number = this._maximum - this._minimum;
var adjustedPage:Number = this._page;
if(adjustedPage === 0)
{
adjustedPage = this._step;
}
if(adjustedPage > range)
{
adjustedPage = range;
}
if(this._touchValue < this._value)
{
var newValue:Number = Math.max(this._touchValue, this._value - adjustedPage);
if(this._step != 0 && newValue != this._maximum && newValue != this._minimum)
{
newValue = roundToNearest(newValue, this._step);
}
this.value = newValue;
}
else if(this._touchValue > this._value)
{
newValue = Math.min(this._touchValue, this._value + adjustedPage);
if(this._step != 0 && newValue != this._maximum && newValue != this._minimum)
{
newValue = roundToNearest(newValue, this._step);
}
this.value = newValue;
}
}
/**
* @private
*/
protected function startRepeatTimer(action:Function):void
{
this.currentRepeatAction = action;
if(this._repeatDelay > 0)
{
if(!this._repeatTimer)
{
this._repeatTimer = new Timer(this._repeatDelay * 1000);
this._repeatTimer.addEventListener(TimerEvent.TIMER, repeatTimer_timerHandler);
}
else
{
this._repeatTimer.reset();
this._repeatTimer.delay = this._repeatDelay * 1000;
}
this._repeatTimer.start();
}
}
/**
* @private
*/
protected function thumbProperties_onChange(proxy:PropertyProxy, name:Object):void
{
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected function removedFromStageHandler(event:Event):void
{
this._touchPointID = -1;
if(this._repeatTimer)
{
this._repeatTimer.stop();
}
}
/**
* @private
*/
protected function track_touchHandler(event:TouchEvent):void
{
if(!this._isEnabled)
{
this._touchPointID = -1;
return;
}
if(this._touchPointID >= 0)
{
var touch:Touch = event.getTouch(this.track, TouchPhase.ENDED, this._touchPointID);
if(!touch)
{
return;
}
this._touchPointID = -1;
this._repeatTimer.stop();
}
else
{
touch = event.getTouch(this.track, TouchPhase.BEGAN);
if(!touch)
{
return;
}
this._touchPointID = touch.id;
touch.getLocation(this, HELPER_POINT);
this._touchStartX = HELPER_POINT.x;
this._touchStartY = HELPER_POINT.y;
this._thumbStartX = HELPER_POINT.x;
this._thumbStartY = HELPER_POINT.y;
this._touchValue = this.locationToValue(HELPER_POINT);
this.adjustPage();
this.startRepeatTimer(this.adjustPage);
}
}
/**
* @private
*/
protected function thumb_touchHandler(event:TouchEvent):void
{
if(!this._isEnabled)
{
return;
}
if(this._touchPointID >= 0)
{
var touch:Touch = event.getTouch(this.thumb, null, this._touchPointID);
if(!touch)
{
return;
}
if(touch.phase == TouchPhase.MOVED)
{
touch.getLocation(this, HELPER_POINT);
var newValue:Number = this.locationToValue(HELPER_POINT);
if(this._step != 0 && newValue != this._maximum && newValue != this._minimum)
{
newValue = roundToNearest(newValue, this._step);
}
this.value = newValue;
}
else if(touch.phase == TouchPhase.ENDED)
{
this._touchPointID = -1;
this.isDragging = false;
if(!this.liveDragging)
{
this.dispatchEventWith(Event.CHANGE);
}
this.dispatchEventWith(FeathersEventType.END_INTERACTION);
}
}
else
{
touch = event.getTouch(this.thumb, TouchPhase.BEGAN);
if(!touch)
{
return;
}
touch.getLocation(this, HELPER_POINT);
this._touchPointID = touch.id;
this._thumbStartX = this.thumb.x;
this._thumbStartY = this.thumb.y;
this._touchStartX = HELPER_POINT.x;
this._touchStartY = HELPER_POINT.y;
this.isDragging = true;
this.dispatchEventWith(FeathersEventType.BEGIN_INTERACTION);
}
}
/**
* @private
*/
protected function repeatTimer_timerHandler(event:TimerEvent):void
{
if(this._repeatTimer.currentCount < 5)
{
return;
}
this.currentRepeatAction();
}
}
}