3.1 Empaquetando Widgets

Al crear una aplicación, puede que quieras colocar más de un widget dentro de una ventana. Nuestro primer ejemplo "hello world" únicamente usaba un widget, por lo que pudimos usar set para especificar un widget containerChild (contenedor de hijo) para window, o usar containerAdd (Añade contenedor) para empaquetar el widget en la ventana. Pero cuando quieres poner más de un widget en una ventana, cómo puedes controlar la posición del mismo? Para esto sirve el empaquetado.

Teoría de las cajas de empaquetado

La mayoría del empaquetado se hace creando cajas. Hay contenedores invisibles de widgets en los que podemos empaquetarlos. Los encontramos de dos tipos: Una caja horizontal y una caja vertical. Al empaquetar los objetos en una caja horizontal, los objetos se insertan horizontalmente de izquierda a derecha o de derecha a izquierda dependiendo de la llamada utilizada. En la caja vertical, los widgets se empaquetan de arriba abajo o viceversa. Puedes usar cualquier combinación de cajas dentro o junto a otras para crear el efecto deseado.

Para crear una nueva caja horizontal, usamos hBoxNew, y para una caja vertical, vBoxNew. Ambos usan un parámetro de tipo Bool y otro de tipo Int. El primer parámetro dará espacios iguales a cada widget hijo si se le pasa el valor True y el segundo establece el número de píxeles a colocar 'por defecto' entre los hijos.

Las funciones boxPackStart y boxPackEnd se usan para colocar objetos dentro de esos contenedores. La función boxPackStart colocará los widgets de arriba abajo en una caja vertical, VBox, y de izquierda a derecha en una caja horizontal. boxPackEnd hará lo opuesto, de abajo arriba en una caja vertical VBox, y de derecha a izquierda en una HBox. Mediante estas funciones podremos justificar por la derecha o por la izquierda nuestros widgets y mezclarlos de modo que obtengamos el efecto deseado. En la mayoría de nuestros ejemplos usaremos boxPackStart.

Un objeto opuede ser otro contenedor o un widget. De hecho, muchos widgets son a la vez contenedores, incluyendo button,(botón) pero habitualmente sólo usamos una label (etiqueta) dentro de un button.

Packed buttons

import Graphics.UI.Gtk

main :: IO ()
main = do
  initGUI
  window  <- windowNew
  hbox    <- hBoxNew True 10
  button1 <- buttonNewWithLabel "Button 1"
  button2 <- buttonNewWithLabel "Button 2"
  set window [windowDefaultWidth := 200, windowDefaultHeight := 200,
              containerBorderWidth := 10, containerChild := hbox]
  boxPackStart hbox button1 PackGrow 0
  boxPackStart hbox button2 PackGrow 0
  onDestroy window mainQuit
  widgetShowAll window
  mainGUI

Usando boxPackStart o boxPackEnd, GTK sabe dónde quieres colocar tus widgets por lo que puede cambiar automáticamente el tamaño y otras cosas interesantes.

boxPackStart :: (WidgetClass child, BoxClass self) => self -> child -> Packing -> Int -> IO ()
boxPackEnd :: (WidgetClass child, BoxClass self) => self -> child -> Packing -> Int -> IO ()

El parámetro de Packing (empaquetado) especifica el modo en que los widgets del contenedor se comportan cuando la ventana cambia de tamaño. PackNatural indica que los widgets mantendrán su tamaño y posición, PackGrow que su tamaño será adaptado, y con PackRepel los widgets serán espaciados entre ambos lados (manteniendo el tamaño). El último parámetro es un Int, que especifica el espaciado que se dejará entre este "hijo" y sus vecinos.

El empaquetado sólo se aplica a la dimensión principal de la caja (horizontal o vertical). Si, por ejemplo, indicas PackNatural en vez de PackGrow en el ejemplo anterior, el cambio de tamaño horizontal mantendrá los botones en su tamaño original, pero los cambios verticales sí le harán cambiar de tamaño. Esto se debe a que los botones se colocan homogéneamente en la caja horizontal. (El primer parámetro es True) y la caja se adapta a los cambios de la ventana. El próximo ejemplo clarificará más las cosas.