Les conteneurs I

Nous nous sommes arrêtés au dernier chapitre sur une déception de taille : notre fenêtre ne comporte qu'un seul gros bouton qui occupe la totalité de la place et qui est redimensionné en même temps que la fenêtre. De plus, il est impossible d'ajouter un second bouton à notre fenêtre. Alors pour avoir plusieurs boutons dans votre application, il y a une solution : les conteneurs. Ces widgets un peu particuliers font l'objet d'un chapitre à part entière.

Des conteneurs pour… contenir !

Qu'est-ce qu'un conteneur ?

Non, quand je parle de conteneur je ne parle pas de des grosses caisses métalliques voyageant sur les camions ou les bateaux !Il s'agit de widgets très particuliers, pas nécessairement visibles mais dont le seul rôle est de contenir d'autres widgets. Les fenêtres ou les boutons dérivent de la classe GTK_Container_Record puisqu'une fenêtre peut contenir un bouton et qu'un bouton peut contenir une étiquette (du texte) et une image. Mais, quand je parlerai de conteneurs, je sous-entendrai désormais des conteneurs conçus pour organiser nos fenêtres.

L'idée est simple : une fenêtre ne peut contenir qu'un seul widget. Mais il est possible de créer des widgets appelés conteneurs pouvant contenir plusieurs autres widgets. Alors pourquoi ne pas placer tous nos widgets dans un seul conteneur que l'on placera quant à lui dans la fenêtre? Nous obtiendrions quelque chose comme cela :

Imbrication des widgets et des conteneurs

Présentation de différents conteneurs

Je vais vous présenter succinctement les quelques conteneurs que nous verrons dans ce chapitre. Il en existe d'autres que nous verrons plus tard, nous allons pour l'instant nous concentrer sur les conteneurs principaux.

Les alignements

Il s'agit d'un conteneur prévu pour un seul sous-widget (ce dernier est appelé widget enfant). Mais il permet, comme son nom l'indique, d'aligner un bouton selon des critères précis, par exemple : «je veux que le bouton reste en bas à gauche de la fenêtre». Cela évite d'avoir un gros bouton tout moche qui se déforme en même temps que la fenêtre. Je sais, c'est pas folichon, mais il est essentiel de comprendre son fonctionnement avant de s'attaquer à des conteneurs plus complexes.

Conteneur d'alignement

Les boîtes

Les boîtes peuvent quant à elles avoir plusieurs widgets alignés verticalement ou horizontalement.

Boîte à boutons horizontale

Boîte horizontale

Boîte verticale

Les tables

Les tables ressemblent peu ou prou aux boîtes mais proposent d'afficher plusieurs lignes ou colonnes de widgets.

Table de boutons

Les conteneurs à position fixée

Ces conteneurs permettent de fixer un widget selon des coordonnées. Plus simple d'emploi, il est en revanche plus compliqué d'arriver à des interfaces agréables, sans bugs et portables aisément sur d'autres ordinateurs avec ces conteneurs.

Boutons dans un conteneur à position fixe

Les alignements

Fiche d'identité

Nous allons voir désormais de nombreux widgets, alors afin de ne pas me répéter, je vous présenterai désormais ces nouveautés par une rapide fiche d'identité.

  • Widget : GTK_Alignment
  • Package : Gtk.Alignment
  • Descendance : GTK_Widget >> GTK_Container >> GTK_Bin (conteneurs pour un unique widget)
  • Description : Conteneur permettant d'aligner de façon fine un unique widget dans une fenêtre.

Créer un GTK_Alignment

Repartons sur un code relativement épuré pour ne pas nous mélanger les pinceaux :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;


PROCEDURE MaFenetre IS
   Win       : Gtk_Window ;
   Btn       : Gtk_Button ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Alignement") ;
   win.set_default_size(250,200) ;

   Gtk_New(Btn, "Bouton") ;
   Win.add(Btn) ;

   Win.Show_all ;
   Main ;
END MaFenetre ;

Si, comme je vous l'avais conseillé, vous avez ouvert les spécifications du package Gtk.Alignment, vous pourrez vous rendre compte que la procédure GTK_New() a subi quelques modifications pour les GTK_Alignment :

1
2
3
4
5
6
procedure Gtk_New
      (Alignment : out Gtk_Alignment;
       Xalign    : Gfloat;
       Yalign    : Gfloat;
       Xscale    : Gfloat;
       Yscale    : Gfloat);

