Moved flash display-specific stuff to nenya: com.threerings.flash

(to match the com.threerings.flex). Let's still put general-purpose flash
stuff here in com.threerings.util.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4613 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2007-02-28 22:49:32 +00:00
parent b49534e7a4
commit 413f08f9f0
8 changed files with 31 additions and 1235 deletions
@@ -22,8 +22,9 @@
package com.threerings.crowd.client { package com.threerings.crowd.client {
import flash.display.DisplayObject; import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import com.threerings.util.DisplayUtil; import mx.core.IRawChildrenContainer; // a simple interface from flex
import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.data.PlaceObject;
@@ -72,19 +73,36 @@ public class PlaceViewUtil
return; return;
} }
DisplayUtil.applyToHierarchy(root as DisplayObject, // much of the code in here is adapted from
function (disp :DisplayObject) :void { // com.threerings.flash.DisplayUtil, which we cannot access because
if (disp is PlaceView) { // it's in the nenya package. So sad, too bad.
try {
(root as PlaceView)[funct](plobj); if (root is PlaceView) {
} catch (e :Error) { try {
var log :Log = Log.getLog(PlaceViewUtil); (root as PlaceView)[funct](plobj);
log.warning("Component choked on " + funct + "() " + } catch (e :Error) {
"[component=" + root + ", plobj=" + plobj + "]."); var log :Log = Log.getLog(PlaceViewUtil);
log.logStackTrace(e); log.warning("Component choked on " + funct + "() " +
} "[component=" + root + ", plobj=" + plobj + "].");
log.logStackTrace(e);
}
}
if (root is DisplayObjectContainer) {
// a little type-unsafety so that we don't have to write two blocks
var o :Object= (root is IRawChildrenContainer) ?
IRawChildrenContainer(root).rawChildren : root;
var nn :int = int(o.numChildren);
for (var ii :int = 0; ii < nn; ii++) {
try {
root = o.getChildAt(ii);
} catch (err :SecurityError) {
// don't try dispatching to children we cannot access!
continue;
} }
}); dispatch(root, plobj, funct);
}
}
} }
} }
} }
@@ -1,88 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.image {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextLineMetrics;
/**
* Image and Bitmap related utility functions.
*/
public class ImageUtil
{
/**
* Create a DisplayObject that will display an error message
* of the specified dimensions.
*/
public static function createErrorImage (width :int, height :int)
:DisplayObject
{
var shape :Shape = new Shape();
shape.graphics.beginBitmapFill(createErrorBitmap());
shape.graphics.drawRect(0, 0, width, height);
shape.graphics.endFill();
return shape;
/*
// Alternate implementation that creates an actual Bitmap object
var src :BitmapData = createErrorBitmap();
var data :BitmapData = new BitmapData(width, height, false);
data.draw(src);
var rect :Rectangle = new Rectangle(0, 0, src.width, src.height);
for (var xx :int = 0; xx < width; xx += src.width) {
for (var yy :int = 0; yy < height; yy += src.height) {
if (xx != 0 || yy != 0) {
data.copyPixels(data, rect, new Point(xx, yy));
}
}
}
return new Bitmap(data);
*/
}
/**
* Create a minimally-sized "error" BitmapData.
*/
public static function createErrorBitmap () :BitmapData
{
var txt :TextField = new TextField();
txt.text = "Error";
var metrics :TextLineMetrics = txt.getLineMetrics(0);
var data :BitmapData = new BitmapData(
metrics.width + ERROR_PADDING, metrics.height + ERROR_PADDING,
false, 0xFF4040);
data.draw(txt);
return data;
}
/** The amount to pad error messages by. */
private static const ERROR_PADDING :int = 5;
}
}
-49
View File
@@ -1,49 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.util {
/**
* Color utility methods.
*
* See also mx.utils.ColorUtil.
*/
public class ColorUtil
{
/**
* Blend the two colors, either 50-50 or according to the ratio specified.
*/
public static function blend (
first :uint, second :uint, firstPerc :Number = 0.5) :uint
{
var secondPerc :Number = 1 - firstPerc;
var result :uint = 0;
for (var shift :int = 0; shift <= 16; shift += 8) {
var c1 :uint = (first >> shift) & 0xFF;
var c2 :uint = (second >> shift) & 0xFF;
result |= uint(Math.max(0, Math.min(255,
(c1 * firstPerc) + (c2 * secondPerc)))) << shift;
}
return result;
}
}
}
-184
View File
@@ -1,184 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.util {
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.geom.Point;
import flash.geom.Rectangle;
import mx.core.IChildList;
import mx.core.IRawChildrenContainer;
public class DisplayUtil
{
/**
* Call the specified function for the display object and all descendants.
*
* This is nearly exactly like mx.utils.DisplayUtil.walkDisplayObjects,
* except this method copes with security errors when examining a child.
*/
public static function applyToHierarchy (
disp :DisplayObject, callbackFunction :Function) :void
{
callbackFunction(disp);
if (disp is DisplayObjectContainer) {
// a little type-unsafety so that we don't have to write two blocks
var o :Object = (disp is IRawChildrenContainer) ?
IRawChildrenContainer(disp).rawChildren : disp;
var nn :int = int(o.numChildren);
for (var ii :int = 0; ii < nn; ii++) {
try {
disp = DisplayObject(o.getChildAt(ii));
} catch (err :SecurityError) {
continue;
}
// and then we apply outside of the try/catch block so that
// we don't hide errors thrown by the callbackFunction.
applyToHierarchy(disp, callbackFunction);
}
}
}
/**
* Returns the most reasonable position for the specified rectangle to
* be placed at so as to maximize its containment by the specified
* bounding rectangle while still placing it as near its original
* coordinates as possible.
*
* @param rect the rectangle to be positioned.
* @param bounds the containing rectangle.
*/
public static function fitRectInRect (
rect :Rectangle , bounds :Rectangle) :Point
{
// Guarantee that the right and bottom edges will be contained
// and do our best for the top and left edges.
var br :Point = bounds.bottomRight;
return new Point(
Math.min(br.x - rect.width, Math.max(rect.x, bounds.x)),
Math.min(br.y - rect.height, Math.max(rect.y, bounds.y)));
}
/**
* Position the specified rectangle within the bounds, avoiding
* any of the Rectangles in the avoid array, which may be destructively
* modified.
*
* @return true if the rectangle was successfully placed, given the
* constraints, or false if the positioning failed (the rectangle will
* be left at its original location.
*/
public static function positionRect (
r :Rectangle, bounds :Rectangle, avoid :Array) :Boolean
{
var origPos :Point = r.topLeft;
var pointSorter :Function = createPointSorter(origPos);
var possibles :Array = new Array();
// start things off with the passed-in point (adjusted to
// be inside the bounds, if needed)
possibles.push(fitRectInRect(r, bounds));
// keep track of area that doesn't generate new possibles
var dead :Array = new Array();
// Note: labeled breaks and continues are supposed to be legal,
// but they throw wacky runtime exceptions for me. So instead
// I'm throwing a boolean and using that to continue the while
/* CHECKPOSSIBLES: */ while (possibles.length > 0) {
try {
var p :Point = (possibles.shift() as Point);
r.x = p.x;
r.y = p.y;
// make sure the rectangle is in the view
if (!bounds.containsRect(r)) {
continue;
}
// and not over a dead area
for each (var deadRect :Rectangle in dead) {
if (deadRect.intersects(r)) {
//continue CHECKPOSSIBLES;
throw true; // continue outer loop
}
}
// see if it hits any rects we're trying to avoid
for (var ii :int = 0; ii < avoid.length; ii++) {
var avoidRect :Rectangle = (avoid[ii] as Rectangle);
if (avoidRect.intersects(r)) {
// remove it from the avoid list
avoid.splice(ii, 1);
// but add it to the dead list
dead.push(avoidRect);
// add 4 new possible points, each pushed in
// one direction
possibles.push(
new Point(avoidRect.x - r.width, r.y),
new Point(r.x, avoidRect.y - r.height),
new Point(avoidRect.x + avoidRect.width, r.y),
new Point(r.x, avoidRect.y + avoidRect.height));
// re-sort the list
possibles.sort(pointSorter);
//continue CHECKPOSSIBLES;
throw true; // continue outer loop
}
}
// hey! if we got here, then it worked!
return true;
} catch (continueWhile :Boolean) {
// simply catch the boolean and use it to continue inner loops
}
}
// we never found a match, move the rectangle back
r.x = origPos.x;
r.y = origPos.y;
return false;
}
/**
* Create a sort Function that can be used to compare Points in an
* Array according to their distance from the specified Point.
*
* Note: The function will always sort according to distance from the
* passed-in point, even if that point's coordinates change after
* the function is created.
*/
public static function createPointSorter (origin :Point) :Function
{
return function (p1 :Point, p2 :Point) :Number {
var dist1 :Number = Point.distance(origin, p1);
var dist2 :Number = Point.distance(origin, p2);
return (dist1 > dist2) ? 1 : ((dist1 < dist2) ? -1 : 0); // signum
};
}
}
}
-67
View File
@@ -1,67 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.util {
import flash.events.Event;
import flash.text.TextField;
import flash.utils.getTimer; // function import
public class FPSDisplay extends TextField
{
public function FPSDisplay (framesToTrack :int = 150)
{
background = true;
text = String(Number.MIN_VALUE);
height = textHeight + 4;
_framesToTrack = framesToTrack;
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
}
protected function handleEnterFrame (event :Event) :void
{
var curStamp :Number = getTimer();
_frameStamps.push(curStamp);
if (_frameStamps.length > _framesToTrack) {
_frameStamps.shift(); // forget the oldest timestamps
}
var firstStamp :Number = Number(_frameStamps[0]);
var seconds :Number = (curStamp - firstStamp) / 1000;
// subtract one from the frames, since we're measuring the time
// elapsed over the frames (when there are two timestamps, that's
// the difference between 1 frame)
var frames :Number = _frameStamps.length - 1;
this.text = String(frames / seconds);
}
/** Timestamps of past ENTER_FRAME events. */
protected var _frameStamps :Array = [];
/** The number of running frames we track. */
protected var _framesToTrack :int;
}
}
@@ -1,517 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.util {
//import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Shape;
import flash.display.Sprite;
import flash.errors.IOError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.events.StatusEvent;
import flash.events.TextEvent;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;
import flash.net.URLRequest;
import com.threerings.util.StringUtil;
import com.threerings.media.image.ImageUtil;
/**
* A wrapper class for all media that will be placed on the screen.
* Subject to change.
*/
public class MediaContainer extends Sprite
{
/** A ValueEvent we dispatch when our size is known.
* Value: [ width, height ]. */
public static const SIZE_KNOWN :String = "mediaSizeKnown";
/** A log instance that can be shared by sprites. */
protected static const log :Log = Log.getLog(MediaContainer);
/**
* Constructor.
*/
public function MediaContainer (url :String = null)
{
if (url != null) {
setMedia(url);
}
mouseEnabled = false;
mouseChildren = true;
}
/**
* Get the media. If the media was loaded using a URL, this will
* likely be the Loader object holding the real media.
*/
public function getMedia () :DisplayObject
{
return _media;
}
/**
* Configure the media to display.
*/
public function setMedia (url :String) :void
{
if (Util.equals(_url, url)) {
return; // no change
}
_url = url;
// shutdown any previous media
if (_media != null) {
shutdown(false);
}
// set up the new media
if (StringUtil.endsWith(url.toLowerCase(), ".flv")) {
setupVideo(url);
} else {
setupSwfOrImage(url);
}
}
/**
* Configure our media as an instance of the specified class.
*/
public function setMediaClass (clazz :Class) :void
{
setMediaObject(new clazz() as DisplayObject);
}
/**
* Configure an already-instantiated DisplayObject as our media.
*/
public function setMediaObject (disp :DisplayObject) :void
{
_url = null;
if (_media != null) {
shutdown(false);
}
addChild(disp);
_media = disp;
updateContentDimensions(disp.width, disp.height);
}
/**
* Configure this sprite to show a video.
*/
protected function setupVideo (url :String) :void
{
var vid :VideoDisplayer = new VideoDisplayer();
vid.addEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown);
vid.addEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError);
_media = vid;
addChild(vid);
vid.setup(url);
}
/**
* Configure this sprite to show an image or flash movie.
*/
protected function setupSwfOrImage (url :String) :void
{
startedLoading();
// create our loader and set up some event listeners
var loader :Loader = new Loader();
_media = loader;
var info :LoaderInfo = loader.contentLoaderInfo;
info.addEventListener(Event.COMPLETE, loadingComplete);
info.addEventListener(IOErrorEvent.IO_ERROR, loadError);
info.addEventListener(ProgressEvent.PROGRESS, loadProgress);
// create a mask to prevent the media from drawing out of bounds
if (getMaxContentWidth() < int.MAX_VALUE &&
getMaxContentHeight() < int.MAX_VALUE) {
configureMask(getMaxContentWidth(), getMaxContentHeight());
}
// start it loading, add it as a child
loader.load(new URLRequest(url), getContext(url));
addChild(loader);
try {
updateContentDimensions(info.width, info.height);
} catch (err :Error) {
// an error is thrown trying to access these props before they're
// ready
}
}
/**
* Display a 'broken image' to indicate there were troubles with
* loading the media.
*/
protected function setupBrokenImage (w :int = -1, h :int = -1) :void
{
if (w == -1) {
w = 100;
}
if (h == -1) {
h = 100;
}
setMediaObject(ImageUtil.createErrorImage(w, h));
}
/**
* Get the application domain being used by this media, or null if
* none or not applicable.
*/
public function getApplicationDomain () :ApplicationDomain
{
return (_media is Loader)
? (_media as Loader).contentLoaderInfo.applicationDomain
: null;
}
/**
* Unload the media we're displaying, clean up any resources.
*
* @param completely if true, we're going away and should stop
* everything. Otherwise, we're just loading up new media.
*/
public function shutdown (completely :Boolean = true) :void
{
try {
// remove the mask
if (_media != null && _media.mask != null) {
removeChild(_media.mask);
_media.mask = null;
}
if (_media is Loader) {
var loader :Loader = (_media as Loader);
// remove any listeners
removeListeners(loader.contentLoaderInfo);
// dispose of media
try {
loader.close();
} catch (ioe :IOError) {
// ignore
}
loader.unload();
removeChild(loader);
} else if (_media is VideoDisplayer) {
var vid :VideoDisplayer = (_media as VideoDisplayer);
vid.removeEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown);
vid.removeEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError);
vid.shutdown();
removeChild(vid);
} else if (_media != null) {
removeChild(_media);
}
} catch (ioe :IOError) {
log.warning("Error shutting down media: " + ioe);
log.logStackTrace(ioe);
}
// clean everything up
_w = 0;
_h = 0;
_media = null;
}
/**
* Get the width of the content, bounded by the maximum.
*/
public function getContentWidth () :int
{
return Math.min(Math.abs(_w * getMediaScaleX()), getMaxContentWidth());
}
/**
* Get the height of the content, bounded by the maximum.
*/
public function getContentHeight () :int
{
return Math.min(Math.abs(_h * getMediaScaleY()), getMaxContentHeight());
}
/**
* Get the maximum allowable width for our content.
*/
public function getMaxContentWidth () :int
{
return int.MAX_VALUE;
}
/**
* Get the maximum allowable height for our content.
*/
public function getMaxContentHeight () :int
{
return int.MAX_VALUE;
}
/**
* Get the X scaling factor to use on the actual media.
*/
public function getMediaScaleX () :Number
{
return 1;
}
/**
* Get the Y scaling factor to use on the actual media.
*/
public function getMediaScaleY () :Number
{
return 1;
}
/**
* Called by MediaWrapper as notification that its size has changed.
*/
public function containerDimensionsUpdated (newWidth :Number, newHeight :Number) :void
{
// do nothing in base MediaContainer
}
override public function toString () :String
{
return "MediaContainer[url=" + _url + "]";
}
/**
* Return the LoaderContext that should be used to load the media
* at the specified url.
*/
protected function getContext (url :String) :LoaderContext
{
if (isImage(url)) {
// load images into our domain so that we can view their pixels
return new LoaderContext(true,
new ApplicationDomain(ApplicationDomain.currentDomain),
SecurityDomain.currentDomain);
} else {
// share nothing, trust nothing
return new LoaderContext(false, new ApplicationDomain(null), null);
}
}
/**
* Does the specified url represent an image?
*/
protected function isImage (url :String) :Boolean
{
// look at the last 4 characters in the lowercased url
switch (url.toLowerCase().slice(-4)) {
case ".png":
case ".jpg":
case ".gif":
return true;
default:
return false;
}
}
/**
* Remove our listeners from the LoaderInfo object.
*/
protected function removeListeners (info :LoaderInfo) :void
{
info.removeEventListener(Event.COMPLETE, loadingComplete);
info.removeEventListener(IOErrorEvent.IO_ERROR, loadError);
info.removeEventListener(ProgressEvent.PROGRESS, loadProgress);
}
/**
* A callback to receive IO_ERROR events.
*/
protected function loadError (event :IOErrorEvent) :void
{
stoppedLoading();
setupBrokenImage(-1, -1);
}
/**
* A callback to receive PROGRESS events.
*/
protected function loadProgress (event :ProgressEvent) :void
{
updateLoadingProgress(event.bytesLoaded, event.bytesTotal);
var info :LoaderInfo = (event.target as LoaderInfo);
try {
updateContentDimensions(info.width, info.height);
} catch (err :Error) {
// an error is thrown trying to access these props before they're
// ready
}
}
/**
* Handles video size known.
*/
protected function handleVideoSizeKnown (event :ValueEvent) :void
{
var args :Array = (event.value as Array);
updateContentDimensions(int(args[0]), int(args[1]));
}
/**
* Handles an error loading a video.
*/
protected function handleVideoError (event :ValueEvent) :void
{
log.warning("Error loading video [cause=" + event.value + "].");
stoppedLoading();
setupBrokenImage(-1, -1);
}
/**
* Callback function to receive COMPLETE events for swfs or images.
*/
protected function loadingComplete (event :Event) :void
{
var info :LoaderInfo = (event.target as LoaderInfo);
removeListeners(info);
// trace("Loading complete: " + info.url +
// ", childAllowsParent=" + info.childAllowsParent +
// ", parentAllowsChild=" + info.parentAllowsChild +
// ", sameDomain=" + info.sameDomain);
updateContentDimensions(info.width, info.height);
updateLoadingProgress(1, 1);
stoppedLoading();
}
/**
* Configure the mask for this object.
*/
protected function configureMask (ww :int, hh :int) :void
{
var mask :Shape;
if (_media.mask != null) {
mask = (_media.mask as Shape);
} else {
mask = new Shape();
// the mask must be added to the display list (which is wacky)
addChild(mask);
_media.mask = mask;
}
mask.graphics.clear();
mask.graphics.beginFill(0xFFFFFF);
mask.graphics.drawRect(0, 0, ww, hh);
mask.graphics.endFill();
}
/**
* Called during loading as we figure out how big the content we're
* loading is.
*/
protected function updateContentDimensions (ww :int, hh :int) :void
{
// update our saved size, and possibly notify our container
if (_w != ww || _h != hh) {
_w = ww;
_h = hh;
contentDimensionsUpdated();
// dispatch an event to any listeners
dispatchEvent(new ValueEvent(SIZE_KNOWN, [ ww, hh ]));
}
}
/**
* Called when we know the true size of the content.
*/
protected function contentDimensionsUpdated () :void
{
// nada, by default
}
/**
* Update the graphics to indicate how much is loaded.
*/
protected function updateLoadingProgress (
soFar :Number, total :Number) :void
{
// nada, by default
}
/**
* Called when we've started loading new media. Will not be called
* for new media that does not require loading.
*/
protected function startedLoading () :void
{
// nada
}
/**
* Called when we've stopped loading, which may be as a result of
* completion, an error while loading, or early termination.
*/
protected function stoppedLoading () :void
{
// nada
}
/** The unaltered URL of the content we're displaying. */
protected var _url :String;
/** The unscaled width of our content. */
protected var _w :int;
/** The unscaled height of our content. */
protected var _h :int;
/** Either a Loader or a VideoDisplay. */
protected var _media :DisplayObject;
}
}
-48
View File
@@ -1,48 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.util {
import flash.ui.ContextMenuItem;
import flash.events.ContextMenuEvent;
/**
*/
public class MenuUtil
{
/**
* Create a menu item that will submit a controller command when selected.
*/
public static function createControllerMenuItem (
caption :String, cmd :String, arg :Object = null,
separatorBefore :Boolean = false, enabled :Boolean = true,
visible :Boolean = true) :ContextMenuItem
{
var item :ContextMenuItem =
new ContextMenuItem(caption, separatorBefore, enabled, visible);
item.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,
function (event :ContextMenuEvent) :void {
CommandEvent.dispatch(event.mouseTarget, cmd, arg);
});
return item;
}
}
}
@@ -1,269 +0,0 @@
package com.threerings.util { // TODO: Move.
import flash.display.Sprite;
import flash.events.AsyncErrorEvent;
import flash.events.IOErrorEvent;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.utils.Timer;
public class VideoDisplayer extends Sprite
{
/** A value event dispatched when the size of the video is known.
* Value: [ width, height ]. */
public static const SIZE_KNOWN :String = "videoDisplayerSizeKnown";
/** A value event sent when there's an error loading the video.
* Value: original error event. */
public static const VIDEO_ERROR :String = "videoDisplayerError";
/**
* Create a video displayer.
*/
public function VideoDisplayer ()
{
_vid = new Video();
addChild(_vid);
addEventListener(MouseEvent.ROLL_OVER, handleRollOver);
addEventListener(MouseEvent.ROLL_OUT, handleRollOut);
_videoChecker.addEventListener(TimerEvent.TIMER, handleVideoCheck);
_pauser = new Sprite();
_pauser.addEventListener(MouseEvent.CLICK, handleClick);
redrawPauser();
}
/**
* Start playing a video!
*/
public function setup (url :String) :void
{
_netCon = new NetConnection();
_netCon.addEventListener(NetStatusEvent.NET_STATUS, handleNetStatus);
// error handlers
_netCon.addEventListener(AsyncErrorEvent.ASYNC_ERROR, handleAsyncError);
_netCon.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
_netCon.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
_netCon.connect(null);
_netStream = new NetStream(_netCon);
// pass in refs to some of our protected methods
_netStream.client = {
onMetaData: metaDataReceived
};
_netStream.addEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
_vid.attachNetStream(_netStream);
_videoChecker.start();
_netStream.play(url);
_paused = false;
}
/**
* Stop playing our video.
*/
public function shutdown () :void
{
_videoChecker.reset();
_vid.attachNetStream(null);
if (_netStream != null) {
_netStream.close();
_netStream.removeEventListener(NetStatusEvent.NET_STATUS, handleStreamNetStatus);
_netStream = null;
}
if (_netCon != null) {
_netCon.close();
_netCon.removeEventListener(NetStatusEvent.NET_STATUS, handleNetStatus);
_netCon.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, handleAsyncError);
_netCon.removeEventListener(IOErrorEvent.IO_ERROR, handleIOError);
_netCon.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
_netCon = null;
}
}
/**
* Check to see if we now know the dimensions of the video.
*/
protected function handleVideoCheck (event :TimerEvent) :void
{
if (_vid.videoWidth == 0 || _vid.videoHeight == 0) {
return; // not known yet!
}
// stop the checker timer
_videoChecker.stop();
// set up the width/height
_vid.width = _vid.videoWidth;
_vid.height = _vid.videoHeight;
redrawPauser();
// tell any interested parties
dispatchEvent(new ValueEvent(SIZE_KNOWN, [ _vid.videoWidth, _vid.videoHeight ]));
}
protected function handleRollOver (event :MouseEvent) :void
{
addChild(_pauser);
}
protected function handleRollOut (event :MouseEvent) :void
{
if (_pauser.parent) {
removeChild(_pauser);
}
}
protected function handleClick (event :MouseEvent) :void
{
// the buck stops here!
event.stopImmediatePropagation();
if (_paused) {
_netStream.resume();
_paused = false;
} else {
_netStream.pause();
_paused = true;
}
redrawPauser();
}
/**
* Draw the pauser sprite, update it's location.
* This will become something artistic, etc.
*/
protected function redrawPauser () :void
{
with (_pauser.graphics) {
clear();
// draw a nice circle
beginFill(0x333333);
drawCircle(0, 0, 20);
endFill();
lineStyle(2, 0);
drawCircle(0, 0, 20);
if (_paused) {
lineStyle(0, 0, 0);
beginFill(0x00FF00);
moveTo(-4, -10);
lineTo(4, 0);
lineTo(-4, 10);
lineTo(-4, -10);
endFill();
} else {
lineStyle(2, 0x00FF00);
moveTo(-4, -10);
lineTo(-4, 10);
moveTo(4, -10);
lineTo(4, 10);
}
}
// and update the location
_pauser.x = _vid.width/2;
_pauser.y = _vid.height/2;
}
protected function handleNetStatus (event :NetStatusEvent) :void
{
var info :Object = event.info;
if ("error" == info.level) {
trace("NetStatus error: " + info.code);
redispatchError(event);
return;
}
// else info.level == "status"
switch (info.code) {
case "NetConnection.Connect.Success":
case "NetConnection.Connect.Closed":
// these status events we ignore
break;
default:
trace("NetStatus status: " + info.code);
break;
}
}
protected function handleStreamNetStatus (event :NetStatusEvent) :void
{
if (event.info.code == "NetStream.Play.Stop") {
_netStream.seek(0);
_netStream.pause();
_paused = true;
redrawPauser();
}
}
protected function handleAsyncError (event :AsyncErrorEvent) :void
{
trace("AsyncError: " + event);
redispatchError(event);
}
protected function handleIOError (event :IOErrorEvent) :void
{
trace("IOError: " + event);
redispatchError(event);
}
protected function handleSecurityError (event :SecurityErrorEvent) :void
{
trace("SecurityError: " + event);
redispatchError(event);
}
/**
* Redispatch some error we received to our listeners.
*/
protected function redispatchError (event :Event) :void
{
dispatchEvent(new ValueEvent(VIDEO_ERROR, event));
}
/**
* Called when metadata (if any) is found in the video stream.
*/
protected function metaDataReceived (obj :Object) :void
{
trace("Got video metadata:");
for (var n :String in obj) {
trace(" " + n + ": " + obj[n]);
}
}
protected var _vid :Video;
protected var _paused :Boolean = false;
protected var _netCon :NetConnection;
protected var _netStream :NetStream;
protected var _pauser :Sprite;
/** Checks the video every 100ms to see if the dimensions are now know.
* Yes, this is how to do it. We could trigger on ENTER_FRAME, but then
* we may not know the dimensions unless we're added on the display list,
* and we want this to work in the general case. */
protected var _videoChecker :Timer = new Timer(100);
}
}