I. Genèse▲
Il existe maintenant de nombreuses solutions POO pour gérer la propagation des événements dans nos applications ActionScript. Et il est certain que depuis l'apparition des « .onPress » et « .onEnterFrame » dans FLASH MX, j'ai eu le temps d'évoluer au niveau de ma conception d'applications basées sur une communication dite « évènementielle » entre les différents objets qui les composent.
Avec l'arrivée de FLASH MX2004 et de ses composants, c'est la classe EventDispatcher qui est apparue et qui sans être une classe native a laissé un peu en recul l'utilisation de la méthode simple et efficace que propose la classe AsBroadcaster documentée seulement depuis FLASH 8.
Nous nous rendons vite compte que la classe EventDispatcher avec son « mixin » et son code parfois un peu alambiqué se base légèrement sur ce que le W3C propose au niveau des spécifications du DOM Level2 desévénements. Nous retrouvons bien l'utilisation des méthodes addEventListener, removeEventListener et dispatchEvent qui sont en fait exactement les noms préconisés par l'interface EventTarget définie dans les indications pour le langage basé ECMAScript. Maintenant en regardant de plus près les spécifications complètes, il est clair que Macromedia est allé peut-être un peu vite… un peu trop vite ? Je ne pense pas, car il y a des contraintes dans tout travail et parfois il faut, à mon avis, faire avec (mais ceci est une autre histoire :D)
Depuis, le temps passe tranquillement et j'ai pu apprécier d'autres systèmes évènementiels comme celui de Pixlib que j'ai pu utiliser il y a quelques mois et qui est vraiment intéressant, mais c'est surtout avec l'arrivée de l'AS3 et de son nouveau système évènementiel que j'ai vraiment eu le déclic face à un système évènementiel que j'avais déjà commencé à mettre en place peu avant l'arrivée de la première alpha de Flex Builder 2 … Finalement, il semble que je n'étais pas dans une si mauvaise voie que cela.
M'inspirant tout d'abord d'un framework JAVA open source lui-même basé sur le framework COCOA et ensuite de ce que le package flash.events du framework AS3, j'ai pu mettre en place les bases du système évènementiel de VEGAS. Maintenant il serait peut-être temps après cette petite description du pourquoi et du comment de rentrer un peu plus dans des explications plus techniques à ce sujet :)
Je vais commencer par la dualité qu'il existe dans un système évènementiel entre l'objet qui va « émettre » des événements et les objets qui vont les réceptionner et en fonction de cela réagir selon le contexte de celui-ci.
Pour reprendre simplement tout cela par le code et en suivant exactement ce que propose le W3C là-dessus, je peux résumer ce principe en trois interfaces :
- EventTarget : l'interface des diffuseurs d'événements ;
- EventListener : l'interface de ceux qui vont être notifiés des événements ;
- Event : l'interface qui représente l'événement qui est envoyé vers les EventListener par un EventTarget.
J'ai essayé comme je le dis depuis le début de cet article, d'utiliser au « maximum » ce que propose le W3C même si je n'ai pas pu m'empêcher d'ajouter quelques fonctionnalités inspirées à droite et à gauche ou tout simplement par des besoins propres à mon travail quotidien.
Nous nous retrouvons face à 3 types d'objets qui vont avoir chacun leur importance, chacun leur champ d'action et leur façon de fonctionner. J'ai déjà expliqué dans un article sur la classe BasicEvent tout le concept qui tourne autour d'un événement « typé ». Je vais donc me concentrer maintenant sur l'interface EventListener et surtout sur l'interface EventTarget qui permet finalement d'utiliser la classe EventDispatcher (pas celle de Adobe/Macromedia, mais celle de VEGAS :)).
II. Les interfaces EventListener, EventTarget▲
L'interface EventListener est vraiment la plus simple, il suffit de la regarder quelques instants pour cerner l'essentiel à son sujet.
import
vegas.
events.
Event;
interface
vegas.
events.
EventListener {
/**
* This method is called whenever an event occurs of the type for which the EventListener interface was registered.
*/
function
handleEvent
(
e:
Event) ;
}
Tout se résume dans cette simple méthode « handleEvent » qui permet à l'écouteur d'intercepter un événement.
En regardant de plus près dans la classe EventDispatcher de Macromedia, nous retrouvons la possibilité de cibler un objet « écouteur » qui aurait une méthode handleEvent()… Mais cette fonctionnalité est vraiment très mal documentée et surtout avec l'arrivée du typage fort en AS2, il est vraiment dommage que Macromedia n'ait pas cherché à approfondir un tout petit peu plus cette spécification.
Dans VEGAS, un objet sera de type EventListener s'il implémente l'interface EventListener et s'il possède du même coup la méthode handleEvent(e:Event). À partir de ce principe, l'interface EventListener devient un contrat pour le code de vos applications et permet d'identifier correctement tous les objets qui doivent recevoir et réagir à des événements.
Il sera possible bien entendu d'avoir des écouteurs qui vont utiliser une autre méthode que la méthode handleEvent avec une notion de délégation avancée qui permet de créer des EventListener virtuels … mais je vous en reparlerai après avec la classe Delegate, chaque chose en son temps :D
Regardons maintenant de plus près l'interface EventTarget :
import
vegas.
events.
Event;
import
vegas.
events.
EventListener;
interface
vegas.
events.
EventTarget {
function
addEventListener
(
eventName:
String
,
listener:
EventListener,
useCapture:
Boolean
,
priority:
Number
,
autoRemove:
Boolean
):
Void
;
function
dispatchEvent
(
event,
isQueue:
Boolean
,
target
,
context):
Event ;
function
removeEventListener
(
eventName:
String
,
listener,
useCapture:
Boolean
):
EventListener ;
}
Vous vous rendez compte si vous avez un peu l'habitude d'utiliser le système EventDispatcher AS2 de Macromedia que les méthodes addEventListener, dispatchEvent et removeEventListener de mon interface ont légèrement plus de paramètres qu'avant. Mais finalement, lorsque l'on regarde ce que propose le tout nouveau framework AS3, c'est pratiquement ce qu'il faut actuellement pour enregistrer des écouteurs et dispatcher correctement des événements.
III. L'interface IEventDispatcher▲
À noter que l'interface EventTarget est vraiment la base des émetteurs d'événements de VEGAS et qu'en fait, il faut rapidement se tourner vers l'interface beaucoup plus complète IEventDispatcher qui permet d'implémenter complètement des objets de type EventDispatcher. À noter que dans Vegas, comme en AS3, la classe EventDispatcher est instanciable, mais pas que cela…
Voici donc l'interface vraiment plus complète IEventDispatcher :
import
vegas.
data
.
Set;
import
vegas.
events.
EventListener;
import
vegas.
events.
EventListenerCollection;
import
vegas.
events.
EventTarget;
interface
vegas.
events.
IEventDispatcher extends
EventTarget {
// function addEventListener( eventName:String, listener:EventListener, useCapture:Boolean, priority:Number, autoRemove:Boolean):Void ;
function
addGlobalEventListener
(
listener:
EventListener,
priority:
Number
,
autoRemove:
Boolean
):
Void
;
// function dispatchEvent(event, isQueue:Boolean, target, context):Event ;
function
getEventListeners
(
eventName:
String
):
EventListenerCollection ;
function
getGlobalEventListeners
(
):
EventListenerCollection ;
function
getRegisteredEventNames
(
):
Set ;
function
hasEventListener
(
eventName:
String
):
Boolean
;
// function removeEventListener(eventName:String, listener, useCapture:Boolean):EventListener ;
function
removeGlobalEventListener
(
listener ):
EventListener ;
function
toString
(
):
String
;
}
Il est pratique de pouvoir hériter dans l'interface IEventDispatcher de l'interface EventTarget, j'attends avec impatience le multihéritage des interfaces en AS3 pour approfondir mes besoins au niveau des interfaces dans VEGAS, car je peux difficilement inclure tout ce que je voudrais dans l'interface de mes IEventDispatcher. À ce sujet, j'ai des tas de choses à dire, mais je pense que je vais m'arrêter là, car sinon je n’en finirai jamais avec cet article.
Le plus important maintenant se trouve au niveau de la méthode addEventListener, le premier paramètre de cette méthode et le « eventName » ou si vous préférez, le « type » de l'événement alors que le second paramètre de la méthode sera principalement un objet qui implémente l'interface EventListener. Il n'est plus possible dans VEGAS d'enregistrer un objet qui n'aura pas l'étiquette d'un écouteur de type EventListener !
L'interface IEventDispatcher est bien entendu utilisée par la classe vegas.events.EventDispatcher, mais aussi la classe abstraite AbstractCoreEventDispatcher qui utilise EventDispatcher par composition. Je me suis rendu compte que la classe AbstractCoreEventDispatcher devient très vite indispensable pour pouvoir à tout moment passer le système évènementiel local d'un objet vers un système évènementiel global.
Je vais finir cet article qui résume l'intérêt de bien structurer son code autour d'interfaces solides, avec ici les interfaces Event, EventTarget et EventListener et j'espère pouvoir très vite revenir vers vous pour la suite des aventures du package vegas.events :)
Voici un petit exemple d'utilisation que vous pouvez retrouver dans le répertoire bin/test sur le SVN de OSFlash de VEGAS :
import
vegas.
events.
BasicEvent ;
import
vegas.
events.
Delegate ;
import
vegas.
events.
Event ;
import
vegas.
events.
EventDispatcher ;
import
vegas.
events.
EventListener ;
// ----o Creation de la fonction utilisée par l'écouteur à la réception de l'événement.
var
debug =
function
(
ev:
Event) {
trace
(
"debug :: "
+
this
+
" : "
+
ev.
getType
(
)) ;
}
// ----o Creation de l'écouteur
var
listener:
EventListener =
new
Delegate
(
this
,
debug) ;
// ----o Creation du diffuseur d'événement
var
dispatcher:
EventDispatcher =
new
EventDispatcher
(
) ;
// ----o Ajout d'un écouteur dans la liste de l'émetteur d'événement pour l'événement de type "onTest".
dispatcher.
addEventListener
(
"onTest"
,
listener) ;
// ----o Mise en place d'un événement
var
e:
Event =
new
BasicEvent
(
"onTest"
,
this
,
"Hello World"
) ;
// ----o Lancer la propagation de l'événement.
dispatcher.
dispatchEvent
(
e) ;
IV. Conclusion▲
Dans un prochain article, je vais essayer de vous parler tout particulièrement des nombreuses possibilités que vous offre la classe EventDispatcher.