Les fenêtres avec défilement sont utiles pour créer une zone déroulante avec un autre widget à l'intérieur. Vous pouvez insérer n'importe quel type de widget dans une fenêtre déroulante qui sera accessible quelle que soit sa taille en utilisant les barres de défilement.
La fonction suivante sert à créer une nouvelle fenêtre avec défilement.
Le premier argument est le réglage pour la direction horizontale et le deuxième est pour le réglage vertical. Ils sont, la plupart du temps définis à Nothing.
Cette fonction sert à définir la politique d'affichage des barres de défilement horizontales et verticales. Le constructeur PolicyAlways affiche toujours les barres de défilement, PolicyNever ne les affichent jamais et PolicyAutomatic les affichent seulement si la taille de la page est plus grande que la fenêtre. La valeur par défaut est PolicyAlways.
Pour placer un objet dans la fenêtre avec défilement, on utilise containerAdd si cet objet a une fenêtre qui lui est associée. Si il n'en a pas, un Viewport est nécessaire, mais on peut en ajouter un automatiquement avec:
Voici un exemple qui empile un tableau de 100 boutons bascules dans une fenêtre avec défilement. Cet exemple est tiré de 'Yet Another Haskell Tutorial' par Hal Daumé III. Ce tutoriel est librement disponible sur le site internet de Haskell. Ce programme laisse l'utilisateur trouver un nombre entre 1 et 100 en lui indiquant si le nombre qu'il indique est trop grand, trop petit ou correct. Le nombre aléatoire est généré avec la fonction randomRIO du module System.Random.
Cet exemple implémente ce programme avec une interface graphique.
Dans la fenêtre principale, on utilise une boite verticale pour empaqueter une étiquette (pour guider l'utilisateur) un séparateur horizontal, une fenêtre avec défilement, un séparateur horizontal et une boite horizontale pour deux boutons. La fenêtre avec défilement est empaquetée avec PackGrow pour qu'elle puisse être redimensionnée avec la fenêtre principale. Les boutons Play et Quit sont empaquetés aux bords opposés de la boite horizontale.
Les 100 boutons sont créés avec :
La fonction numButton est définie par:
Chaque bouton a le numéro approprié comme étiquette.
À l'intérieur de la fenêtre avec défilement, on crée un tableau de 10 par 10 pour les 100 boutons. Pour positionner les boutons, on utilise la fonction cross.
La fonction attachButton prend un tableau, un bouton et un couple de coordonnées pour placer le bouton dans le tableau. (Voir Chapitre 3.3 pour plus d'informations sur l'empaquetage des tableaux).
Le morceau de code suivant empaquette tous les boutons dans le tableau avec buttonlist décrit précédemment.
Chaque fois que l'utilisateur appuie le bouton Play, un nombre aléatoire est généré qui peut ensuite être comparé au choix de l'utilisateur. Mais le gestionnaire de signaux de Gtk2Hs onClicked prend un bouton et une fonction sans paramètre de type IO (). Il faut donc quelque chose qui ressemble à des variables globales qui sont apportées par le module Data.IORef. On peut alors utiliser les morceaux de code suivant dans différentes fonctions pour initialiser, écrire et lire le nombre aléatoire.
Le premier reçoit une variable de type IORef Int et l'initialise à 50. Le second est implémenté dans la fonction randomButton :
la fonction actionButton implémente la lecture de randstore. Elle compare alors le nombre obtenu de l'étiquette du bouton qui a été cliqué et affiche l'information appropriée sur info.
Enfin, il faut surveiller tous les 100 boutons pour trouver celui qui a été pressé.
Le code ci-dessus est similaire aux autres combinaisons de sequence et map que l'on a déjà utilisé mais dans le cas présent, seul un des 100 signaux sera déclenché lorsque l'utilisateur presse ce bouton en particulier.
Le code complet de l'exemple est: