Une classe Barre de menus
(re ?)Bonjour à tous
L'objet de ce tuto est de proposer une technique pour faire de la barre de menus.
De nombreuses et diverses voies s'offrent à nous pour atteindre l'objectif et nous en avons déjà explorées quelques-unes à l'occasion d'un précédent tuto dont la conclusion était, qu'au fin fond du final, la classe reste le meilleur recours.
Ce texte prend donc la suite et considère que vous vous y reporterez en cas de besoin, en effet je ne reprendrai pas le pourquoi du comment de la construction proprement dite, puisqu'elle a y été expliquée au pas à pas.
Je reprendrai aussi la même terminologie.
Rêver
Prenons le toujours même menu pour objectif :
En considérant qu'on souhaite décrire la barre via un tableau d'objets et qu'à chaque bouton de commande correspond l'url d'un fichier à charger, rêvons de la classe (Menu
) qui nous arrangerait et de son “mode d'emploi”.
On se propose de l'utiliser comme suit :
// un tableau d'objets pour décrire le menu var _tbTitresLiens:Array=[ [{lbl:"Accueil"}], [{lbl:"Galeries"},{lbl:"Nature",ref:"nature.swf"},{lbl:"fleurs",ref:"fleurs.swf"},{lbl:"Divers",ref:"divers.swf"}], [{lbl:"Infos"},{lbl:"du jour",ref:"truc.swf"},{lbl:"du mois",ref:"truc.swf"}], [{lbl:"L'équipe"},{lbl:"machin",ref:"truc.swf"},{lbl:"chose",ref:"truc.swf"}]]; //créer un "clip barre de menus" via la classe Menu var menu:Menu = new Menu(_tbTitresLiens,Mv_BtMenu,Mv_BtCmd); // afficher la barre de menus addChild(menu); menu.x = menu.y = 10; // disposer // ajouter des écouteurs menu.addEventListener(Menu.CLIC_MENU,clicMenu);// clic sur bouton de menu menu.addEventListener(Menu.CLIC_CMD,clicCmd);// clic sur bouton de commande // les fonctions de rappel function clicMenu(e:Event):void { trace(e.target.txtTitre.text); } function clicCmd(e:Event):void { trace(e.target.ref); }
la classe Menu
Pour fabriquer une une barre de menu, de quoi a-t-on besoin ?
• Des symboles à partir desquels instancier les boutons qui la constituent.
• Des infos qui la caractérisent : intitulés des boutons d'une part ainsi que ce qui fera leur spécificité quand on les sollicite1), d'autre part.
C'est pourquoi on se propose d'utiliser la classe Menu comme ça :
var menu:Menu = new Menu(_tbTitresLiens,Mv_BtMenu,Mv_BtCmd);
Le constructeur se débrouillera donc avec les objets du tableau et les deux symboles concernés.
Il disposera les boutons de menus, préparera les boutons de commande, les stockera au passage dans un tableau… exactement sur le même principe qu'au chapitre précédent.
La seule chose à réfléchir c'est ce qu'il se passera au clic sur les boutons…
Dans un premier temps, vérifions qu'on sait afficher les boutons de menus et déployer les commandes qui en dépendent au survol. Pour le clic, un trace fera temporairement l'affaire.
package { import flash.display.Sprite; import flash.display.MovieClip; import flash.events.MouseEvent; import flash.events.Event; import fl.transitions.*; import fl.transitions.easing.*; public class Menu extends Sprite { // puisqu'on n'utilise pas la tête de lecture private var _tbMenus:Array; public function Menu(ptbDescript:Array,pBouton:Class,pCmd:Class,pDeltaY:Number=5) { _tbMenus=new Array();// tableau 2D de clips var tbTemp:Array;// tableau de clips menu (un boutons des commandes) var btTemp:MovieClip; var nbMenus:int=ptbDescript.length;// nombre de menus for (var i:int =0; i<nbMenus; i++) { // contient le bouton de menu et contiendra aussi les boutons de cmd; var menu:MovieClip=new MovieClip(); tbTemp = new Array();// un nouveau tableau 'menu' btTemp=new pBouton();// le bouton btTemp.txtTitre.text=ptbDescript[i][0].lbl;// son intitulé btTemp.addEventListener(MouseEvent.CLICK,qdClicMenu); menu.addChild(btTemp); tbTemp.push(btTemp); //ajoute bouton à 'menu'; addChild(menu); //// ajoute le menu à la barre; menu.y=i*(btTemp.height+pDeltaY); menu.idx=i;// pour identifier (plus tard)le tableau à utiliser dans _tbMenus menu.addEventListener(MouseEvent.ROLL_OVER,qdSurvolMenu); menu.addEventListener(MouseEvent.ROLL_OUT,qdQuitteMenu); // les boutons de commandes pour chaque menu; var max:int=ptbDescript[i].length;// nombre de commandes for (var k:int=1; k<max; k++) {// btTemp=new Mv_BtCmd();// nouveau bouton de commande btTemp.txtTitre.text=ptbDescript[i][k].lbl;// son intitulé btTemp.ref=ptbDescript[i][k].ref;// la référence à associer btTemp.addEventListener(MouseEvent.CLICK,qdClicCmd); tbTemp.push(btTemp); } _tbMenus[i]=tbTemp;// ajouter le menu à la barre de menus } } //=================== Rappel ============================= //=================== Survole private function qdSurvolMenu(me:MouseEvent):void { var cont:MovieClip=MovieClip(me.currentTarget); var max:int=_tbMenus[cont.idx].length; var largBtMenu:Number=_tbMenus[cont.idx][0].width; for (var i:int= 1; i<max; i++) { var cmd:Mv_BtCmd=Mv_BtCmd(_tbMenus[cont.idx][i]); cont.addChild(cmd); //cmd.x = (i - 1) * cmd.width + largBtMenu; cmd.tw = new Tween(cmd,"x",Bounce.easeOut,0,(i - 1) * cmd.width + largBtMenu,1,true); } } //=================== Quitte private function qdQuitteMenu(me:MouseEvent):void { var max:int=_tbMenus[me.currentTarget.idx].length; for (var i:int= 1; i<max; i++) { // Aller chercher - et virer - depuis _tbMenus les boutons de commande du menu considéré (donc à partir de l'index 1) var cmd:Mv_BtCmd=Mv_BtCmd(_tbMenus[me.target.idx][i]); me.currentTarget.removeChild(cmd); } } } }
Temporairement donc, le temps de vérifier, que jusque là… ça va
private function qdClicMenu(me:MouseEvent):void { trace("clic menu"; } private function qdClicCmd(me:MouseEvent):void { trace("clic cmd"); }
Diffuser malin
Maintenant que ça marche, occupons nous du nerf de la guerre pour une barre de menus : la gestion du clic sur ses boutons.
Tel qu'on s'y est pris, depuis le fichier maitre on ne peut pas monter un écouteur par bouton de commande vu qu'on n'y a pas accès (et ça nous arrange, pcq… pff le boulot !). C'est donc directement sur l'instance menu
qu'on va monter le ou les écouteurs.
La question reste : comment connaitre le bouton cliqué et2) l'url à charger (ou n'importe quelle autre donnée qu'on aurait choisie de transmettre) ?
Eh oui : si on se contente de diffuser un événement au clic sur chacun des boutons, comme ça par exemple (pour aller vite) :
private function qdClicMenu(me:MouseEvent):void { dispatchEvent(new Event("clicMenu",true)); } private function qdClicCmd(me:MouseEvent):void { dispatchEvent(new Event("clicCommande",true)); }
C'est bel et bien le menu qui diffuse, quoi qu'il arrive.
A l'écoute, target
et currentTarget
renverront donc le menu lui même, et aucun moyen de savoir quel bouton a été cliqué
.fla
menu.addEventListener("clicMenu",clicMenu); menu.addEventListener("clicCommande",clicCmd); function clicMenu(e:Event):void { trace("menu "+e.target); trace("menu "+e.currentTarget); } function clicCmd(e:Event):void { trace("cmd "+e.target); trace("cmd "+e.currentTarget); }
menu [object Menu] menu [object Menu] cmd [object Menu] cmd [object Menu]
Remédier au problème c'est tout bêtement faire diffuser l'événement par le bouton lui même.
private function qdClicMenu(me:MouseEvent):void { me.currentTarget.dispatchEvent(new Event("clicMenu",true)); } private function qdClicCmd(me:MouseEvent):void { me.currentTarget.dispatchEvent(new Event("clicCommande",true)); }
Prenez garde de bien utiliser currentTarget si vous voulez ne pas avoir de mauvaises surprises un jour où vous ferez du bouton sophistiqué avec des clips contenus.
menu [object Mv_BtMenu] menu [object Menu] cmd [object Mv_BtCmd] cmd [object Menu]
Et voilà, si on peut récupérer le clip bouton, on a accès à toutes ses propriétés.
Mission accomplie !
… Ou presque… Juste histoire de faire pro (et de respecter les conventions ) passons les finitions en déclarant deux constantes statiques de type String (pour les événements) :
.as
public static const CLIC_MENU:String = "clicMenu"; public static const CLIC_CMD:String = "clicCmd"; […] private function qdClicMenu(me:MouseEvent):void { me.currentTarget.dispatchEvent(new Event(CLIC_MENU,true)); } private function qdClicCmd(me:MouseEvent):void { me.currentTarget.dispatchEvent(new Event(CLIC_CMD,true)); }
.fla
menu.addEventListener(Menu.CLIC_MENU,clicMenu); menu.addEventListener(Menu.CLIC_CMD,clicCmd); function clicMenu(e:Event):void { trace(e.target.txtTitre.text); } function clicCmd(e:Event):void { trace(e.target.ref); }
Finitions
Et puis, finitions pour finitions, c'est tout de même trop bête de se contraindre à passer des objets qui ont une propriété ref. Pourquoi pas url, ou adresse, et pourquoi une seule ? Si un jour ça nous arrangeait de passer deux informations ou plus !…
En convenant que les propriétés des objets sont “transférées” sur le clip-bouton on se simplifie considérablement la vie à l'utilisation.
Il suffit alors, par exemple, de décider d'une propriété choz
sur le premier bouton pour la retrouver sur ledit bouton :
var _tbTitresLiens:Array=[ [{lbl:"Accueil",choz:"machin"}], // <-- Premier objet, on ajoute une propriété choz === [suite] === function clicMenu(e:Event):void { trace(e.target.lbl); trace("--> "+e.target.choz);// on la retrouve ici }
Accueil --> machin
Un malheureux for-in pour boucler sur les propriétés de chaque objet du tableau et le tour est joué :
public function Menu(ptbDescript:Array,pBouton:Class,pCmd:Class,pDeltaY:Number=5) { trace("yep"); _tbMenus=new Array();// tableau 2D de clips var tbTemp:Array;// tableau de clips menu (un boutons des commandes) var btTemp:MovieClip; var nbMenus:int = ptbDescript.length;// nombre de menus var prop:String;// vairable de boucle for-in for (var i:int =0; i<nbMenus; i++) {// // contient le bouton de menu et contiendra aussi les boutons de cmd; var menu:MovieClip=new MovieClip(); tbTemp = new Array();// un nouveau tableau 'menu' btTemp=new pBouton();// le bouton btTemp.txtTitre.text = ptbDescript[i][0].lbl;// son intitulé ICI for (prop in ptbDescript[i][0]) { btTemp[prop] = ptbDescript[i][0][prop]; } btTemp["truc"] = 567;// son intitulé btTemp.addEventListener(MouseEvent.CLICK,qdClicMenu); menu.addChild(btTemp); tbTemp.push(btTemp); //ajoute bouton à 'menu'; addChild(menu); //// ajoute le menu à la barre; menu.y=i*(btTemp.height+pDeltaY); menu.idx = i;// pour identifier (plus tard)le tableau à utiliser dans _tbMenus menu.addEventListener(MouseEvent.ROLL_OVER,qdSurvolMenu); menu.addEventListener(MouseEvent.ROLL_OUT,qdQuitteMenu); // les boutons de commandes pour chaque menu; var max:int = ptbDescript[i].length;// nombre de commandes for (var k:int=1; k<max; k++) {// btTemp=new Mv_BtCmd();// nouveau bouton de commande btTemp.txtTitre.text = ptbDescript[i][k].lbl;// son intitulé ICI for (prop in ptbDescript[i][k]) { btTemp[prop] = ptbDescript[i][k][prop]; } btTemp.addEventListener(MouseEvent.CLICK,qdClicCmd); tbTemp.push(btTemp); } _tbMenus[i] = tbTemp;// ajouter le menu à la barre de menus } }
Et voilà, pour le reste c'est à votre libre créativité amusez vous bien !
