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.
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 */
#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);
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 */
#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);
}
/* 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);
}
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 */
#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);
}
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 */
#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.
He_node *tog;
tog = HeCreateToggle (panel, nature);
HeSetToggleLabel (tog, "Nom du toggle");
où 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);
où 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 */
#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);
}
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 */
#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);
}
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 */
#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);
}