Tutoriel Gtk2Hs

6.3 Le conteneur de disposition

Jusqu’à maintenant, nous avons empaqueté des widgets soit avec des boites verticales et horizontales, soit avec des tableaux. Vous pouvez, aussi positionner des widgets dans toutes les positions que vous voulez en utilisant un widget Fixed ou Layout. L’utilisation du widget Fixed n’est pas recommandé car il ne se redimensionne pas très bien.

Le conteneur disposition est similaire au conteneur fixé,à la différence qu’il implémente une zone de défilement infinie (en réalité limitée à 2^32).

Un widget disposition se crée avec:

layoutNew :: Maybe Adjustment -> Maybe Adjustment -> IO Layout

Comme vous pouvez le voir, on peut spécifier optionnellement les objets d’ajustement que le conteneur de disposition utilisera pour le défilement.

Vous pouvez ajouter et déplacer des widgets dans le conteneur disposition en utilisant les fonctions suivantes:

layoutPut :: (LayoutClass self, WidgetClass childWidget)=> self -> childWidget -> Int -> Int -> IO ()
layoutMove :: (LayoutClass self, WidgetClass childWidget)=> self -> childWidget -> Int -> Int -> IO () 

Le premier paramètre est la position en x, le deuxième la position en y. Le coté en haut à gauche est à (0,0), x augmente de gauche à droite et y de haut en bas.

La taille du conteneur Layout peut être définie en utilisant la fonction:

layoutSetSize :: LayoutClass self => self  -> Int -> Int -> IO ()

Le premier paramètre est la largeur de la totalité de la zone pouvant défiler, le second, la hauteur.

Dans l’exemple, on place une liste d’étiquettes, chacune avec une lettre majuscule dans un cercle autour d’un centre. Les étiquettes sont positionnées perpendiculairement au rayon au moyen de la fonction :

labelSetAngle :: labelClass self => self -> Double -> IO ()

L’angle est en degrés, pris dans le sens trigonométrique (anti-horaire).

Gtk Gtk2Hs Alphabet

Le widget disposition est placé dans une fenêtre avec défilement au moyen de containerAdd car elle n’a pas besoin de view port (comme vu dans le chapitre 6.1). Les étiquettes sont positionnées en utilisant les coordonnées angulaires qui sont converties en coordonnées cartésiennes avec les fonctions sin et cos. Celles-ci prennent des radians comme arguments (entre 0 et 2*pi). Dans l’exemple, la largeur et la hauteur sont paramétrables, comme l’est la liste à afficher. De plus, dans la fonction main, les coins de Layout sont marqués, de sorte que vous puissiez facilement modifier sa taille si vous le souhaitez.

import Graphics.UI.Gtk

main :: IO ()
main = do
     initGUI
     window <- windowNew
     set window [windowTitle := "Alphabet" , windowDefaultWidth := 350,
             windowDefaultHeight := 350 , containerBorderWidth := 10]
     sw <- scrolledWindowNew Nothing Nothing
     set sw [scrolledWindowPlacement := CornerBottomRight, 
             scrolledWindowShadowType := ShadowEtchedIn,
             scrolledWindowHscrollbarPolicy := PolicyAutomatic,
             scrolledWindowVscrollbarPolicy := PolicyAutomatic ]
     containerAdd window sw

     layt <- layoutNew Nothing Nothing
     layoutSetSize layt myLayoutWidth myLayoutHeight
     widgetModifyBg layt StateNormal (Color 65535 65535 65535)
     containerAdd sw layt     
 
     upleft  <- labelNew (Just "+(0,0)")
     layoutPut layt upleft 0 0
     upright <- labelNew (Just ("+(" ++ (show (myLayoutWidth - 50)) ++",0)"))
     layoutPut layt upright (myLayoutWidth -50)  0
     dwnright <- labelNew (Just ("+(0," ++ (show (myLayoutHeight -20)) ++ ")"))
     layoutPut layt dwnright 0 (myLayoutHeight -20)
     dwnleft <- labelNew (Just ("+(" ++ (show(myLayoutWidth -70)) ++ "," ++
                                  (show (myLayoutHeight -20)) ++ ")"))
     layoutPut layt dwnleft (myLayoutWidth -70) (myLayoutHeight - 20)
     
     labels <- sequence $ map (labelNew . Just) txtls
     sequence_ $ map (\x -> widgetModifyFg x StateNormal (Color 0 0 45000)) labels
     
     let wnums = zip labels [0..]
     sequence_ $ map (myLayoutPut layt) wnums     

     widgetShowAll window
     onDestroy window mainQuit
     mainGUI

-- parameters
myLayoutWidth :: Int
myLayoutWidth = 800

myLayoutHeight :: Int
myLayoutHeight = 800

txtls :: [String]
txtls = map (\x -> x:[]) ['A'..'Z']
-- end parameters

step :: Double
step = (2 * pi)/(fromIntegral (length txtls))

ox :: Int
ox =  myLayoutWidth `div` 2

oy :: Int
oy = myLayoutHeight `div` 2

radius :: Double
radius = 0.25 * (fromIntegral ox)

angle :: Int -> Double
angle num = 1.5 * pi + (fromIntegral num) * step

num2x :: Int -> Int
num2x n = ox + relx where 
              relx = round $ radius * (cos (angle n))

num2y :: Int -> Int
num2y n = oy + rely where
              rely = round $ radius * (sin (angle n))

myLayoutPut :: Layout -> (Label, Int) -> IO ()
myLayoutPut lt (lb, n) = do 
         layoutPut lt lb (num2x n) (num2y n) 
         labelSetAngle lb (letterAngle n)

letterAngle :: Int -> Double
letterAngle n = (270 - degree) where
                    degree = (angle n) * (180.0 /pi)