Index   Table   Précédent   Suivant

5. Widgets du Panel

On a déjà vu que les boutons doivent obligatoirement être créés dans un Panel, dont le rôle est de placer les boutons et de leur distribuer les évènements.

Dans ce chapitre, on présente d'autres widgets, qui doivent aussi être créés dans un Panel : le message sur une ligne, le champ de saisie, et le bouton à bascule.

Index   Table   Précédent   Suivant

5.1. Message sur une ligne

Le widget Message sert à afficher une légende sur une seule ligne, en fonte normale (par défaut) ou en gras.
    He_node *mess;
    mess = HeCreateMessage (panel);
    HeSetMessageLabel (mess, "Le texte");
    HeSetMessageBold (mess, TRUE);
Un raccourcis équivalent est
    mess = HeCreateMessageP (panel, "Le texte", TRUE);
On peut récupérer le texte et l'attribut gras par
    char *texte = HeGetMessageLabel (mess);
    int   gras  = HeGetMessageBold (mess);
Dans l'exemple suivant, on affiche 4 boutons sur une ligne et un Message sur la ligne du dessous ; la callback attachée aux boutons récupère le nom du bouton pressé, et change le texte du Message.

examples/panel/message.c
    /* examples/panel/message.c */
    
    #include <helium.h>
    
    He_node *princ, *panel, *mess;
    
    void butt_proc (He_node *hn)
    {
        char buf[200],
             *nom = HeGetButtonLabel (hn);
        
        sprintf (buf, "Vous avez cliqué sur \"%s\"", nom);
        HeSetMessageLabel (mess, buf);
        
        if (!strcmp (nom, "Quit")) HeQuit(0);
    }
    
    int main (int argc, char *argv[])
    {
        HeInit (&argc, &argv);
    
        princ = HeCreateFrame ();
        HeSetFrameLabel (princ, "Message sur une ligne");
        
        panel = HeCreatePanel (princ);
        
        HeCreateButtonP (panel, "Ne pressez pas", butt_proc, NULL);
        HeCreateButtonP (panel, "sur le", butt_proc, NULL);
        HeCreateButtonP (panel, "bouton", butt_proc, NULL);
        HeCreateButtonP (panel, "Quit", butt_proc, NULL);
        
        HeSetPanelLayout(panel, HE_LINE_FEED);
    
        mess = HeCreateMessageP (panel, "Cliquez sur un bouton", FALSE);
        
        HeFit(panel);
        HeFit(princ);
        
        return HeMainLoop (princ);
    }

Chaque fois que l'on change le texte du Message, la largeur du widget est automatiquement ajustée. Par défaut, la longueur maximale du texte est limitée à 80 caractères (la suite est tronquée). Pour modifier cette limite, utiliser

    void HeSetMessageMaxLen (He_node *hn, int max_len);
    int HeGetMessageMaxLen (He_node *hn);
Pour supprimer la limite, appeler HeSetMessageMaxLen(hn, 0);

Index   Table   Précédent   Suivant

5.2. Champ de Saisie

5.2.1. Principe

Le widget Text permet d'afficher un champ de saisie ; c'est un véritable éditeur de texte sur une ligne, avec mode insertion, affichage des caractères de contrôle, sélection, double et triple clic, déplacement de sélection, copier/coller avec les boutons gauche et milieu de la souris, couper/copier/coller dans le clipboard avec ALT-x,c,v, la complétion de noms de fichiers, etc.

Il y a de très nombreuses fonctions dans l'API pour manipuler ce widget ; nous y reviendrons plus loin dans le tutorial.

    He_node *text;
    text = HeCreateText (panel);
    HeSetTextVisibleLen (text, 20);
    HeSetTextValue (text, "Texte par défaut");
