Progression: 2008年11月アーカイブ
現在の Progression にはシーンオブジェクトにイベントフローが実装されていますが、
表示オブジェクトとは少し挙動が違っています。(Progression 3.0.7 現在)
useCapture や eventPhase, bubbles あたりの値が有効化されていませんので、これを実装してみました。
変更を加えたファイルを以下に置いておきます。
jp/progression/events/progression_event
jp/progression/events/SceneEvent
jp/progression/scenes/SceneObject
未だ as ファイルだと表示できなくて、拡張子を .txt に変えてますが。
リンク先を trac.progression.jp の方に変更しました。
まとめてダウンロードされる場合はこちら。
eventflow.zip
以下、解説を少し。
出力結果
変更したのは SceneEvent クラスと SceneObject クラスの 2 つ。
まず SceneEvent クラスに以下を追記。
dispatchEvent() にイベントオブジェクトを渡したときに target に値が設定されていると、 dispatchEvent 内で Event.clone() が呼び出されるので、 super.target が null のときは null を返すようにしています。
もし $target が設定されていて super.target も設定されている場合は $target の値を、 $target が null でも super.target が設定されている場合は super.target の値を返します。
これは、 dispatchEvent() の内部以外での clone() メソッドの呼び出しへの配慮でもあります。
次はちょっと長いですが、 SceneObject に追加した内容。
具体的には、 addEventListener() と removeEventListener() で useCapture の値で 登録するオブジェクトを分けています。これはイベント呼び出し時にイベントフェーズによる振り分けができないためです。
dispatchEvent() 内は、親シーンへ遡ってイベントを呼び出すように変更しています。
更に、 _registerScene() と _unregisterScene() にも少し修正を加えます。
_registerScene の変更箇所
_unregisterScene の変更箇所
また、イベントフローのための親シーンの参照を登録・解除しています。
あとは、 dispatchEvent に渡されるイベントオブジェクトの bubbles の値を true に変更しました。
bubbles が false に設定された場合はイベントフローを発生させないようにしているためです。
以上で、表示オブジェクトのようなイベントフローの実装ができます。
ただ、 EventIntegrator での実装を、あまり理解せずに手を加えているので、どこかに不都合はあるかもしれないです。
すこし使い倒してみる必要はあるかと思います。
useCapture や eventPhase, bubbles あたりの値が有効化されていませんので、これを実装してみました。
変更を加えたファイルを以下に置いておきます。
jp/progression/events/progression_event
jp/progression/events/SceneEvent
jp/progression/scenes/SceneObject
リンク先を trac.progression.jp の方に変更しました。
まとめてダウンロードされる場合はこちら。
eventflow.zip
以下、解説を少し。
var sceneA:SceneObject = createScene("sceneA");
var sceneB:SceneObject = createScene("sceneB");
var sceneC:SceneObject = createScene("sceneC");
sceneA.addScene(sceneB);
trace("----");
sceneB.addScene(sceneC);
trace("----");
sceneA.removeScene(sceneB);
function createScene(name:String):SceneObject
{
var scene:SceneObject = new SceneObject(name);
scene.addEventListener(SceneEvent.SCENE_ADDED, eventHandler, false);
scene.addEventListener(SceneEvent.SCENE_REMOVED, eventHandler, false);
scene.addEventListener(SceneEvent.SCENE_ADDED, eventHandler, true);
scene.addEventListener(SceneEvent.SCENE_REMOVED, eventHandler, true);
return scene;
}
function eventHandler(event:Event):void
{
trace(event.target.name, event.currentTarget.name, event.type, event.eventPhase);
}
出力結果
sceneB sceneA sceneAdded 1 sceneB sceneB sceneAdded 2 sceneB sceneA sceneAdded 3 ---- sceneC sceneA sceneAdded 1 sceneC sceneB sceneAdded 1 sceneC sceneC sceneAdded 2 sceneC sceneB sceneAdded 3 sceneC sceneA sceneAdded 3 ---- sceneB sceneA sceneRemoved 1 sceneB sceneB sceneRemoved 2 sceneB sceneA sceneRemoved 3
変更したのは SceneEvent クラスと SceneObject クラスの 2 つ。
まず SceneEvent クラスに以下を追記。
override public function get target():Object
{
return super.target
&& progression_event::$target
|| super.target;
}
override public function get currentTarget():Object
{
return super.currentTarget
&& progression_event::$currentTarget
|| super.currentTarget;
}
override public function get eventPhase():uint
{
return progression_event::$eventPhase || super.eventPhase;
}
progression_event var $target:Object = null;
progression_event var $currentTarget:Object = null;
progression_event var $eventPhase:uint = 0;
ここで SceneObject から値に手を加えられるようにするために、
progression_event という namespace ファイルを jp/progression/events/ 内に追加しています。
package jp.progression.events
{
public namespace progression_event
= "http://www.progression.jp/events/progression_event";
}
dispatchEvent() にイベントオブジェクトを渡したときに target に値が設定されていると、 dispatchEvent 内で Event.clone() が呼び出されるので、 super.target が null のときは null を返すようにしています。
もし $target が設定されていて super.target も設定されている場合は $target の値を、 $target が null でも super.target が設定されている場合は super.target の値を返します。
これは、 dispatchEvent() の内部以外での clone() メソッドの呼び出しへの配慮でもあります。
次はちょっと長いですが、 SceneObject に追加した内容。
/**
* イベントフローのための内部参照
*/
private var $parent:SceneObject;
/**
* キャプチャ段階のイベントに登録されたリスナーを保持しておくための EventIntegrator オブジェクト
*
* コンストラクタ内に以下を追記
* $captureEvent = new EventIntegrator(this);
*/
private var $captureEvent:EventIntegrator;
override public function dispatchEvent(event:Event):Boolean
{
if (event is SceneEvent)
{
return $dispatchEvent(event as SceneEvent);
}
return super.dispatchEvent(event);
}
private function $dispatchEvent(event:SceneEvent):Boolean
{
var bool:Boolean = true;
if (event.eventPhase == EventPhase.AT_TARGET)
event.progression_event::$target = this;
event.progression_event::$currentTarget = this;
if (event.bubbles
&& $parent
&& event.eventPhase <= EventPhase.AT_TARGET)
{
var phase1:SceneEvent = event.clone() as SceneEvent;
phase1.progression_event::$eventPhase = EventPhase.CAPTURING_PHASE;
phase1.progression_event::$target
= event.progression_event::$target;
bool = $parent.dispatchEvent(phase1);
}
if (event.eventPhase == EventPhase.CAPTURING_PHASE)
{
bool = $captureEvent.dispatchEvent(event);
}
else
{
bool = super.dispatchEvent(event);
}
if (event.bubbles
&& $parent
&& event.eventPhase >= EventPhase.AT_TARGET)
{
var phase3:SceneEvent = event.clone() as SceneEvent;
phase3.progression_event::$eventPhase = EventPhase.BUBBLING_PHASE;
phase3.progression_event::$target = event.target;
bool = $parent.dispatchEvent(phase3);
}
return bool;
}
override public function addEventListener(
type:String, listener:Function, useCapture:Boolean = false,
priority:int = 0, useWeakReference:Boolean = false):void
{
if (useCapture)
{
$captureEvent.addEventListener(
type, listener, false, priority, useWeakReference);
}
else
{
super.addEventListener(
type, listener, false, priority, useWeakReference);
}
}
override public function removeEventListener(
type:String, listener:Function, useCapture:Boolean = false):void
{
if (useCapture)
{
$captureEvent.removeEventListener(type, listener, false);
}
else
{
super.removeEventListener(type, listener, false);
}
}
override public function hasEventListener(type:String):Boolean
{
return super.hasEventListener(type)
|| $captureEvent.hasEventListener(type);
}
override public function willTrigger(type:String):Boolean
{
var bool:Boolean = hasEventListener(type);
if ($parent) bool = bool || $parent.hasEventListener(type);
return bool;
}
override public function removeAllListeners(
completely:Boolean = false):void
{
super.removeAllListeners(completely);
$captureEvent.removeAllListeners(completely);
}
override public function restoreRemovedListeners():void
{
super.restoreRemovedListeners();
$captureEvent.restoreRemovedListeners();
}
ここでは EventDispatcher のメソッドをそれぞれオーバーライドして処理内容に手を加えています。具体的には、 addEventListener() と removeEventListener() で useCapture の値で 登録するオブジェクトを分けています。これはイベント呼び出し時にイベントフェーズによる振り分けができないためです。
dispatchEvent() 内は、親シーンへ遡ってイベントを呼び出すように変更しています。
更に、 _registerScene() と _unregisterScene() にも少し修正を加えます。
_registerScene の変更箇所
// イベントフロー時の参照先を登録する
scene.$parent = scene._parent;
// イベントリスナーを登録する
//scene.addExclusivelyEventListener( SceneEvent.SCENE_ADDED,
dispatchEvent, false, int.MAX_VALUE, true );
//scene.addExclusivelyEventListener( SceneEvent.SCENE_REMOVED,
dispatchEvent, false, int.MAX_VALUE, true );
//addExclusivelyEventListener( SceneEvent.SCENE_ADDED_TO_ROOT,
scene.dispatchEvent, false, int.MAX_VALUE, true );
//addExclusivelyEventListener( SceneEvent.SCENE_REMOVED_FROM_ROOT,
scene.dispatchEvent, false, int.MAX_VALUE, true );
// イベントを送出する
scene.dispatchEvent( new SceneEvent(
SceneEvent.SCENE_ADDED, true, false, scene ) );
// ルートシーンが存在し、変更されていれば
if ( root && previousRoot != root ) {
// イベントを送出する
scene.dispatchEvent( new SceneEvent(
SceneEvent.SCENE_ADDED_TO_ROOT, true, false, scene ) );
}
_unregisterScene の変更箇所
// イベントを送出する
scene.dispatchEvent( new SceneEvent( SceneEvent.SCENE_REMOVED, true, false, scene ) );
// ルートシーンが存在せず、変更されていれば
if ( !scene._root && previousRoot != scene._root ) {
// イベントを送出する
scene.dispatchEvent( new SceneEvent( SceneEvent.SCENE_REMOVED_FROM_ROOT, true, false, scene ) );
}
// イベントフローのための参照を削除
scene.$parent = null;
// イベントリスナーを解除する
//scene.completelyRemoveEventListener(
SceneEvent.SCENE_ADDED, dispatchEvent );
//scene.completelyRemoveEventListener(
SceneEvent.SCENE_REMOVED, dispatchEvent );
//completelyRemoveEventListener(
SceneEvent.SCENE_ADDED_TO_ROOT, scene.dispatchEvent );
//completelyRemoveEventListener(
SceneEvent.SCENE_REMOVED_FROM_ROOT, scene.dispatchEvent );
追加・削除された子シーンにイベントリスナーの登録を行っていた処理をコメントアウトしています。また、イベントフローのための親シーンの参照を登録・解除しています。
あとは、 dispatchEvent に渡されるイベントオブジェクトの bubbles の値を true に変更しました。
bubbles が false に設定された場合はイベントフローを発生させないようにしているためです。
以上で、表示オブジェクトのようなイベントフローの実装ができます。
ただ、 EventIntegrator での実装を、あまり理解せずに手を加えているので、どこかに不都合はあるかもしれないです。
すこし使い倒してみる必要はあるかと思います。
