blob: f5dcd2f7775f54ce035d49be63eb039303b4fe96 [file] [log] [blame]
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.apache.spark.components
{
import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Timer;
import mx.core.EdgeMetrics;
import mx.core.FlexGlobals;
import mx.core.IFactory;
import mx.core.IMXMLObject;
import mx.core.IVisualElement;
import mx.core.UIComponent;
import mx.managers.ISystemManager;
import org.apache.spark.components.supportClasses.PopUpLayoutPlacement;
import org.apache.spark.events.DialogEvent;
import org.apache.spark.utils.PopUpLayoutUtils;
import spark.core.IDisplayText;
//----------------------------------
// Events
//----------------------------------
/**
* Dispatched when the IDialog is opened.
*/
[Event(name="dialogOpen", type="org.apache.spark.events.DialogEvent")]
/**
* Dispatched when the IDialog is closed.
*/
[Event(name="dialogClose", type="org.apache.spark.events.DialogEvent")]
//----------------------------------
// Class
//----------------------------------
/**
* A PopUp is a user interface wrapper for creating IDialog and other popups
* seamlessly.
*
* <p>The <code>PopUp</code> class helps you <em>create</em> and <em>show</em>
* message views and dialogs.</p>
*
* @productversion 1.0
* @see PopUp#show()
* @see PopUp#makeText()
* @see PopUp#makeContent()
* @see PopUp#makeDialog()
* @see PopUp#makeAlert()
* @see PopUp#makeMessageAlert()
*/
public class PopUp implements IMXMLObject
{
//--------------------------------------------------------------------------
//
// Public :: Constants
//
//--------------------------------------------------------------------------
/**
* Show the <strong>view</strong> or <strong>text</strong> notification for
* a long period of time (<em>4 seconds</em>).
*
* @default 2000
*/
public static const LENGTH_SHORT:int = 2000;
/**
* Show the <strong>view</strong> or <strong>text</strong> notification for
* a short period of time (<em>2 seconds</em>).
*
* @default 4000
*/
public static const LENGTH_LONG:int = 4000;
/**
* Show the popup for an indefinite amount of time.
*/
public static const LENGTH_NONE:int = 0;
//--------------------------------------------------------------------------
//
// Private :: Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var mTimer:Timer;
/**
* @private
*/
private var mOwner:DisplayObjectContainer;
//--------------------------------------------------------------------------
//
// Public :: Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// modal
//----------------------------------
/**
* @private
*/
private var _modal:Boolean = false;
/**
* Whether the dialog being popped up will stop underlying screen interaction.
*/
public function get modal():Boolean
{
return _modal;
}
/**
* @private
*/
public function set modal(value:Boolean):void
{
_modal = value;
}
//----------------------------------
// duration
//----------------------------------
/**
* @private
*/
private var _duration:int = LENGTH_SHORT;
/**
* The length of time in milliseconds the notification is shown, the
* default is <code>LENGTH_SHORT</code>.
*
* @default LENGTH_SHORT
*/
public function get duration():int
{
return _duration;
}
/**
* @private
*/
public function set duration(value:int):void
{
_duration = Math.max(LENGTH_NONE, value);
}
//----------------------------------
// contentRenderer
//----------------------------------
/**
* @private
*/
private var _contentRenderer:IFactory;
/**
* The content renderer <code>IFactory</code> that can deffer creation until
* a later time.
*/
public function get contentRenderer():IFactory
{
return _contentRenderer;
}
/**
* @private
*/
public function set contentRenderer(value:IFactory):void
{
_contentRenderer = value;
}
//----------------------------------
// content
//----------------------------------
/**
* @private
*/
private var _content:IVisualElement;
/**
* The <code>IVisualElement</code> instance used as the view for the notification.
* asdasdasdasd
*/
public function get content():IVisualElement
{
return _content;
}
/**
* @private
*/
public function set content(value:IVisualElement):void
{
_content = value;
}
//----------------------------------
// dialog
//----------------------------------
/**
* @private
*/
private var _dialog:IDialog;
/**
* Returns the <code>IDialog</code> currently being managed by this
* <code>PopUp</code>.
*/
public function get dialog():IDialog
{
return _dialog;
}
/**
* @private
*/
public function set dialog(value:IDialog):void
{
setDialog(value);
}
/**
* @private
*/
protected function setDialog(value:IDialog):void
{
_dialog = value;
}
//----------------------------------
// text
//----------------------------------
/**
* @private
*/
private var _text:String;
/**
* The <code>String</code> text used for the notification.
* @see #makeText()
*/
public function get text():String
{
return _text;
}
/**
* @private
*/
public function set text(value:String):void
{
_text = value;
}
//----------------------------------
// layoutPlacement
//----------------------------------
/**
* @private
*/
private var _layoutPlacement:String = PopUpLayoutPlacement.CENTER;
/**
* The layout placement of the notification.
*
* @see org.apache.spark.components.supportClasses.PopUpLayoutPlacement
*/
public function get layoutPlacement():String
{
return _layoutPlacement;
}
/**
* @private
*/
public function set layoutPlacement(value:String):void
{
if (!PopUpLayoutPlacement.isValid(value))
return;
_layoutPlacement = value;
}
//----------------------------------
// layoutOffset
//----------------------------------
/**
* @private
*/
private var _layoutOffset:Point = new Point();
/**
* The layout offset (<code>x</code> and <code>y</code>) of
* the notification.
*/
public function get layoutOffset():Point
{
return _layoutOffset;
}
/**
* @private
*/
public function set layoutOffset(value:Point):void
{
_layoutOffset = value;
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*/
public function PopUp()
{
}
//--------------------------------------------------------------------------
//
// Public :: Methods
//
//--------------------------------------------------------------------------
/**
* Shows the <code>PopUp</code> notification on the <code>owner</code>.
*
* <p>If the <code>content</code> property has not been set, the method
* will use the <code>contentRenderer</code> factory to create the
* content.</p>
*
* <pre>
* var popup:PopUp = new PopUp();
* popup.duration = PopUp.LENGTH_LONG;
* popup.text = "Hello PopUp!";
* popup.show(this);
* </pre>
*
* @param owner The <code>DisplayObjectContianer</code> that will host the notification.
* Note that the PopUpManager uses this reference and the bounds of the owner will be used
* for positioning.
* @param modal A Boolean indicating whether the PopUp blocks background activity.
* @throws Error content or contentRenderer must be defined in PopUp
*/
public function show(owner:DisplayObjectContainer = null, modal:Boolean = false):void
{
mOwner = owner;
if (mOwner == null)
mOwner = DisplayObjectContainer(FlexGlobals.topLevelApplication);
var instance:IDialog = dialog;
// create the dialog that will hold the content
if (!instance)
{
instance = new TextDialog();
setDialog(instance);
}
content = createContent();
configureContent(content);
configureDialog(instance);
addDialogHandlers(instance);
startTimer();
openDialog(instance, mOwner, modal);
layoutDialog(instance, mOwner);
}
/**
* @private
*/
protected function startTimer():void
{
if (duration != LENGTH_NONE)
{
// create the timer
mTimer = new Timer(duration, 1);
mTimer.addEventListener(TimerEvent.TIMER_COMPLETE, timer_timeCompleteHandler);
mTimer.start();
}
}
/**
* Creates the <code>content</code> if the <code>content</code>
* is undefined.
*/
protected function createContent():IVisualElement
{
var instance:IVisualElement = content;
// - contentRenderer is null until a user sets it on this instance
// - if the user sets it that means it overrides the dialog.contentRenderer
// AND it overriddes the content of the dialog
// if the content is set on the PopUp, that instance overrides everything
if (contentRenderer != null)
instance = contentRenderer.newInstance() as IVisualElement;
return instance;
}
/**
* Configures the dialog's content component.
*/
protected function configureContent(content:IVisualElement):void
{
}
/**
* Configures the popup's dialog.
*
* @param dialog The dialog to configure.
*/
protected function configureDialog(dialog:IDialog):void
{
// if content was set on this instance, it will override content
// already set on the dialog
if (content != null)
dialog.content = content;
if (dialog is IDisplayText)
IDisplayText(dialog).text = text;
}
/**
* Opens the popup's dialog.
*
* @param dialog The dialog to open.
* @param owner The display object owner of the dialog.
* @param isModal Whether to dialog will be opened as a modal or non modal popup.
*/
protected function openDialog(dialog:IDialog, owner:DisplayObjectContainer, isModal:Boolean):void
{
// dimensions are not available until open() is called
dialog.open(owner, (modal) ? true : isModal);
}
/**
* Lays out the dialog, uses LayoutUtils and alyoutPlacement to determine
* the position of the popup.
*
* @param dialog The dialog being layed out.
* @param owner The display object owner of the dialog.
*/
protected function layoutDialog(dialog:IDialog, owner:DisplayObjectContainer):void
{
// the top and left need to be converted to owner coords, usually this is Stage
var parent:DisplayObjectContainer = owner.parent;
var edges:EdgeMetrics = new EdgeMetrics(layoutOffset.x, layoutOffset.y, layoutOffset.x, layoutOffset.y);
// to be able to center on the owner, the owners coords have to be
// converted to gloabl stage coords AND the edges
var point:Point = owner.parent.localToGlobal(new Point(owner.x, owner.y));
var rect:Rectangle = new Rectangle(point.x, point.y, owner.width, owner.height);
PopUpLayoutUtils.layoutElement2(dialog, layoutPlacement, rect, edges);
}
/**
* @private
*/
protected function addDialogHandlers(dialog:IDialog):void
{
dialog.addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
dialog.addEventListener(DialogEvent.DIALOG_CLOSE, dialogCloseHandler, false, 0, false);
// StageOrientationEvent.ORIENTATION_CHANGE
systemManager.getSandboxRoot().stage.addEventListener(
"orientationChange", stage_orientationChangeHandler, true);
}
/**
* @private
*/
protected function removedFromStageHandler(event:Event):void
{
removeDialogHandlers(IDialog(event.currentTarget));
}
/**
* @private
*/
protected function removeDialogHandlers(dialog:IDialog):void
{
dialog.removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
dialog.removeEventListener(DialogEvent.DIALOG_CLOSE, dialogCloseHandler);
// StageOrientationEvent.ORIENTATION_CHANGE
systemManager.getSandboxRoot().stage.removeEventListener(
"orientationChange", stage_orientationChangeHandler, true);
}
/**
* @private
*/
protected function dialogCloseHandler(event:DialogEvent):void
{
removeDialogHandlers(IDialog(event.currentTarget));
close();
}
/**
* @private
*/
public function initialized(document:Object, id:String):void
{
}
/**
* Cancels or <em>closes</em> the <code>PopUp</code> notification.
*
* <pre>popup.cancel();</pre>
*/
public function close():void
{
if (!dialog) // this dosn't feel right
return;
dialog.close();
setDialog(null);
}
//--------------------------------------------------------------------------
//
// Public Class :: Properties
//
//--------------------------------------------------------------------------
/**
* Creates a <code>PopUp</code> notification using the default
* <code>PopUpTextView</code> or the <code>contentRenderer</code> if defined.
*
* <pre>PopUp.makeText("Hello World", PopUp.LENGTH_LONG).show();</pre>
*
* @param text The <code>String</code> text that will appear in the notification.
* @param duration The time in milliseconds to show the notification.
* @return A new <code>PopUp</code> notification, Note; the <code>show()</code>
* method still needs be called.
* @see com.teotigraphix.ui.components.TextDialog
* @productversion 1.0
*/
public static function makeText(text:String, duration:int):PopUp
{
var instance:PopUp = new PopUp();
instance.setDialog(new TextDialog());
instance.duration = duration;
instance.text = text;
return instance;
}
/**
* Creates a <code>PopUp</code> notification using the default
* <code>content</code> or the <code>contentRenderer</code> if defined for
* the actual view.
*
* <pre>
* [Embed(source="assets/icons/android.png")]
* private var iconClass:Class;
*
* protected function makeContentButton_clickHandler(event:MouseEvent):void
* {
* var content:IconLabelRenderer = new IconLabelRenderer();
* content.icon = iconClass;
* content.text = "Hello makeContent()!";
* PopUp.makeContent(content, PopUp.LENGTH_SHORT).show(this);
* }</pre>
*
* @param content The <code>IVisualElement</code> to display as the
* notifications view.
* @param duration The time in milliseconds to show the notification.
* @return A new <code>PopUp</code> notification, Note; the <code>show()</code>
* method still needs be called.
* @see com.teotigraphix.ui.components.Dialog
* @productversion 1.0
*/
public static function makeContent(content:IVisualElement, duration:int):PopUp
{
/*
var instance:PopUp = new PopUp();
instance.duration = duration;
var dialog:Dialog = new Dialog();
dialog.content = content;
instance.setDialog(dialog);
return instance;
*/
return null;
}
/**
* Creates a <code>PopUp</code> notification using the default
* <code>Dialog</code>.
*
* The <code>contentRenderer</code>, <code>title</code> and <code>icon</code>
* are set on the new <code>IDialog</code> instance.
*
* <pre>
* protected function makeDialog_clickHandler(event:MouseEvent):void
* {
* PopUp.makeDialog(new ClassFactory(DeclartiveDialog), "Hello Dialog!", titleIconClass).show();
* }</pre>
*
* @param contentRenderer The <code>IFactory</code> used to create the
* <code>IDialog#content</code>.
* @param title The <code>String</code> title set on the <code>IDialog</code>.
* @param icon The visual icon set in the <code>IDialog</code> <code>titleBar</code>.
* @return A new <code>PopUp</code> notification, note; the <code>show()</code>
* method still needs be called.
* @productversion 1.0
*/
public static function makeDialog(contentRenderer:IFactory,
title:String = null,
icon:Object = null):PopUp
{
/*
var instance:PopUp = new PopUp();
instance.duration = LENGTH_NONE;
var dialog:Dialog = new Dialog();
dialog.contentRenderer = contentRenderer;
dialog.title = title;
dialog.icon = icon;
instance.setDialog(dialog);
return instance;
*/
return null;
}
/**
* Creates a <code>PopUp</code> <strong>modal</strong> <code>IDialog</code>
* using the default <code>AlertDialog</code>.
*
* <p>The <code>AlertDialog</code> will be populated with the
* <code>title</code>, <code>icon</code>, <code>message</code> and
* <code>messageIcon</code>. The Alert dialog by default will use
* the <strong>OK</strong> and <strong>Cancel</strong> buttons
* for dismissal.</p>
*
* <p>This method also uses the <code>TextIconView</code> for it's
* content renderer. To style the content target the <code>tg|TextIconView</code>
* type selector.</p>
*
* <p>The <code>PopUp.modal</code> property is set to true to
* disallow any user interaction below the <code>AlertDialog</code>.</p>
*
* [Embed(source="assets/common/android_normal.png")]
* private var iconClass:Class;
*
* <pre>
* [Embed(source="assets/common/emblem-important-160.png")]
* private var titleIconClass:Class;
*
* protected function makeAlert_clickHandler(event:MouseEvent):void
* {
* PopUp.makeAlert("Hello AlertDialog!", titleIconClass,
* "The alert message", iconClass).show();
* }
* </pre>
*
* @param title The <code>String</code> title set on the <code>IDialog</code>.
* @param icon The visual icon set in the <code>IDialog</code> <code>titleBar</code>.
* @param message The <code>String</code> message set on the <code>IDialog</code>.
* @param messageIcon The visual icon set in the <code>IDialog</code> <code>content</code>.
* @see com.teotigraphix.ui.components.supportClasses.TextIconView
* @productversion 1.0
*/
public static function makeAlert(title:String,
icon:Object = null,
message:String = null,
messageIcon:Object = null):PopUp
{
/*
var instance:PopUp = new PopUp();
instance.duration = LENGTH_NONE;
instance.modal = true;
var dialog:AlertDialog = new AlertDialog();
dialog.title = title;
dialog.icon = icon;
dialog.message = message;
dialog.messageIcon = messageIcon;
dialog.contentRenderer = new ClassFactory(TextIconView);
instance.setDialog(dialog);
return instance;
*/
return null;
}
/**
* Creates a <code>PopUp</code> message alert using the default
* <code>AlertDialog</code>.
*
* <p>The message alert dialog has no <code>titleBar</code>, just an
* <code>icon</code> and <code>message</code>.</p>
*
* <pre>
* [Embed(source="assets/common/android_normal.png")]
* private var iconClass:Class;
*
* protected function makeMessageAlert_clickHandler(event:MouseEvent):void
* {
* PopUp.makeMessageAlert("The alert message without\n" +
* "the titlebar", iconClass).show();
* }</pre>
*
* @param message The <code>String</code> message for the dialog.
* @param messageIcon The <code>Object</code> used for the <code>content</code>'s icon.
* @see com.teotigraphix.ui.components.supportClasses.TextIconView
* @productversion 1.0
*/
public static function makeMessageAlert(message:String, messageIcon:Object = null):PopUp
{
/*
var instance:PopUp = new PopUp();
instance.duration = LENGTH_NONE;
var dialog:AlertDialog = new AlertDialog();
dialog.message = message;
dialog.messageIcon = messageIcon;
dialog.contentRenderer = new ClassFactory(TextIconView);
instance.setDialog(dialog);
return instance;
*/
return null;
}
//--------------------------------------------------------------------------
//
// Protected :: Handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
protected function timer_timeCompleteHandler(event:TimerEvent):void
{
mTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, timer_timeCompleteHandler);
mTimer = null;
close();
}
//--------------------------------------------------------------------------
//
// Stage :: Handlers
//
//--------------------------------------------------------------------------
/**
* @private [StageOrientationEvent]
*/
protected function stage_orientationChangeHandler(event:Event):void
{
UIComponent(topLevelApplication).callLater(layoutDialog, [dialog, mOwner]);
}
//--------------------------------------------------------------------------
//
// Private :: Properties
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static function get topLevelApplication():DisplayObjectContainer
{
return DisplayObjectContainer(FlexGlobals.topLevelApplication);
}
/**
* @private
*/
private static function get systemManager():ISystemManager
{
return FlexGlobals.topLevelApplication.systemManager;
}
}
}