scaffold.libs_as.feathers.controls.ScrollBar.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.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 interacting with the scroll bar's thumb,
* track, or buttons.
*
* 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 interacting with the scroll bar's thumb,
* track, or buttons.
*
* 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 or by using step buttons. This is a desktop-centric
* scroll bar with many skinnable parts. For mobile, the
* SimpleScrollBar
is probably a better choice as it provides
* only the thumb to indicate position without all the extra chrome.
*
* The following example updates a list to use scroll bars:
*
*
* list.horizontalScrollBarFactory = function():IScrollBar
* {
* return new ScrollBar();
* };
* list.verticalScrollBarFactory = function():IScrollBar
* {
* return new ScrollBar();
* };
*
* @see ../../../help/scroll-bar.html How to use the Feathers ScrollBar component
* @see feathers.controls.SimpleScrollBar
*/
public class ScrollBar extends FeathersControl implements IDirectionalScrollBar
{
/**
* @private
*/
private static const HELPER_POINT:Point = new Point();
/**
* @private
*/
protected static const INVALIDATION_FLAG_THUMB_FACTORY:String = "thumbFactory";
/**
* @private
*/
protected static const INVALIDATION_FLAG_MINIMUM_TRACK_FACTORY:String = "minimumTrackFactory";
/**
* @private
*/
protected static const INVALIDATION_FLAG_MAXIMUM_TRACK_FACTORY:String = "maximumTrackFactory";
/**
* @private
*/
protected static const INVALIDATION_FLAG_DECREMENT_BUTTON_FACTORY:String = "decrementButtonFactory";
/**
* @private
*/
protected static const INVALIDATION_FLAG_INCREMENT_BUTTON_FACTORY:String = "incrementButtonFactory";
/**
* @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";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.TrackLayoutMode.SINGLE
.
*
* 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 TRACK_LAYOUT_MODE_SINGLE:String = "single";
/**
* @private
* DEPRECATED: Replaced by feathers.controls.TrackLayoutMode.SPLIT
.
*
* 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 TRACK_LAYOUT_MODE_MIN_MAX:String = "minMax";
/**
* The default value added to the styleNameList
of the minimum
* track.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_MINIMUM_TRACK:String = "feathers-scroll-bar-minimum-track";
/**
* The default value added to the styleNameList
of the maximum
* track.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_MAXIMUM_TRACK:String = "feathers-scroll-bar-maximum-track";
/**
* 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-scroll-bar-thumb";
/**
* The default value added to the styleNameList
of the decrement
* button.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_DECREMENT_BUTTON:String = "feathers-scroll-bar-decrement-button";
/**
* The default value added to the styleNameList
of the increment
* button.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_INCREMENT_BUTTON:String = "feathers-scroll-bar-increment-button";
/**
* The default IStyleProvider
for all ScrollBar
* components.
*
* @default null
* @see feathers.core.FeathersControl#styleProvider
*/
public static var globalStyleProvider:IStyleProvider;
/**
* @private
*/
protected static function defaultThumbFactory():Button
{
return new Button();
}
/**
* @private
*/
protected static function defaultMinimumTrackFactory():Button
{
return new Button();
}
/**
* @private
*/
protected static function defaultMaximumTrackFactory():Button
{
return new Button();
}
/**
* @private
*/
protected static function defaultDecrementButtonFactory():Button
{
return new Button();
}
/**
* @private
*/
protected static function defaultIncrementButtonFactory():Button
{
return new Button();
}
/**
* Constructor.
*/
public function ScrollBar()
{
super();
this.addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
}
/**
* The value added to the styleNameList
of the minimum
* track. This variable is protected
so that sub-classes
* can customize the minimum track style name in their constructors
* instead of using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_MINIMUM_TRACK
.
*
* To customize the minimum track style name without subclassing, see
* customMinimumTrackStyleName
.
*
* @see #customMinimumTrackStyleName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var minimumTrackStyleName:String = DEFAULT_CHILD_STYLE_NAME_MINIMUM_TRACK;
/**
* The value added to the styleNameList
of the maximum
* track. This variable is protected
so that sub-classes
* can customize the maximum track style name in their constructors
* instead of using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_MAXIMUM_TRACK
.
*
* To customize the maximum track style name without subclassing, see
* customMaximumTrackStyleName
.
*
* @see #customMaximumTrackStyleName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var maximumTrackStyleName:String = DEFAULT_CHILD_STYLE_NAME_MAXIMUM_TRACK;
/**
* 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;
/**
* The value added to the styleNameList
of the decrement
* button. This variable is protected
so that sub-classes
* can customize the decrement button style name in their constructors
* instead of using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_DECREMENT_BUTTON
.
*
* To customize the decrement button style name without subclassing,
* see customDecrementButtonStyleName
.
*
* @see #customDecrementButtonStyleName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var decrementButtonStyleName:String = DEFAULT_CHILD_STYLE_NAME_DECREMENT_BUTTON;
/**
* The value added to the styleNameList
of the increment
* button. This variable is protected
so that sub-classes
* can customize the increment button style name in their constructors
* instead of using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_INCREMENT_BUTTON
.
*
* To customize the increment button style name without subclassing,
* see customIncrementButtonName
.
*
* @see #customIncrementButtonStyleName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var incrementButtonStyleName:String = DEFAULT_CHILD_STYLE_NAME_INCREMENT_BUTTON;
/**
* @private
*/
protected var thumbOriginalWidth:Number = NaN;
/**
* @private
*/
protected var thumbOriginalHeight:Number = NaN;
/**
* @private
*/
protected var minimumTrackOriginalWidth:Number = NaN;
/**
* @private
*/
protected var minimumTrackOriginalHeight:Number = NaN;
/**
* @private
*/
protected var maximumTrackOriginalWidth:Number = NaN;
/**
* @private
*/
protected var maximumTrackOriginalHeight:Number = NaN;
/**
* The scroll bar's decrement button sub-component.
*
* For internal use in subclasses.
*
* @see #decrementButtonFactory
* @see #createDecrementButton()
*/
protected var decrementButton:Button;
/**
* The scroll bar's increment button sub-component.
*
* For internal use in subclasses.
*
* @see #incrementButtonFactory
* @see #createIncrementButton()
*/
protected var incrementButton:Button;
/**
* The scroll bar's thumb sub-component.
*
* For internal use in subclasses.
*
* @see #thumbFactory
* @see #createThumb()
*/
protected var thumb:DisplayObject;
/**
* The scroll bar's minimum track sub-component.
*
* For internal use in subclasses.
*
* @see #minimumTrackFactory
* @see #createMinimumTrack()
*/
protected var minimumTrack:DisplayObject;
/**
* The scroll bar's maximum track sub-component.
*
* For internal use in subclasses.
*
* @see #maximumTrackFactory
* @see #createMaximumTrack()
*/
protected var maximumTrack:DisplayObject;
/**
* @private
*/
override protected function get defaultStyleProvider():IStyleProvider
{
return ScrollBar.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_DECREMENT_BUTTON_FACTORY);
this.invalidate(INVALIDATION_FLAG_INCREMENT_BUTTON_FACTORY);
this.invalidate(INVALIDATION_FLAG_MINIMUM_TRACK_FACTORY);
this.invalidate(INVALIDATION_FLAG_MAXIMUM_TRACK_FACTORY);
this.invalidate(INVALIDATION_FLAG_THUMB_FACTORY);
}
/**
* @private
*/
protected var _value:Number = 0;
/**
* @inheritDoc
*
* @default 0
*
* @see #minimum
* @see #maximum
* @see #step
* @see #page
* @see #event:change
*/
public function get value():Number
{
return this._value;
}
/**
* @private
*/
public function set value(newValue:Number):void
{
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 content, not
* including the track(s).
*
* 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 content, not
* including the track(s).
*
* 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 content, not
* including the track(s).
*
* 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 content, not
* including the track(s).
*
* 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 _trackLayoutMode:String = TrackLayoutMode.SINGLE;
[Inspectable(type="String",enumeration="single,split")]
/**
* Determines how the minimum and maximum track skins are positioned and
* sized.
*
* In the following example, the scroll bar is given two tracks:
*
*
* scrollBar.trackLayoutMode = TrackLayoutMode.SPLIT;
*
* @default feathers.controls.TrackLayoutMode.SINGLE
*
* @see feathers.controls.TrackLayoutMode#SINGLE
* @see feathers.controls.TrackLayoutMode#SPLIT
*/
public function get trackLayoutMode():String
{
return this._trackLayoutMode;
}
/**
* @private
*/
public function set trackLayoutMode(value:String):void
{
if(value === TRACK_LAYOUT_MODE_MIN_MAX)
{
value = TrackLayoutMode.SPLIT;
}
if(this._trackLayoutMode == value)
{
return;
}
this._trackLayoutMode = value;
this.invalidate(INVALIDATION_FLAG_LAYOUT);
}
/**
* @private
*/
protected var _minimumTrackFactory:Function;
/**
* A function used to generate the scroll bar's minimum track
* sub-component. The minimum track must be an instance of
* Button
. This factory can be used to change properties on
* the minimum track 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 minimum track.
*
* The function should have the following signature:
* function():Button
*
* In the following example, a custom minimum track factory is passed
* to the scroll bar:
*
*
* scrollBar.minimumTrackFactory = function():Button
* {
* var track:Button = new Button();
* track.defaultSkin = new Image( upTexture );
* track.downSkin = new Image( downTexture );
* return track;
* };
*
* @default null
*
* @see feathers.controls.Button
* @see #minimumTrackProperties
*/
public function get minimumTrackFactory():Function
{
return this._minimumTrackFactory;
}
/**
* @private
*/
public function set minimumTrackFactory(value:Function):void
{
if(this._minimumTrackFactory == value)
{
return;
}
this._minimumTrackFactory = value;
this.invalidate(INVALIDATION_FLAG_MINIMUM_TRACK_FACTORY);
}
/**
* @private
*/
protected var _customMinimumTrackStyleName:String;
/**
* A style name to add to the scroll bar's minimum track sub-component.
* Typically used by a theme to provide different styles to different
* scroll bars.
*
* In the following example, a custom minimum track style name is
* passed to the scroll bar:
*
*
* scrollBar.customMinimumTrackStyleName = "my-custom-minimum-track";
*
* In your theme, you can target this sub-component style name to provide
* different styles than the default:
*
*
* getStyleProviderForClass( Button ).setFunctionForStyleName( "my-custom-minimum-track", setCustomMinimumTrackStyles );
*
* @default null
*
* @see #DEFAULT_CHILD_STYLE_NAME_MINIMUM_TRACK
* @see feathers.core.FeathersControl#styleNameList
* @see #minimumTrackFactory
* @see #minimumTrackProperties
*/
public function get customMinimumTrackStyleName():String
{
return this._customMinimumTrackStyleName;
}
/**
* @private
*/
public function set customMinimumTrackStyleName(value:String):void
{
if(this._customMinimumTrackStyleName == value)
{
return;
}
this._customMinimumTrackStyleName = value;
this.invalidate(INVALIDATION_FLAG_MINIMUM_TRACK_FACTORY);
}
/**
* @private
*/
protected var _minimumTrackProperties:PropertyProxy;
/**
* An object that stores properties for the scroll bar's "minimum"
* track, and the properties will be passed down to the "minimum" track 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 minimumTrackFactory
function
* instead of using minimumTrackProperties
will result in
* better performance.
*
* In the following example, the scroll bar's minimum track properties
* are updated:
*
*
* scrollBar.minimumTrackProperties.defaultSkin = new Image( upTexture );
* scrollBar.minimumTrackProperties.downSkin = new Image( downTexture );
*
* @default null
*
* @see #minimumTrackFactory
* @see feathers.controls.Button
*/
public function get minimumTrackProperties():Object
{
if(!this._minimumTrackProperties)
{
this._minimumTrackProperties = new PropertyProxy(minimumTrackProperties_onChange);
}
return this._minimumTrackProperties;
}
/**
* @private
*/
public function set minimumTrackProperties(value:Object):void
{
if(this._minimumTrackProperties == 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._minimumTrackProperties)
{
this._minimumTrackProperties.removeOnChangeCallback(minimumTrackProperties_onChange);
}
this._minimumTrackProperties = PropertyProxy(value);
if(this._minimumTrackProperties)
{
this._minimumTrackProperties.addOnChangeCallback(minimumTrackProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _maximumTrackFactory:Function;
/**
* A function used to generate the scroll bar's maximum track
* sub-component. The maximum track must be an instance of
* Button
. This factory can be used to change properties on
* the maximum track 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 maximum track.
*
* The function should have the following signature:
* function():Button
*
* In the following example, a custom maximum track factory is passed
* to the scroll bar:
*
*
* scrollBar.maximumTrackFactory = function():Button
* {
* var track:Button = new Button();
* track.defaultSkin = new Image( upTexture );
* track.downSkin = new Image( downTexture );
* return track;
* };
*
* @default null
*
* @see feathers.controls.Button
* @see #maximumTrackProperties
*/
public function get maximumTrackFactory():Function
{
return this._maximumTrackFactory;
}
/**
* @private
*/
public function set maximumTrackFactory(value:Function):void
{
if(this._maximumTrackFactory == value)
{
return;
}
this._maximumTrackFactory = value;
this.invalidate(INVALIDATION_FLAG_MAXIMUM_TRACK_FACTORY);
}
/**
* @private
*/
protected var _customMaximumTrackStyleName:String;
/**
* A style name to add to the scroll bar's maximum track sub-component.
* Typically used by a theme to provide different styles to different
* scroll bars.
*
* In the following example, a custom maximum track style name is
* passed to the scroll bar:
*
*
* scrollBar.customMaximumTrackStyleName = "my-custom-maximum-track";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( Button ).setFunctionForStyleName( "my-custom-maximum-track", setCustomMaximumTrackStyles );
*
* @default null
*
* @see #DEFAULT_CHILD_STYLE_NAME_MAXIMUM_TRACK
* @see feathers.core.FeathersControl#styleNameList
* @see #maximumTrackFactory
* @see #maximumTrackProperties
*/
public function get customMaximumTrackStyleName():String
{
return this._customMaximumTrackStyleName;
}
/**
* @private
*/
public function set customMaximumTrackStyleName(value:String):void
{
if(this._customMaximumTrackStyleName == value)
{
return;
}
this._customMaximumTrackStyleName = value;
this.invalidate(INVALIDATION_FLAG_MAXIMUM_TRACK_FACTORY);
}
/**
* @private
*/
protected var _maximumTrackProperties:PropertyProxy;
/**
* An object that stores properties for the scroll bar's "maximum"
* track, and the properties will be passed down to the "maximum" track 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 maximumTrackFactory
function
* instead of using maximumTrackProperties
will result in
* better performance.
*
* In the following example, the scroll bar's maximum track properties
* are updated:
*
*
* scrollBar.maximumTrackProperties.defaultSkin = new Image( upTexture );
* scrollBar.maximumTrackProperties.downSkin = new Image( downTexture );
*
* @default null
*
* @see #maximumTrackFactory
* @see feathers.controls.Button
*/
public function get maximumTrackProperties():Object
{
if(!this._maximumTrackProperties)
{
this._maximumTrackProperties = new PropertyProxy(maximumTrackProperties_onChange);
}
return this._maximumTrackProperties;
}
/**
* @private
*/
public function set maximumTrackProperties(value:Object):void
{
if(this._maximumTrackProperties == 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._maximumTrackProperties)
{
this._maximumTrackProperties.removeOnChangeCallback(maximumTrackProperties_onChange);
}
this._maximumTrackProperties = PropertyProxy(value);
if(this._maximumTrackProperties)
{
this._maximumTrackProperties.addOnChangeCallback(maximumTrackProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @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 _decrementButtonFactory:Function;
/**
* A function used to generate the scroll bar's decrement button
* sub-component. The decrement button must be an instance of
* Button
. This factory can be used to change properties on
* the decrement button 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 decrement button.
*
* The function should have the following signature:
* function():Button
*
* In the following example, a custom decrement button factory is passed
* to the scroll bar:
*
*
* scrollBar.decrementButtonFactory = function():Button
* {
* var button:Button = new Button();
* button.defaultSkin = new Image( upTexture );
* button.downSkin = new Image( downTexture );
* return button;
* };
*
* @default null
*
* @see feathers.controls.Button
* @see #decrementButtonProperties
*/
public function get decrementButtonFactory():Function
{
return this._decrementButtonFactory;
}
/**
* @private
*/
public function set decrementButtonFactory(value:Function):void
{
if(this._decrementButtonFactory == value)
{
return;
}
this._decrementButtonFactory = value;
this.invalidate(INVALIDATION_FLAG_DECREMENT_BUTTON_FACTORY);
}
/**
* @private
*/
protected var _customDecrementButtonStyleName:String;
/**
* A style name to add to the scroll bar's decrement button
* sub-component. Typically used by a theme to provide different styles
* to different scroll bars.
*
* In the following example, a custom decrement button style name is
* passed to the scroll bar:
*
*
* scrollBar.customDecrementButtonStyleName = "my-custom-decrement-button";
*
* In your theme, you can target this sub-component style name to
* provide different skins than the default style:
*
*
* getStyleProviderForClass( Button ).setFunctionForStyleName( "my-custom-decrement-button", setCustomDecrementButtonStyles );
*
* @default null
*
* @see #DEFAULT_CHILD_STYLE_NAME_DECREMENT_BUTTON
* @see feathers.core.FeathersControl#styleNameList
* @see #decrementButtonFactory
* @see #decrementButtonProperties
*/
public function get customDecrementButtonStyleName():String
{
return this._customDecrementButtonStyleName;
}
/**
* @private
*/
public function set customDecrementButtonStyleName(value:String):void
{
if(this._customDecrementButtonStyleName == value)
{
return;
}
this._customDecrementButtonStyleName = value;
this.invalidate(INVALIDATION_FLAG_DECREMENT_BUTTON_FACTORY);
}
/**
* @private
*/
protected var _decrementButtonProperties:PropertyProxy;
/**
* An object that stores properties for the scroll bar's decrement
* button, and the properties will be passed down to the decrement
* button 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 decrementButtonFactory
* function instead of using decrementButtonProperties
will
* result in better performance.
*
* In the following example, the scroll bar's decrement button properties
* are updated:
*
*
* scrollBar.decrementButtonProperties.defaultSkin = new Image( upTexture );
* scrollBar.decrementButtonProperties.downSkin = new Image( downTexture );
*
* @default null
*
* @see #decrementButtonFactory
* @see feathers.controls.Button
*/
public function get decrementButtonProperties():Object
{
if(!this._decrementButtonProperties)
{
this._decrementButtonProperties = new PropertyProxy(decrementButtonProperties_onChange);
}
return this._decrementButtonProperties;
}
/**
* @private
*/
public function set decrementButtonProperties(value:Object):void
{
if(this._decrementButtonProperties == 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._decrementButtonProperties)
{
this._decrementButtonProperties.removeOnChangeCallback(decrementButtonProperties_onChange);
}
this._decrementButtonProperties = PropertyProxy(value);
if(this._decrementButtonProperties)
{
this._decrementButtonProperties.addOnChangeCallback(decrementButtonProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _incrementButtonFactory:Function;
/**
* A function used to generate the scroll bar's increment button
* sub-component. The increment button must be an instance of
* Button
. This factory can be used to change properties on
* the increment button 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 increment button.
*
* The function should have the following signature:
* function():Button
*
* In the following example, a custom increment button factory is passed
* to the scroll bar:
*
*
* scrollBar.incrementButtonFactory = function():Button
* {
* var button:Button = new Button();
* button.defaultSkin = new Image( upTexture );
* button.downSkin = new Image( downTexture );
* return button;
* };
*
* @default null
*
* @see feathers.controls.Button
* @see #incrementButtonProperties
*/
public function get incrementButtonFactory():Function
{
return this._incrementButtonFactory;
}
/**
* @private
*/
public function set incrementButtonFactory(value:Function):void
{
if(this._incrementButtonFactory == value)
{
return;
}
this._incrementButtonFactory = value;
this.invalidate(INVALIDATION_FLAG_INCREMENT_BUTTON_FACTORY);
}
/**
* @private
*/
protected var _customIncrementButtonStyleName:String;
/**
* A style name to add to the scroll bar's increment button
* sub-component. Typically used by a theme to provide different styles
* to different scroll bars.
*
* In the following example, a custom increment button style name is
* passed to the scroll bar:
*
*
* scrollBar.customIncrementButtonStyleName = "my-custom-increment-button";
*
* In your theme, you can target this sub-component style name to
* provide different styles than the default:
*
*
* getStyleProviderForClass( Button ).setFunctionForStyleName( "my-custom-increment-button", setCustomIncrementButtonStyles );
*
* @default null
*
* @see #DEFAULT_CHILD_STYLE_NAME_INCREMENT_BUTTON
* @see feathers.core.FeathersControl#styleNameList
* @see #incrementButtonFactory
* @see #incrementButtonProperties
*/
public function get customIncrementButtonStyleName():String
{
return this._customIncrementButtonStyleName;
}
/**
* @private
*/
public function set customIncrementButtonStyleName(value:String):void
{
if(this._customIncrementButtonStyleName == value)
{
return;
}
this._customIncrementButtonStyleName = value;
this.invalidate(INVALIDATION_FLAG_INCREMENT_BUTTON_FACTORY);
}
/**
* @private
*/
protected var _incrementButtonProperties:PropertyProxy;
/**
* An object that stores properties for the scroll bar's increment
* button, and the properties will be passed down to the increment
* button 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 incrementButtonFactory
* function instead of using incrementButtonProperties
will
* result in better performance.
*
* In the following example, the scroll bar's increment button properties
* are updated:
*
*
* scrollBar.incrementButtonProperties.defaultSkin = new Image( upTexture );
* scrollBar.incrementButtonProperties.downSkin = new Image( downTexture );
*
* @default null
*
* @see #incrementButtonFactory
* @see feathers.controls.Button
*/
public function get incrementButtonProperties():Object
{
if(!this._incrementButtonProperties)
{
this._incrementButtonProperties = new PropertyProxy(incrementButtonProperties_onChange);
}
return this._incrementButtonProperties;
}
/**
* @private
*/
public function set incrementButtonProperties(value:Object):void
{
if(this._incrementButtonProperties == 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._incrementButtonProperties)
{
this._incrementButtonProperties.removeOnChangeCallback(incrementButtonProperties_onChange);
}
this._incrementButtonProperties = PropertyProxy(value);
if(this._incrementButtonProperties)
{
this._incrementButtonProperties.addOnChangeCallback(incrementButtonProperties_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._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 layoutInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_LAYOUT);
var thumbFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_THUMB_FACTORY);
var minimumTrackFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_MINIMUM_TRACK_FACTORY);
var maximumTrackFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_MAXIMUM_TRACK_FACTORY);
var incrementButtonFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_INCREMENT_BUTTON_FACTORY);
var decrementButtonFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_DECREMENT_BUTTON_FACTORY);
if(thumbFactoryInvalid)
{
this.createThumb();
}
if(minimumTrackFactoryInvalid)
{
this.createMinimumTrack();
}
if(maximumTrackFactoryInvalid || layoutInvalid)
{
this.createMaximumTrack();
}
if(decrementButtonFactoryInvalid)
{
this.createDecrementButton();
}
if(incrementButtonFactoryInvalid)
{
this.createIncrementButton();
}
if(thumbFactoryInvalid || stylesInvalid)
{
this.refreshThumbStyles();
}
if(minimumTrackFactoryInvalid || stylesInvalid)
{
this.refreshMinimumTrackStyles();
}
if((maximumTrackFactoryInvalid || stylesInvalid || layoutInvalid) && this.maximumTrack)
{
this.refreshMaximumTrackStyles();
}
if(decrementButtonFactoryInvalid || stylesInvalid)
{
this.refreshDecrementButtonStyles();
}
if(incrementButtonFactoryInvalid || stylesInvalid)
{
this.refreshIncrementButtonStyles();
}
if(dataInvalid || stateInvalid || thumbFactoryInvalid ||
minimumTrackFactoryInvalid || maximumTrackFactoryInvalid ||
decrementButtonFactoryInvalid || incrementButtonFactoryInvalid)
{
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.minimumTrackOriginalWidth !== this.minimumTrackOriginalWidth || //isNaN
this.minimumTrackOriginalHeight !== this.minimumTrackOriginalHeight) //isNaN
{
if(this.minimumTrack is IValidating)
{
IValidating(this.minimumTrack).validate();
}
this.minimumTrackOriginalWidth = this.minimumTrack.width;
this.minimumTrackOriginalHeight = this.minimumTrack.height;
}
if(this.maximumTrack)
{
if(this.maximumTrackOriginalWidth !== this.maximumTrackOriginalWidth || //isNaN
this.maximumTrackOriginalHeight !== this.maximumTrackOriginalHeight) //isNaN
{
if(this.maximumTrack is IValidating)
{
IValidating(this.maximumTrack).validate();
}
this.maximumTrackOriginalWidth = this.maximumTrack.width;
this.maximumTrackOriginalHeight = this.maximumTrack.height;
}
}
if(this.thumbOriginalWidth !== this.thumbOriginalWidth || //isNaN
this.thumbOriginalHeight !== this.thumbOriginalHeight) //isNaN
{
if(this.thumb is IValidating)
{
IValidating(this.thumb).validate();
}
this.thumbOriginalWidth = this.thumb.width;
this.thumbOriginalHeight = this.thumb.height;
}
this.decrementButton.validate();
this.incrementButton.validate();
var needsWidth:Boolean = this._explicitWidth !== this._explicitWidth; //isNaN
var needsHeight:Boolean = this._explicitHeight !== this._explicitHeight; //isNaN
if(!needsWidth && !needsHeight)
{
return false;
}
var newWidth:Number = this._explicitWidth;
var newHeight:Number = this._explicitHeight;
if(needsWidth)
{
if(this._direction == Direction.VERTICAL)
{
if(this.maximumTrack)
{
newWidth = Math.max(this.minimumTrackOriginalWidth, this.maximumTrackOriginalWidth);
}
else
{
newWidth = this.minimumTrackOriginalWidth;
}
}
else //horizontal
{
if(this.maximumTrack)
{
newWidth = Math.min(this.minimumTrackOriginalWidth, this.maximumTrackOriginalWidth) + this.thumb.width / 2;
}
else
{
newWidth = this.minimumTrackOriginalWidth;
}
newWidth += this.incrementButton.width + this.decrementButton.width;
}
}
if(needsHeight)
{
if(this._direction == Direction.VERTICAL)
{
if(this.maximumTrack)
{
newHeight = Math.min(this.minimumTrackOriginalHeight, this.maximumTrackOriginalHeight) + this.thumb.height / 2;
}
else
{
newHeight = this.minimumTrackOriginalHeight;
}
newHeight += this.incrementButton.height + this.decrementButton.height;
}
else //horizontal
{
if(this.maximumTrack)
{
newHeight = Math.max(this.minimumTrackOriginalHeight, this.maximumTrackOriginalHeight);
}
else
{
newHeight = this.minimumTrackOriginalHeight;
}
}
}
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.keepDownStateOnRollOut = true;
thumb.isFocusEnabled = false;
thumb.addEventListener(TouchEvent.TOUCH, thumb_touchHandler);
this.addChild(thumb);
this.thumb = thumb;
}
/**
* Creates and adds the minimumTrack
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 #minimumTrack
* @see #minimumTrackFactory
* @see #customMinimumTrackStyleName
*/
protected function createMinimumTrack():void
{
if(this.minimumTrack)
{
this.minimumTrack.removeFromParent(true);
this.minimumTrack = null;
}
var factory:Function = this._minimumTrackFactory != null ? this._minimumTrackFactory : defaultMinimumTrackFactory;
var minimumTrackStyleName:String = this._customMinimumTrackStyleName != null ? this._customMinimumTrackStyleName : this.minimumTrackStyleName;
var minimumTrack:Button = Button(factory());
minimumTrack.styleNameList.add(minimumTrackStyleName);
minimumTrack.keepDownStateOnRollOut = true;
minimumTrack.isFocusEnabled = false;
minimumTrack.addEventListener(TouchEvent.TOUCH, track_touchHandler);
this.addChildAt(minimumTrack, 0);
this.minimumTrack = minimumTrack;
}
/**
* Creates and adds the maximumTrack
sub-component and
* removes the old instance, if one exists. If the maximum track is not
* needed, it will not be created.
*
* Meant for internal use, and subclasses may override this function
* with a custom implementation.
*
* @see #maximumTrack
* @see #maximumTrackFactory
* @see #customMaximumTrackStyleName
*/
protected function createMaximumTrack():void
{
if(this._trackLayoutMode == TrackLayoutMode.SPLIT)
{
if(this.maximumTrack)
{
this.maximumTrack.removeFromParent(true);
this.maximumTrack = null;
}
var factory:Function = this._maximumTrackFactory != null ? this._maximumTrackFactory : defaultMaximumTrackFactory;
var maximumTrackStyleName:String = this._customMaximumTrackStyleName != null ? this._customMaximumTrackStyleName : this.maximumTrackStyleName;
var maximumTrack:Button = Button(factory());
maximumTrack.styleNameList.add(maximumTrackStyleName);
maximumTrack.keepDownStateOnRollOut = true;
maximumTrack.isFocusEnabled = false;
maximumTrack.addEventListener(TouchEvent.TOUCH, track_touchHandler);
this.addChildAt(maximumTrack, 1);
this.maximumTrack = maximumTrack;
}
else if(this.maximumTrack) //single
{
this.maximumTrack.removeFromParent(true);
this.maximumTrack = null;
}
}
/**
* Creates and adds the decrementButton
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 #decrementButton
* @see #decrementButtonFactory
* @see #customDecremenButtonStyleName
*/
protected function createDecrementButton():void
{
if(this.decrementButton)
{
this.decrementButton.removeFromParent(true);
this.decrementButton = null;
}
var factory:Function = this._decrementButtonFactory != null ? this._decrementButtonFactory : defaultDecrementButtonFactory;
var decrementButtonStyleName:String = this._customDecrementButtonStyleName != null ? this._customDecrementButtonStyleName : this.decrementButtonStyleName;
this.decrementButton = Button(factory());
this.decrementButton.styleNameList.add(decrementButtonStyleName);
this.decrementButton.keepDownStateOnRollOut = true;
this.decrementButton.isFocusEnabled = false;
this.decrementButton.addEventListener(TouchEvent.TOUCH, decrementButton_touchHandler);
this.addChild(this.decrementButton);
}
/**
* Creates and adds the incrementButton
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 #incrementButton
* @see #incrementButtonFactory
* @see #customIncrementButtonStyleName
*/
protected function createIncrementButton():void
{
if(this.incrementButton)
{
this.incrementButton.removeFromParent(true);
this.incrementButton = null;
}
var factory:Function = this._incrementButtonFactory != null ? this._incrementButtonFactory : defaultIncrementButtonFactory;
var incrementButtonStyleName:String = this._customIncrementButtonStyleName != null ? this._customIncrementButtonStyleName : this.incrementButtonStyleName;
this.incrementButton = Button(factory());
this.incrementButton.styleNameList.add(incrementButtonStyleName);
this.incrementButton.keepDownStateOnRollOut = true;
this.incrementButton.isFocusEnabled = false;
this.incrementButton.addEventListener(TouchEvent.TOUCH, incrementButton_touchHandler);
this.addChild(this.incrementButton);
}
/**
* @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 refreshMinimumTrackStyles():void
{
for(var propertyName:String in this._minimumTrackProperties)
{
var propertyValue:Object = this._minimumTrackProperties[propertyName];
this.minimumTrack[propertyName] = propertyValue;
}
}
/**
* @private
*/
protected function refreshMaximumTrackStyles():void
{
if(!this.maximumTrack)
{
return;
}
for(var propertyName:String in this._maximumTrackProperties)
{
var propertyValue:Object = this._maximumTrackProperties[propertyName];
this.maximumTrack[propertyName] = propertyValue;
}
}
/**
* @private
*/
protected function refreshDecrementButtonStyles():void
{
for(var propertyName:String in this._decrementButtonProperties)
{
var propertyValue:Object = this._decrementButtonProperties[propertyName];
this.decrementButton[propertyName] = propertyValue;
}
}
/**
* @private
*/
protected function refreshIncrementButtonStyles():void
{
for(var propertyName:String in this._incrementButtonProperties)
{
var propertyValue:Object = this._incrementButtonProperties[propertyName];
this.incrementButton[propertyName] = propertyValue;
}
}
/**
* @private
*/
protected function refreshEnabled():void
{
var isEnabled:Boolean = this._isEnabled && this._maximum > this._minimum;
if(this.thumb is IFeathersControl)
{
IFeathersControl(this.thumb).isEnabled = isEnabled;
}
if(this.minimumTrack is IFeathersControl)
{
IFeathersControl(this.minimumTrack).isEnabled = isEnabled;
}
if(this.maximumTrack is IFeathersControl)
{
IFeathersControl(this.maximumTrack).isEnabled = isEnabled;
}
this.decrementButton.isEnabled = isEnabled;
this.incrementButton.isEnabled = isEnabled;
}
/**
* @private
*/
protected function layout():void
{
this.layoutStepButtons();
this.layoutThumb();
if(this._trackLayoutMode == TrackLayoutMode.SPLIT)
{
this.layoutTrackWithMinMax();
}
else //single
{
this.layoutTrackWithSingle();
}
}
/**
* @private
*/
protected function layoutStepButtons():void
{
if(this._direction == Direction.VERTICAL)
{
this.decrementButton.x = (this.actualWidth - this.decrementButton.width) / 2;
this.decrementButton.y = 0;
this.incrementButton.x = (this.actualWidth - this.incrementButton.width) / 2;
this.incrementButton.y = this.actualHeight - this.incrementButton.height;
}
else
{
this.decrementButton.x = 0;
this.decrementButton.y = (this.actualHeight - this.decrementButton.height) / 2;
this.incrementButton.x = this.actualWidth - this.incrementButton.width;
this.incrementButton.y = (this.actualHeight - this.incrementButton.height) / 2;
}
var showButtons:Boolean = this._maximum != this._minimum;
this.decrementButton.visible = showButtons;
this.incrementButton.visible = showButtons;
}
/**
* @private
*/
protected function layoutThumb():void
{
var range:Number = this._maximum - this._minimum;
this.thumb.visible = range > 0 && range < Number.POSITIVE_INFINITY && this._isEnabled;
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;
}
if(adjustedPage > range)
{
adjustedPage = range;
}
if(this._direction == Direction.VERTICAL)
{
contentHeight -= (this.decrementButton.height + this.incrementButton.height);
var thumbMinHeight:Number = this.thumbOriginalHeight;
if(this.thumb is IMeasureDisplayObject)
{
thumbMinHeight = IMeasureDisplayObject(this.thumb).minHeight;
}
this.thumb.width = this.thumbOriginalWidth;
this.thumb.height = Math.max(thumbMinHeight, contentHeight * adjustedPage / range);
var trackScrollableHeight:Number = contentHeight - this.thumb.height;
this.thumb.x = this._paddingLeft + (this.actualWidth - this._paddingLeft - this._paddingRight - this.thumb.width) / 2;
this.thumb.y = this.decrementButton.height + this._paddingTop + Math.max(0, Math.min(trackScrollableHeight, trackScrollableHeight * (this._value - this._minimum) / range));
}
else //horizontal
{
contentWidth -= (this.decrementButton.width + this.decrementButton.width);
var thumbMinWidth:Number = this.thumbOriginalWidth;
if(this.thumb is IMeasureDisplayObject)
{
thumbMinWidth = IMeasureDisplayObject(this.thumb).minWidth;
}
this.thumb.width = Math.max(thumbMinWidth, contentWidth * adjustedPage / range);
this.thumb.height = this.thumbOriginalHeight;
var trackScrollableWidth:Number = contentWidth - this.thumb.width;
this.thumb.x = this.decrementButton.width + this._paddingLeft + Math.max(0, Math.min(trackScrollableWidth, trackScrollableWidth * (this._value - this._minimum) / range));
this.thumb.y = this._paddingTop + (this.actualHeight - this._paddingTop - this._paddingBottom - this.thumb.height) / 2;
}
}
/**
* @private
*/
protected function layoutTrackWithMinMax():void
{
var range:Number = this._maximum - this._minimum;
this.minimumTrack.touchable = range > 0 && range < Number.POSITIVE_INFINITY;
if(this.maximumTrack)
{
this.maximumTrack.touchable = range > 0 && range < Number.POSITIVE_INFINITY;
}
var showButtons:Boolean = this._maximum != this._minimum;
if(this._direction == Direction.VERTICAL)
{
this.minimumTrack.x = 0;
if(showButtons)
{
this.minimumTrack.y = this.decrementButton.height;
}
else
{
this.minimumTrack.y = 0;
}
this.minimumTrack.width = this.actualWidth;
this.minimumTrack.height = (this.thumb.y + this.thumb.height / 2) - this.minimumTrack.y;
this.maximumTrack.x = 0;
this.maximumTrack.y = this.minimumTrack.y + this.minimumTrack.height;
this.maximumTrack.width = this.actualWidth;
if(showButtons)
{
this.maximumTrack.height = this.actualHeight - this.incrementButton.height - this.maximumTrack.y;
}
else
{
this.maximumTrack.height = this.actualHeight - this.maximumTrack.y;
}
}
else //horizontal
{
if(showButtons)
{
this.minimumTrack.x = this.decrementButton.width;
}
else
{
this.minimumTrack.x = 0;
}
this.minimumTrack.y = 0;
this.minimumTrack.width = (this.thumb.x + this.thumb.width / 2) - this.minimumTrack.x;
this.minimumTrack.height = this.actualHeight;
this.maximumTrack.x = this.minimumTrack.x + this.minimumTrack.width;
this.maximumTrack.y = 0;
if(showButtons)
{
this.maximumTrack.width = this.actualWidth - this.incrementButton.width - this.maximumTrack.x;
}
else
{
this.maximumTrack.width = this.actualWidth - this.maximumTrack.x;
}
this.maximumTrack.height = this.actualHeight;
}
//final validation to avoid juggler next frame issues
if(this.minimumTrack is IValidating)
{
IValidating(this.minimumTrack).validate();
}
if(this.maximumTrack is IValidating)
{
IValidating(this.maximumTrack).validate();
}
}
/**
* @private
*/
protected function layoutTrackWithSingle():void
{
var range:Number = this._maximum - this._minimum;
this.minimumTrack.touchable = range > 0 && range < Number.POSITIVE_INFINITY;
var showButtons:Boolean = this._maximum != this._minimum;
if(this._direction == Direction.VERTICAL)
{
this.minimumTrack.x = 0;
if(showButtons)
{
this.minimumTrack.y = this.decrementButton.height;
}
else
{
this.minimumTrack.y = 0;
}
this.minimumTrack.width = this.actualWidth;
if(showButtons)
{
this.minimumTrack.height = this.actualHeight - this.minimumTrack.y - this.incrementButton.height;
}
else
{
this.minimumTrack.height = this.actualHeight - this.minimumTrack.y;
}
}
else //horizontal
{
if(showButtons)
{
this.minimumTrack.x = this.decrementButton.width;
}
else
{
this.minimumTrack.x = 0;
}
this.minimumTrack.y = 0;
if(showButtons)
{
this.minimumTrack.width = this.actualWidth - this.minimumTrack.x - this.incrementButton.width;
}
else
{
this.minimumTrack.width = this.actualWidth - this.minimumTrack.x;
}
this.minimumTrack.height = this.actualHeight;
}
//final validation to avoid juggler next frame issues
if(this.minimumTrack is IValidating)
{
IValidating(this.minimumTrack).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.decrementButton.height - this.incrementButton.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 - this.decrementButton.height), trackScrollableHeight);
percentage = yPosition / trackScrollableHeight;
}
}
else //horizontal
{
var trackScrollableWidth:Number = this.actualWidth - this.thumb.width - this.decrementButton.width - this.incrementButton.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 - this.decrementButton.width), trackScrollableWidth);
percentage = xPosition / trackScrollableWidth;
}
}
return this._minimum + percentage * (this._maximum - this._minimum);
}
/**
* @private
*/
protected function decrement():void
{
this.value -= this._step;
}
/**
* @private
*/
protected function increment():void
{
this.value += this._step;
}
/**
* @private
*/
protected function adjustPage():void
{
var range:Number = this._maximum - this._minimum;
var adjustedPage:Number = this._page;
if(this._page == 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 minimumTrackProperties_onChange(proxy:PropertyProxy, name:Object):void
{
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected function maximumTrackProperties_onChange(proxy:PropertyProxy, name:Object):void
{
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected function decrementButtonProperties_onChange(proxy:PropertyProxy, name:Object):void
{
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected function incrementButtonProperties_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;
}
var track:DisplayObject = DisplayObject(event.currentTarget);
if(this._touchPointID >= 0)
{
var touch:Touch = event.getTouch(track, TouchPhase.ENDED, this._touchPointID);
if(!touch)
{
return;
}
this._touchPointID = -1;
this._repeatTimer.stop();
this.dispatchEventWith(FeathersEventType.END_INTERACTION);
}
else
{
touch = event.getTouch(track, TouchPhase.BEGAN);
if(!touch)
{
return;
}
this._touchPointID = touch.id;
this.dispatchEventWith(FeathersEventType.BEGIN_INTERACTION);
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)
{
this._touchPointID = -1;
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 decrementButton_touchHandler(event:TouchEvent):void
{
if(!this._isEnabled)
{
this._touchPointID = -1;
return;
}
if(this._touchPointID >= 0)
{
var touch:Touch = event.getTouch(this.decrementButton, TouchPhase.ENDED, this._touchPointID);
if(!touch)
{
return;
}
this._touchPointID = -1;
this._repeatTimer.stop();
this.dispatchEventWith(FeathersEventType.END_INTERACTION);
}
else //if we get here, we don't have a saved touch ID yet
{
touch = event.getTouch(this.decrementButton, TouchPhase.BEGAN);
if(!touch)
{
return;
}
this._touchPointID = touch.id;
this.dispatchEventWith(FeathersEventType.BEGIN_INTERACTION);
this.decrement();
this.startRepeatTimer(this.decrement);
}
}
/**
* @private
*/
protected function incrementButton_touchHandler(event:TouchEvent):void
{
if(!this._isEnabled)
{
this._touchPointID = -1;
return;
}
if(this._touchPointID >= 0)
{
var touch:Touch = event.getTouch(this.incrementButton, TouchPhase.ENDED, this._touchPointID);
if(!touch)
{
return;
}
this._touchPointID = -1;
this._repeatTimer.stop();
this.dispatchEventWith(FeathersEventType.END_INTERACTION);
}
else //if we get here, we don't have a saved touch ID yet
{
touch = event.getTouch(this.incrementButton, TouchPhase.BEGAN);
if(!touch)
{
return;
}
this._touchPointID = touch.id;
this.dispatchEventWith(FeathersEventType.BEGIN_INTERACTION);
this.increment();
this.startRepeatTimer(this.increment);
}
}
/**
* @private
*/
protected function repeatTimer_timerHandler(event:TimerEvent):void
{
if(this._repeatTimer.currentCount < 5)
{
return;
}
this.currentRepeatAction();
}
}
}