Librairie pour la gestion de menus multiples

Répondre
Partager Rechercher
-----------------------------------------------------------------------
Librairie pour la gestion de menus multiples
-----------------------------------------------------------------------

La gestion des menus avec LSL n'est pas toujours évidente et plusieurs solutions ont été proposées mais aucune ne me satisfait vraiment. J'ai un peu reconsidéré le problème en essayant de créer une librairie souple et solide. Fonctionalités :

* Gestion simultanée de multiples avatars avec menus distincts,
* Gestion d'un nombre quelconque de boutons,
* Automatisation de l'écoute du Chat avec canal unique par avatar,
* Gestion du timeout pour libérer les ressources,
* Simplicité d'utilisation de la librairie

J'ai adopté la structure de commentaires de BlackShade parce que je la trouve très claire (merci à toi).

J'ai aussi abondamment commenté le code pour ceux qui désirent comprendre le fonctionnement.

-----------------------------------------------------------------------
Utilisation de la librairie
-----------------------------------------------------------------------

Vous pouvez regarder l'exemple dans l'état par défaut...

* Voici la syntaxe de l'initialisation d'un menu avec la fonction "InitMenu" :

InitMenu(clé avatar, texte du bouton, liste des boutons);


* Il faut aussi prévoir la gestion du changement de page dans l'événement "listen" dans le cas où vos menus comportent plus de 12 boutons avec la fonction "GestIndexBoutons" :

// Test de changement de page
if(llListFindList([PREVIOUS,NEXT], [message]) != -1)
GestIndexBoutons(id, message);


* La libération d'un menu se fait avec la fonction "CancelMenu" dont le seul paramètre est la clé de l'avatar :

CancelMenu(clé avatar);


* Il faut enfin renseigner l'événement "timer" si vous voulez bénéficier du timeout avec la fonction "FinMenu" :

timer() {FinMenu();}


* Si vous devez filtrer dans l'événement "listen" les canaux affectés aux menus :

