Nous avons déjà vu les conteneurs, pourquoi y revenir ?
Nous avons effectivement abordé les conteneurs principaux, les plus utiles. Ce chapitre est fait pour que nous abordions quelques conteneurs plus spécifiques de par leur apparence : ils ne sont plus seulement là pour organiser discrètement vos widgets ; ces conteneurs sont faits pour être vus ! Parmi eux, nous verrons les barres d'onglets, les barres de défilement, les panneaux, les cadres, les extensions et les boîtes détachables. Cela complètera votre boîte à outils, même si je ne serai pas aussi exhaustif que pour le précédent chapitre (eh oui, il sera bientôt temps pour vous de voler de vos propres ailes et de lire le manuel ).
Les onglets
Fiche d'identité
Le premier conteneur de cette nouvelle série est donc l'onglet, ou plutôt la barre d'onglets. De quoi s'agit-il ? Eh bien c'est un widget que vous utilisez très certainement en ce moment même si vous lisez ce cours via internet :
Eh oui, ce sont ces petites cases que vous ouvrez lorsque vous voulez visiter plusieurs sites web en même temps. Il est tout à fait possible d'en créer avec GTK, par exemple si vous souhaitez créer un navigateur internet ou un logiciel qui ouvrira tous ses fichiers dans une même fenêtre à l'aide de plusieurs onglets (c'est ce que fait par exemple le lecteur de fichier pdf Foxit Reader ou l'éditeur de code NotePad++). Voici donc la fiche d'identité des barres d'onglets :
- Widget : GTK_Notebook
- Package : Gtk.Notebook
- Descendance : GTK_Widget >> GTK_Container
- Description : Conteneur comportant plusieurs pages, chaque page permettant d'afficher un seul widget à la fois.
Méthodes
Créer des onglets
Le constructeur de Gtk_Notebook
est simplissime, je n'en parlerai donc pas. Vous devez toutefois savoir qu'une barre d'onglet GTK_Notebook
, seule, ne fait pas grand chose et ne contiendra pas vos widgets. En fait, elle contient (ou pas) des Gtk_Notebook_Page
, c'est-à-dire des onglets ; et ce sont ces onglets qui contiendront un unique widget chacun. Il est donc important d'en créer et de les ajouter à votre barre. Tout cela se fait simplement avec les méthodes suivantes :
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 | procedure Append_Page(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Tab_Label : access Gtk_Widget_Record'Class); procedure Append_Page(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class); procedure Append_Page_Menu(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Tab_Label : access Gtk_Widget_Record'Class; Menu_Label : access Gtk_Widget_Record'Class); procedure Prepend_Page(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Tab_Label : access Gtk_Widget_Record'Class); procedure Prepend_Page_Menu(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Tab_Label : access Gtk_Widget_Record'Class; Menu_Label : access Gtk_Widget_Record'Class); procedure Insert_Page(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Tab_Label : access Gtk_Widget_Record'Class; Position : Gint); procedure Insert_Page_Menu(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Tab_Label : access Gtk_Widget_Record'Class; Menu_Label : access Gtk_Widget_Record'Class; Position : Gint); procedure Remove_Page(Notebook : access Gtk_Notebook_Record; Page_Num : Gint); |
Avec l'expérience acquise, vous devriez être capable de comprendre le sens de ces différentes méthodes sans mon aide. Toujours pas ? Rappelez vous : Append signifie Ajouter à la fin ; Prepend signifie Ajouter au début ; Insert signifie Insérer (à une position donnée) et Remove signifie Retirer, supprimer. Ces méthodes vont donc vous permettre d'ajouter, insérer ou supprimer des onglets (des GTK_Notebook_Page
) à votre barre.
Que signifient les différentes paramètres ? Prenons un exemple : la première méthode Append_Page()
. Le paramètre Notebook
est, ai-je encore besoin de le préciser, votre barre d'onglets. Le paramètre Child
indique le widget que vous souhaitez ajouter. L'onglet sera automatiquement généré, pas besoin de constructeur pour les GTK_Notebook_Page
. Bien entendu, ce widget peut être un conteneur complexe, contenant lui-même divers boutons, étiquettes, zones de saisie… Enfin, le troisième paramètre Tab_Label
correspond au nom de l'onglet. Celui-ci n'est pas un string
comme on aurait pu le croire mais un GTK_Widget
. Pourquoi ? Eh bien parce que le titre de votre onglet peut-être du texte (généralement un GTK_Label
) mais aussi une GTK_Box afin d'afficher un image avec votre texte ! Si vous ne renseignez pas le paramètre Tab_Label
, l'onglet sera nommé «Page 1», «Page 2»… par défaut.
Ah, d'accord ! Et je me doute que les paramètres Position
correspondent à la position où l'on souhaite insérer un onglet. Par contre à quoi servent les paramètres Menu_Label
?
Nous reviendrons sur ce paramètre un peu plus tard. Retenez qu'il s'agit d'un nom alternatif pour votre onglet et qui sera affiché lorsque l'utilisateur passera par le menu. Voyons maintenant les méthodes pour modifier ou accéder aux paramètres de base de vos onglets.
Modifier ou lire les paramètres de base des onglets
Si vous souhaitez obtenir ou modifier l'étiquette servant de titre à votre onglet, vous pourrez utiliser les méthodes suivantes :
1 2 3 4 5 | function Get_Tab_Label(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class) return Gtk_Widget; procedure Set_Tab_Label(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Tab_Label : access Gtk_Widget_Record'Class); |
Vous devez renseigner la barre d'onglets (Notebook
) bien entendu, mais également le widget contenu dans l'onglet visé. À noter que si vous modifier le titre (Tab_Label
), vous devrez actualiser son affichage avec show()
ou show_all()
. Vous pouvez obtenir les mêmes résultats seulement en utilisant les string
avec les méthodes suivantes :
1 2 3 4 5 | procedure Set_Tab_Label_Text(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Tab_Text : UTF8_String); function Get_Tab_Label_Text(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class) return UTF8_String; |
Vous pouvez également modifier un onglet via son numéro, sans connaître le widget qu'elle contient :
1 2 3 | procedure Set_Tab(Notebook : access Gtk_Notebook_Record; Page_Num : Gint; Tab_Label : access Gtk_Widget_Record'Class); |
Enfin, vous retrouvez les mêmes méthodes pour modifier le paramètre Menu_Label
de votre onglet :
1 2 3 4 5 6 7 8 9 10 | function Get_Menu_Label (Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class) return Gtk_Widget; procedure Set_Menu_Label(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Menu_Label : access Gtk_Widget_Record'Class); procedure Set_Menu_Label_Text(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Menu_Text : UTF8_String); function Get_Menu_Label_Text (Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class) return UTF8_String; |
Jouer avec les onglets
Ensuite, le nombre d'onglets pouvant augmenter ou diminuer au cours de l'utilisation de votre programme, il est important que vous puissiez connaître le nombre d'onglets ou lequel est actif. Ainsi pour connaître quel onglet est actif, utilisez la méthode Get_Current_Page()
et si vous souhaitez changer d'onglet, utilisez la méthode Set_Current_Page()
:
1 2 3 4 | function Get_Current_Page(Notebook : access Gtk_Notebook_Record) return Gint; procedure Set_Current_Page(Notebook : access Gtk_Notebook_Record; Page_Num : Gint := -1); --Si Page_Num vaut -1, cela renverra automatiquement à la dernière page. |
La première page porte le numéro 0 et non 1 !
Vous pourrez obtenir le nombre total de pages grâce à Get_N_Pages()
ainsi que le numéro correspondant à un widget avec Page_Num()
1 2 3 | function Get_N_Pages(Notebook : access Gtk_Notebook_Record) return Gint; function Page_Num (Widget : access Gtk_Notebook_Record'Class; Child : access Gtk_Widget_Record'Class) return Gint; |
Maintenant, si vous souhaitez pouvoir changer d'onglet en passant au suivant (next) ou au précédent (previous) :
1 2 | procedure Next_Page (Notebook : access Gtk_Notebook_Record); procedure Prev_Page (Notebook : access Gtk_Notebook_Record); |
Modifier le comportement et le menu
Lorsque l'utilisateur a ouvert de très nombreux onglets, il peut être pratique d'accéder à n'importe quel onglet, non pas en cliquant dessus après l'avoir longuement cherché dans la liste mais plutôt en le sélectionnant via un menu accessible d'un simple clic droit :
Pour cela vous devrez autoriser (ou interdire) ces menus grâces aux méthodes suivantes :
1 2 | procedure Popup_Enable (Notebook : access Gtk_Notebook_Record); --autoriser procedure Popup_Disable (Notebook : access Gtk_Notebook_Record); --interdire |
Par défaut, le titre du menu est celui de l'onglet. Mais nous avons vu que vous pouviez modifier ce titre pour notamment indiquer l'action correspondante («enregistrez-vous», «Lire les conditions d'utilisations»…). Mais un autre problème se pose : lorsque l'utilisateur ouvrira beaucoup d'onglets, soit la fenêtre devra s'agrandir en conséquence, soit les onglets devront se rétrécir pour laisser de la place aux nouveaux venus. Pas très esthétique tout cela. Une solution pour pallier à ce problème est de ne pas tous les afficher et de permettre l'accès aux onglets cachés via des boutons :
Pour cela il suffit d'utiliser la méthode Set_Scrollable()
en indiquant TRUE
comme paramètre.
1 2 3 | procedure Set_Scrollable(Notebook : access Gtk_Notebook_Record; Scrollable : Boolean := True); function Get_Scrollable (Notebook : access Gtk_Notebook_Record) return Boolean; |
Déplacer les onglets
Pour organiser son travail, l'utilisateur souhaitera peut-être regrouper certains onglets entre eux. Si vous souhaitez qu'un onglet puisse être déplacé au sein d'une barre d'onglets, utilisez la méthode Set_Tab_Reorderable()
:
1 2 3 4 5 6 | function Get_Tab_Reorderable (Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Position : Gint) return Boolean; procedure Set_Tab_Reorderable(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Reorderable : Boolean := True); |
Mais il est possible de pousser le vice encore plus loin en permettant de déplacer un onglet d'une barre dans une autre, pour peu que ces deux barres fassent partie d'un même groupe. Voici les méthodes en jeu, je déroulerai un exemple tout de suite après :
1 2 3 4 5 6 7 8 9 10 | function Get_Tab_Detachable (Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Position : Gint) return Boolean; procedure Set_Tab_Detachable(Notebook : access Gtk_Notebook_Record; Child : access Gtk_Widget_Record'Class; Detachable : Boolean := True); procedure Set_Group_Id(Notebook : access Gtk_Notebook_Record; Group_Id : Gint); function Get_Group_Id (Notebook : access Gtk_Notebook_Record) return Gint; |
Vous devrez tout d'abord indiquer quel onglet sera détachable en indiquant le widget qu'il contient (child
). Puis vous devrez lier les barres d'onglet entre elles en leur attribuant un même numéro de groupe avec Set_Group_Id()
. Voici un exemple plus concret :
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | WITH Gtk.Main ; USE Gtk.Main ; WITH Gtk.Window ; USE Gtk.Window ; WITH Gtk.Label ; USE Gtk.Label ; WITH Gtk.Notebook ; USE Gtk.Notebook ; WITH Glib.Convert ; USE Glib.Convert ; WITH Gtk.Box ; USE Gtk.Box ; PROCEDURE MesOnglets IS Win : Gtk_Window ; Boite : Gtk_HBox ; Onglets1, Onglets2 : Gtk_Notebook ; Lbl1, Lbl2, Lbl3, Lbl4 : Gtk_Label ; BEGIN Init ; --Création de la fenêtre Gtk_New(Win) ; Win.Set_Default_Size(150,200) ; Win.Set_Title(locale_to_utf8("Une fenêtre à onglets")) ; --Création d'une Horizontal Box pour séparer les deux barres d'onglets Gtk_new_hbox(boite) ; Win.Add(Boite) ; --Création des deux barres d'onglets Gtk_New(Onglets1) ; Gtk_New(Onglets2) ; Boite.Pack_Start(Onglets1) ; Boite.Pack_Start(Onglets2) ; --Création de quatre étiquettes qui constitueront les quatre onglets Gtk_New(Lbl1, Locale_To_Utf8("Ceci est la première page")) ; Gtk_New(Lbl2, Locale_To_Utf8("Ceci est la seconde page")) ; Gtk_New(Lbl3, Locale_To_Utf8("Ceci est la troisième page")) ; Gtk_New(Lbl4, Locale_To_Utf8("Ceci est la quatrième page")) ; --Création des onglets de la barre n°1 Onglets1.Append_Page(Lbl1) ; Onglets1.Append_Page(Lbl2) ; Onglets1.Set_Tab_detachable(Lbl1) ; Onglets1.Set_Tab_detachable(Lbl2) ; --Création des onglets de la barre n°2 Onglets2.Append_Page(Lbl3) ; Onglets2.Append_Page(Lbl4) ; Onglets2.Set_Tab_detachable(Lbl3) ; Onglets2.Set_Tab_detachable(Lbl4) ; --Création d'un même groupe pour les deux barres d'onglet Onglets1.Set_Group_Id(1) ; Onglets2.Set_Group_Id(1) ; Win.show_all ; Main ; END MesOnglets ; |
Le code ci-dessus nous donne la fenêtre sur la figure suivante. Remarquez que je n'ai pas donné de titre particulier à mes onglets et que GTK les renumérote automatiquement lors d'un déplacement, ce qui crée un décalage entre le titre de l'onglet et son contenu.
Les barres de défilement
Fiche d'identité
Si vous avez déjà effectué quelques tests des divers widgets abordés depuis le début de cette partie, vous avez du vous rendre compte que selon le dimensionnement de la fenêtre, tout n'était pas toujours visible ou bien, inversement, que la fenêtre ne pouvait pas toujours être redimensionnée afin que certains widgets restent affichés. Par exemple, lorsque l'on utilise un éditeur de texte, la fenêtre ne se redimensionne pas quand on écrit trop de texte ; et pourtant, c'est bien ce qui se passe actuellement. Nous devons donc ajouter des barres de défilement horizontales et verticales à nos fenêtres pour pouvoir tout afficher.
- Widget : GTK_Scrolled_Window
- Package : Gtk.Scrolled_Window
- Descendance : GTK_Widget >> GTK_Container >> GTK_Bin
- Description : Ce widget permet d'afficher des barres de défilement horizontal en bas de la fenêtre et vertical à gauche de la fenêtre (voir la figure suivante)
Exemples d'utilisation
Utilisation avec un GTK_Text_View
La construction d'une GTK_Scrolled_Window
se fait très simplement. Il vous suffit ensuite d'y ajouter avec la méthode Add()
n'importe quel widget. Les barres de défilement ne sont donc rien de plus qu'un conteneur bordé de deux ascenseurs. Pour réaliser le programme que vous avez pu admirer ci-dessus, il m'a suffi d'écrire le code suivant :
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.Scrolled_Window ; USE Gtk.Scrolled_Window ; WITH Gtk.Text_View ; USE Gtk.Text_View ; WITH Glib.Convert ; USE Glib.Convert ; PROCEDURE Ascenseur IS Win : GTK_Window ; Defilement : GTK_Scrolled_Window ; Texte : GTK_Text_View ; BEGIN Init ; --Création de la fenêtre principale Gtk_New(Win) ; Win.Set_Default_Size(200,100) ; Win.Set_Title(Locale_To_Utf8("Défilement")) ; --Création des barres de défilement Gtk_New(Defilement) ; Win.Add(Defilement) ; --Création de la zone de texte et ajout aux barres de défilement Gtk_New(Texte) ; Defilement.Add(Texte) ; Win.Show_All ; Main ; END Ascenseur ; |
Utilisation avec un GTK_Fixed
Un problème se pose toutefois avec certains widgets. Tous ne supportent pas aussi bien les barres de défilement (aussi appelés ascenseurs). Les GTK_Text_View
sont dits scrollable en langage GTK, c'est-à-dire qu'ils supportent très bien les ascenseurs. Mais les GTK_Fixed
, par exemple, ne les supportent pas et sont dits non scrollables.
Pour mieux comprendre, faisons un petit exercice : vous allez réaliser un programme qui affiche une carte. Celle-ci est composée d'images simples : des cases vertes pour la terre, des cases bleues pour la mer. Celles-ci seront placées sur un GTK_Fixed auquel nous adjoindrons des ascenseurs pour pouvoir garder l'accès à toutes les cases même lorsque l'on redimensionnera la fenêtre. Comme pour le démineur, je vous conseille de créer un tableau de widget. Voici une solution :
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | WITH Gtk.Main ; USE Gtk.Main ; WITH Gtk.Window ; USE Gtk.Window ; WITH Gtk.Scrolled_Window ; USE Gtk.Scrolled_Window ; WITH Gtk.Image ; USE Gtk.Image ; WITH Gtk.Fixed ; USE Gtk.Fixed ; WITH Glib.Convert ; USE Glib.Convert ; WITH Glib ; USE Glib ; PROCEDURE Carte IS Win : GTK_Window ; Defilement : GTK_Scrolled_Window ; Fond : GTK_Fixed ; --Création d'un type tableau pour savoir si les cases sont terrestres ou maritimes TYPE T_Array IS ARRAY(1..6,1..6) OF Boolean; Terre : CONSTANT T_Array := ((True,True,True,False,False,False), (True,True,True,True,False,False), (True,False,False,False,True,True), (False,False,False,False,False,True), (True,False,False,False,False,False), (True,True,True,False,False,True)); --Création d'un tableau contenant les images TYPE T_Image_Array IS ARRAY(1..6,1..6) OF GTK_Image ; Image : T_Image_Array ; BEGIN Init ; --Création de la fenêtre Gtk_New(Win) ; Win.Set_Default_Size(200,100) ; Win.Set_Title(Locale_To_Utf8("Défilement")) ; --Création des ascenseurs Gtk_New(Defilement) ; Win.Add(Defilement) ; --Création du GTK_Fixed Gtk_New(Fond) ; Defilement.Add(Fond) ; --Création et ajout des images (chaque image a une taille de 50 par 50 pixels FOR J IN 1..6 LOOP FOR I IN 1..6 LOOP IF Terre(I,J) THEN Gtk_New(Image(I,J),"terre.png") ; ELSE Gtk_New(Image(I,J),"mer.png") ; END IF ; fond.put(Image(i,j),Gint((j-1)*50),Gint((i-1)*50)) ; END LOOP ; END LOOP ; Win.Show_All ; Main ; END Carte ; |
Malheureusement, le résultat n'est pas à la hauteur de nos attentes : les barres de défilement sont inutilisables et nous obtenons en sus une jolie erreur (voir la figure suivante) :
Gtk-WARNING **: gtk_scrolled_window_add() : cannot add non scrollable widget use gtk_scrolled_window_add_with_viewport() instead
Nous allons devoir utiliser un GTK_Viewport
, un conteneur dont le seul but est de jouer les intermédiaires entre notre GTK_Fixed
et notre GTK_Scrolled_Window
. Deux solutions s'offrent à nous :
- Créer nous-même un
GTK_Viewport
. - Utiliser une méthode en remplacement de
Add()
qui créera ceGTK_Viewport
pour nous.
Dans le premier cas, sachez que le package concerné est (rien d'original me direz-vous) GTK.Viewport
. Le constructeur est simplissime, vous vous en tirerez tout seul. Une fois vos widgets initialisés, vous devrez ajouter le GTK_Fixed
à votre GTK_Viewport
qui à son tour devra être ajouté à votre GTK_Scrolled_Window
. Long et fastidieux, non ?
Pour aller plus vite, nous opterons pour la seconde méthode en utilisant Add_With_Viewport()
. Le GTK_Viewport
est ainsi généré automatiquement sans encombrer votre code. Notre programme donne ainsi :
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 42 43 44 45 46 47 48 49 50 | WITH Gtk.Main ; USE Gtk.Main ; WITH Gtk.Window ; USE Gtk.Window ; WITH Gtk.Scrolled_Window ; USE Gtk.Scrolled_Window ; WITH Gtk.Image ; USE Gtk.Image ; WITH Gtk.Fixed ; USE Gtk.Fixed ; WITH Glib.Convert ; USE Glib.Convert ; WITH Glib ; USE Glib ; PROCEDURE Carte IS Win : GTK_Window ; Defilement : GTK_Scrolled_Window ; Fond : GTK_Fixed ; TYPE T_Array IS ARRAY(1..6,1..6) OF Boolean; Terre : CONSTANT T_Array := ((True,True,True,False,False,False), (True,True,True,True,False,False), (True,False,False,False,True,True), (False,False,False,False,False,True), (True,False,False,False,False,False), (True,True,True,False,False,True)); TYPE T_Image_Array IS ARRAY(1..6,1..6) OF GTK_Image ; Image : T_Image_Array ; BEGIN Init ; Gtk_New(Win) ; Win.Set_Default_Size(200,100) ; Win.Set_Title(Locale_To_Utf8("Défilement")) ; Gtk_New(Defilement) ; Win.Add(Defilement) ; Gtk_New(Fond) ; Defilement.Add_With_Viewport(Fond) ; FOR J IN 1..6 LOOP FOR I IN 1..6 LOOP IF Terre(I,J) THEN Gtk_New(Image(I,J),"terre.png") ; ELSE Gtk_New(Image(I,J),"mer.png") ; END IF ; fond.put(Image(i,j),Gint((j-1)*50),Gint((i-1)*50)) ; END LOOP ; END LOOP ; Win.Show_All ; Main ; END Carte ; |
Méthodes
Voici quelques méthodes supplémentaires. Tout d'abord, si vous souhaitez obtenir l'une des deux barres de défilement :
1 2 3 4 | function Get_Hscrollbar(Scrolled_Window : access Gtk_Scrolled_Window_Record) return Gtk_Scrollbar; function Get_Vscrollbar(Scrolled_Window : access Gtk_Scrolled_Window_Record) return Gtk_Scrollbar; |
Chacune de ces méthodes renvoie une Gtk_Scrollbar
, c'est-à-dire le widget servant de barre de défilement (je vous laisse regarder son package si vous souhaitez en savoir plus). Evidemment, Get_Hscrollbar()
renvoie la barre horizontale et Get_Vscrollbar()
la barre verticale. Enfin, voici quelques dernières méthodes utiles :
1 2 3 4 5 6 7 8 9 10 | procedure Set_Policy(Scrolled_Window : access Gtk_Scrolled_Window_Record; H_Scrollbar_Policy : Gtk_Policy_Type; V_Scrollbar_Policy : Gtk_Policy_Type); procedure Get_Policy(Scrolled_Window : access Gtk_Scrolled_Window_Record; H_Scrollbar_Policy : out Gtk_Policy_Type; V_Scrollbar_Policy : out Gtk_Policy_Type); procedure Set_Placement(Scrolled_Window : access Gtk_Scrolled_Window_Record; Window_Placement : Gtk_Corner_Type); function Get_Placement(Scrolled_Window : access Gtk_Scrolled_Window_Record) return Gtk_Corner_Type; |
Set_Policy()
permet de gérer l'affichage des barres. Pour l'heure, celles-ci s'affichent toujours car les paramètres H_Scrollbar_Policy
et V_Scrollbar_Policy
ont leur valeur par défaut : Policy_Always
. En fouillant dans le package Gtk.Enums
vous trouverez les autres valeurs :
Policy_Always
: les barres sont toujours affichées.Policy_Automatic
: les barres ne s'affichent que si nécessaire.Policy_Never
: les barres ne sont jamais affichées.
Bien entendu, chaque barre peut avoir une politique d'affichage différente, comme toujours afficher la barre verticale mais jamais la barre horizontale. La méthode Set_Placement()
indique l'emplacement des deux ascenseurs ou plus exactement, il indique dans quel coin de la fenêtre les ascenseurs ne sont pas présents. Par défaut, la barre verticale est à droite et la barre horizontale en bas. Le seul coin qui ne touche pas les ascenseurs est donc le coin supérieur gauche. Par défaut, le paramètre Window_Placement
vaut donc Corner_Top_Left
. Les autres valeurs sont : Corner_Top_Right
, Corner_Bottom_Left
et Corner_Bottom_Right
.
Les panneaux
Fiche d'identité
- Widget : GTK_Paned
- Package : Gtk.Paned
- Descendance : GTK_Widget >> GTK_Container
- Description : Ce widget est une sorte de boîte, horizontale ou verticale, séparant deux widgets. À la différence des boîtes classiques, les panneaux peuvent être redimensionnés par l'utilisateur, à la façon des explorateurs de fichiers (voir la figure suivante).
Méthodes
Comme pour les boîtes, il existe deux sortes de GTK_Paned
, les GTK_HPaned
(panneau horizontal comme ci-dessus) et les GTK_VPaned
(panneaux verticaux). Chacun dispose de son propre constructeur : GTK_New_HPaned()
et GTK_New_VPaned()
.
Pour ajouter des widgets à vos panneaux, deux méthodes existent : Add1()
et Add2()
. La première ajoute le widget à gauche pour un panneau horizontal ou en haut pour un panneau vertical. Le second ajoutera donc le widget à droite ou en bas :
1 2 3 4 | procedure Add1(Paned : access Gtk_Paned_Record; Child : access Gtk_Widget_Record'Class); procedure Add2(Paned : access Gtk_Paned_Record; Child : access Gtk_Widget_Record'Class); |
Vous pouvez également paramétrer la répartition des widgets au sein du panneau avec les méthodes ci-dessous :
1 2 3 4 5 6 7 8 | procedure Pack1(Paned : access Gtk_Paned_Record; Child : access Gtk_Widget_Record'Class; Resize : Boolean := False; Shrink : Boolean := True); procedure Pack2(Paned : access Gtk_Paned_Record; Child : access Gtk_Widget_Record'Class; Resize : Boolean := False; Shrink : Boolean := False); |
L'attribut Resize
indique si l'emplacement réservé au widget peut être redimensionné pour qu'il occupe le plus de place possible au démarrage. Bien sûr, cela n'est visible que si les widgets de gauche et de droite ne sont pas paramétrés de la même façon. L'attribut Shrink
indique s'il est possible que l'utilisateur puisse redimensionné le panneau au point que le widget soit partiellement ou entièrement caché. Il est également possible de récupérer ces widgets avec les méthodes Get_Child1()
et Get_Child2()
:
1 2 | function Get_Child1(Paned : access Gtk_Paned_Record) return Gtk_Widget; function Get_Child2(Paned : access Gtk_Paned_Record) return Gtk_Widget; |
Enfin, dernière méthode. Si vous souhaitez positionner vous-même la séparation entre les deux widgets, utilisez la méthode Set_Position()
en indiquant la distance voulue en pixels :
1 2 | function Get_Position (Paned : access Gtk_Paned_Record) return Gint; procedure Set_Position (Paned : access Gtk_Paned_Record; Position : Gint); |
Les cadres
Les cadres simples
Fiche d'identité
- Widget : GTK_Frame
- Package : GTK.Frame
- Descendance : GTK_Widget >> GTK_Container >> GTK_Bin
- Description : C'est un conteneur pour un seul widget. Son but est en général de grouper de façon esthétique plusieurs widgets. De plus, il peut être doté d'un titre (voir la figure suivante).
Méthodes
Nous aurons très vite effectué le tour des méthodes car ce conteneur est simple d'utilisation. Commençons par le constructeur qui comporte un paramètre supplémentaire pour le titre du cadre (dans l'exemple ci-dessus, ce titre est "Cadre pour le bouton"
). Notez que le titre est optionnel :
1 | procedure Gtk_New (Frame : out Gtk_Frame; Label : UTF8_String := ""); |
Vous disposez également de deux méthodes pour lire ou (re)définir directement le titre (Get_Label()
et Set_Label()
) mais aussi pour éditer l'étiquette elle-même (Get_Label_Widget()
et Set_Label_Widget()
) :
1 2 3 4 5 6 | function Get_Label (Frame : access Gtk_Frame_Record) return UTF8_String; procedure Set_Label (Frame : access Gtk_Frame_Record; Label : UTF8_String); function Get_Label_Widget (Frame : access Gtk_Frame_Record) return Gtk_Widget; procedure Set_Label_Widget (Frame : access Gtk_Frame_Record; Label_Widget : access Gtk_Widget_Record'Class); |
Enfin, vous pouvez obtenir ou modifier l'alignement du titre avec les méthodes suivantes :
1 2 3 4 5 6 | procedure Get_Label_Align(Frame : access Gtk_Frame_Record; Xalign : out Gfloat; Yalign : out Gfloat); procedure Set_Label_Align(Frame : access Gtk_Frame_Record; Xalign : Gfloat; Yalign : Gfloat); |
Les paramètres Xalign
et Yalign
définissent bien entendu les alignements horizontaux et verticaux mais surtout, ont des valeurs comprises entre 0.0
et 1.0
. Pour Xalign
, la valeur 0.0
indique un titre aligné à gauche et 1.0
à droite. Une valeur de 0.25
indiquera que le titre est placé à un quart de la longueur. Pour Yalign
, 0.0
indiquera que la bordure du cadre sera en bas par rapport au titre, une valeur de 1.0
indiquera une bordure en haut du titre. Généralement, cette valeur est laissée à 0.5
pour obtenir une bordure partant du «milieu du titre».
Les cadres d'aspect
Fiche d'identité
- Widget : GTK_Aspect_Frame
- Package : GTK.Aspect_Frame
- Descendance : GTK_Widget >> GTK_Container >> GTK_Bin >> GTK_Frame
- Description : Ce conteneur dérive des
GTK_Frame
et a donc les mêmes attributs. Il permet toutefois de placer de façon harmonieuse vos widgets en plaçant des espaces vides tout autour.
Méthodes
Le constructeur est ici la seule méthode vraiment complexe, il mérite donc que l'on s'y arrête :
1 2 3 4 5 6 | procedure Gtk_New (Aspect_Frame : out Gtk_Aspect_Frame; Label : UTF8_String := ""; Xalign : Gfloat; Yalign : Gfloat; Ratio : Gfloat; Obey_Child : Boolean); |
Les paramètres Xalign
et Yalign
indiquent ici non plus l'alignement du titre mais celui du cadre tout entier. Imaginons que votre fenêtre soit bien plus large que nécessaire, si Xalign
vaut 0.0
, votre cadre s'alignera alors à gauche. S'il vaut 1.0
, il s'alignera à droite. Et ainsi de suite, le fonctionnement est similaire à ce que nous venons de voir.
Le paramètre Ratio
est un peu plus complexe à comprendre. Il s'agit du rapport $largeur \over hauteur$ du widget contenu dans votre cadre. Si vous placez un bouton dans votre cadre avec un ratio de 0.5
, cela indiquera que la largeur du bouton doit valoir la moitié de sa hauteur. Avec un ratio de 2.0
, c'est l'inverse : la largeur est deux fois plus grande que la hauteur.
Mais je n'en sais rien moi, du ratio dont j'ai besoin !
Alors dans ce cas, le dernier paramètre devrait vous satisfaire : si Obey_Child
vaut TRUE
, alors le ratio sera ignoré et le cadre devra «obéir au fils», c'est-à-dire prendre les dimensions voulues par le widget qu'il contient. Enfin, si vous souhaitez connaître ces paramètres ou les modifier, voici ce dont vous aurez besoin :
1 2 3 4 5 6 7 8 | procedure Set(Aspect_Frame : access Gtk_Aspect_Frame_Record; Xalign : Gfloat; Yalign : Gfloat; Ratio : Gfloat; Obey_Child : Boolean); function Get_Xalign(Aspect_Frame : access Gtk_Aspect_Frame_Record) return Gfloat; function Get_Yalign(Aspect_Frame : access Gtk_Aspect_Frame_Record) return Gfloat; function Get_Ratio (Aspect_Frame : access Gtk_Aspect_Frame_Record) return Gfloat; |
Les extenseurs
Fiche d'identité
- Widget : GTK_Expander
- Package : GTK.Expander
- Descendance : GTK_Widget >> GTK_Container >> GTK_Bin
- Description : C'est un conteneur pour un seul widget. Son but est d'afficher ou de masquer le widget qu'il contient (voir la figure suivante).
Méthodes
Comme pour les cadres simples, le constructeur des extenseurs exige un titre. Il vous est possible également de souligner certaines lettres comme pour les étiquettes avec GTK_New_With_Mnemonic()
:
1 2 3 4 | procedure Gtk_New (Expander : out Gtk_Expander; Label : UTF8_String); procedure Gtk_New_With_Mnemonic(Expander : out Gtk_Expander; Label : UTF8_String); |
On retrouve également de nombreuses méthodes déjà vues avec les étiquettes ou les boutons, comme Set_Use_Markup()
et Set_Use_Underline()
. Je ne réexplique pas leur fonctionnement, mais je vous invite à relire les chapitres 2 et 5 de la partie V si vous avez tout oublié.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function Get_Use_Markup(Expander : access Gtk_Expander_Record) return Boolean; procedure Set_Use_Markup(Expander : access Gtk_Expander_Record; Use_Markup : Boolean); function Get_Use_Underline(Expander : access Gtk_Expander_Record) return Boolean; procedure Set_Use_Underline(Expander : access Gtk_Expander_Record; Use_Underline : Boolean); function Get_Label_Fill(Expander : access Gtk_Expander_Record) return Boolean; procedure Set_Label_Fill(Expander : access Gtk_Expander_Record; Label_Fill : Boolean); function Get_Label(Expander : access Gtk_Expander_Record) return UTF8_String; procedure Set_Label(Expander : access Gtk_Expander_Record; Label : UTF8_String); function Get_Label_Widget(Expander : access Gtk_Expander_Record) return Gtk_Widget; procedure Set_Label_Widget(Expander : access Gtk_Expander_Record; Label_Widget : access Gtk_Widget_Record'Class); |
La méthode Set_Label_Fill()
vous permet d'indiquer si vous souhaitez que le titre «remplisse» tout l'espace horizontal qui lui est attribué. Plus simplement, si le paramètre Label_Fill
vaut TRUE
, votre titre apparaîtra centré et non aligné à gauche. Enfin, vous pouvez obtenir ou modifier le titre soit sous forme de String (avec Get_Label()
et Set_Label()
), soit sous forme de widget (avec Set_Label_Widget()
et Get_Label_Widget()
).
Rien de bien complexe dans tout cela. Voici maintenant les dernières méthodes (eh oui, ce conteneur est simple d'emploi) :
1 2 3 4 5 6 7 | function Get_Spacing (Expander : access Gtk_Expander_Record) return Gint; procedure Set_Spacing(Expander : access Gtk_Expander_Record; Spacing : Gint); function Get_Expanded(Expander : access Gtk_Expander_Record) return Boolean; procedure Set_Expanded(Expander : access Gtk_Expander_Record; Expanded : Boolean); |
La méthode Set_Spacing()
permet de spécifier le nombre de pixels séparant le titre et le widget fils lorsque l'extenseur est déroulé. Quand à Set_Expanded()
, il permet d'indiquer si l'extenseur est déroulé (TRUE
) ou pas (FALSE
).
Les boîtes détachables
Fiche d'identité
- Widget : GTK_Handle_Box
- Package : GTK.Handle_Box
- Descendance : GTK_Widget >> GTK_Container >> GTK_Bin
- Description : Ce conteneur est conçu pour un unique widget. Mais il est possible de le «détacher» de la fenêtre, à la façon de certaines barres d'outils (voir la figure suivante).
Méthodes
Malgré ce que l'on pourrait croire, les boîtes détachables sont très simples à créer (mais peut-être pas toujours à utiliser). Je ne m'attarde pas sur le constructeur et j'en viens tout de suite aux trois méthodes utiles (oui c'est peu mais je n'y peux rien) :
1 2 3 4 5 6 7 8 9 | procedure Set_Handle_Position(Handle_Box : access Gtk_Handle_Box_Record; Position : Gtk_Position_Type); function Get_Handle_Position(Handle_Box : access Gtk_Handle_Box_Record) return Gtk_Position_Type; procedure Set_Snap_Edge(Handle_Box : access Gtk_Handle_Box_Record; Edge : Gtk_Position_Type); function Get_Snap_Edge(Handle_Box : access Gtk_Handle_Box_Record) return Gtk_Position_Type; function Get_Child_Detached(Handle_Box : access Gtk_Handle_Box_Record) return Boolean; |
Tout d'abord, la méthode Set_Handle_Position()
permet de positionner la zone de capture (vous savez, cette barre qui ressemble à une tôle gaufrée et qui permet de saisir le conteneur pour le déplacer). Par défaut, cette zone de capture est placée à gauche, mais vous pouvez bien entendu modifier cette valeur en jetant un œil au package Gtk.Enums
. Les valeurs possibles sont :
Pos_Left
: Zone de saisie à gauche.Pos_Right
: Zone de saisie à droite.Pos_Top
: Zone de saisie en haut.Pos_Bottom
: Zone de saisie en bas.
Vous devez également savoir que la GTK_Handle_Box
ne bouge pas. Lorsque vous croyez la détacher, en réalité, GTK crée une sorte de conteneur fantôme pour vous donner l'illusion du mouvement. Mais votre GTK_Handle_Box
est toujours en place ! Mais lorsque vous voudrez replacer le fantôme dans la GTK_Handle_Box
, où devrez vous le lâcher ? Il faut en fait faire coïncider un bord du fantôme avec un bord de la GTK_Handle_Box
. Oui, mais lequel ? Pour le spécifier, utiliser la méthode Set_Snap_Edge()
.
Enfin, la dernière méthode (Get_Child_Detached()
), vous permettra de savoir si le widget à été détaché ou pas.
En résumé :
- Ces conteneurs ne peuvent afficher qu'un seul widget (à l'exception du panneau qui peut en afficher deux), vous devrez donc combiner ces conteneurs avec ceux vus précédemment (conteneurs à positions fixes, boîtes, tables… ).
- Ces conteneurs ont un intérêt principalement esthétique. Leur rôle est minime en ce qui concerne l'organisation des widgets dans votre fenêtre, mais énorme pour ce qui est de sa lisibilité. Ne les négligez pas.