Ceci crée un champ de saisie de 20 caractères de larges (mais sans limite de taille pour la chaîne de caractère saisie par l'utilisateur), avec un "Texte par défaut".

Pour récupérer la chaîne saisie, il faut faire

    char *blabla;
    blabla = HeGetTextValue (text);
La longueur de cette chaîne est
    int len = HeGetTextLen (text);
ATTENTION, la chaîne renvoyée n'est pas une copie, c'est l'adresse de la chaîne interne au widget. Cette adresse est donc très VOLATILE, car la moindre opération de l'utilisateur ou par le programme sur le widget Text peut libérer le string pointé par blabla. L'utilisation normale de blabla est donc la comparaison immédiate avec !strcmp(), ou la copie avec strdup() ou sprintf().

On peut déclencher une callback NotifyProc lorsque l'utilisateur valide avec la touche [Entrée] :

    HeSetTextNotifyProc (text, entree_proc);
Le prototype de la callback est
    void entree_proc (He_node *hn);
Dans l'exemple suivant on crée un Message, un champ de saisie, un bouton "lit valeur" et un bouton "Quit". Le texte saisi est affiché lorsque l'utilisateur clique sur le bouton ou lorsque l'utilisateur appuie sur la touche [Entrée].

examples/panel/text.c
    /* examples/panel/text.c */
    
    #include <helium.h>
    
    He_node *princ, *panel, *text;
    
    void entree_proc (He_node *hn)
    {
        printf ("[enter] : \"%s\"\n", HeGetTextValue (hn));
    }
    
    void butt_proc (He_node *hn)
    {
        char *nom = HeGetButtonLabel (hn);
        
        if (!strcmp (nom, "Quit"))
             HeQuit(0);
        else printf ("lit valeur : \"%s\"\n", HeGetTextValue (text));
    }
    
    int main (int argc, char *argv[])
    {
        HeInit (&argc, &argv);
    
        princ = HeCreateFrame ();
        HeSetFrameLabel (princ, "Champ de saisie");
        
        panel = HeCreatePanel (princ);
        
        HeCreateMessageP (panel, "Texte:", TRUE);
        
        text = HeCreateText (panel);
        HeSetTextVisibleLen (text, 20);
        HeSetTextNotifyProc (text, entree_proc);
        
        HeCreateButtonP (panel, "lit valeur", butt_proc, NULL);
        HeCreateButtonP (panel, "Quit", butt_proc, NULL);
        
        HeFit(panel);
        HeFit(princ);
        
        return HeMainLoop (princ);
    }

5.2.2. Saisie d'un entier

On peut se servir d'un widget Text pour saisir un entier. Dans l'exemple suivant on a rajouté 4 petits boutons "--", "-", "+" et "++" qui incrémentent la valeur de -10, -1, 1 et 10 respectivement :

examples/panel/entier.c
    /* examples/panel/entier.c */
    
    #include <helium.h>
    
    He_node *princ, *panel, *text;
    
    void entree_proc (He_node *hn)
    {
        int val = atoi(HeGetTextValue(hn));
    
        printf ("L'entier lu est %d\n", val);
    }
    
    void butt_proc (He_node *hn)
    {
        char *nom = HeGetButtonLabel(hn), bla[100];
        int  val  = atoi(HeGetTextValue(text));
        
        if (!strcmp (nom, "--")) val -= 10;
        else if (!strcmp (nom, "-")) val --;
        else if (!strcmp (nom, "+")) val ++;
        else if (!strcmp (nom, "++")) val += 10;
        
        sprintf (bla, "%d", val);
        HeSetTextValue (text, bla);
        
        entree_proc(text);
    }
    
    int main (int argc, char *argv[])
    {
        HeInit (&argc, &argv);
    
        princ = HeCreateFrame ();
        HeSetFrameLabel (princ, "Saisie d'un entier");
        
        panel = HeCreatePanel (princ);
        
        HeCreateMessageP (panel, "Entier:", TRUE);
        
        text = HeCreateText (panel);
        HeSetTextVisibleLen (text, 8);
        HeSetTextNotifyProc (text, entree_proc);
        HeSetTextValue (text, "100");
        
        HeCreateButtonP (panel, "--", butt_proc, NULL);
        HeCreateButtonP (panel, "-",  butt_proc, NULL);
        HeCreateButtonP (panel, "+",  butt_proc, NULL);
        HeCreateButtonP (panel, "++", butt_proc, NULL);
        
        HeFit(panel);
        HeFit(princ);
        
        return HeMainLoop (princ);
    }

5.2.3. Completion d'un nom de fichier

Lorsqu'un champ de saisie sert à donner un nom de fichier, on peut demander à Helium d'essayer de compléter tout seul le nom du fichier en faisant
    HeSetTextCompletion (text, TRUE);
Chaque fois que l'on tape un caractère à la fin de la ligne, Helium parcourt le système de fichier, regarde si le chemin est bon, et tente de compléter le nom du fichier ou du répertoire. Si la solution est unique, Helium rajoute tout le reste du mot. Si la solution est multiple, Helium rajoute la partie commune. La partie rajoutée est sélectionnée, si bien que lorsque vous décidez d'écrire autre chose, ce que vous écrivez écrase immédiatement la sélection. L'exemple suivant permet de tester la completion :

examples/panel/completion.c
    /* examples/panel/completion.c */
    
    #include <helium.h>
    
    He_node *princ, *panel, *text;
    
    void entree_proc (He_node *hn)
    {
        printf ("Nom de fichier lu : \"%s\"\n", HeGetTextValue (hn));
    }
    
    int main (int argc, char *argv[])
    {
        HeInit (&argc, &argv);
    
        princ = HeCreateFrame ();
        HeSetFrameLabel (princ, "Complétion");
        
        panel = HeCreatePanel (princ);
        
        HeCreateMessageP (panel, "Entrez un chemin de fichier :", TRUE);
        HeSetPanelLayout (panel, HE_LINE_FEED);
        
        text = HeCreateText (panel);
        HeSetTextVisibleLen (text, 50);
        HeSetTextNotifyProc (text, entree_proc);
        HeSetTextCompletion (text, TRUE);
        
        HeFit(panel);
        HeFit(princ);
        
        return HeMainLoop (princ);
    }

5.2.4. Filtrage du clavier

On peut attacher une callback KbdProc qui est appelée chaque fois qu'une touche est enfoncée ou relachée, avec
    HeSetTextKbdProc (text, clavier_proc);
Le prototype de la callback est
    int clavier_proc (He_node *hn, He_event *hev);
On reçoit chaque fois un string (et non un caractère, car une touche peut être programmée pour générer un string), et le code symbolique de la touche pressée ou relachée ; le tout est stocké dans la variable hev. La callback doit renvoyer TRUE ou FALSE ; si elle renvoie FALSE, l'évènement est intercepté : cela veut dire que lorsque la touche est enfoncée, le string reçu n'est pas inséré dans le texte du widget (cela n'a aucun effet lorsque la touche est relachée).

Dans l'exemple suivant, on affiche le string reçu ainsi que le code symbolique des touches pressées, et on filtre la lettre 'a' :

examples/panel/filtre.c
    /* examples/panel/filtre.c */
    
    #include <helium.h>
    
    He_node *princ, *panel, *text;
    
    int clavier_proc (He_node *hn, He_event *hev)
    {
        switch (hev->type) {
            case KeyPress   : printf ("Touche enfoncée"); break;
            case KeyRelease : printf ("Touche relachée"); break;
        }
        printf ("   symbole XK_%s   string \"%s\"   longueur %d\n", 
            XKeysymToString(hev->sym), hev->str, hev->len);
        
        if (strcmp(hev->str, "a") == 0) return FALSE;
        return TRUE;
    }
    
    int main (int argc, char *argv[])
    {
        HeInit (&argc, &argv);
    
        princ = HeCreateFrame ();
        HeSetFrameLabel (princ, "Filtre du clavier");
        
        panel = HeCreatePanel (princ);
        
        HeCreateMessageP (panel, "Tapez un mot avec un 'a' :", TRUE);
        HeSetPanelLayout (panel, HE_LINE_FEED);
        
        text = HeCreateText (panel);
        HeSetTextVisibleLen (text, 50);
        HeSetTextKbdProc (text, clavier_proc);
        
        HeFit(panel);
        HeFit(princ);
        
        return HeMainLoop (princ);
    }

Les touches [Tab] et [Shift][Tab] permettent de passer le focus du clavier de widget en widget. Lorsque le focus arrive sur un widget Text de cette façon, le contenu du Text est entièrement sélectionné ; de la sorte, vous pouvez remplir une suite de champs sans à chaque fois tout sélectionner avec un triple clic.

Remarque : les évènements [Tab] et [Shift][Tab] sont filtrés au niveau du Panel (c'est lui qui réparti les évènements dans les widgets dont il est propriétaire) et n'arrivent dans la KbdProc que dans le widget Text destination et lorsque la touche est enfoncée. De la sorte on peut aussi filtrer ces évènement dans la KbdProc.

Index   Table   Précédent   Suivant

5.3. Boutons à cocher

5.3.1. Principe

Le widget Toggle permet de créer un bouton à bascule, qui peut être dans un état allumé ou éteint. Le widget Toggle peut prendre 3 aspects différents, aspect que l'on choisit à la création avec
    He_node *tog;
    tog = HeCreateToggle (panel, nature);
    HeSetToggleLabel (tog, "Nom du toggle");
nature est l'une des trois constantes HE_LED, HE_CHECK et HE_RADIO. On peut être allerté chaque fois que l'utilisateur clique sur un Toggle, en lui attachant une callback NotifyProc :
    HeSetToggleNotifyProc (tog, etat_proc);
Le prototype de la callback est
    void etat_proc (He_node *hn);
hn est le Toggle. Pour connaitre l'état du Toggle, on utilise
    int HeGetToggleValue (tog);
et pour le modifier on utilise
    HeSetToggleValue (tog, value);
où value est TRUE pour allumé, FALSE pour éteint. Voici un exemple qui montre les trois catégories de Toggle et affiche l'état d'un Toggle lorsqu'on clique dessus :

examples/panel/toggle.c
    /* examples/panel/toggle.c */
    
    #include <helium.h>
    
    He_node *princ, *panel, *tmp;
    
    void etat_proc (He_node *hn)
    {
        char *nom = HeGetToggleLabel (hn);
        int  etat = HeGetToggleValue (hn);
        printf ("%s : %s\n", nom, etat ? "allumé" : "éteint");
    }
    
    int main (int argc, char *argv[])
    {
        HeInit (&argc, &argv);
    
        princ = HeCreateFrame ();
        HeSetFrameLabel (princ, "Toggle");
        
        panel = HeCreatePanel (princ);
        HeSetPanelLayout (panel, HE_VERTICAL);    
        
        tmp = HeCreateToggle (panel, HE_LED);
        HeSetToggleLabel (tmp, "Toggle HE_LED");
        HeSetToggleNotifyProc (tmp, etat_proc);
        
        tmp = HeCreateToggle (panel, HE_CHECK);
        HeSetToggleLabel (tmp, "Toggle HE_CHECK");
        HeSetToggleNotifyProc (tmp, etat_proc);
        
        tmp = HeCreateToggle (panel, HE_RADIO);
        HeSetToggleLabel (tmp, "Toggle HE_RADIO");
        HeSetToggleNotifyProc (tmp, etat_proc);
        
        HeFit(panel);
        HeFit(princ);
        
        return HeMainLoop (princ);
    }

5.3.2. Regroupement

Les 3 catégories de Toggle ont été créées dans un but précis : Pour les Toggle de catégorie HE_RADIO, il faut dire à Helium quels sont les Toggle à regrouper dans un ensemble, pour qu'il puisse automatiquement éteindre un Toggle lorsqu'un autre est allumé. Helium mémorise un tel ensemble sous forme d'une liste circulaire, chaque Toggle du groupe pointant sur le suivant. Pour racrocher un Toggle ou un groupe à autre Toggle ou à un autre groupe, on utilise
    HeGroupToggle (h1, h2);
où l'un des Toggle h1 ou h2 (ça n'a pas d'importance) désigne un membre de chaque groupe ; la seule contrainte est que les deux groupes soient distincts avant l'appel. On peut isoler un Toggle de groupe avec
    HeUngroupToggle (hn);
(c'est fait automatiquement lorsqu'on détruit un Toggle). Le fait de regrouper des Toggle n'a aucun effet sur leur état : il faut donc veiller à ce qu'ils soient tous à FALSE (sauf éventuellement l'un deux) quand on les groupe (Ils sont à FALSE par défaut à la création). On peut ensuite utiliser les deux fonctions de haut niveau suivantes :
    He_node *HeGetToggleLighted (He_node *hn);
    void HeSetToggleLighted (He_node *hn);
La première renvoie le toggle du groupe qui est allumé, ou NULL si aucun n'est allumé ; le second allume le Toggle hn, éteint celui qui était allumé et appelle la callback NotifyProc de chacun des deux Toggle.

Remarque : on peut utiliser ces fonctions sur les 3 catégories de Toggle (et même grouper des HE_LED et HE_CHECK) ; on peut en particulier utiliser HeSetToggleLighted à la place de HeSetToggleValue si on veut que la callback NotifyProc soit appelée.

Dans l'exemple suivant, on crée un groupe de HE_RADIO et on allume le premier, et dans la callback on affiche l'état des Toggle :

examples/panel/radio.c
    /* examples/panel/radio.c */
    
    #include <helium.h>
    
    He_node *princ, *panel, *tog1, *tog2, *tog3;
    
    void etat_proc (He_node *hn)
    {
        char *nom = HeGetToggleLabel (hn);
        int  etat = HeGetToggleValue (hn);
        printf ("%s : %s\n", nom, etat ? "allumé" : "éteint");
    }
    
    int main (int argc, char *argv[])
    {
        HeInit (&argc, &argv);
    
        princ = HeCreateFrame ();
        HeSetFrameLabel (princ, "Toggle radio");
        
        panel = HeCreatePanel (princ);
        HeCreateMessageP (panel, "Radio :", TRUE);
       
        tog1 = HeCreateToggle (panel, HE_RADIO);
        HeSetToggleLabel (tog1, "Choix 1");
        HeSetToggleNotifyProc (tog1, etat_proc);
        
        tog2 = HeCreateToggle (panel, HE_RADIO);
        HeSetToggleLabel (tog2, "Choix 2");
        HeSetToggleNotifyProc (tog2, etat_proc);
        HeGroupToggle (tog1, tog2);
        
        tog3 = HeCreateToggle (panel, HE_RADIO);
        HeSetToggleLabel (tog3, "Choix 3");
        HeSetToggleNotifyProc (tog3, etat_proc);
        HeGroupToggle (tog2, tog3);
        
        HeSetToggleLighted (tog1);
        
        HeFit(panel);
        HeFit(princ);
        
        return HeMainLoop (princ);
    }

5.3.3. Raccourcis

La fonction suivante crée un Toggle de nature HE_LED, lui affecte un label, une callback NotifyProc et une valeur :
    He_node *HeCreateToggleLedP (He_node *owner, char *label, 
                He_notify_proc proc, int value)
Il en va de même pour cette fonction qui crée un Toggle de nature HE_CHECK :
    He_node *HeCreateToggleCheckP (He_node *owner, char *label, 
                He_notify_proc proc, int value)
Enfin, cette fonction crée un ensemble de Toggles de nature HE_RADIO (à choix unique). Lors de la création d'un groupe, il faut passer NULL au paramètre group pour le premier élément, puis aux éléments suivant il faut passer le premier élément au paramètre group. Le premier élément du groupe est automatiquement mis à vrai, les autres à faux. Ont peut alors utiliser HeSetToggleLighted pour changer le Toggle qui est allumé.
    He_node *HeCreateToggleRadioP (He_node *owner, char *label, 
                He_notify_proc proc, He_node *group)
On utilise souvent des Toggle pour griser certaines parties de l'application ; voici un exemple récapitulatif :

examples/panel/griser.c
    /* examples/panel/griser.c */
    
    #include <helium.h>
    
    He_node *princ, *panel, *tog1, *tog2, *tog3, *tog4, *tog5, 
        *mess1, *mess2, *mess3, *mess4, *mess5, *text1, *text2;
    
    void tog1_proc (He_node *hn)
    {
        int etat = HeGetToggleValue (hn);
        HeSetActive (mess2, etat);
        HeSetActive (tog2, etat);
        HeSetActive (tog3, etat);
    }
    
    void tog2_proc (He_node *hn)
    {
        int etat = HeGetToggleValue (hn);
        HeSetActive (mess1, etat);
        HeSetActive (tog1, etat);
    }
    
    void tog3_proc (He_node *hn)
    {
        int etat = HeGetToggleValue (hn);
        HeSetActive (mess3, etat);
        HeSetActive (tog4, etat);
        HeSetActive (tog5, etat);
    }
    
    void tog4_proc (He_node *hn)
    {
        int etat = HeGetToggleValue (hn);
        HeSetActive (mess4, etat);
        HeSetActive (text1, etat);
    }
    
    void tog5_proc (He_node *hn)
    {
        int etat = HeGetToggleValue (hn);
        HeSetActive (mess5, etat);
        HeSetActive (text2, etat);
    }
    
    int main (int argc, char *argv[])
    {
        HeInit (&argc, &argv);
    
        princ = HeCreateFrame ();
        HeSetFrameLabel (princ, "Toggle qui grise");
        
        panel = HeCreatePanel (princ);
        
        mess1 = HeCreateMessageP (panel, "Bouton Led   :", TRUE);
        tog1 = HeCreateToggleLedP (panel, "Ligne dessous", tog1_proc, TRUE);
        HeSetPanelLayout (panel, HE_LINE_FEED);
        
        mess2 = HeCreateMessageP (panel, "Bouton Check :", TRUE);
        tog2 = HeCreateToggleCheckP (panel, "Dessus" , tog2_proc, TRUE);
        tog3 = HeCreateToggleCheckP (panel, "Dessous", tog3_proc, TRUE);
        HeSetPanelLayout (panel, HE_LINE_FEED);
        
        mess3 = HeCreateMessageP (panel, "Bouton Radio :", TRUE);
        tog4 = HeCreateToggleRadioP (panel, "Gauche", tog4_proc, NULL);
        tog5 = HeCreateToggleRadioP (panel, "Droite", tog5_proc, tog4);
        HeSetPanelLayout (panel, HE_LINE_FEED);
        
        mess4 = HeCreateMessageP (panel, "Texte 1 :", TRUE);
        text1 = HeCreateText (panel);
        HeSetTextVisibleLen (text1, 10);
        HeSetTextValue (text1, "Bonjour");
        mess5 = HeCreateMessageP (panel, "Texte 2 :", TRUE);
        text2 = HeCreateText (panel);
        HeSetTextVisibleLen (text2, 10);
        HeSetTextValue (text2, "Bonsoir");
        
        /* On force l'appel des callback pour griser */
        HeSetToggleLighted (tog4);
        HeSetToggleLighted (tog5);
        
        HeFit(panel);
        HeFit(princ);
        
        return HeMainLoop (princ);
    }
     


Index   Table   Début   Suivant