listen(integer channel, string name, key id, string message) {
...
// Test des canaux du Chat
if(llListFindList(lCanalMenu, [channel]) != -1) {


-----------------------------------------------------------------------
Gestion des boutons
-----------------------------------------------------------------------

Dans le cas où il y a plus de 12 boutons j'ai considéré deux cas :

* Moins de 23 boutons : deux pages de menus avec un bouton NEXT sur la première et un bouton PREVIOUS sur la seconde
* A partir de 23 boutons : plusieurs pages de menus avec sur chacune un boutons NEXT et un bouton PREVIOUS avec un fonctionnement "circulaire".

Le nom des boutons NEXT et PREVIOUS est défini dans les paramètres (par défaut "<<<" et ">>>")


-----------------------------------------------------------------------
TimeOut
-----------------------------------------------------------------------

J'ai prévu un timeout global, il n'y a pas trop d'intérêt de différencier par avatar. A chaque initialisation d'un menu le compteur est remis à zéro pour tous les avatars. La valeur est défini dans les paramètres par la variable TIMEOUT (par défaut 30 secondes).


-----------------------------------------------------------------------

J'attends vos remarques et commentaires et si vous trouvez des bugs...


-----------------------------------------------------------------------
Script
-----------------------------------------------------------------------

A tester à plusieurs avatars

Code PHP:


//----------------------------------------------------------------------------------
//                              Test Menu V1.0
//----------------------------------------------------------------------------------


//----------------------------------------------------------------------------------
//                              LIBRARY FUNCTIONS
//----------------------------------------------------------------------------------


//---------------------------------------------------------------
//                        MENU FUNCTIONS
//---------------------------------------------------------------

// -- Initialisation menu --
// @ param [key]     clé de l'avatar
// @ param [string] texte pour le menu
// @ param 
[list] noms des boutons
InitMenu
(key idstring texte, list boutons) {
    
// Test avatar déjà présent
    
if(llListFindList(lAvaMenu, [id]) != -1CancelMenu(id);
    
// Mise en route ou réinitialisation du timer
    
llSetTimerEvent(TIMEOUT);
    
// Détermination de l'index du menu
    
integer i llGetListLength(lAvaMenu);
    
// Choix d'un canal
    
integer c GetRandomChannel();
    
// Vérification unicité de ce canal
    
while(llListFindList(lCanalMenu, [c]) != -1)
        
GetRandomChannel();
    
// Enregistrement des paramètres
    
lAvaMenu += id;
    
lBoutonsMenu += llDumpList2String(boutons"|");
    
lCanalMenu += c;
    
lTexteMenu += texte;
    
lIndexMenu += 0;
    
// Mise en place et enregistrement de l'écoute
    
lEcouteMenu += llListen(llList2Integer(lCanalMenui), ""id"");
    
// Envoi du menu
    
GestMenu(idi);
}

// -- Gestion de l'index des boutons du menu --
// @ param [key]     clé de l'avatar
// @ param [string] bouton de déplacement
GestIndexBoutons(key idstring browse) {
    
// Index du menu
    
integer i llListFindList(lAvaMenu, [id]);
    
// Index dans les boutons du menu
    
integer IndexBoutons llList2Integer(lIndexMenui);
    
// Nombre de boutons
    
integer n llGetListLength(llParseString2List(llList2String(lBoutonsMenui), ["|"], []));
    
// Navigation simple pour deux pages
    
if(23) {
        if(
IndexBoutonsIndexBoutons 0;
        else 
IndexBoutons 11;}
    
// Navigation riche pour plus de deux pages
    
else {
        if(
browse == NEXT) {
            
IndexBoutons += 10;
            if(
IndexBoutons >= nIndexBoutons 0;}
        else {
            
IndexBoutons -= 10;
            if(
IndexBoutons 0IndexBoutons 10;}
    }
    
// Mise à jour de l'index des boutons du menu
    
lIndexMenu llListReplaceList(lIndexMenu, [IndexBoutons], ii);
    
// Envoi du menu
    
GestMenu(idi);
}

//    -- Gestion menu --
// @ param [key]     clé de l'avatar
// @ param [index]     index du menu
GestMenu(key idinteger i) {
    
// Index pour les boutons
    
integer IndexBoutons llList2Integer(lIndexMenui);
    
// Liste globale des boutons
    
list lBoutons llParseString2List(llList2String(lBoutonsMenui), ["|"], []);
    
// Nombre total de boutons
    
integer n llGetListLength(lBoutons);
    
// Si plusieurs pages
    
if(12) {
        
// Que 2 pages -> navigation simple
        
if(23) {
            
// Deuxième page
            
if(IndexBoutons)
                
lBoutons = [PREVIOUS] + llList2List(lBoutonsIndexBoutons, -1);
            
// Première page
            
else
                
lBoutons llList2List(lBoutons01) + [NEXT] + llList2List(lBoutons210);}
        
// Plus de 2 pages -> navigation riche
        
else {
            list 
= [PREVIOUSllList2String(lBoutonsIndexBoutons), NEXT];
            
// Première page ou page intermédiaire
            
if(IndexBoutons 10)
                
lBoutons llList2List(lBoutonsIndexBoutons 1IndexBoutons 9);
            
// Dernière page
            
else {
                if(
IndexBoutons n)
                    
lBoutons llList2List(lBoutonsIndexBoutons 1, -1);
                else 
lBoutons l;}
        }
    }
    
// Envoi du menu
    
llDialog(idllList2String(lTexteMenui), lBoutonsllList2Integer(lCanalMenui));
}

// -- Suppression d'un menu --
// @ param [key] clé de l'avatar
CancelMenu(key id) {
    
// Index du menu
    
integer i llListFindList(lAvaMenu, [id]);
    
// On teste s'il y a quelque chose à arrêter
    
if(!= -1) {
        
// Arrêt de l'écoute du canal
        
llListenRemove(llList2Integer(lEcouteMenui));
        
// Suppression des paramètres pour ce menu
        
lAvaMenu llDeleteSubList(lAvaMenuii);
        
lBoutonsMenu llDeleteSubList(lBoutonsMenuii);
        
lCanalMenu llDeleteSubList(lCanalMenuii);
        
lEcouteMenu llDeleteSubList(lEcouteMenuii);
        
lIndexMenu llDeleteSubList(lIndexMenuii);
        
lTexteMenu llDeleteSubList(lTexteMenuii);
        
// Si c'est le dernier on arrête le timer
        
if(llGetListLength(lAvaMenu))
            
llSetTimerEvent(.0);}
}

// -- Fin du délai d'écoute des menu --
FinMenu() {
    
// Balayage de tous les menus
    
while(llGetListLength(lAvaMenu)) {
        
key k llList2Key(lAvaMenu0);
        
// On prévient l'avatar
        
llInstantMessage(llList2Key(lAvaMenu0), "Time out for dialog.");
        
// On supprime le menu
        
CancelMenu(k);}
}

// -- Fonction qui génère un canal au hasard (négatif) --
// @ return [integer] retourne le canal
integer GetRandomChannel() {
    return ~(integer)
llFrand((float)DEBUG_CHANNEL);
}    
// fonction de BlackShade NightFire

//----------------------------------------------------------------------------------
//                        LIBRARY VARIABLES
//----------------------------------------------------------------------------------

//----------------------------------------------
//                  MENUS
//----------------------------------------------
string    PREVIOUS        "<<<";    // Bouton pour page arrière
string    NEXT            ">>>";    // Bouton pour page avant
float    TIMEOUT            30.0;        // Délai pour réponse au menu

list    lCanalMenu;                    // Canaux du Chat
list    lEcouteMenu;                // Handle de l'écoute
list    lIndexMenu;                    // Index de position dans les boutons
list     lTexteMenu;                    // Texte pour le menu
list    lAvaMenu;                    // Clé de l'avatar
list    lBoutonsMenu;                // Boutons du menu



//--------------------------------------------------------------------------------------
//                         SCRIPT GLOBAL VARIABLES
//--------------------------------------------------------------------------------------

// Liste des boutons
list lBoutons;

//--------------------------------------------------------------------------------------
//        STATE default de test
//--------------------------------------------------------------------------------------
default
{
    
state_entry() {
        
// Création d'une liste de boutons pour les tests
        
integer c;
        for(
054; ++c)
            
lBoutons += (string)c;
    }

    
listen(integer channelstring namekey idstring message) {
        
// Test de changement de page pour navigation
        
if(llListFindList([PREVIOUS,NEXT], [message]) != -1)
            
GestIndexBoutons(idmessage);
        
// Traitement du bouton choisi
        
else {
            
llInstantMessage(id"Vous avez choisi le bouton " message);
            
CancelMenu(id);
        }
    }

    
touch_start(integer total_number) {
        
// Traitement pour tous les avatars qui ont cliqué
        
integer c;
        for(
0total_number; ++c)
            
InitMenu(llDetectedKey(c), "\nChoisissez une option :"lBoutons);
    }

    
timer()    {FinMenu();}

ça a l'air très bien tes menus multiples Best.
Trop tard pour moi : j'ai eu à faire récemment et je m'en suis vu. Et le besoin est rare : c'était le 1ére fois depuis 2 ans que je scripte.
Quoique j'ai du éluder quelques cas avec un dialog genre "le menu est occupé essayez plus tard".
Citation :
Publié par Elenia Boucher
ça a l'air très bien tes menus multiples Best.
Trop tard pour moi : j'ai eu à faire récemment et je m'en suis vu. Et le besoin est rare : c'était le 1ére fois depuis 2 ans que je scripte.
Quoique j'ai du éluder quelques cas avec un dialog genre "le menu est occupé essayez plus tard".
C'est vrai que le cas ne se présente pas souvent. Dans la même idée voilà une version pour un seul avatar. Elle a le mérite d'automatiser toute l'intendance et de gérer de multiples boutons.

Code PHP:


//----------------------------------------------------------------------------------
//                              Test Menu un avatar V1.0
//----------------------------------------------------------------------------------


//----------------------------------------------------------------------------------
//                              LIBRARY FUNCTIONS
//----------------------------------------------------------------------------------


//---------------------------------------------------------------
//                        MENU FUNCTIONS
//---------------------------------------------------------------

// -- Initialisation menu --
// @ param [key]     clé de l'avatar
// @ param [string] texte pour le menu
// @ param 
[list] noms des boutons
InitMenu
(key idstring texte, list boutons) {
    
// Mise en route du timer
    
llSetTimerEvent(TIMEOUT);
    
// Enregistrement des paramètres
    
lBoutonsMenu boutons;
    
iCanalMenu GetRandomChannel();
    
sTexteMenu texte;
    
iIndexMenu 0;
    
kAvaMenu id;
    
// Mise en place et enregistrement de l'écoute
    
iEcouteMenu llListen(iCanalMenu""id"");
    
// Envoi du menu
    
GestMenu();
}

// -- Gestion de l'index des boutons du menu --
// @ param [string] bouton de déplacement
GestIndexBoutons(string browse) {
    
// Nombre de boutons
    
integer n llGetListLength(lBoutonsMenu);
    
// Navigation simple pour deux pages
    
if(23) {
        if(
iIndexMenuiIndexMenu 0;
        else 
iIndexMenu 11;}
    
// Navigation riche pour plus de deux pages
    
else {
        if(
browse == NEXT) {
            
iIndexMenu += 10;
            if(
iIndexMenu >= niIndexMenu 0;}
        else {
            
iIndexMenu -= 10;
            if(
iIndexMenu 0iIndexMenu 10;}
    }
    
// Envoi du menu
    
GestMenu();
}

//    -- Gestion menu --
GestMenu() {
    
// Liste globale des boutons
    
list lBoutons lBoutonsMenu;
    
// Nombre total de boutons
    
integer n llGetListLength(lBoutonsMenu);
    
// Si plusieurs pages
    
if(12) {
        
// Que 2 pages -> navigation simple
        
if(23) {
            
// Deuxième page
            
if(iIndexMenu)
                
lBoutons = [PREVIOUS] + llList2List(lBoutonsMenuiIndexMenu, -1);
            
// Première page
            
else
                
lBoutons llList2List(lBoutonsMenu01) + [NEXT] + llList2List(lBoutonsMenu210);}
        
// Plus de 2 pages -> navigation riche
        
else {
            list 
= [PREVIOUSllList2String(lBoutonsMenuiIndexMenu), NEXT];
            
// Première page ou page intermédiaire
            
if(iIndexMenu 10)
                
lBoutons llList2List(lBoutonsMenuiIndexMenu 1iIndexMenu 9);
            
// Dernière page
            
else {
                if(
iIndexMenu n)
                    
lBoutons llList2List(lBoutonsMenuiIndexMenu 1, -1);
                else 
lBoutons l;}
        }
    }
    
// Envoi du menu
    
llDialog(kAvaMenusTexteMenulBoutonsiCanalMenu);
}

// -- Suppression d'un menu --
// @ param [key] clé de l'avatar
CancelMenu() {
    
// On teste s'il y a quelque chose à arrêter
    
if(iEcouteMenu) {
        
// Arrêt de l'écoute du canal
        
llListenRemove(iEcouteMenu);
        
iEcouteMenu 0;
        
// On arrête le timer
        
llSetTimerEvent(.0);}
}

// -- Fin du délai d'écoute des menu --
FinMenu() {
    
// On prévient l'avatar
    
llInstantMessage(kAvaMenu"Time out for dialog.");
    
// On supprime le menu
    
CancelMenu();
}

// -- Fonction qui génère un canal au hasard (négatif) --
// @ return [integer] retourne le canal
integer GetRandomChannel() {
    return ~(integer)
llFrand((float)DEBUG_CHANNEL);
}    
// fonction de BlackShade NightFire

//----------------------------------------------------------------------------------
//                        LIBRARY VARIABLES
//----------------------------------------------------------------------------------

//----------------------------------------------
//                  MENUS
//----------------------------------------------
string    PREVIOUS        "<<<";    // Bouton pour page arrière
string    NEXT            ">>>";    // Bouton pour page avant
float    TIMEOUT            30.0;        // Délai pour réponse au menu

integer    iCanalMenu;                    // Canaux du Chat
integer    iEcouteMenu;                // Handle de l'écoute
integer    iIndexMenu;                    // Index de position dans les boutons
string  sTexteMenu;                    // Texte pour le menu
list    lBoutonsMenu;                // Boutons du menu
key        kAvaMenu;                        // Clé de l'avatar



//--------------------------------------------------------------------------------------
//                         SCRIPT GLOBAL VARIABLES
//--------------------------------------------------------------------------------------

// Liste des boutons
list lBoutons;

//--------------------------------------------------------------------------------------
//        STATE default de test
//--------------------------------------------------------------------------------------
default
{
    
state_entry() {
        
// Création d'une liste de boutons pour les tests
        
integer c;
        for(
054; ++c)
            
lBoutons += (string)c;
    }

    
listen(integer channelstring namekey idstring message) {
        
// Test de changement de page pour navigation
        
if(llListFindList([PREVIOUS,NEXT], [message]) != -1)
            
GestIndexBoutons(message);
        
// Traitement du bouton choisi
        
else {
            
llInstantMessage(id"Vous avez choisi le bouton " message);
            
CancelMenu();
        }
    }

    
touch_start(integer total_number) {
        
// Traitement du menu
        
InitMenu(llDetectedKey(0), "\nChoisissez une option :"lBoutons);
    }

    
timer()    {FinMenu();}

Bonjour,

super boulot !!
Juste une petite question :

je ne comprends pourquoi, tu écris
Code:
iEcouteMenu += llListen(iCanalMenu, "", id, "");
au lieu de
Code:
iEcouteMenu = llListen(iCanalMenu, "", id, "");
pourquoi utiliser +=

Si j'ai plusieurs clics,que va devenir iEcouteMenu?

je comprends pas
Citation :
Publié par XanorK

je ne comprends pourquoi, tu écris
Code:
iEcouteMenu += llListen(iCanalMenu, "", id, "");
au lieu de
Code:
iEcouteMenu = llListen(iCanalMenu, "", id, "");
pourquoi utiliser +=

Si j'ai plusieurs clics,que va devenir iEcouteMenu?

je comprends pas
Bien vu le bug ! J'ai dégradé la version pour plusieurs avatars pour la rendre mono avatar et j'ai raté ce truc. merci
Ah oui ?
Donc si on écoute plusieurs avatars, il faut additionner les listen?

parce que tu spécifie l'ID dans le llListen ?



Code:
// Arrêt de l'écoute du canal
        llListenRemove(llList2Integer(lEcouteMenu, i));
ceci fait-il évoluer iEcouteMenu ? y compris en cas de time out ?


/me va peut-etre aller au cours de ce soir sur les menus finalement
@XanorK

Est-ce que tu as vu qu'il y a deux scripts différents :
  • le premier qui concerne plusieurs avatars disposant d'un menu simultanément,
  • le second qui gère seulement un avatar avec un menu à un moment donné
Dans le premier cas les écoutes sont empilées dans la liste iEcouteMenu et pas "ajoutés" et ensuite utilisées pour supprimer les écoutes,

Dans le second cas il n'y a qu'une valeur mémorisée dans la variable iEcouteMenu.

En espérant que ça éclaire ta lanterne
Citation :
Publié par bestmomo
Dans le premier cas les écoutes sont empilées dans la liste iEcouteMenu et pas "ajoutés" et ensuite utilisées pour supprimer les écoutes
Précisément, quelle différence entre empilées et ajoutées. c'est le += qui me perturbe


XanorK
Citation :
Publié par XanorK
Précisément, quelle différence entre empilées et ajoutées. c'est le += qui me perturbe


XanorK
J'ai l'impression qu'on s'empêtre un peu au niveau du vocabulaire. Pour la notation les deux sont équivalentes :

lEcouteMenu += llListen(iCanalMenu, "", id, "");

lEcouteMenu = lEcouteMenu + llListen(iCanalMenu, "", id, "");


Donc on "ajoute" bien un item dans la liste "lEcouteMenu" . Je voulais juste dire qu'on ajoute pas les deux valeurs comme dans ce cas :

iEcouteMenu += llListen(iCanalMenu, "", id, "");
Dans ce cas c'est une simple variable de type "integer" et la nouvelle valeur vient s'ajouter à la précédente.

J'espère que cette fois on se comprend
Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés