Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Une classe Barre de menus

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par Nataly, le 07 septembre 2011

(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.

Je considère par ailleurs que vous savez construire une classe dans les principes fondamentaux et que vous avez une pratique courante d'AS3.



Rêver


Prenons le toujours même menu pour objectif :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.


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 !

1) fichier à charger, numéro de page où se rendre, symbole à instancier
2) s'il s'agit d'une commande