Hasta ahora el empaquetado de widgets se ha realizado por secuenciado
en cajas verticales u horizontales, o en una tabla. Puedes, sin embargo, colocar
los widgets en la posición que desees usando un widget
Fixed
(Fijo) o un
Layout
(algo así como plano de distribución). No se
recomienda usar un contenedor Fixed
, ya que no se adapta bien
a los cambios de tamaño de la ventana.
El contenedor Layout es parecido al contenedor Fijo, pero se diferencia en
que implementa un área de desplazamiento infinita (cuando infinito es menor que 2^32).
El sistema X window (sobre el que está basado Gtk+) tiene una limitación de anchura o
altura, y no pueden superar los 32767 pixels. El contenedor Layout puede esquivar
este problema haciendo cosas exóticas, lo que permite tener suaves deplazamientos
incluso cuando tienes muchos widgets hijos en el área de desplazamiento. De este modo
las desventajas del widget Fixed
se esquivan.
Para crear un contenedor Layout usamos:
layoutNew :: Maybe Adjustment -> Maybe Adjustment -> IO Layout
Como puedes ver, puedes optar por especificar los objetos Adjustment
que el widget Layout usará para su desplazamiento.
Puedes añadir y mover widgets en el contenedor Layout container usando las dos funciones siguientes:
layoutPut :: (LayoutClass self, WidgetClass childWidget) => self -> childWidget -> Int -> Int -> IO () layoutMove :: (LayoutClass self, WidgetClass childWidget) => self -> childWidget -> Int -> Int -> IO ()
El primer argumento es la posición x, el segundo la posición y. La posición superior izquierda es (0,0), x crece de izquierda a derecha e y de arriba abajo.
Se puede fijar el tamaño del contenedor
Layout
usando la siguiente función:
layoutSetSize :: LayoutClass self => self -> Int -> Int -> IO ()
El primer argumento es la anchura de toda el área desplazable, el segundo su altura.
En el ejemplo hemos puesto una lista de etiquetas, cada una con una letra mayúscula del alfabeto, en un círculo. La etiquetas se posicionan perpendicularmente al radio, usando la función :
labelSetAngle :: labelClass self => self -> Double -> IO ()
El ángulo se mide en grados, medidos en contra de las agujas del reloj.
El widget layout se posiciona en una ventana desplazable con containerAdd
ya que en sí es desplazable, y además no necesita un viewport, como vimos en
el capítulo 6.1. Las etiquetas se posicionan usando coordenadas angulares,
que son transformadas en coordenadas cartesianas con las funciones de Prelude
sin
(seno) y
cos
(coseno). Estas toman el argumento en radianes
(entre 0 y (2 * pi)). En el ejemplo, la anchura y la altura están
parametrizadas, como lo está la lista a mostrar.
Además, en
main
las esquinas del
Layout
se marcan, por lo que es sencillo experimentar con su tamaño,
si quieres. Fíjate en que el marcador ha sido reemplazado por un '+' debido a
las quejas del validador.
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)