Je passe sur le type GFloat, chacun aura compris qu'il s'agit d'un type virgule flottante défini très certainement par Glib. La question est surtout : que sont tous ces paramètres ?

  • Le paramètre Xalign permet de positionner votre conteneur horizontalement : si Xalign = 0.0 alors le widget est complètement à gauche. Si Xalign = 1.0, le conteneur sera le plus à droite possible. Si Xalign = 0.5, le conteneur sera situé au milieu (horizontalement).
  • Le paramètre Yalign permet de positionner votre conteneur verticalement. Si Yalign = 0.0, votre conteneur sera tout en haut ; si Yalign = 1.0, votre conteneur sera tout en bas.

Ces deux premiers paramètres ont des valeurs entre 0.0 et 1.0 et doivent être vus comme des pourcentages : Xalign = 0.50 et Yalign = 1.00, signifie que le conteneur est aligné à 50% selon l'horizontale et à 100% selon la verticale. Autrement dit, il se trouve au milieu en bas.

Alignement des conteneurs

  • Le paramètre Xscale signifie littéralement «échelle horizontale». Il correspond à «l'étalement» du conteneur horizontalement. Les images ci-dessus ont été faites avec un Xscale valant 0.0, c'est-à-dire que le conteneur ne s'étale pas, il prend le moins de place possible, la seule limite étant la place nécessaire à l'affichage du texte du bouton. Si Xscale = 1.0 (sa valeur maximale), alors le conteneur prendra 100% de la place disponible horizontalement.
  • Le paramètre Yscale a lui le même rôle mais verticalement, vous l'aurez compris.

Mieux vaut une petite image pour bien comprendre. Je vais placer mon conteneur au centre (Xalign = 0.50 et Yalign = 0.50) et faire varier Xscale et Yscale :

Échelles horizontale et verticale

Sachant cela, nous allons créer un Alignement contenant un bouton, situé en haut au milieu et s'étalant de 10% horizontalement et 0% verticalement. Il faudra penser à ajouter l'alignement à la fenêtre puis à ajouter le bouton à l'alignement (ou l'inverse) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH Gtk.Alignment ;     USE Gtk.Alignment ;


PROCEDURE MaFenetre IS
   Win       : Gtk_Window ;
   Conteneur : Gtk_Alignment ;
   Btn       : Gtk_Button ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Alignement") ;
   win.set_default_size(250,200) ;

   Gtk_New(Conteneur,0.5,0.0,0.1,0.0) ;
   Win.Add(Conteneur) ;

   Gtk_New(Btn, "Bouton") ;
   Conteneur.add(Btn) ;

   Win.Show_all ;
   Main ;
END MaFenetre ;

Ces paramètres pourront être modifiés plus tard grâce à la méthode conteneur.set() :

1
2
3
4
5
6
procedure Set
      (Alignment : access Gtk_Alignment_Record;
       Xalign    : Gfloat;
       Yalign    : Gfloat;
       Xscale    : Gfloat;
       Yscale    : Gfloat);

Le padding

Le padding correspond à l'écart séparant le bord du conteneur du bord du bouton. Pour l'instant, le padding est de 0 pixels, ce qui veut dire que le conteneur enserre parfaitement le bouton. Pour créer un peu d'air (et éviter à terme que d'autres widgets viennent se coller à votre conteneur) nous pouvons utiliser cette méthode :

1
2
3
4
5
6
procedure Set_Padding
      (Alignment      : access Gtk_Alignment_Record;
       Padding_Top    : Guint;      --espace au dessus du bouton
       Padding_Bottom : Guint;      --espace en dessous du bouton
       Padding_Left   : Guint;      --espace à gauche du bouton
       Padding_Right  : Guint);     --espace à droite du bouton

Padding d'un Gtk_Alignment

Je vous conseille d'avoir un padding de seulement quelques pixels (3 par exemple) : «conteneur.set_padding(3,3,3,3)».

Les boîtes

Boîtes classiques

Fiche d'identité

  • Widgets : GTK_Box, GTK_HBox, GTK_VBox
  • Package : Gtk.Box
  • Descendance : GTK_Widget >> GTK_Container
  • Description : Conteneurs permettant d'aligner plusieurs widgets horizontalement (HBox) ou verticalement (VBox).

Créer des boîtes

