Index   Table   Précédent   Suivant

4. Fenêtres : le widget Frame

Dans ce chapitre on explique comment créer plusieurs fenêtres dans le même programme, les afficher, changer leur taille, les positionner à l'écran, et intercepter leur fermeture.

Index   Table   Précédent   Suivant

4.1. Afficher une fenêtre

Dans l'exemple suivant, on crée plusieurs fenêtres :

examples/frame/show1.c
    /* 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
    /* 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.

Index   Table   Précédent   Suivant

4.2. Géométrie de la fenêtre

On appelle "géométrie de la fenêtre" la taille et de la position de la fenêtre.

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 (wxh+-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
    /* 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);
    }

Index   Table   Précédent   Suivant

4.3. Fenêtre redimensionnée

Le programme peut être averti chaque fois que la taille de la fenêtre est modifiée ; il suffit d'attacher une callback ResizeProc à la fenêtre avec
    HeSetFrameResizeProc (fen, fen_resize_proc);
Le prototype de la callback est
    void fen_resize_proc (He_node *hn, int width, int height);
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
    /* 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
    /* 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);
    }

Index   Table   Précédent   Suivant

4.4. Fenêtre déplacée

Le programme peut être averti chaque fois que l'utilisateur déplace la fenêtre ; il suffit d'attacher une callback ReplaceProc à la fenêtre avec
    HeSetFrameReplaceProc (fen, fen_replace_proc);
Le prototype de la callback est
    void fen_replace_proc (He_node *fen, int xb, int yb);
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
    /* 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 :

Index   Table   Précédent   Suivant

4.5. Intercepter la fermeture

Lorsque l'utilisateur ferme la fenêtre principale par le Window Manager, l'application est quittée. On peut intercepter la fermeture d'une fenêtre et dire à ce moment ce qu'on veut faire.

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 :

Dans l'exemple suivant on crée une fenêtre principale qui refuse de se fermer :

examples/frame/close.c
    /* 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 ».


Index   Table   Début   Suivant