scaffold.libs_as.feathers.media.MuteToggleButton.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.media
{
import feathers.controls.ToggleButton;
import feathers.controls.popups.DropDownPopUpContentManager;
import feathers.controls.popups.IPopUpContentManager;
import feathers.core.PropertyProxy;
import feathers.events.MediaPlayerEventType;
import feathers.layout.Direction;
import feathers.skins.IStyleProvider;
import flash.media.SoundTransform;
import starling.events.Event;
import starling.events.EventDispatcher;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
/**
* Dispatched when the pop-up volume slider is opened.
*
* 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.OPEN
*/
[Event(name="open",type="starling.events.Event")]
/**
* Dispatched when the pop-up volume slider is closed.
*
* 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.CLOSE
*/
[Event(name="close",type="starling.events.Event")]
/**
* A specialized toggle button that controls whether a media player's volume
* is muted or not.
*
* @see ../../../help/sound-player.html How to use the Feathers SoundPlayer component
* @see ../../../help/video-player.html How to use the Feathers VideoPlayer component
*/
public class MuteToggleButton extends ToggleButton implements IMediaPlayerControl
{
/**
* @private
*/
protected static const INVALIDATION_FLAG_VOLUME_SLIDER_FACTORY:String = "volumeSliderFactory";
/**
* The default value added to the styleNameList
of the
* pop-up volume slider.
*
* @see feathers.core.FeathersControl#styleNameList
*/
public static const DEFAULT_CHILD_STYLE_NAME_VOLUME_SLIDER:String = "feathers-volume-toggle-button-volume-slider";
/**
* The default IStyleProvider
for all
* MuteToggleButton
components.
*
* @default null
* @see feathers.core.FeathersControl#styleProvider
*/
public static var globalStyleProvider:IStyleProvider;
/**
* @private
*/
protected static function defaultVolumeSliderFactory():VolumeSlider
{
var slider:VolumeSlider = new VolumeSlider();
slider.direction = Direction.VERTICAL;
return slider;
}
/**
* Constructor.
*/
public function MuteToggleButton()
{
super();
this.addEventListener(Event.CHANGE, muteToggleButton_changeHandler);
this.addEventListener(TouchEvent.TOUCH, muteToggleButton_touchHandler);
}
/**
* The default value added to the styleNameList
of the
* pop-up volume slider. This variable is protected
so that
* sub-classes can customize the list style name in their constructors
* instead of using the default style name defined by
* DEFAULT_CHILD_STYLE_NAME_VOLUME_SLIDER
.
*
* To customize the pop-up list name without subclassing, see
* customListStyleName
.
*
* @see #customListStyleName
* @see feathers.core.FeathersControl#styleNameList
*/
protected var volumeSliderStyleName:String = DEFAULT_CHILD_STYLE_NAME_VOLUME_SLIDER;
/**
* @private
*/
protected var slider:VolumeSlider;
/**
* @private
*/
protected var _oldVolume:Number;
/**
* @private
*/
protected var _ignoreChanges:Boolean = false;
/**
* @private
*/
protected var _touchPointID:int = -1;
/**
* @private
*/
protected var _popUpTouchPointID:int = -1;
/**
* @private
*/
override protected function get defaultStyleProvider():IStyleProvider
{
return MuteToggleButton.globalStyleProvider;
}
/**
* @private
*/
protected var _mediaPlayer:IAudioPlayer;
/**
* @inheritDoc
*/
public function get mediaPlayer():IMediaPlayer
{
return this._mediaPlayer;
}
/**
* @private
*/
public function set mediaPlayer(value:IMediaPlayer):void
{
if(this._mediaPlayer == value)
{
return;
}
this._mediaPlayer = value as IAudioPlayer;
this.refreshVolumeFromMediaPlayer();
if(this._mediaPlayer)
{
this._mediaPlayer.addEventListener(MediaPlayerEventType.SOUND_TRANSFORM_CHANGE, mediaPlayer_soundTransformChangeHandler);
}
this.invalidate(INVALIDATION_FLAG_DATA);
}
/**
* @private
*/
protected var _popUpContentManager:IPopUpContentManager;
/**
* A manager that handles the details of how to display the pop-up
* volume slider.
*
* In the following example, a pop-up content manager is provided:
*
*
* button.popUpContentManager = new CalloutPopUpContentManager();
*
* @default null
*/
public function get popUpContentManager():IPopUpContentManager
{
return this._popUpContentManager;
}
/**
* @private
*/
public function set popUpContentManager(value:IPopUpContentManager):void
{
if(this._popUpContentManager == value)
{
return;
}
if(this._popUpContentManager is EventDispatcher)
{
var dispatcher:EventDispatcher = EventDispatcher(this._popUpContentManager);
dispatcher.removeEventListener(Event.OPEN, popUpContentManager_openHandler);
dispatcher.removeEventListener(Event.CLOSE, popUpContentManager_closeHandler);
}
this._popUpContentManager = value;
if(this._popUpContentManager is EventDispatcher)
{
dispatcher = EventDispatcher(this._popUpContentManager);
dispatcher.addEventListener(Event.OPEN, popUpContentManager_openHandler);
dispatcher.addEventListener(Event.CLOSE, popUpContentManager_closeHandler);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _showVolumeSliderOnHover:Boolean = false;
/**
* Determines if a VolumeSlider
component is displayed as a
* pop-up when hovering over the toggle button. This property is
* intended for use on desktop platforms only. On mobile platforms,
* Starling does not dispatch events for hover, so the volume slider
* will not be shown.
*
* In the following example, showing the volume slider is enabled:
*
*
* button.showVolumeSliderOnHover = true;
*
* @default false
*
* @see feathers.media.VolumeSlider
*/
public function get showVolumeSliderOnHover():Boolean
{
return this._showVolumeSliderOnHover;
}
/**
* @private
*/
public function set showVolumeSliderOnHover(value:Boolean):void
{
if(this._showVolumeSliderOnHover == value)
{
return;
}
this._showVolumeSliderOnHover = value;
this.invalidate(INVALIDATION_FLAG_VOLUME_SLIDER_FACTORY);
}
/**
* @private
*/
protected var _volumeSliderFactory:Function;
/**
* A function used to generate the button's pop-up volume slider
* sub-component. The volume slider must be an instance of
* VolumeSlider
. This factory can be used to change
* properties on the volume slider 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
* volume slider.
*
* The function should have the following signature:
* function():VolumeSlider
*
* In the following example, a custom volume slider factory is passed
* to the button:
*
*
* button.volumeSliderFactory = function():VolumeSlider
* {
* var popUpSlider:VolumeSlider = new VolumeSlider();
* popUpSlider.direction = Direction.VERTICAL;
* return popUpSlider;
* };
*
* @default null
*
* @see feathers.media.VolumeSlider
* @see #showVolumeSliderOnHover
* @see #volumeSliderProperties
*/
public function get volumeSliderFactory():Function
{
return this._volumeSliderFactory;
}
/**
* @private
*/
public function set volumeSliderFactory(value:Function):void
{
if(this._volumeSliderFactory == value)
{
return;
}
this._volumeSliderFactory = value;
this.invalidate(INVALIDATION_FLAG_VOLUME_SLIDER_FACTORY);
}
/**
* @private
*/
protected var _customVolumeSliderStyleName:String;
/**
* A style name to add to the button's volume slider sub-component.
* Typically used by a theme to provide different styles to different
* buttons.
*
* In the following example, a custom volume slider style name is
* passed to the button:
*
*
* button.customVolumeSliderStyleName = "my-custom-volume-slider";
*
* In your theme, you can target this sub-component style name to provide
* different styles than the default:
*
*
* getStyleProviderForClass( VolumeSlider ).setFunctionForStyleName( "my-custom-volume-slider", setCustomVolumeSliderStyles );
*
* @default null
*
* @see #showVolumeSliderOnHover
* @see #DEFAULT_CHILD_STYLE_NAME_VOLUME_SLIDER
* @see feathers.core.FeathersControl#styleNameList
* @see #volumeSliderFactory
* @see #volumeSliderProperties
*/
public function get customVolumeSliderStyleName():String
{
return this._customVolumeSliderStyleName;
}
/**
* @private
*/
public function set customVolumeSliderStyleName(value:String):void
{
if(this._customVolumeSliderStyleName == value)
{
return;
}
this._customVolumeSliderStyleName = value;
this.invalidate(INVALIDATION_FLAG_VOLUME_SLIDER_FACTORY);
}
/**
* @private
*/
protected var _volumeSliderProperties:PropertyProxy;
/**
* An object that stores properties for the button's pop-up volume
* slider sub-component, and the properties will be passed down to the
* volume slider when the button validates. For a list of available
* properties, refer to feathers.media.VolumeSlider
.
*
* 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 volumeSliderFactory
function
* instead of using volumeSliderProperties
will result in
* better performance.
*
* In the following example, the volume slider properties are passed
* to the button:
*
*
* button.volumeSliderProperties.direction = Direction.VERTICAL;
*
* @default null
*
* @see #showVolumeSliderOnHover
* @see #volumeSliderFactory
* @see feathers.media.VolumeSlider
*/
public function get volumeSliderProperties():Object
{
if(!this._volumeSliderProperties)
{
this._volumeSliderProperties = new PropertyProxy(childProperties_onChange);
}
return this._volumeSliderProperties;
}
/**
* @private
*/
public function set volumeSliderProperties(value:Object):void
{
if(this._volumeSliderProperties == 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._volumeSliderProperties)
{
this._volumeSliderProperties.removeOnChangeCallback(childProperties_onChange);
}
this._volumeSliderProperties = PropertyProxy(value);
if(this._volumeSliderProperties)
{
this._volumeSliderProperties.addOnChangeCallback(childProperties_onChange);
}
this.invalidate(INVALIDATION_FLAG_STYLES);
}
/**
* @private
*/
protected var _isOpenPopUpPending:Boolean = false;
/**
* @private
*/
protected var _isClosePopUpPending:Boolean = false;
/**
* Opens the pop-up list, if it isn't already open.
*/
public function openPopUp():void
{
this._isClosePopUpPending = false;
if(this._popUpContentManager.isOpen)
{
return;
}
if(!this._isValidating && this.isInvalid())
{
this._isOpenPopUpPending = true;
return;
}
this._isOpenPopUpPending = false;
this._popUpContentManager.open(this.slider, this);
this.slider.validate();
this._popUpTouchPointID = -1;
this.slider.addEventListener(TouchEvent.TOUCH, volumeSlider_touchHandler);
}
/**
* Closes the pop-up list, if it is open.
*/
public function closePopUp():void
{
this._isOpenPopUpPending = false;
if(!this._popUpContentManager.isOpen)
{
return;
}
if(!this._isValidating && this.isInvalid())
{
this._isClosePopUpPending = true;
return;
}
this._isClosePopUpPending = false;
this.slider.validate();
//don't clean up anything from openList() in closeList(). The list
//may be closed by removing it from the PopUpManager, which would
//result in closeList() never being called.
//instead, clean up in the Event.REMOVED_FROM_STAGE listener.
this._popUpContentManager.close();
}
/**
* @inheritDoc
*/
override public function dispose():void
{
if(this.slider)
{
this.closePopUp();
this.slider.mediaPlayer = null;
this.slider.dispose();
this.slider = null;
}
if(this._popUpContentManager)
{
this._popUpContentManager.dispose();
this._popUpContentManager = null;
}
super.dispose();
}
/**
* @private
*/
override protected function initialize():void
{
if(!this._popUpContentManager)
{
var popUpContentManager:DropDownPopUpContentManager = new DropDownPopUpContentManager();
popUpContentManager.fitContentMinWidthToOrigin = false;
popUpContentManager.primaryDirection = DropDownPopUpContentManager.PRIMARY_DIRECTION_UP;
this.popUpContentManager = popUpContentManager;
}
}
/**
* @private
*/
override protected function draw():void
{
var stylesInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_STYLES);
var volumeSliderFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_VOLUME_SLIDER_FACTORY);
if(volumeSliderFactoryInvalid)
{
this.createVolumeSlider();
}
if(this.slider && (volumeSliderFactoryInvalid || stylesInvalid))
{
this.refreshVolumeSliderProperties();
}
super.draw();
this.handlePendingActions();
}
/**
* Creates and adds the list
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 #list
* @see #listFactory
* @see #customListStyleName
*/
protected function createVolumeSlider():void
{
if(this.slider)
{
this.slider.removeFromParent(false);
//disposing separately because the slider may not have a parent
this.slider.dispose();
this.slider = null;
}
if(!this._showVolumeSliderOnHover)
{
return;
}
var factory:Function = this._volumeSliderFactory != null ? this._volumeSliderFactory : defaultVolumeSliderFactory;
var volumeSliderStyleName:String = this._customVolumeSliderStyleName != null ? this._customVolumeSliderStyleName : this.volumeSliderStyleName;
this.slider = VolumeSlider(factory());
this.slider.focusOwner = this;
this.slider.styleNameList.add(volumeSliderStyleName);
}
/**
* @private
*/
protected function refreshVolumeSliderProperties():void
{
for(var propertyName:String in this._volumeSliderProperties)
{
var propertyValue:Object = this._volumeSliderProperties[propertyName];
this.slider[propertyName] = propertyValue;
}
this.slider.mediaPlayer = this._mediaPlayer;
}
/**
* @private
*/
protected function handlePendingActions():void
{
if(this._isOpenPopUpPending)
{
this.openPopUp();
}
if(this._isClosePopUpPending)
{
this.closePopUp();
}
}
/**
* @private
*/
protected function refreshVolumeFromMediaPlayer():void
{
var oldIgnoreChanges:Boolean = this._ignoreChanges;
this._ignoreChanges = true;
if(this._mediaPlayer)
{
this.isSelected = this._mediaPlayer.soundTransform.volume == 0;
}
else
{
this.isSelected = false;
}
this._ignoreChanges = oldIgnoreChanges;
}
/**
* @private
*/
protected function mediaPlayer_soundTransformChangeHandler(event:Event):void
{
this.refreshVolumeFromMediaPlayer();
}
/**
* @private
*/
protected function muteToggleButton_changeHandler(event:Event):void
{
if(this._ignoreChanges || !this._mediaPlayer)
{
return;
}
var soundTransform:SoundTransform = this._mediaPlayer.soundTransform;
if(this._isSelected)
{
this._oldVolume = soundTransform.volume;
if(this._oldVolume === 0)
{
this._oldVolume = 1;
}
soundTransform.volume = 0;
this._mediaPlayer.soundTransform = soundTransform;
}
else
{
var newVolume:Number = this._oldVolume;
if(newVolume !== newVolume) //isNaN
{
//volume was already zero, so we should fall back to some
//default value
newVolume = 1;
}
soundTransform.volume = newVolume;
this._mediaPlayer.soundTransform = soundTransform;
}
}
/**
* @private
*/
protected function muteToggleButton_touchHandler(event:TouchEvent):void
{
if(!this.slider)
{
this._touchPointID = -1;
return;
}
if(this._touchPointID >= 0)
{
var touch:Touch = event.getTouch(this, null, this._touchPointID);
if(touch)
{
return;
}
this._touchPointID = -1;
touch = event.getTouch(this.slider);
if(this._popUpTouchPointID < 0 && !touch)
{
this.closePopUp();
}
}
else
{
touch = event.getTouch(this, TouchPhase.HOVER);
if(!touch)
{
return;
}
this._touchPointID = touch.id;
this.openPopUp();
}
}
/**
* @private
*/
protected function volumeSlider_touchHandler(event:TouchEvent):void
{
if(this._popUpTouchPointID >= 0)
{
var touch:Touch = event.getTouch(this.slider, null, this._popUpTouchPointID);
if(touch)
{
return;
}
this._popUpTouchPointID = -1;
touch = event.getTouch(this);
if(this._touchPointID < 0 && !touch)
{
this.closePopUp();
}
}
else
{
touch = event.getTouch(this.slider, TouchPhase.HOVER);
if(!touch)
{
return;
}
this._popUpTouchPointID = touch.id;
}
}
/**
* @private
*/
protected function popUpContentManager_openHandler(event:Event):void
{
this.dispatchEventWith(Event.OPEN);
}
/**
* @private
*/
protected function popUpContentManager_closeHandler(event:Event):void
{
this.slider.removeEventListener(TouchEvent.TOUCH, volumeSlider_touchHandler);
this.dispatchEventWith(Event.CLOSE);
}
}
}