Si vous regardez en détail le début des spécifications du package, vous remarquerez qu'il y a trois widgets : GTK_Box et deux sous-types GTK_HBox et GTK_VBox. Ce sont ces deux derniers que nous allons utiliser : GTK_HBox est une boîte horizontale et GTK_VBox est une boîte verticale. Il existe donc un constructeur pour chacun de ces widgets : GTK_New_HBox() et GTK_New_VBox().

Boîte horizontale
Boîte verticale

1
2
3
4
5
6
7
8
procedure Gtk_New_Hbox
      (Box         : out Gtk_Hbox;
       Homogeneous : Boolean := False;
       Spacing     : Gint := 0);
procedure Gtk_New_Vbox
      (Box         : out Gtk_Vbox;
       Homogeneous : Boolean := False;
       Spacing     : Gint := 0);

Si le paramètre Homogeneous vaut TRUE, alors les widgets auront des tailles similaires, homogènes. Le paramètre Spacing est similaire au paramètre Padding des alignements : il indique le nombre de pixels (l'espace) qui séparera les widgets enfants. Ces deux paramètres pourront être modifiés plus tard grâce aux méthodes Set_Homogeneous() et Set_Spacing(). Créons par exemple une boîte verticale homogène avec un espacement de 3 pixels.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH Gtk.Box ;           USE Gtk.Box ;


PROCEDURE MaFenetre IS
   Win              : Gtk_Window ;
   Boite            : Gtk_VBox ;
   Btn1, Btn2, Btn3 : Gtk_Button ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Boite verticale") ;
   win.set_default_size(250,200) ;

   Gtk_New_VBox(Boite, 
                homogeneous => true, 
                spacing => 3) ;
   Win.Add(Boite) ;

   Gtk_New(Btn1, "Bouton 1") ;
   Gtk_New(Btn2, "Bouton 2") ;
   Gtk_New(Btn3, "Bouton 3") ;
   -- Ici, nous ajouterons les trois boutons dans notre boite

   Win.Show_all ;
   Main ;
END MaFenetre ;

Ajout de widgets

Reste maintenant à intégrer nos trois boutons à notre boîte verticale. La méthode add() est inopérante avec les boîtes : ajouter des widgets, c'est bien gentil, mais où et comment les ajouter ? Pour cela, imaginez une grande boîte en carton posée devant vous. Vous posez un objet au centre puis, pour gagner de la place, vous le poussez soit vers le haut du carton, soit vers le bas. C'est exactement ce que nous allons faire avec nos widgets en les «poussant autant que possible» soit vers le début (le haut pour les VBox, la gauche pour les HBox), soit vers la fin (le bas ou la droite). Pour «pousser» un widget vers le haut de notre GTK_VBox, vous utiliserez la méthode Pack_start(). Pour «pousser» un widget vers le bas, vous utiliserez la méthode Pack_end().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH Gtk.Box ;           USE Gtk.Box ;


PROCEDURE MaFenetre IS
   Win              : Gtk_Window ;
   Boite            : Gtk_VBox ;
   Btn1, Btn2, Btn3 : Gtk_Button ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Boite verticale") ;
   win.set_default_size(250,200) ;

   Gtk_New_VBox(Boite, 
                homogeneous => true, 
                spacing => 3) ;
   Win.Add(Boite) ;

   Gtk_New(Btn1, "Bouton 1") ; Boite.Pack_End(Btn1) ; 
   Gtk_New(Btn2, "Bouton 2") ; Boite.Pack_End(Btn2) ; 
   Gtk_New(Btn3, "Bouton 3") ; Boite.Pack_End(Btn3) ; 

   Win.Show_all ;
   Main ;
END MaFenetre ;

Assemblage avec Pack_end()

Si l'ordre obtenu vous déplaisait, vous pouvez repositionner certains widgets. Par exemple, Boite.reorder_child(Btn1,2) repositionnerait le Bouton 1 en deuxième position.

Si vous testez le code ci-dessus, vous remarquerez que Btn1 est le premier bouton placé à la fin : ce sera donc le bouton le plus en bas.

Paramétrez vos widgets enfants

Maintenant, si vous avez regardé les spécifications de ces méthodes, vous aurez remarqué qu'elles comportent trois autres paramètres : Expand, Fill et Padding.

  • Vous savez désormais ce qu'est le padding, les méthodes Pack_End() et Pack_Start() vous proposent un Padding supplémentaire pour certains widget, Padding qui s'ajoute au Spacing de la boîte.
  • Le paramètre Expand, s'il vaut TRUE, indique que l'encart contenant votre widget va pouvoir «prendre ses aises» et s'étendre autant qu'il le peut, même au détriment de ses camarades. Si la fenêtre est redimensionnée, l'encart le sera aussi. Ce paramètre n'a aucun intérêt si vous avez paramétré Homogeneous à TRUE.
  • Le paramètre Fill, s'il vaut TRUE, indique que le widget doit occuper tout l'espace disponible dans son encart. S'il vaut FALSE, le widget se limitera à l'espace dont il a réellement besoin. Ce paramètre n'a d'intérêt que si Expand vaut TRUE.

Pour mieux comprendre, reprenons notre exemple :

1
2
3
4
5
6
7
8
...
   Gtk_New_VBox(Boite, 
                homogeneous => false, 
                spacing => 3) ;

   Gtk_New(Btn1, "Bouton 1") ; Boite.Pack_End(Btn1, Expand => false, Fill => false) ; 
   Gtk_New(Btn2, "Bouton 2") ; Boite.Pack_End(Btn2, Expand => true,  Fill => false) ; 
   Gtk_New(Btn3, "Bouton 3") ; Boite.Pack_End(Btn3, Expand => false, Fill => false) ;

Expand vaut TRUE pour le bouton 2

Pour le bouton2, Expand = true, donc son encart prend toute la place disponible, au détriment des autres.

1
2
3
4
...
   Gtk_New(Btn1, "Bouton 1") ; Boite.Pack_End(Btn1, Expand => false, Fill => false) ; 
   Gtk_New(Btn2, "Bouton 2") ; Boite.Pack_End(Btn2, Expand => true,  Fill => true) ; 
   Gtk_New(Btn3, "Bouton 3") ; Boite.Pack_End(Btn3, Expand => false, Fill => false) ;

Expand et Fill sont vrais pour le bouton 2

Cette fois, Expand = TRUE et Fill = TRUE, donc son encart prend toute la place disponible et le bouton remplit l'encart.

Avec plusieurs types de widgets

Un avantage des boîtes, c'est qu'elles peuvent contenir divers widgets, pas nécessairement de même type. Par exemple, nous allons remplacer le troisième bouton par un GTK_Label, c'est-à-dire une étiquette (du texte quoi) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH Gtk.Box ;           USE Gtk.Box ;
WITH Gtk.Label ;         USE Gtk.Label ;


PROCEDURE MaFenetre IS
   Win              : Gtk_Window ;
   Boite            : Gtk_VBox ;
   Btn1, Btn2       : Gtk_Button ;
   Lbl              : GTK_Label ; 
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Boite verticale") ;
   win.set_default_size(250,200) ;

   Gtk_New_VBox(Boite, 
                homogeneous => false, 
                spacing => 3) ;
   Win.Add(Boite) ;

   Gtk_New(Btn1, "Bouton 1") ; Boite.Pack_End(Btn1, Expand => false, Fill => false) ; 
   Gtk_New(Btn2, "Bouton 2") ; Boite.Pack_End(Btn2, Expand => true,  Fill => true) ; 
   Gtk_New(Lbl, "Ceci est du texte") ; Boite.Pack_End(Lbl, Expand => False, Fill => False) ; 

   Win.Show_all ;
   Main ;
END MaFenetre ;

Boîte contenant deux types de widget

Avoir tous ces différents widgets ne posera pas de soucis à GTK ni à notre boîte.

Nous reverrons les GTK_Label plus en détail, rassurez-vous. ;)

