diff --git a/src/as/com/threerings/ezgame/EZGameControl.as b/src/as/com/threerings/ezgame/EZGameControl.as index 81f305ef..2f75824f 100644 --- a/src/as/com/threerings/ezgame/EZGameControl.as +++ b/src/as/com/threerings/ezgame/EZGameControl.as @@ -4,6 +4,8 @@ import flash.errors.IllegalOperationError; import flash.events.Event; import flash.events.EventDispatcher; +import flash.events.KeyboardEvent; +import flash.events.MouseEvent; import flash.display.DisplayObject; @@ -20,6 +22,41 @@ public class EZGameControl extends EventDispatcher populateProperties(event.userProps); disp.root.loaderInfo.sharedEvents.dispatchEvent(event); setEZProps(event.ezProps); + + // set up our focusing click handler + disp.root.addEventListener(MouseEvent.CLICK, handleRootClick); + } + + // documentation inherited + override public function addEventListener ( + type :String, listener :Function, useCapture :Boolean = false, + priority :int = 0, useWeakReference :Boolean = false) :void + { + super.addEventListener(type, listener, useCapture, priority, + useWeakReference); + + switch (type) { + case KeyboardEvent.KEY_UP: + case KeyboardEvent.KEY_DOWN: + if (hasEventListener(type)) { // ensure it was added + callEZCode("alterKeyEvents_v1", type, true); + } + } + } + + // documentation inherited + override public function removeEventListener ( + type :String, listener :Function, useCapture :Boolean = false) :void + { + super.removeEventListener(type, listener, useCapture); + + switch (type) { + case KeyboardEvent.KEY_UP: + case KeyboardEvent.KEY_DOWN: + if (!hasEventListener(type)) { // once it's no longer needed + callEZCode("alterKeyEvents_v1", type, false); + } + } } // from EZGame @@ -240,11 +277,6 @@ public class EZGameControl extends EventDispatcher callEZCode.apply(null, args); } - override public function willTrigger (type :String) :Boolean - { - throw new IllegalOperationError(); - } - override public function dispatchEvent (event :Event) :Boolean { // Ideally we want to not be an IEventDispatcher so that people @@ -285,6 +317,7 @@ public class EZGameControl extends EventDispatcher o["messageReceived_v1"] = messageReceived_v1; o["gameDidStart_v1"] = gameDidStart_v1; o["gameDidEnd_v1"] = gameDidEnd_v1; + o["dispatchEvent_v1"] = dispatch; } private function propertyWasSet_v1 ( @@ -341,6 +374,20 @@ public class EZGameControl extends EventDispatcher } } + /** + * Internal method that is called whenever the mouse enters our root. + */ + protected function handleRootClick (evt :MouseEvent) :void + { + try { + if (evt.target.stage.focus != null) { + return; + } + } catch (err :SecurityError) { + } + callEZCode("focusContainer_v1"); + } + /** * Secret function to dispatch property changed events. */ diff --git a/src/as/com/threerings/ezgame/client/EZGamePanel.as b/src/as/com/threerings/ezgame/client/EZGamePanel.as index 94e931bf..fdee100d 100644 --- a/src/as/com/threerings/ezgame/client/EZGamePanel.as +++ b/src/as/com/threerings/ezgame/client/EZGamePanel.as @@ -49,10 +49,10 @@ public class EZGamePanel extends VBox _ezObj = (plobj as EZGameObject); backend = new GameControlBackend(_ctx, _ezObj); - _gameView = new GameContainer(cfg.configData); // TODO - _gameView.tabEnabled = true; + _gameView = new GameContainer(cfg.configData); // TODO? backend.setSharedEvents( Loader(_gameView.getMedia()).contentLoaderInfo.sharedEvents); + backend.setContainer(_gameView); addChild(_gameView); } diff --git a/src/as/com/threerings/ezgame/client/GameContainer.as b/src/as/com/threerings/ezgame/client/GameContainer.as index 7772db10..a295c14b 100644 --- a/src/as/com/threerings/ezgame/client/GameContainer.as +++ b/src/as/com/threerings/ezgame/client/GameContainer.as @@ -1,27 +1,55 @@ package com.threerings.ezgame.client { -import flash.display.InteractiveObject; import flash.display.Loader; +import flash.geom.Rectangle; + +import mx.core.mx_internal; +import mx.core.IFlexDisplayObject; +import mx.core.IInvalidating; + import mx.managers.IFocusManagerComponent; +import mx.skins.ProgrammaticSkin; + import com.threerings.util.MediaContainer; +// TODO: there are focus issues in here that need dealing with. +// +// 1) The focus rectangle is drawn at the wrong size in scrolling games +// 2) We can get focus without having the pink focus rectangle... +// 3) When the mouse leaves the flash player and returns, this +// damn thing doesn't seem to grip onto the focus. +// public class GameContainer extends MediaContainer implements IFocusManagerComponent { public function GameContainer (url :String) { super(url); + + tabEnabled = true; // turned off by Container +// focusRect = true; // we need the focus rect } - override public function setFocus () :void + override protected function adjustFocusRect (obj :DisplayObject = null) :void { - super.setFocus(); - if (systemManager.stage.focus == this) { - // TODO - //systemManager.stage.focus = InteractiveObject(Loader(_media).content); - systemManager.stage.focus = Loader(_media); + super.adjustFocusRect(obj); + + // TODO: this is probably all wrong + var focusObj :IFlexDisplayObject = + IFlexDisplayObject(mx_internal::getFocusObject()); + if (focusObj) { + var r :Rectangle = transform.pixelBounds; + focusObj.setActualSize(r.width - 2, r.height - 2); + focusObj.move(0, 0); + + if (focusObj is IInvalidating) { + IInvalidating(focusObj).validateNow(); + + } else if (focusObj is ProgrammaticSkin) { + ProgrammaticSkin(focusObj).validateNow(); + } } } } diff --git a/src/as/com/threerings/ezgame/client/GameControlBackend.as b/src/as/com/threerings/ezgame/client/GameControlBackend.as index 501932c6..830c2715 100644 --- a/src/as/com/threerings/ezgame/client/GameControlBackend.as +++ b/src/as/com/threerings/ezgame/client/GameControlBackend.as @@ -5,6 +5,12 @@ import flash.errors.IllegalOperationError; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; +import flash.events.KeyboardEvent; + +import flash.events.MouseEvent; + +import flash.display.DisplayObject; +import flash.display.InteractiveObject; import flash.utils.IExternalizable; import flash.utils.ByteArray; @@ -38,6 +44,9 @@ import com.threerings.ezgame.data.PropertySetListener; import com.threerings.ezgame.data.UserCookie; import com.threerings.ezgame.util.EZObjectMarshaller; +/** + * Manages the backend of the game. + */ public class GameControlBackend implements MessageListener, SetListener, PropertySetListener { @@ -59,6 +68,11 @@ public class GameControlBackend disp.addEventListener("ezgameQuery", handleEZQuery); } + public function setContainer (container :GameContainer) :void + { + _container = container; + } + public function shutdown () :void { _ezObj.removeListener(this); @@ -76,6 +90,8 @@ public class GameControlBackend { // here we would handle adapting old functions to a new version + _ezDispatcher = (o["dispatchEvent_v1"] as Function); + _userFuncs = o; } @@ -121,6 +137,8 @@ public class GameControlBackend o["endGame_v1"] = endGame_v1; o["populateCollection_v1"] = populateCollection_v1; o["getFromCollection_v1"] = getFromCollection_v1; + o["alterKeyEvents_v1"] = alterKeyEvents_v1; + o["focusContainer_v1"] = focusContainer_v1; } public function setProperty_v1 ( @@ -342,6 +360,30 @@ public class GameControlBackend playerIndex, listener); } + public function alterKeyEvents_v1 ( + keyEventType :String, add :Boolean) :void + { + if (add) { + _container.addEventListener(keyEventType, handleKeyEvent); + } else { + _container.removeEventListener(keyEventType, handleKeyEvent); + } + } + + public function focusContainer_v1 () :void + { + _container.setFocus(); + } + + /** + * Handle key events on our container and pass them into the game. + */ + protected function handleKeyEvent (evt :KeyboardEvent) :void + { + // dispatch a cloned copy of the event, so that it's safe + _ezDispatcher(evt.clone()); + } + /** * Convenience function to get our name. */ @@ -564,10 +606,16 @@ public class GameControlBackend protected var _userListener :MessageAdapter = new MessageAdapter(messageReceivedOnUserObject); + protected var _container :GameContainer; + protected var _ezObj :EZGameObject; protected var _userFuncs :Object; + /** The function on the EZGameControl which we can use to directly + * dispatch events to the user's game. */ + protected var _ezDispatcher :Function; + protected var _gameData :GameData; /** playerIndex -> callback functions waiting for the cookie. */