26fe6018ab
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@147 ed5b42cb-e716-0410-a449-f6a68f950b19
190 lines
5.2 KiB
ActionScript
190 lines
5.2 KiB
ActionScript
package com.threerings.flex {
|
|
|
|
import flash.display.DisplayObject;
|
|
import flash.display.DisplayObjectContainer;
|
|
|
|
import flash.geom.Rectangle;
|
|
|
|
import mx.controls.Menu;
|
|
import mx.core.mx_internal;
|
|
import mx.core.Application;
|
|
import mx.core.ScrollPolicy;
|
|
import mx.events.MenuEvent;
|
|
|
|
import com.dougmccune.controls.ScrollableArrowMenu;
|
|
|
|
import com.threerings.util.CommandEvent;
|
|
|
|
use namespace mx_internal;
|
|
|
|
/**
|
|
* A pretty standard menu that can submit CommandEvents if menu items
|
|
* have "command" and possibly "arg" properties. Commands are submitted to
|
|
* controllers for processing. Alternatively, you may specify
|
|
* "callback" properties that specify a function closure to call, with the
|
|
* "arg" property containing either a single arg or an array of args.
|
|
*
|
|
* Example dataProvider array:
|
|
* [ { label: "Go home", icon: homeIconClass,
|
|
* command: Controller.GO_HOME, arg: homeId },
|
|
* { type: "separator"},
|
|
{ label: "Crazytown", callback: setCrazy, arg: [ true, false ] },
|
|
* { label: "Other places", children: subMenuArray }
|
|
* ];
|
|
*
|
|
* See "Defining menu structure and data" in the Flex manual for the
|
|
* full list.
|
|
*/
|
|
public class CommandMenu extends ScrollableArrowMenu
|
|
{
|
|
public function CommandMenu ()
|
|
{
|
|
super();
|
|
verticalScrollPolicy = ScrollPolicy.OFF;
|
|
|
|
addEventListener(MenuEvent.ITEM_CLICK, itemClicked);
|
|
}
|
|
|
|
/**
|
|
* Factory method to create a command menu.
|
|
*
|
|
* @param items an array of menu items.
|
|
*/
|
|
public static function createMenu (items :Array) :CommandMenu
|
|
{
|
|
var menu :CommandMenu = new CommandMenu();
|
|
menu.owner = DisplayObjectContainer(Application.application);
|
|
menu.tabEnabled = false;
|
|
menu.showRoot = true;
|
|
Menu.popUpMenu(menu, null, items);
|
|
return menu;
|
|
}
|
|
|
|
/**
|
|
* Actually pop up the menu. This can be used instead of show().
|
|
*/
|
|
public function popUp (
|
|
trigger :DisplayObject, popUpwards :Boolean = false) :void
|
|
{
|
|
var r :Rectangle = trigger.getBounds(trigger.stage);
|
|
|
|
if (popUpwards) {
|
|
show(r.x, 0);
|
|
// then, reposition the y once we know our size
|
|
y = r.y - getExplicitOrMeasuredHeight();
|
|
|
|
} else {
|
|
// position it below the trigger
|
|
show(r.x, r.y + r.height);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Just like our superclass's show(), except that when invoked
|
|
* with no args, causes the menu to show at the current mouse location
|
|
* instead of the top-left corner of the application.
|
|
*/
|
|
override public function show (xShow :Object = null, yShow :Object = null) :void
|
|
{
|
|
if (xShow == null) {
|
|
xShow = DisplayObject(Application.application).mouseX;
|
|
}
|
|
if (yShow == null) {
|
|
yShow = DisplayObject(Application.application).mouseY;
|
|
}
|
|
super.show(xShow, yShow);
|
|
}
|
|
|
|
/**
|
|
* Callback for MenuEvent.ITEM_CLICK.
|
|
*/
|
|
protected function itemClicked (event :MenuEvent) :void
|
|
{
|
|
var arg :Object = getItemArgument(event.item);
|
|
var cmd :String = getItemCommand(event.item);
|
|
var fn :Function;
|
|
if (cmd == null) {
|
|
fn = getItemCallback(event.item);
|
|
}
|
|
if (cmd != null || fn != null) {
|
|
event.stopImmediatePropagation();
|
|
if (cmd != null) {
|
|
CommandEvent.dispatch(mx_internal::parentDisplayObject, cmd, arg);
|
|
|
|
} else {
|
|
try {
|
|
var args :Array = (arg as Array);
|
|
if (args == null && arg != null) {
|
|
args = [ arg ];
|
|
}
|
|
fn.apply(null, args);
|
|
|
|
} catch (err :Error) {
|
|
Log.getLog(this).warning("Unable to call menu callback: " +
|
|
event.item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the command for the specified item, if any.
|
|
* Somewhat similar to bits in the DefaultDataDescriptor.
|
|
*/
|
|
protected function getItemCommand (item :Object) :String
|
|
{
|
|
try {
|
|
if (item is XML) {
|
|
return (item.@command as String);
|
|
|
|
} else if (item is Object) {
|
|
return (item.command as String);
|
|
}
|
|
} catch (e :Error) {
|
|
// fall through
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the callback function for the specified item, if any.
|
|
*/
|
|
protected function getItemCallback (item :Object) :Function
|
|
{
|
|
try {
|
|
if (item is XML) {
|
|
return (item.@callback as Function);
|
|
|
|
} else if (item is Object) {
|
|
return (item.callback as Function);
|
|
}
|
|
} catch (e :Error) {
|
|
// fall through
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the command for the specified item, if any.
|
|
* Somewhat similar to bits in the DefaultDataDescriptor.
|
|
*/
|
|
protected function getItemArgument (item :Object) :Object
|
|
{
|
|
try {
|
|
if (item is XML) {
|
|
return item.@arg;
|
|
|
|
} else if (item is Object) {
|
|
return item.arg;
|
|
}
|
|
} catch (e :Error) {
|
|
// fall through
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|