5.4 Bloc de notas

El widget Notebook (bloc de notas) es una colección de "páginas" que se superponen. Cada página es diferente, y sólo una es visible en cada momento. Las páginas contienen otros widgets que el programador suministra.

Para crear un nuevo widget Notebook:

NotebookNew :: IO Notebook

Una vez que el bloc de notas ha sido creado, dispones de funciones y atributos para ajustarlo a tus necesidades o gustos. Los siguientes atributos determinan la posición de las pestañas (tabs), y si son visibles o no.

notebookTabPos :: NotebookClass self => Attr self PositionType
notebookShowTabs :: NotebookClass self => Attr self Bool

PositionType (tipo de posición) tiene los siguientes cosntructores : PosLeft , PosRight , PosTop (por defecto) y PosBottom.

A continuación echaremos un vistazo a la manera de añadir páginas al notebook. Hay tres modos, append (añadir detrás), prepend (añadir delante) e insert (insertar).

noteBookAppendPage :: (NotebookClass self, WidgetClass child)
=> self
-> child         -- El widget que tiene los contenidos de la página
-> String        -- la etiqueta de texto
-> IO Int        -- el índice (número de página) de la nueva página (empieza en 0)

La función notebookPrependPage tiene la misma signatura. Y, por supuesto, devuelve 0 como valor del índice. La función notebookInsertPage toma el índice (lugar donde quieres insertar la página) como un parámetro adicional. Se pueden eliminar páginas con notebookRemovePage.

Un Notebook es un widget contenedor y puedes usar otros contenedores como hijos, incluyendo cajas horizontales y verticales. Esto te permite crear páginas bastante complejas, y establecer su distribución con las funciones de empaquetado habituales.

Las funciones listadas para añadir, pre-añadir e insertar páginas, sólo sirven con etiquetas de texto. Las tres tienen versiones que permiten que aparezca un menú emergente (popup), y en los cuales puedes usar cualquier widget como etiqueta.

notebookAppendPageMenu ::
(NotebookClass self, WidgetClass child, WidgetClass tabLabel, WidgetClass menuLabel)
=> self
-> child           -- el widget contenido en la página
-> tabLabel        -- el widget para usar como etiqueta de la página
-> menuLabel       -- el widget para usar como etiqueta del menú emergente
-> IO Int          -- el índice (número de página) de la nueva página (empieza en 0)

notebookPrependPageMenu y notebookInsertPageMenu colocarán la página en primer lugar o en la posición indicada por el índice respectivamente.

Algunos atributos interesantes son: (consulta la docuemntación de la API (en inglés) para verlos todos):

notebookScrollable :: NotebookClass self => Attr self Bool
notebookCurrentPage :: NotebookClass self => Attr self Int
notebookEnablePopup :: NotebookClass self => Attr self Bool

Si hay muchas páginas puedes usar notebookScrollable . Usa notebookCurrentPage o la función notebookSetCurrentPage para abrir el notebook en una página diferente que la primera (valor por defecto). El atributo notebookEnablePopup determina si la pulsación del botón derecho del ratón en una pestaña mostrará un menú emergente de todas las páginas disponibles, siempre que las funciones de menú hayan sido definidas.

Un widget Notebook tiene su propia función de manejo de la señal:

onSwitchPage :: NotebookClass nb => nb -> (Int -> IO ()) -> IO (ConnectId nb)

La función, que tú debes suministrar, emplea un índice de página devuelto por onSwitchPage y debe realizar alguna salida.

Los ejemplos muestran un catálogo StockItem de conjuntos de iconos de maneras diversas.

Notebook Example 1

Vimos los Stock items en el capítulo 4.5. Recuerda que un StockItem se conoce a partir de GTK+ (y Gtk2Hs). La siguiente función produce una lista de todos los identificadores de Stock Items.

stockListIds :: IO [StockId]

Un StockId es una String y en Gtk2Hs tiene la forma: stockCopy , stockDialogError etc. En GTK+ la forma correspondiente es: gtk-copy, gtk-dialog-error y así sucesivamente. El ejemplo define una función tabName para convertir los identificadores GTK+ en la lista de StockId a nombres para las solapas del notebook. La función myNewPage usa imageNewFromStock para poner el icono en un widget Image, que será después añadido a la página. Devuelve el índice de la página, pero no lo usa. Para conseguir una lista de todas las páginas puedes usar sequence en vez de sequence_

Fíjate en que el tamaño del icono, en píxeles debe ser limitado. El valor por defecto es 4, el valor usado aquí, 6, también está permitido pero un tamaño de 8 produce un error de ejecución con GHCi.

import Graphics.UI.Gtk
import Data.Char (toUpper)

main :: IO ()
main= do
     initGUI
     window <- windowNew
     set window [windowTitle := "Notebook Example 1", windowDefaultWidth := 300,
                 windowDefaultHeight := 200 ]
     
     ntbk <- notebookNew
     containerAdd window ntbk
     set ntbk [notebookScrollable := True, notebookTabPos := PosBottom]

     stls <- stockListIds
     sequence_ (map (myNewPage ntbk) stls)

     onSwitchPage ntbk (putStrLn . ((++)"Page: ") . show)

     widgetShowAll window
     onDestroy window mainQuit
     mainGUI

tabName :: StockId -> String
tabName st = (drop 3) (conv st) where
                  conv (x:[]) = x:[]
                  conv (x:y:ys) | x == '-' = (toUpper y):(conv ys)
                                | otherwise = x: (conv (y:ys))

myNewPage :: Notebook -> StockId -> IO Int
myNewPage noteb stk = 
          do img <- imageNewFromStock stk 6
             pagenum <- notebookAppendPage noteb img (tabName stk)
             return pagenum          

Otra manera de mostrar el catálogo es poner los iconos en las solapas del notebook.

Notebook Example 2

Para hacer esto necesitamos el estilo de menú para añadir páginas, y también hemos definido un menú de solapas que consta de la primera letra de la cadena nombre. El resultado es un menú emergente de 98 letras, con desplazamiento. Esto puede ser inhabilitado de un modo sencillo a través del atributo notebookEnablePopup. El contenido de cada página es el identificador de icono de Gtk2Hs (mira Graphics.UI.Gtk.General.StockItems).

import Graphics.UI.Gtk
import Data.Char (toUpper)

main :: IO ()
main= do
     initGUI
     window <- windowNew
     set window [windowTitle := "Notebook Example 2", windowDefaultWidth := 300,
                 windowDefaultHeight := 200 ]
     
     ntbk <- notebookNew
     containerAdd window ntbk
     set ntbk [notebookScrollable := True, notebookEnablePopup := True,
                    notebookTabPos := PosRight ]

     stls <- stockListIds
     sequence_ (map (myNewPage ntbk) stls)

     onSwitchPage ntbk (putStrLn . ((++)"Page: ") . show)

     widgetShowAll window
     onDestroy window mainQuit
     mainGUI

tabName :: StockId -> String
tabName st = (drop 3) (conv st) where
                  conv (x:[]) = x:[]
                  conv (x:y:ys) | x == '-' = (toUpper y):(conv ys)
                                | otherwise = x: (conv (y:ys))

myNewPage :: Notebook -> StockId -> IO Int
myNewPage noteb stk = 
          do img <- imageNewFromStock stk 4
             let nmstr = tabName stk
             men <- labelNew (Just ((take 1) nmstr))
             cont <- labelNew (Just ("stock" ++ nmstr))
             pagenum <- notebookAppendPageMenu noteb cont img men
             return pagenum