# SkinnablePopUpContainer - Functional and Design Specification
----
## Glossary
## Summary and Background
The primary purpose for this feature is to provide necessary infrastructure for the following use cases:
* ViewMenu
* Modal Dialog / Alert / Drop-down list
* Modal Shield with Busy indicator
Through prototyping, it was identified that for the mobile use cases the biggest hurdles are defining animations for pop-ups and pop-up / tear-down.
To solve these issues, the feature introduces the SkinnablePopUpContainer class:
* Allows customizing of animations on a pop-up through mxml skinning following the standard patters from the Spark skinning architecture.
* Facilitates pop-up / tear-down
## Usage Scenarios
### Out-of the Box Usage
Josh needs a simple pop-up message for Alert, but he wants to configure the text the way he likes it. He notices the SkinnablePopUpContainer and gives it a try:
<s:application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:declarations>
<s:skinnablepopupcontainer id="user-content-myAlert">
<s:panel title="Launching World Domination...">
<s:vgroup horizontalalign="center" paddingtop="20" gap="20" width="100%">
<s:label text="Server connection failed. Sorry!">
<s:button label="OK" click="myAlert.close()">
</s:button></s:label></s:vgroup>
</s:panel>
</s:skinnablepopupcontainer>
</fx:declarations>
<s:button label="Launch World Domination" click="myAlert.open(this, false)">
</s:button></s:application>
### Custom Dialog with Skinning, Animations
Mike needs a dialog for his app. He needs some title, a list of action items and some nice animations for opening and closing the dialog. Mike decides to go with the SkinnablePopUpContainer since it holds the promise to allow him to define the animations through skinning and states. First, Mike creates a mxml skin - MyDialogSkin.mxml. He arranges the visuals, hard-codes the title. He makes sure to add the "contentGroup" part.
<s:skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark">
<s:states>
<s:state name="closed">
<s:state name="normal">
<s:state name="disabled">
<s:state name="disabledAndClosed">
</s:state></s:state></s:state></s:state></s:states>
<s:rect id="user-content-bg" left="0" right="0" top="0" bottom="0" alpha.closed="0" alpha.normal="0.4">
<s:fill>
<s:solidcolor color="0">
</s:solidcolor></s:fill>
</s:rect>
<s:group id="user-content-chromeGroup" width="80%" height="80%" horizontalcenter="0" verticalcenter="0" alpha="1" alpha.closed="0">
<s:layout>
<s:verticallayout gap="0" horizontalalign="justify">
</s:verticallayout></s:layout>
<s:group id="user-content-topGroup">
<s:rect id="user-content-tbFill" left="0" right="0" top="0" bottom="1">
<s:rect id="user-content-tbHilite" left="0" right="0" top="0" bottom="0">
<s:rect id="user-content-tbDiv" left="0" right="0" height="1" bottom="0">
<s:label id="user-content-titleDisplay" maxdisplayedlines="1" text="Select Action" left="9" right="3" top="1" bottom="0" minheight="30" verticalalign="middle" textalign="start" fontweight="bold">
</s:label>
</s:rect></s:rect></s:rect></s:group>
<s:group id="user-content-contentGroup" width="100%" height="100%" minwidth="0" minheight="0">
</s:group>
</s:group>
</s:skin>
For the animations, Mike defines transitions between the "closed" and the "normal" states.
<s:transitions>
<s:transition fromstate="closed" tostate="normal">
<s:sequence>
<s:parallel>
<s:fade target="{chromeGroup}" duration="150">
<s:scale target="{chromeGroup}" duration="200" applychangespostlayout="true" autocentertransform="true" scalexfrom="0.8" scalexto="1" scaleyfrom="0.8" scaleyto="1">
</s:scale></s:fade></s:parallel>
<s:fade target="{bg}" startdelay="250" duration="1500" easer="{new Power(0.2, 2)}">
</s:fade></s:sequence>
</s:transition>
<s:transition fromstate="normal" tostate="closed">
<s:parallel>
<s:fade target="{chromeGroup}" duration="150">
<s:scale target="{chromeGroup}" duration="200" applychangespostlayout="true" autocentertransform="true" scalexfrom="1" scalexto="0.8" scaleyfrom="1" scaleyto="0.8">
<s:fade target="{bg}" duration="200" easer="{new Power(0.2, 2)}">
</s:fade></s:scale></s:fade></s:parallel>
</s:transition>
</s:transitions>
Mike then creates the dialog component MyDialog.mxml. He uses the "close()" method from the cancel button click handler to dismiss the dialog, but he hasn't yet hooked up the dialog to perform any actions:
<s:skinnablepopupcontainer xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" skinclass="skins.MyDialogSkin">
<s:rect width="100%" height="100%">
<s:fill>
<s:solidcolor color="0x444444">
</s:solidcolor></s:fill>
</s:rect>
<s:vgroup width="100%" height="100%">
<s:list id="user-content-list1" width="100%" height="100%">
<s:layout>
<s:verticallayout horizontalalign="justify" gap="-1">
</s:verticallayout></s:layout>
<s:dataprovider>
<s:arraycollection source="{<a href=" %26%2339%3bdelete%26%2339%3b%2c%20%26%2339%3bduplicate%26%2339%3b%2c%20%26%2339%3bshare%26%2339%3b"="">'Delete', 'Duplicate', 'Share'}"/>
</s:arraycollection></s:dataprovider>
</s:list>
<s:hgroup horizontalalign="center" width="100%">
<s:button label="Calcel" click="close()">
</s:button></s:hgroup>
</s:vgroup>
</s:skinnablepopupcontainer>
Now in his code that needs to bring up the dialog, Mike writes some code like this:
protected function showDialog():void
{
// Create
var dialog:MyDialog = new MyDialog();
// Size to the stage (the skin relies on that)
dialog.width = systemManager.screen.width;
dialog.height = systemManager.screen.height;
dialog.open(this, true /*modal*/);
}
The final step is to hook-up the dialog to perform some actions based on the user choice. Mike notices that there are default parameters to the "close()" method and the ASDoc suggest using them when committing some data/action. He uses these parameters to pass in the action choice to close() and adds an event listener in his view code (same code that brings up the dialog):
<s:skinnablepopupcontainer xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" skinclass="skins.MyDialogSkin">
...
<s:list id="user-content-list1" width="100%" height="100%" ="" change="close(true, list1.selectedItem);">
...
</s:list></s:skinnablepopupcontainer>
protected function showDialog():void
{
// Create
var dialog:MyDialog = new MyDialog();
// Size to the stage (the skin relies on that)
dialog.width = systemManager.screen.width;
dialog.height = systemManager.screen.height;
// add event listeners
dialog.addEventListener(PopUpEvent.CLOSE, dialogClose_handler);
dialog.open(this, true /*modal*/);
}
private function dialogClose_handler(event:PopUpEvent):void
{
MyDialog(event.target).removeEventListener(PopUpEvent.CLOSE,
dialogClose_handler);
if (!event.commit)
return;
switch (event.data as String)
{
case "Delete":
var index:int = list.selectedIndex;
list.dataProvider.removeItemAt(index);
list.selectedIndex = index - 1;
break;
case "Duplicate":
list.dataProvider.addItemAt(list.selectedItem,
list.selectedIndex);
break;
}
}
### ViewMenu Animations
Vito is a Flex developer and he needs to change the animations of the ViewMenu skin. Vito creates a copy of the default ViewMenu skin as a starting point of his custom skin. Corleone then updates the skin to include transitions between the open and the close states.
## Detailed Description
### New Component: SkinnablePopUpContainer
* class SkinnablePopUpContainer extends SkinnableContainer
* package: spark.components
* New property "isOpen" - whether the container is currently in its "open" state; defaults to false.
* defines "closed" and "disabledAndClosed" states in addition to super's "normal" and "disabled".
* Initially the container is in its "closed" state.
* open() / close() methods.
* Switch the component's state
* Dispatches "PopUpEvent.OPEN" and "PopUpEvent.CLOSE" events when animations for opening/closing are complete (dispatches immediately if no animations are defined). The CLOSE event has a seperate class since it is used to pass results from the PopUp to the owner / listener.
* Calling open() while the container is playing a close transition will cause a transition to the "normal" state, and no "close" event will be dispatched. The opposite is true as well - calling close() while in transition to "normal" will not dispatch the "open" event, but will close the container and dispatch the "close" event.New Event: StateChangeComplete
* The close() method has two optional parameters - "commit:Boolean" and "data:*" to facilitate developers to pass result from the PopUp to the listener / owner. See usage scenario and API sections for code examples and details.
State
enabled
isOpen
"closed"
true
false
"normal"
true
true
"disabled"
false
true/false
The default SkinnablePopUpContainer skins is just the same as the SkinnalbeContainerSkin with the added fade of 200ms between open and close states.
### New Event: stateChangeComplete, stateChangeInterrupted
Two new events "stateChangeComplete" and "stateChangeInterrupted" events are introduced for UIComponent.
The stateChangeComplete event is dispatched when the component switches state, after any state transition animations are finished playing. If there are no state transitions, the event is dispatched immediately. If the component switches to a third state, interrupting the state transition, the event doesn't dispatch for the interrupted state.
The stateChangeInterrupted event is dispatched when the component is interrupted (by switching to state C) while playing a transition from state A to state B.
The new events allows developers to execute code after transitioning to a state, including animations, completes. This is also what SkinnablePopUpContainer uses to dsipatch "open" and "close" events and to remove itself from the PopUpManager when close transition completes.
### Modifications to ViewMenu and ViewMenuSkin
* ViewMenu will extend SkinnableContainerBase
* The ViewMenu, being a mobile component, will define the following states:
State
enabled
isOpen
lanscape
"closed"
true
false
false
"normal"
true
true
false
"disabled"
false
true/false
false
"closedAndLandscape"
true
false
true
"normalAndLandscape"
true
true
true
"disabledAndLandscape"
false
true/false
true
* MobileApplicationBase will resize the ViewMenu to match its size (basically the size of the stage).
* The ViewMenuSkin will be modified to include transitions between the corresponding "closed" and "normal" states.
### Theme Styles
In the Mobile theme, the PopUpManager styles that define default animation/effects will be turned off.
global
{
...
modalTransparency : 0;
modalTransparencyBlur : 0;
modalTransparencyDuration: 0;
modalTransparencyColor : 0;
}
## API Description
/**
* Dispatched after the component has entered a new state and
* any state transition animation to that state has finished playing.
*
* The event is dispatched immediately if there's no transition playing
* between the states.
*
* If the component switches to a different state while the transition is
* underway, this event will be dispatched after the component completes the
* transition to that new state.
*
*
This event is only dispatched when there are one or more
* relevant listeners attached to the dispatching object.
*
* @eventType mx.events.FlexEvent.STATE_CHANGE_COMPLETE
*/
Event(name="stateChangeComplete", type="mx.events.FlexEvent")
/**
* Dispatched when a component interrupts a transition to its current
* state in order to switch to a new state.
*
*
This event is only dispatched when there are one or more
* relevant listeners attached to the dispatching object.
*
* @eventType mx.events.FlexEvent.STATE_CHANGE_INTERRUPTED
*
*/
Event(name="stateChangeInterrupted", type="mx.events.FlexEvent")
public class UIComponent
{
...
}
package spark.components
{
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched by the container when it's opened and ready for user interaction.
*
* This event is dispatched when the container switches from "closed" to "normal"
* state and the transition to that state completes.
*
* @eventType mx.events.PopUpEvent.OPEN
*
*/
Event(name="open", type="spark.events.PopUpEvent")
/**
* Dispatched by the container when it's closed.
*
* This event is dispatched when the container switches from "normal" to "closed"
* state and the transition to that state completes.
*
*
The event provides a mechanism to pass commit information from the container to
* a listener. One typical usage scenario is building a multiple-choice dialog with a
* cancel button. When a valid option is selected, the developer closes the dialog
* with a call to the SkinnablePopUpContainer.close() method, passing
* true to the commit parameter and optionally passing in
* any relevant data. When the SkinnablePopUpContainer has completed closing,
* it will dispatch this event. Then, in the listener, the developer can check
* the commit parameter and perform the appropriate action.
*
* @eventType mx.events.PopUpEvent.CLOSE
*
*/
Event(name="close", type="spark.events.PopUpEvent")
//--------------------------------------
// States
//--------------------------------------
/**
* Closed State
*
*/
SkinState("closed")
/**
* The SkinnablePopUpContainer class is a SkinnableContainer that
* also acts as a pop-up.
*
* The SkinnablePopUpContainer is initially in its "closed" state and
* when it's opened it will add itself as a pop-up to the PopUpManager
* and transition to its "normal" state.
*
*
When using SkinnablePopUpContainer the pop-up is defined in mxml
* as a SkinnablePopUpContainer component. To show the component create
* an instance and call the open() method. The developers
* are responsible for the sizing and positioning of the component.
* To close the component call the close() method.
* If the pop-up needs to pass data back to a handler, you can add a
* listener for the PopUp.CLOSE event and specify the data
* in the close() method.
*
* To define open and close animations, use a custom skin with transitions
* between the "closed" and "normal" states.
*
* @see spark.skins.spark.SkinnablePopUpContainerSkin
*/
public class SkinnablePopUpContainer extends SkinnableContainer
{
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*/
public function SkinnablePopUpContainer()
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// isOpen
//----------------------------------
Inspectable(category="General", defaultValue="false")
/**
* True when the container is open and is currently showing as a pop-up.
*
* @see #open
* @see #close
*
*/
public function get isOpen():Boolean
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Brings up the container as a pop-up and switches the state from
* "closed" to "normal", waits till any state transitions are finished
* playing and dispatches
FlexEvent.OPEN event.
*
* @param owner The owner of the container.
*
* @param modal Whether the container should be modal.
*
* @see #close
*
*/
public function open(owner:DisplayObjectContainer,
modal:Boolean = false):void
/**
* Changes the current state to "closed", waits till any state transitions
* are finished playing, dispatches a
PopUpEvent.CLOSE event
* and removes the container from the PopUpManager.
*
*
The PopUpEvent provides a mechanism to pass commit information from
* the container to a listener. One typical usage scenario is building a
* multiple-choice dialog with a cancel button. When a valid option is
* selected, the developer closes the dialog with a call to the
* SkinnablePopUpContainer.close() method, passing
* true to the commit parameter and optionally
* passing in any relevant data. When the SkinnablePopUpContainer
* has completed closing, it will dispatch this event. Then, in the listener,
* the developer can check the commit parameter and perform
* the appropriate actions.
*
* @param commit The value for the
commit property of the
*
PopUpEvent event.
* @param data The value for the
data property for the
*
PopUpEvent event.
*
* @see #open
*
*/
public function close(commit:Boolean = false, data:* = undefined):void
}
package spark.events
{
/**
* Dispatched by
SkinnablePopUpContainer to single
* opening or closing.
*
*
When closing, this event provides a mechanism to pass
* commit information to a listener. One typical usage scenario
* is building a multiple-choice dialog with a cancel button.
* When a valid option is selected, the developer closes the dialog
* with a call to the SkinnablePopUpContainer.close()
* method, passing true to the commit
* parameter and optionally passing in any relevant data.
* When the SkinnablePopUpContainer has completed
* closing, it will dispatch this event. Then, in the listener,
* the developer can check the commit parameter and
* perform the appropriate action.
*/
public class PopUpEvent extends Event
{
//--------------------------------------------------------------------------
//
// Class constants
//
//--------------------------------------------------------------------------
/**
* @eventType open
*/
public static const OPEN:String = "open";
/**
* @eventType close
*/
public static const CLOSE:String = "close";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @param type The event type; indicates the action that caused the event.
*
* @param bubbles Specifies whether the event can bubble up the display list
* hierarchy.
*
* @param cancelable Specifies whether the behavior associated with the event
* can be prevented.
*
* @param commit
Indicates whether the listener should commit the data
* from the PopUp. Only used with PopUpEvent.CLOSE.
*
* @param data
The PopUp data to commit. Only used with
* PopUpEvent.CLOSE.
*
*/
public function PopUpEvent(type:String,
bubbles:Boolean = false,
cancelable:Boolean = false,
commit:Boolean = false,
data:* = undefined)
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// commit
//----------------------------------
/**
* Whether the listener should commit the PopUp data.
* Only used with
PopUpEvent.CLOSE.
*
*/
public var commit:Boolean;
//----------------------------------
// data
//----------------------------------
/**
* The PopUp data to commit.
* Only used with
PopUpEvent.CLOSE.
*
*/
public var data:*;
}
}
public class FlexEvent extends Event
{
...
/**
* The
FlexEvent.STATE_CHANGE_COMPLETE constant defines the
* value of the
type property of the event object for a
*
stateChangeComplete event.
*
*
This event will only be dispatched when there are one or more relevant
* listeners attached to the dispatching object.
*
*
The properties of the event object have the following values:
*
*
*
*
*
*
*
| Property | Value |
|---|
bubbles | false |
cancelable | 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. |
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 stateChangeComplete
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public static const STATE_CHANGE_COMPLETE:String = "stateChangeComplete";
/**
* The
FlexEvent.STATE_CHANGE_INTERRUPTED constant defines the
* value of the
type property of the event object for a
*
stateChangeInterrupted event.
*
*
This event will only be dispatched when there are one or more relevant
* listeners attached to the dispatching object.
*
*
The properties of the event object have the following values:
*
*
*
*
*
*
*
| Property | Value |
|---|
bubbles | false |
cancelable | 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. |
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 stateChangeInterrupted
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public static const STATE_CHANGE_INTERRUPTED:String = "stateChangeInterrupted";
...
}
## B Features
_Detail features here that are to be considered for B Feature time._
## Examples and Usage
See usage scenarios section above.
## Additional Implementation Details
## Compiler Work
No
## Backwards Compatibility
N/A
## Accessibility
N/A
## Performance
N/A
## Globalization
N/A
## Localization
h3.
N/A
## Cross-Platform Considerations
N/A
## Issues and Recommendations
N/A
[[ include ref='flexsdk_rightnav' ]]
[[ include ref='site:open_commentlogin' ]]