/* examples/frame/show1.c */
#include <helium.h>
He_node *princ, *fen1, *fen2;
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Fenêtre principale");
fen1 = HeCreateFrame ();
HeSetFrameLabel (fen1, "Fenêtre 1");
fen2 = HeCreateFrame ();
HeSetFrameLabel (fen2, "Fenêtre 2");
return HeMainLoop (princ);
}
Si on exécute cet exemple, on ne verra que la fenêtre principale. En effet, on a vu dans la section « 3.5. Afficher/cacher » la règle suivante : par défaut, à la création tous les widgets sont visibles, sauf les Frame qui sont tous cachés.
Pour qu'une fenêtre fen
apparaisse à l'écran il faut donc faire :
HeSetShow (fen, TRUE);
Alors pourquoi la fenêtre principale princ
apparaît-elle ? Parce que
HeMainLoop(princ)
se charge de l'afficher en appelant en interne
HeSetShow(princ, TRUE)
.
Le but est le suivant : on crée les fenêtres d'une application
dans le main()
, et celles-ci restent cachées. On affiche ensuite ces
fenêtres à la demande (par exemple dans la callback d'un bouton).
On réécrit l'exemple show1.c
en affichant fen1
tout de suite, et avec
un bouton qui fait apparaître ou disparaître fen2
:
/* examples/frame/show2.c */
#include <helium.h>
He_node *princ, *fen1, *fen2, *panel;
void show_proc (He_node *hn)
{
HeSetShow (fen2, !HeGetShow(fen2));
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Fenêtre principale");
panel = HeCreatePanel (princ);
HeCreateButtonP (panel, "Afficher/cacher", show_proc, NULL);
fen1 = HeCreateFrame ();
HeSetFrameLabel (fen1, "Fenêtre 1");
HeSetShow (fen1, TRUE);
fen2 = HeCreateFrame ();
HeSetFrameLabel (fen2, "Fenêtre 2");
return HeMainLoop (princ);
}
Remarque : lorsque les fenêtres sont affichées, le fait d'iconifier la fenêtre principale fait disparaître toutes les autres fenêtres ; la désiconification réalise l'opération inverse.
Dans la philosophie X11, c'est le Window Manager qui est responsable en dernier ressort de la géométrie de la fenêtre, et on considère en général que se battre contre un Window Manager est une très mauvaise idée. A fortiori quand on sait qu'il existe au moins une 20aine de Window Managers différents, qui ont tous des stratégies différentes.
Nonobstant, Helium fait tout ce qu'il peut pour vous aider à placer les fenêtres où vous voulez qu'elles soient.
Le Window Manager décore une fenêtre en y rajoutant une barre de titre, des coins de redimensionnements, etc. On distingue la taille intérieure de la fenêtre, de la taille extérieure de la fenêtre (taille extérieure = intérieure + décoration).
La taille de la décoration est calculée par Helium et stockée dans les
variables globales he_wmdeco_width
et he_wmdeco_height
.
Ainsi, la largeur et la hauteur totale de la fenêtre fen
sont
HeGetWidth (fen) + he_wmdeco_width
HeGetHeight(fen) + he_wmdeco_height
ou plus simplement (cf section 3.4.)
HeGetExtWidth (fen)
HeGetExtHeight(fen)
Les coordonnées x,y que l'on emploie avec HeSetX/Y ou HeGetX/Y sont
les coordonnées du coin extérieur en haut à gauche de la fenêtre, par
rapport au coin en haut à gauche de l'écran. Pour placer une fenêtre
en haut à gauche on fait donc
HeSetX (fen, 0); HeSetY (fen, 0);
Pour placer une fenêtre en bas à droite il faut se servir de la taille
de la décoration, et aussi de la taille de l'écran, qui est stockée
dans les variables globales he_root_width
et he_root_height
:
HeSetX(fen, he_root_width - HeGetWidth(fen) - he_wmdeco_width);
HeSetY(fen, he_root_height- HeGetHeight(fen)- he_wmdeco_height);
on obtient le même résultat en justifiant la fenêtre en bas à droite
(cf section 3.4.) :
HeJustify(fen, NULL, HE_BOTTOM_RIGHT);
Helium fournit une fonction qui permet un placement simple des fenêtres :
HeSetFrameGeom (fen, char *geom);
où geom est un string désignant la position et/ou la taille de la
fenêtre, dans la plus pure syntaxe de X11 (wx
h+-
x+-
y) ;
C'est la même syntaxe que pour l'argument "-geom
" de la ligne de
commande. Par exemple, pour placer la fenêtre en haut à gauche on fait :
HeSetFrameGeom (fen, "+0+0");
Pour la placer en bas à droite on fait :
HeSetFrameGeom (fen, "-0-0");
Pour faire une fenêtre de taille intérieure 500,400 située à 100 du
bord gauche et à 200 du bord bas on fait :
HeSetFrameGeom (fen, "500x400+100-200");
Voici un exemple avec des boutons qui repositionnent la fenêtre ; le
positionnement reste correct si on agrandit la fenêtre :
/* examples/frame/hautbas.c */
#include <helium.h>
He_node *princ, *panel;
void butt1_proc (He_node *hn)
{
HeSetFrameGeom (princ, "+0+0");
}
void butt2_proc (He_node *hn)
{
HeSetFrameGeom (princ, "-0-0");
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
printf ("he_wmdeco_width = %d\n", he_wmdeco_width);
printf ("he_wmdeco_height = %d\n", he_wmdeco_height);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Positionnement de la fenêtre");
panel = HeCreatePanel (princ);
HeCreateButtonP (panel, "En haut à gauche", butt1_proc, NULL);
HeCreateButtonP (panel, "En bas à droite", butt2_proc, NULL);
HeFit (panel);
HeFit (princ);
return HeMainLoop (princ);
}
HeSetFrameResizeProc (fen, fen_resize_proc);
Le prototype de la callback est
void fen_resize_proc (He_node *hn, int width, int height);
où hn
est le Frame, width
et height
sont les nouvelles dimensions
de l'intérieur de la fenêtre. Dans l'exemple suivant on fait afficher la nouvelle taille de la fenêtre chaque fois que l'utilisateur redimensionne la fenêtre.
/* examples/frame/resize1.c */
#include <helium.h>
He_node *princ;
void fen_resize_proc (He_node *hn, int width, int height)
{
printf ("fen_resize_proc %d %d\n", width, height);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Change ma taille");
HeSetFrameResizeProc (princ, fen_resize_proc);
return HeMainLoop (princ);
}
Comme on le voit, la ResizeProc est appelée exactement une fois à l'ouverture de la fenêtre (juste avant que son contenu ne soit affiché) puis une fois à chaque redimensionnement.
Un HeSetWidth ou un HeSetHeight sur la fenêtre provoque aussi un appel à la ResizeProc. Plusieurs HeSetWidth/Height à la suite ne provoquent qu'un seul appel.
En fait la ResizeProc est l'endroit où l'on repositionne le contenu de la fenêtre en fonction de la nouvelle taille, à coups de HeSetX/Y/Width/Height, HeJustify, HeExpand, etc, sur les widgets contenus.
Dans l'exemple suivant, on crée deux boutons, et on force le deuxième bouton à se placer toujours à la droite de la fenêtre :
/* examples/frame/resize2.c */
#include <helium.h>
He_node *princ, *panel, *butt1, *butt2;
int largeur_min;
void fen_resize_proc (He_node *hn, int width, int height)
{
/* largeur minimale ; provoque un nouveau resize */
if (width < largeur_min)
{ HeSetWidth (hn, largeur_min); return; }
/* ajuste largeur du panel pour que son contenu soit visible */
HeSetWidth (panel, width);
/* Justifie butt2 à droite */
HeJustify (butt2, NULL, HE_RIGHT);
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Bouton à droite");
HeSetFrameResizeProc (princ, fen_resize_proc);
panel = HeCreatePanel (princ);
butt1 = HeCreateButtonP (panel, "Gauche", NULL, NULL);
butt2 = HeCreateButtonP (panel, "Droite", NULL, NULL);
HeFit (panel);
largeur_min = HeGetWidth(panel);
HeFit (princ);
return HeMainLoop (princ);
}
HeSetFrameReplaceProc (fen, fen_replace_proc);
Le prototype de la callback est
void fen_replace_proc (He_node *fen, int xb, int yb);
où fen
est le Frame, xb
et yb
sont les nouvelles coordonnées du
coin extérieur en haut à gauche de la fenêtre.
Dans l'exemple suivant, on crée deux fenêtres princ
et fen1
; chaque
fois que princ
est déplacée par l'utilisateur, on place fen1
à sa
droite :
/* examples/frame/replace.c */
#include <helium.h>
He_node *princ, *fen1;
void fen_replace_proc (He_node *hn, int xb, int yb)
{
printf ("fen_replace_proc : %d %d\n", xb, yb);
HeJustify (fen1, princ, HE_LEFT);
HeSetY (fen1, HeGetY(princ));
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Deplace-moi");
HeSetFrameReplaceProc (princ, fen_replace_proc);
fen1 = HeCreateFrame ();
HeSetFrameLabel (fen1, "Je me mets à droite");
HeSetShow (fen1, TRUE);
return HeMainLoop (princ);
}
Exercice : rajouter une ResizeProc qui met fen1
à droite de princ
chaque fois que l'utilisateur redimensionne princ
.
Remarques :
On commence par attacher une callback CloseProc à la fenêtre fen
:
HeSetFrameCloseProc (fen, fen_close_proc);
Le prototype de la callback est :
void fen_close_proc (He_node *hn);
où hn est le Frame.Lorsqu'une fenêtre est fermée par le biais du Window Manager, le comportement de Helium est le suivant :
HeQuit(0);
HeSetShow (hn, FALSE);
/* examples/frame/close.c */
#include <helium.h>
He_node *princ;
void fen_close_proc (He_node *hn)
{
/* Décommenter pour que le programme accepte de quitter */
/* HeQuit(0); */
}
int main (int argc, char *argv[])
{
HeInit (&argc, &argv);
princ = HeCreateFrame ();
HeSetFrameLabel (princ, "Essaie de me quitter");
HeSetFrameCloseProc (princ, fen_close_proc);
return HeMainLoop (princ);
}
Pour que la fenêtre se ferme et que l'application soit quittée, il
suffit de rajouter HeQuit(0);
dans la callback.
Remarque : cette callback est l'emplacement idéal pour appeler une boîte de dialogue "Voulez-vous quitter ? Oui/Non". Voir la section « 6.1. Boîte de dialogue ».