Moved here.
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@126 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package com.threerings.flex {
|
||||
|
||||
import flash.display.DisplayObjectContainer;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
import mx.controls.TextArea;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatDirector;
|
||||
import com.threerings.crowd.chat.client.ChatDisplay;
|
||||
import com.threerings.crowd.chat.data.ChatMessage;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
/**
|
||||
* A very simple chat display.
|
||||
*/
|
||||
public class ChatDisplayBox extends TextArea
|
||||
implements ChatDisplay
|
||||
{
|
||||
public function ChatDisplayBox (ctx :CrowdContext)
|
||||
{
|
||||
_ctx = ctx;
|
||||
this.editable = false;
|
||||
|
||||
// TODO
|
||||
width = 400;
|
||||
height = 150;
|
||||
}
|
||||
|
||||
// documentation inherited from interface ChatDisplay
|
||||
public function clear () :void
|
||||
{
|
||||
this.htmlText = "";
|
||||
}
|
||||
|
||||
// documentation inherited from interface ChatDisplay
|
||||
public function displayMessage (
|
||||
msg :ChatMessage, alreadyDisplayed :Boolean) :Boolean
|
||||
{
|
||||
if (!_scrollBot) {
|
||||
_scrollBot = (verticalScrollPosition == maxVerticalScrollPosition);
|
||||
}
|
||||
|
||||
// display the message
|
||||
if (msg is UserMessage) {
|
||||
this.htmlText += "<font color=\"red\"><" +
|
||||
(msg as UserMessage).speaker + "></font> ";
|
||||
}
|
||||
this.htmlText += msg.message;
|
||||
return true;
|
||||
}
|
||||
|
||||
override public function parentChanged (p :DisplayObjectContainer) :void
|
||||
{
|
||||
super.parentChanged(p);
|
||||
|
||||
var chatdir :ChatDirector = _ctx.getChatDirector();
|
||||
if (p != null) {
|
||||
chatdir.addChatDisplay(this);
|
||||
} else {
|
||||
chatdir.removeChatDisplay(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
override protected function updateDisplayList (uw :Number, uh :Number) :void
|
||||
{
|
||||
super.updateDisplayList(uw, uh);
|
||||
|
||||
if (_scrollBot) {
|
||||
verticalScrollPosition = maxVerticalScrollPosition;
|
||||
_scrollBot = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** The giver of life. */
|
||||
protected var _ctx :CrowdContext;
|
||||
|
||||
protected var _scrollBot :Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.threerings.flex {
|
||||
|
||||
import flash.events.MouseEvent;
|
||||
|
||||
import mx.controls.Button;
|
||||
|
||||
/**
|
||||
* A command button simply dispatches a Controller command (with an optional
|
||||
* argument) when it is clicked.
|
||||
*/
|
||||
public class CommandButton extends Button
|
||||
{
|
||||
/**
|
||||
* Create a command button.
|
||||
*/
|
||||
public function CommandButton (cmd :String = null, arg :Object = null)
|
||||
{
|
||||
setCommand(cmd, arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command and argument to be issued when this button is pressed.
|
||||
*/
|
||||
public function setCommand (cmd :String, arg :Object = null) :void
|
||||
{
|
||||
_cmd = cmd;
|
||||
_arg = arg;
|
||||
}
|
||||
|
||||
override protected function clickHandler (event :MouseEvent) :void
|
||||
{
|
||||
super.clickHandler(event);
|
||||
|
||||
if (enabled) {
|
||||
// stop the click event
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// dispatch the command event
|
||||
// if the arg is null and we're a toggle button, dispatch our state
|
||||
var arg :Object = _arg;
|
||||
if (arg == null && toggle) {
|
||||
arg = selected;
|
||||
}
|
||||
CommandEvent.dispatch(this, _cmd, arg);
|
||||
}
|
||||
}
|
||||
|
||||
/** The command to submit when clicked. */
|
||||
protected var _cmd :String;
|
||||
|
||||
/** The argument that accompanies our command. */
|
||||
protected var _arg :Object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.threerings.flex {
|
||||
|
||||
import flash.errors.IllegalOperationError;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.IEventDispatcher;
|
||||
|
||||
public class CommandEvent extends Event
|
||||
{
|
||||
/** The event type for all controller events. */
|
||||
public static const TYPE :String = "commandEvt";
|
||||
|
||||
/**
|
||||
* Use this method to dispatch CommandEvents.
|
||||
*/
|
||||
public static function dispatch (
|
||||
disp :IEventDispatcher, cmd :String, arg :Object = null) :void
|
||||
{
|
||||
// Create the event
|
||||
var event :CommandEvent = create(cmd, arg);
|
||||
|
||||
// Dispatch it. A return value of true means that the event was
|
||||
// never cancelled, so we complain.
|
||||
if (disp == null || disp.dispatchEvent(event)) {
|
||||
Log.getLog(CommandEvent).warning("Unhandled controller command " +
|
||||
"[cmd=" + cmd + ", arg=" + arg + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/** The command. */
|
||||
public var command :String;
|
||||
|
||||
/** An optional argument. */
|
||||
public var arg :Object;
|
||||
|
||||
/**
|
||||
* Command events may not be directly constructed, use the dispatch
|
||||
* method to do your work.
|
||||
*/
|
||||
public function CommandEvent (command :String, arg :Object)
|
||||
{
|
||||
super(TYPE, true, true);
|
||||
if (_blockConstructor) {
|
||||
throw new IllegalOperationError();
|
||||
}
|
||||
this.command = command;
|
||||
this.arg = arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this command as handled, stopping its propagation up the
|
||||
* hierarchy.
|
||||
*/
|
||||
public function markAsHandled () :void
|
||||
{
|
||||
preventDefault();
|
||||
stopImmediatePropagation();
|
||||
}
|
||||
|
||||
override public function clone () :Event
|
||||
{
|
||||
return create(command, arg);
|
||||
}
|
||||
|
||||
override public function toString () :String
|
||||
{
|
||||
return "CommandEvent[" + command + " (" + arg + ")]";
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory method for privately creating command events.
|
||||
*/
|
||||
protected static function create (cmd :String, arg :Object) :CommandEvent
|
||||
{
|
||||
var event :CommandEvent;
|
||||
_blockConstructor = false;
|
||||
try {
|
||||
event = new CommandEvent(cmd, arg);
|
||||
} finally {
|
||||
_blockConstructor = true;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
/** Used to prevent unauthorized construction. */
|
||||
protected static var _blockConstructor :Boolean = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
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.ScrollPolicy;
|
||||
import mx.events.MenuEvent;
|
||||
|
||||
/**
|
||||
* 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 Menu
|
||||
{
|
||||
public function CommandMenu ()
|
||||
{
|
||||
super();
|
||||
|
||||
addEventListener(MenuEvent.ITEM_CLICK, itemClicked);
|
||||
// addEventListener(MenuEvent.MENU_SHOW, handleMenuShown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a command menu.
|
||||
*
|
||||
* @param parent The parent of this menu.
|
||||
* @param items an array of menu items.
|
||||
*/
|
||||
public static function createMenu (
|
||||
parent :DisplayObjectContainer, items :Array) :CommandMenu
|
||||
{
|
||||
var menu :CommandMenu = new CommandMenu();
|
||||
menu.tabEnabled = false;
|
||||
menu.showRoot = true;
|
||||
Menu.popUpMenu(menu, parent, 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 {
|
||||
// simply position it below the trigger
|
||||
show(r.x, r.y + r.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Callback for MenuEvent.MENU_SHOW.
|
||||
// */
|
||||
// protected function handleMenuShown (event :MenuEvent) :void
|
||||
// {
|
||||
// // just ensure that every menu can scroll
|
||||
// event.menu.verticalScrollPolicy = ScrollPolicy.ON;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package com.threerings.flex {
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.display.InteractiveObject;
|
||||
import flash.display.Sprite;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFieldType;
|
||||
import flash.ui.Mouse;
|
||||
|
||||
import mx.core.Application;
|
||||
import mx.managers.SystemManager;
|
||||
import mx.styles.CSSStyleDeclaration;
|
||||
import mx.styles.StyleManager;
|
||||
|
||||
import com.threerings.util.HashMap;
|
||||
|
||||
/**
|
||||
* A better CursorManager.
|
||||
*/
|
||||
public class CursorManager
|
||||
{
|
||||
public static const SYSTEM_CURSOR :int = 0;
|
||||
|
||||
public static const BUSY_CURSOR :int = 1;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Class methods
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Makes the cursor visible.
|
||||
* Cursor visibility is not reference-counted.
|
||||
* A single call to the <code>showCursor()</code> method
|
||||
* always shows the cursor regardless of how many calls
|
||||
* to the <code>hideCursor()</code> method were made.
|
||||
*/
|
||||
public static function showCursor () :void
|
||||
{
|
||||
if (_currentCursorId == SYSTEM_CURSOR) {
|
||||
Mouse.show();
|
||||
} else {
|
||||
_cursorHolder.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the cursor invisible.
|
||||
* Cursor visibility is not reference-counted.
|
||||
* A single call to the <code>hideCursor()</code> method
|
||||
* always hides the cursor regardless of how many calls
|
||||
* to the <code>showCursor()</code> method were made.
|
||||
*/
|
||||
public static function hideCursor () :void
|
||||
{
|
||||
_cursorHolder.visible = false;
|
||||
Mouse.hide();
|
||||
}
|
||||
|
||||
public static function getCurrentCursorId () :int
|
||||
{
|
||||
return _currentCursorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor.
|
||||
*
|
||||
* @param cursorClass Class of the cursor to display.
|
||||
*
|
||||
* @param xOffset Number that specifies the x offset
|
||||
* of the cursor, in pixels, relative to the mouse pointer.
|
||||
* The default value is 0.
|
||||
*
|
||||
* @param yOffset Number that specifies the y offset
|
||||
* of the cursor, in pixels, relative to the mouse pointer.
|
||||
* The default value is 0.
|
||||
*
|
||||
* @return The ID of the cursor.
|
||||
*/
|
||||
public static function addCursor (
|
||||
cursorClass :Class, xOffset :int = 0, yOffset :int = 0) :int
|
||||
{
|
||||
var cursorId :int = _nextCursorId++;
|
||||
|
||||
var rec :CursorRecord = new CursorRecord(cursorClass);
|
||||
rec.x = xOffset;
|
||||
rec.y = yOffset;
|
||||
|
||||
_cursors.put(cursorId, rec);
|
||||
|
||||
return cursorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current cursor.
|
||||
*/
|
||||
public static function setCursor (id :int) :void
|
||||
{
|
||||
if (id == _currentCursorId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_initialized) {
|
||||
// oh, let's set it up
|
||||
_systemManager = Application.application.systemManager;
|
||||
|
||||
// set up the busy cursor
|
||||
var cursorManagerStyleDeclaration :CSSStyleDeclaration =
|
||||
StyleManager.getStyleDeclaration("CursorManager");
|
||||
var busyCursorClass :Class =
|
||||
cursorManagerStyleDeclaration.getStyle("busyCursor");
|
||||
_busyCursor = new CursorRecord(busyCursorClass);
|
||||
|
||||
// The first time a cursor is requested of the CursorManager,
|
||||
// create a Sprite to hold the cursor symbol
|
||||
_cursorHolder = new Sprite();
|
||||
_cursorHolder.mouseEnabled = false;
|
||||
_systemManager.cursorChildren.addChild(_cursorHolder);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
// figure out what the new cursor will be like
|
||||
var rec :CursorRecord;
|
||||
if (id == BUSY_CURSOR) {
|
||||
rec = _busyCursor;
|
||||
|
||||
} else if (id != SYSTEM_CURSOR) {
|
||||
rec = (_cursors.get(id) as CursorRecord);
|
||||
if (rec == null) {
|
||||
// a bogus id was specified
|
||||
// TODO: throw an error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rec != null && (rec.cursor is Class)) {
|
||||
// go ahead and instantiate the class
|
||||
try {
|
||||
var disp :DisplayObject = new (rec.cursor as Class);
|
||||
rec.cursor = disp;
|
||||
if (disp is InteractiveObject) {
|
||||
(disp as InteractiveObject).mouseEnabled = false;
|
||||
}
|
||||
|
||||
} catch (err :Error) {
|
||||
// this cursor is not usable, bail
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// always remove any custom cursors from the hierarchy
|
||||
if (_cursorHolder.numChildren > 0) {
|
||||
_cursorHolder.removeChildAt(0);
|
||||
}
|
||||
|
||||
if (id != SYSTEM_CURSOR) {
|
||||
Mouse.hide();
|
||||
var currentCursor :DisplayObject = (rec.cursor as DisplayObject);
|
||||
_cursorHolder.addChild(currentCursor);
|
||||
_cursorHolder.x = _systemManager.mouseX + rec.x;
|
||||
_cursorHolder.y = _systemManager.mouseY + rec.y;
|
||||
|
||||
_currentXOffset = rec.x;
|
||||
_currentYOffset = rec.y;
|
||||
_systemManager.stage.addEventListener(
|
||||
MouseEvent.MOUSE_MOVE, mouseMoveHandler);
|
||||
|
||||
|
||||
} else {
|
||||
_currentXOffset = 0;
|
||||
_currentYOffset = 0;
|
||||
_systemManager.stage.removeEventListener(
|
||||
MouseEvent.MOUSE_MOVE, mouseMoveHandler);
|
||||
|
||||
Mouse.show();
|
||||
}
|
||||
|
||||
_currentCursorId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a cursor from the cursor list.
|
||||
* If the cursor being removed is the currently displayed cursor,
|
||||
* the CursorManager displays the next cursor in the list, if one exists.
|
||||
* If the list becomes empty, the CursorManager displays
|
||||
* the default system cursor.
|
||||
*
|
||||
* @param cursorID ID of cursor to remove.
|
||||
*/
|
||||
public static function removeCursor (id :int) :void
|
||||
{
|
||||
var rec :CursorRecord = (_cursors.remove(id) as CursorRecord);
|
||||
|
||||
if (rec != null && id == _currentCursorId) {
|
||||
setCursor(SYSTEM_CURSOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of the cursors from the cursor list
|
||||
* and restores the system cursor.
|
||||
*/
|
||||
public static function removeAllCursors () :void
|
||||
{
|
||||
_cursors.clear();
|
||||
|
||||
if (_currentCursorId != BUSY_CURSOR) {
|
||||
setCursor(SYSTEM_CURSOR);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Class event handlers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private static function mouseMoveHandler (event :MouseEvent) :void
|
||||
{
|
||||
_cursorHolder.x = _systemManager.mouseX + _currentXOffset;
|
||||
_cursorHolder.y = _systemManager.mouseY + _currentYOffset;
|
||||
|
||||
var target :Object = event.target;
|
||||
|
||||
// Do target test.
|
||||
if (!_overTextField &&
|
||||
target is TextField && target.type == TextFieldType.INPUT) {
|
||||
_overTextField = true;
|
||||
_showSystemCursor = true;
|
||||
|
||||
} else if (_overTextField &&
|
||||
!(target is TextField && target.type == TextFieldType.INPUT)) {
|
||||
_overTextField = false;
|
||||
_showCustomCursor = true;
|
||||
}
|
||||
|
||||
// Handle switching between system and custom cursor.
|
||||
if (_showSystemCursor) {
|
||||
_showSystemCursor = false;
|
||||
_cursorHolder.visible = false;
|
||||
Mouse.show();
|
||||
}
|
||||
if (_showCustomCursor) {
|
||||
_showCustomCursor = false;
|
||||
_cursorHolder.visible = true;
|
||||
Mouse.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/** A mapping of all assigned cursor ids. */
|
||||
private static const _cursors :HashMap = new HashMap();
|
||||
|
||||
private static var _currentCursorId :int = 0; // SYSTEM_CURSOR
|
||||
|
||||
private static var _nextCursorId :int = 2; // skip BUSY
|
||||
|
||||
private static var _initialized :Boolean = false;
|
||||
|
||||
private static var _cursorHolder :Sprite;
|
||||
|
||||
private static var _currentXOffset :int = 0;
|
||||
private static var _currentYOffset :int = 0;
|
||||
|
||||
/** A record for the busy cursor (where it can't be removed). */
|
||||
private static var _busyCursor :CursorRecord;
|
||||
|
||||
private static var _overTextField :Boolean = false;
|
||||
|
||||
private static var _overLink :Boolean = false;
|
||||
|
||||
private static var _showSystemCursor :Boolean = false;
|
||||
|
||||
private static var _showCustomCursor :Boolean = false;
|
||||
|
||||
private static var _systemManager :SystemManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
class CursorRecord extends Object
|
||||
{
|
||||
public function CursorRecord (clazz :Class)
|
||||
{
|
||||
cursor = clazz;
|
||||
}
|
||||
|
||||
/** The class, or instantiated cursor. */
|
||||
public var cursor :Object
|
||||
|
||||
/** The x/y offset for the hotspot. */
|
||||
public var x :Number;
|
||||
public var y :Number;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.threerings.flex {
|
||||
|
||||
import mx.effects.IEffectInstance;
|
||||
import mx.effects.Effect;
|
||||
|
||||
public class FunctionEffect extends Effect
|
||||
{
|
||||
/** The function to call. */
|
||||
public var func :Function;
|
||||
|
||||
/** The arguments to pass to the function. */
|
||||
public var args :Array;
|
||||
|
||||
public function FunctionEffect (target :Object = null)
|
||||
{
|
||||
super(target);
|
||||
|
||||
instanceClass = FunctionEffectInstance;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
override protected function initInstance (instance :IEffectInstance) :void
|
||||
{
|
||||
super.initInstance(instance);
|
||||
|
||||
var fe :FunctionEffectInstance = (instance as FunctionEffectInstance);
|
||||
fe.func = func;
|
||||
fe.args = args;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.threerings.flex {
|
||||
|
||||
import mx.effects.effectClasses.ActionEffectInstance;
|
||||
|
||||
public class FunctionEffectInstance extends ActionEffectInstance
|
||||
{
|
||||
public function FunctionEffectInstance (target :Object)
|
||||
{
|
||||
super(target);
|
||||
}
|
||||
|
||||
/** The function to call. */
|
||||
public var func :Function;
|
||||
|
||||
/** The args to pass. */
|
||||
public var args :Array;
|
||||
|
||||
override public function play () :void
|
||||
{
|
||||
super.play();
|
||||
|
||||
// call the function!
|
||||
func.apply(null, args);
|
||||
|
||||
// and we're done
|
||||
finishRepeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package com.threerings.flex {
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.display.Graphics;
|
||||
import flash.display.Sprite;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.MouseEvent;
|
||||
|
||||
import flash.geom.Matrix;
|
||||
import flash.geom.Rectangle;
|
||||
|
||||
import mx.containers.Canvas;
|
||||
|
||||
/**
|
||||
* A control for scrolling some component separately.
|
||||
*/
|
||||
public class ScrollBox extends Canvas
|
||||
{
|
||||
public function ScrollBox (
|
||||
target :DisplayObject, maxWidth :int, maxHeight :int)
|
||||
{
|
||||
_target = target;
|
||||
_maxWidth = maxWidth;
|
||||
_maxHeight = maxHeight;
|
||||
|
||||
opaqueBackground = 0xFFFFFF;
|
||||
|
||||
_box = new Sprite();
|
||||
rawChildren.addChild(_box);
|
||||
|
||||
recheckBounds();
|
||||
|
||||
// TODO: only when something changes
|
||||
addEventListener(Event.ENTER_FRAME, enterFrame);
|
||||
|
||||
_box.addEventListener(MouseEvent.MOUSE_DOWN, spritePressed);
|
||||
}
|
||||
|
||||
override public function setActualSize (w :Number, h :Number) :void
|
||||
{
|
||||
super.setActualSize(w, h);
|
||||
|
||||
recheckBounds();
|
||||
|
||||
graphics.clear();
|
||||
graphics.lineStyle(1, 0);
|
||||
graphics.drawRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
protected function enterFrame (vent :Event) :void
|
||||
{
|
||||
if (!_dragging) {
|
||||
recheckBounds();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bounds of the scrollable area. Broken-out for easy overridding.
|
||||
*/
|
||||
protected function getScrollBounds () :Rectangle
|
||||
{
|
||||
var bounds :Rectangle = _target.transform.pixelBounds;
|
||||
// var m :Matrix = _target.transform.concatenatedMatrix;
|
||||
// bounds.topLeft = m.transformPoint(bounds.topLeft);
|
||||
// bounds.bottomRight = m.transformPoint(bounds.bottomRight);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
protected function recheckBounds () :void
|
||||
{
|
||||
var bounds :Rectangle = getScrollBounds();
|
||||
|
||||
// see what's visible, what's not..
|
||||
var scroller :Rectangle = _target.scrollRect;
|
||||
if (scroller == null) {
|
||||
scroller = bounds.clone();
|
||||
}
|
||||
|
||||
if ((_bounds != null) && _bounds.equals(bounds) &&
|
||||
(_scroller != null) && _scroller.equals(scroller)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_bounds = bounds.clone();
|
||||
_scroller = scroller;
|
||||
|
||||
_scale = Math.min(1,
|
||||
Math.min(_maxWidth / _bounds.width, _maxHeight / _bounds.height));
|
||||
width = _scale * _bounds.width;
|
||||
height = _scale * _bounds.height;
|
||||
|
||||
drawBox(_box, _scroller.width * _scale, _scroller.height * _scale);
|
||||
|
||||
_box.x = (_scroller.x - _bounds.x) * _scale;
|
||||
_box.y = (_scroller.y - _bounds.y) * _scale;
|
||||
}
|
||||
|
||||
protected function drawBox (box :Sprite, ww :Number, hh :Number) :void
|
||||
{
|
||||
var g :Graphics = _box.graphics;
|
||||
g.clear();
|
||||
g.beginFill(0x0000FF);
|
||||
g.drawRect(0, 0, ww, hh);
|
||||
g.endFill();
|
||||
}
|
||||
|
||||
protected function spritePressed (evt :MouseEvent) :void
|
||||
{
|
||||
_box.addEventListener(Event.ENTER_FRAME, boxUpdate);
|
||||
_box.startDrag(true,
|
||||
new Rectangle(0, 0, width - _box.width, height - _box.height));
|
||||
stage.addEventListener(MouseEvent.MOUSE_UP, spriteReleased);
|
||||
_dragging = true;
|
||||
}
|
||||
|
||||
protected function spriteReleased (evt :MouseEvent) :void
|
||||
{
|
||||
stage.removeEventListener(MouseEvent.MOUSE_UP, spriteReleased);
|
||||
_box.stopDrag();
|
||||
_box.removeEventListener(Event.ENTER_FRAME, boxUpdate);
|
||||
_dragging = false;
|
||||
}
|
||||
|
||||
protected function boxUpdate (evt :Event) :void
|
||||
{
|
||||
var r :Rectangle = _target.scrollRect;
|
||||
r.x = (_box.x / _scale) + _bounds.x;
|
||||
r.y = (_box.y / _scale) + _bounds.y;
|
||||
_target.scrollRect = r;
|
||||
}
|
||||
|
||||
protected var _dragging :Boolean;
|
||||
|
||||
protected var _target :DisplayObject;
|
||||
|
||||
protected var _maxWidth :int;
|
||||
protected var _maxHeight :int;
|
||||
|
||||
protected var _box :Sprite;
|
||||
|
||||
protected var _scale :Number;
|
||||
|
||||
protected var _bounds :Rectangle;
|
||||
protected var _scroller :Rectangle;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user