Mélanger le tout

Ça veut dire que mes widgets seront soit à l'horizontale, soit à la verticale, mais qu'il n'y a pas moyen de panacher… c'est un peu limité quand même. :(

Allons, soyez imaginatifs. Les conteneurs sont des widgets faits pour contenir d'autres widgets. Il n'est donc pas interdit qu'un conteneur contienne un autre conteneur ! Par exemple, notre GTK_VBox, pourrait contenir une GTK_HBox à l'emplacement du bouton 2 !

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH GTK.Label ;         USE Gtk.Label ; 
WITH Gtk.Box ;           USE Gtk.Box ;


PROCEDURE MaFenetre IS
   Win              : Gtk_Window ;
   VBoite           : Gtk_HBox ;
   HBoite           : Gtk_HBox ;
   Btn1, Btn2, Btn3 : Gtk_Button ;
   lbl              : Gtk_Label ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Boite horizontale") ;
   win.set_default_size(250,200) ;

   Gtk_New_VBox(VBoite,
                homogeneous => false,
                spacing => 3) ;
   Win.Add(VBoite) ;

      --On remplit la boite verticale
   Gtk_New(Btn1, "Bouton 1") ; 
   Gtk_New_HBox(HBoite, homogeneous => false, Spacing => 3) ; 
   Gtk_New(Btn3, "Bouton 3") ; 
   VBoite.Pack_End(Btn1) ;
   VBoite.Pack_End(HBoite) ;    
   VBoite.Pack_End(Btn3) ;
      --On remplit la boite horizontale
   Gtk_New(Btn2, "Bouton 2") ; 
   Gtk_New(Lbl, "Ceci est toujours du texte") ; 
   HBoite.Pack_Start(Btn2) ;
   HBoite.Pack_Start(Lbl) ; 

   Win.Show_all ;
   Main ;
END MaFenetre ;

VBox contenant une HBox

Comme vous pouvez le constater, la GTK_VBox comprend deux boutons ainsi qu'une GTK_HBox, laquelle contient une étiquette et un bouton.

Boîtes à boutons

Fiche d'identité

  • Widgets : GTK_HButton_Box et GTK_VButton_Box
  • Package : GTK.Button_Box, GTK.HButton_Box et GTK.VButton_Box
  • Descendance : GTK_Widget >> GTK_Container >> GTK_Box >> GTK_Button_Box
  • Description : Conteneurs spécifiquement prévu pour organiser les boutons de façon harmonieuse, horizontalement ou verticalement.

Créer et remplir une boîte à boutons

Nos boîtes, si elles sont très pratiques, ne sont malheureusement pas très esthétiques. Nos boutons sont soit écrasés soit énormes. Et cela ne s'arrange pas si l'on agrandit la fenêtre. Pour pallier à ce problème existe un deuxième type de boîte : les boîtes à boutons. Comme pour les boîtes classiques, il existe un modèle vertical et un modèle horizontal. Toutefois, chacun a son package spécifique. Celles-ci dérivant du type GTK_Button_Box, lui-même dérivant du type GTK_Box, les méthodes pack_end() et pack_start() vues précédemment restent valides :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH Gtk.HButton_Box ;   USE Gtk.HButton_Box ;


PROCEDURE MaFenetre IS
   Win              : Gtk_Window ;
   Boite            : Gtk_HButton_Box ;
   Btn1, Btn2, Btn3 : Gtk_Button ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Boite a boutons horizontale") ;
   win.set_default_size(350,200) ;

   Gtk_New(Boite) ;
   Win.Add(Boite) ;

   Gtk_New(Btn1, "Bouton 1") ; Boite.Pack_End(Btn1) ;
   Gtk_New(Btn2, "Bouton 2") ; Boite.Pack_End(Btn2) ;
   Gtk_New(Btn3, "Bouton 3") ; Boite.Pack_End(Btn3) ;

   Win.Show_all ;
   Main ;
END MaFenetre ;

Boîte à boutons horizontale

Ainsi, les boutons sont positionnés de manière agréable sans avoir à se soucier des Padding et autres Spacing. Malgré l'agrandissement de la fenêtre, les boutons garderont un aspect normal.

Paramétrages spécifiques à la boîte à boutons

Les boîtes à boutons présentent de nouveaux paramètres pour affiner la présentation. Tout d'abord avec la méthode Set_Layout() (package GTK.Button_Box) ou la méthode Set_Layout_Default() (packages GTK.HButton_Box et GTK.VButton_Box). Celles-ci proposent différents placements par défaut pour vos boutons. Ces placements sont de type Gtk.Enums.Gtk_Button_Box_Style (encore Gtk.Enums !) dont voici les valeurs possibles :

  • Buttonbox_Spread : les boutons sont placés de manière régulière dans la boîte.
  • Buttonbox_Spread
  • Buttonbox_Edge : les boutons sont placés de manière régulière, mais le premier et le dernier boutons sont collés aux bords de la boîte.
  • Buttonbox_Edge
  • Buttonbox_Start : les boutons sont placés le plus près possible du début de la boîte (le haut pour les boîtes verticales, la gauche pour les boîtes horizontales).
  • Buttonbox_Start
  • Buttonbox_End : les boutons sont placés le plus près possible de la fin de la boîte (le bas pour les boîtes verticales, la droite pour les boîtes horizontales).
  • Buttonbox_End

Autre personnalisation possible : rendre secondaire certains boutons. Cela est souvent utiliser pour les boutons «Aide» ou «Options». Ces derniers sont mis à l'écart des autres. Les GTK_Button_Box permettent cela grâce à la méthode Set_Child_Secondary(). Par exemple, si nous modifions notre code pour que les boutons soient alignés sur la gauche et que le bouton n°3 soit secondaire, cela nous donnerait :

1
2
3
4
5
6
7
8
9
...
   Gtk_New(Boite) ;
   Boite.set_layout(Buttonbox_Start) ; 
   Win.Add(Boite) ;

   Gtk_New(Btn1, "Bouton 1") ; Boite.Pack_Start(Btn1) ;
   Gtk_New(Btn2, "Bouton 2") ; Boite.Pack_Start(Btn2) ;
   Gtk_New(Btn3, "Bouton 3") ; Boite.Pack_Start(Btn3) ;
   Boite.set_child_secondary(child => Btn3, Is_secondary => true) ;

Le bouton 3 est secondaire

Les tables

Fiche d'identité

  • Widget : GTK_Table
  • Package : Gtk.Table
  • Descendance : GTK_Widget >> GTK_Container
  • Description : Conteneur permettant de stocker plusieurs widgets répartis sur plusieurs lignes et plusieurs colonnes.

Table à widgets

Créer une table de widgets

Vous devriez rapidement comprendre le fonctionnement du constructeur à la lecture de sa spécification :

1
2
3
4
5
procedure Gtk_New
      (Table       : out Gtk_Table;
       Rows        : Guint;
       Columns     : Guint;
       Homogeneous : Boolean);

En créant votre table, vous devrez indiquer le nombre de lignes (rows), de colonnes (columns) et indiquer si la table sera homogène (relire la partie sur les boîtes si vous avez déjà oublier ce que cela signifie). Pour information, les types Guint sont encore des types entiers comme les Gint et signifient «Glib Unsigned Integer», autrement dit, des entiers positifs. En cas d'erreur, vous pourrez toujours revoir ces paramètres à l'aide des deux méthodes suivantes :

1
2
3
4
5
6
7
procedure Resize
      (Table   : access Gtk_Table_Record;
       Rows    : Guint;
       Columns : Guint);
procedure Set_Homogeneous
      (Table       : access Gtk_Table_Record;
       Homogeneous : Boolean);

Vous pouvez également régler l'écart entre les colonnes ou entre les lignes avec les deux méthodes ci-dessous. L'écart correspond bien entendu au nombre de pixels.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
procedure Set_Col_Spacings                   --définit l'espace entre toutes les colonnes
      (Table   : access Gtk_Table_Record;
       Spacing : Guint);
procedure Set_Col_Spacing                   --définit l'espace entre deux colonnes spécifiques
      (Table   : access Gtk_Table_Record;
       Column  : Guint;
       Spacing : Guint); 

procedure Set_Row_Spacings                   --définit l'espace entre toutes les lignes
      (Table   : access Gtk_Table_Record;
       Spacing : Guint);
procedure Set_Row_Spacing                   --définit l'espace entre deux lignes spécifiques
      (Table   : access Gtk_Table_Record;
       Row     : Guint;
       Spacing : Guint);

Voici le code initial de notre fenêtre :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH Gtk.Table ;         USE Gtk.Table ;


PROCEDURE MaFenetre IS
   Win                     : Gtk_Window ;
   Tableau                 : Gtk_Table ;
   Btn1, Btn2, Btn3        : Gtk_Button ;
   Btn4, Btn5, Btn6        : Gtk_Button ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Table a widgets") ;
   win.set_default_size(350,200) ;

   Gtk_New(Tableau,3,2,True) ;
   Tableau.Set_Row_Spacings(1) ; 
   Tableau.Set_Col_Spacings(2) ;
   Win.Add(Tableau) ;

   --Nous ajouterons ici les boutons à notre table

   Win.Show_all ;
   Main ;
END MaFenetre ;

Ajouter des widgets

Avant de commencer à ajouter des boutons à notre table, vous devez savoir qu'un widget peut occuper plusieurs cases alors que son voisin n'en occupera qu'une seule. Cette liberté offerte au programmeur va quelque peu alourdir notre code car nous devrons renseigner où se situe les bords gauche, droite, haut et bas du widget. Pour comprendre de quoi je parle, regardez la spécification (allégée) de la méthode pour ajouter des widgets :

1
2
3
4
5
6
7
procedure Attach               --ou Attach_Default
      (Table         : access Gtk_Table_Record;
       Widget        : access Gtk.Widget.Gtk_Widget_Record'Class;
       Left_Attach   : Guint;
       Right_Attach  : Guint;
       Top_Attach    : Guint;
       Bottom_Attach : Guint);

Vous devez vous imaginer que chaque bordure ou séparation de la table porte un numéro. La première bordure, celle du haut ou de gauche, porte le numéro 0. La bordure de droite porte le numéro du nombre de colonnes ; la bordure du bas porte le numéro du nombre de lignes :

Numérotation des bordures

Ainsi, on peut affirmer que le bouton n°2 s'étend de 0 à 1 horizontalement et de 1 à 2 verticalement, ce qui se traduit en langage Ada par :

1
2
3
4
5
6
Tableau.Attach(
       Widget        => Btn2 ;
       Left_Attach   => 0 ;
       Right_Attach  => 1 ;
       Top_Attach    => 1 ;
       Bottom_Attach => 2);

Pour obtenir la table de widgets donnée en exemple, voici donc ce que j'ai écrit :

1
2
3
4
5
6
7
...
   Gtk_New(Btn1, "Bouton 1") ; Tableau.attach(Btn1,0,1,0,1) ;
   Gtk_New(Btn2, "Bouton 2") ; Tableau.attach(Btn2,0,1,1,2) ;
   Gtk_New(Btn3, "Bouton 3") ; Tableau.attach(Btn3,0,1,2,3) ;
   Gtk_New(Btn4, "Bouton 4") ; Tableau.Attach(Btn4,1,2,0,1) ;
   Gtk_New(Btn5, "Bouton 5") ; Tableau.Attach(Btn5,1,2,1,2) ;
   Gtk_New(Btn6, "Bouton 6") ; Tableau.attach(Btn6,1,2,2,3) ;

Les paramètres supplémentaires

Malheureusement, je vous ai bien dit que le constructeur que je vous avais transmis était allégé. Le véritable constructeur a plutôt la spécification suivante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
procedure Attach
      (Table         : access Gtk_Table_Record;
       Child         : access Gtk.Widget.Gtk_Widget_Record'Class;
       Left_Attach   : Guint;
       Right_Attach  : Guint;
       Top_Attach    : Guint;
       Bottom_Attach : Guint;
       Xoptions      : Gtk.Enums.Gtk_Attach_Options := Expand or Fill;
       Yoptions      : Gtk.Enums.Gtk_Attach_Options := Expand or Fill;
       Xpadding      : Guint := 0;
       Ypadding      : Guint := 0);

Vous vous souvenez du padding ? Nous l'avions vu avec les alignements. Il s'agit du nombre de pixels séparant le bord de l'encart et le widget qui s'y trouve. Xpadding indique donc l'écart horizontal, Ypadding l'écart vertical. Attention, le padding n'influe pas sur le conteneur (sauf si la fenêtre est trop petite) mais sur la taille du widget enfant.

Les paramètres Xoptions et Yoptions correspondent au comportement du widget à l'horizontale et à la verticale : notre bouton doit-il prendre le maximum ou le minimum de place ? Les valeurs possibles sont les suivantes (disponibles avec Gtk.Enums, encore et toujours) :

  • Expand : déjà vu avec les boîtes. Le widget tente de maximiser la place occupée par son encart. N'a vraiment de sens que pour une table non homogène.
  • Fill : déjà vu avec les boîtes. Le widget remplit entièrement l'encart qui lui est réservé.
  • Expand or fill : permet de combiner les deux caractéristiques précédentes. D'ailleurs, c'est la valeur par défaut du paramètre.
  • Shrink : indique que le widget ne prendra que la place strictement nécessaire.

Dans les faits, soit vous ne modifierez pas ces paramètres et votre bouton occupera tout l'espace alloué, soit vous utiliserez la valeur shrink pour restreindre la taille du widget au maximum. Modifions notre code pour y voir plus clair et supprimons l'un des boutons :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH Gtk.Table ;         USE Gtk.Table ;


PROCEDURE MaFenetre IS
   Win                     : Gtk_Window ;
   Tableau                 : Gtk_Table ;
   Btn1, Btn2, Btn3        : Gtk_Button ;
   Btn4, Btn5              : Gtk_Button ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Table a widgets") ;
   win.set_default_size(350,200) ;

   Gtk_New(Tableau,3,2,false) ;
   Tableau.Set_Row_Spacings(1) ; 
   Tableau.Set_Col_Spacings(2) ;
   Win.Add(Tableau) ;

   Gtk_New(Btn1, "Bouton 1") ; Tableau.attach(Btn1,0,1,0,1, Xoptions => shrink, 
                                                            Yoptions => fill) ;
   Gtk_New(Btn2, "Bouton 2") ; Tableau.attach(Btn2,0,1,1,2, Xoptions => shrink, 
                                                            Yoptions => shrink) ;
   Gtk_New(Btn3, "Bouton 3") ; Tableau.attach(Btn3,0,1,2,3, Xoptions => fill,
                                                            Yoptions => fill) ;
   Gtk_New(Btn4, "Bouton 4") ; Tableau.Attach(Btn4,1,2,0,1, Xpadding => 25,
                                                            Xoptions => fill,
                                                            Yoptions => shrink) ;
   Gtk_New(Btn5, "Bouton 5") ; Tableau.Attach(Btn5,1,2,1,3, Xoptions => expand or fill,
                                                            Yoptions => expand or fill) ;

   Win.Show_all ;
   Main ;
END MaFenetre ;

Je vous laisse comparer les effets des différents paramètres sur les boutons de ma nouvelle fenêtre :

Table non homogène

Le widget pour position fixe

Fiche d'identité

  • Widgets : GTK_Fixed
  • Package : GTK.Fixed
  • Descendance : GTK_Widget >> GTK_Container
  • Description : Conteneur de taille infinie permettant de positionner divers widgets à partir de coordonnées.

Utilisation des GTK_Fixed

Ce sera certainement le widget le plus simple de ce chapitre. Son constructeur se nomme simplement Gtk_New() et n'a aucun paramètre particulier. Pour positionner un widget à l'intérieur d'un GTK_Fixed, la méthode à employer s'appelle simplement put(). Ses paramètres sont simplement les coordonnées $(x;y)$ du coin supérieur gauche de votre widget.

Exemple :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
WITH Gtk.Main ;          USE Gtk.Main ;
WITH Gtk.Window ;        USE Gtk.Window ;
WITH Gtk.Enums ;         USE Gtk.Enums ;
WITH Gtk.Button ;        USE Gtk.Button ;
WITH Gtk.Fixed ;         USE Gtk.Fixed ;


PROCEDURE MaFenetre IS
   Win                     : Gtk_Window ;
   Couche                  : Gtk_Fixed ;
   Btn1, Btn2, Btn3        : Gtk_Button ;
BEGIN
   Init ;
   Gtk_New(Win,Window_Toplevel) ;
   Win.Set_Title("Fixes") ;
   win.set_default_size(150,100) ;

   Gtk_New(Couche) ;
   Win.Add(Couche) ;

   Gtk_New(Btn1, "Bouton 1") ; Couche.put(Btn1,20,50) ;
   Gtk_New(Btn2, "Bouton 2") ; Couche.put(Btn2,30,60) ;
   Gtk_New(Btn3, "Bouton 3") ; Couche.put(Btn3,200,150) ;

   Win.Show_all ;
   Main ;
END MaFenetre ;

Le GTK_Fixed

Vous remarquerez deux choses : ce conteneur autorise le chevauchements de widgets. C'est à vous de faire en sorte qu'il n'y ait pas de souci d'affichage, ce qui est plus contraignant qu'avec les boîtes ou les tables. Ensuite, le Bouton n°3 aurait du être en dehors de la fenêtre : en effet, celle-ci a une taille de $150 \times 100$ alors que le bouton 3 est situé au point $(200 ; 150)$, soit en dehors. Mais le GTK_Fixed a agrandi automatiquement la fenêtre pour pouvoir l'afficher. Il existe un conteneur similaire, les GTK_Layout, qui fonctionne de la même façon mais pour lequel la fenêtre ne serait pas agrandielui ne s'embêtera pas avec cela.

Enfin, si l'un de vos widgets devait être déplacé, il suffirait d'utiliser la méthode move() qui fonctionne comme put().


En résumé :

  • Utilisez les GTK_Alignment pour positionner finement un unique widget.
  • Pour positionner plusieurs widgets, utilisez les GTK_Box, GTK_Button_Box ou GTK_Table.
  • Si vous avez besoin de positionner manuellement vos widgets (au pixel près) optez pour les GTK_Fixed mais pensez que le rendu ne sera pas forcément le même sur un autre ordinateur.
  • N'hésitez pas à imbriquer divers conteneurs les uns dans les autres pour obtenir la présentation souhaitée.