Un diálogo es un ejemplo de widget compuesto. Consta de una ventana, una parte superior que es una caja vertical, y un área de acción que es una caja horizontal que suele constar de uno o varios botones. Normalmente ambas partes están separadas por un separador horizontal.
El widget Dialog
puede usarse para mensajes pop-up (surgen de la aplicación) al usuario, u
otras tareas similares. Las funciones básicas necesarias son:
dialogNew :: IO Dialog dialogRun :: DialogClass self => self -> IO ResponseID
Puedes añadir botones al area de acción con:
dialogAddButton :: DialogClass self => self -> String -> ResponseId -> IO Button
Cualquier widget puede ser añadido de un modo semejante con dialogAddActionWidget
.
El String
en dialogAddButton
puede ser el texto del botón, pero como los
diálogos se suelen usar en situaciones estándar, un StockItem
suele ser más apropiado.
Los StockItem
s son recursos conocidos en Gtk2Hs, como los IconSet
s estándar.
Puedes definir los tuyos, pero hay muchos predefinidos en el módulo Graphics.UI.Gtk.General.StockItems
.
Tienen un identificador, StockId
, que es un tipo sinónimo de String
.
Con este identificador, un widget (normalemente un botón) con el texto y el icono apropiados se
selecciona automáticamente.
Si usas un StockId
al añadir un botón al diálogo, puedes usar un constructor ResponseId
predefinido con los botones.
(ResponseId
no es un String
.) Las respuestas pueden construirse con ResponseUser Int
.
Siempre que se pulsa un botón de diálogo, su respuesta se pasa a la aplicación llamante
a través de dialogRun
. Según la documentación de la API de Gtk2Hs, dialogRun
se bloquea en un bucle recursivo hasta que al diálogo o bien emite la señal de respuesta, o es destruido.
El modo por defecto es modal, lo que indica que el usuario no puede acceder a ninguna otra
ventana (de esta aplicación) mientras dialogRun
esté esperando una respuesta.
Las barras de progreso se emplean para mostrar el estado de una operación en curso.
progressBarNew :: IO ProgressBar
A pesar de que sólo hay un tipo, hay dos modos diferentes de usar una barra de progreso. Si se conoce la cantidad de tarea realizada, la fracción (entre 0.0 y 1.0 incluido) se puede establecer con:
progressBarSetFraction :: ProgressBarClass self => self -> Double -> IO ()
Esto origina que la barra de progreso se "llene" con la cantidad indicada (entre 0.0 y 1.0). Para marcar el progreso, esta función se debe llamar cada cierto tiempo durante la operación.
Si no se conoce la parte de la operación completada, la barra puede moverse de atrás hacia adelante con:
progressBarPulse :: ProgressBarClass self => self -> IO ()
También esta función debe ser llamada repetidamente, para mostrar que la actividad sigue en marcha. Hay otras muchas funciones para controlar la presentación de una barra de progreso, como la orientación, texto adicional, etc.; Son muy sencillas.
La aplicación, sin embargo, no es trivial debido a que las barras de progreso se aplican normalmente con temporizadores u otras funciones para dar la ilusión de multitarea. Con Haskell concurrente puedes usar hilos(threads) y comunicación entre hilos.
En el siguiente ejemplo simulamos una actividad usando timeoutAdd
,
que ejecuta una función repetidamente con el intervalo especificado en milisegundos.
La función se pasa a timeoutAdd
y debe devolver un valor del tipo IO Bool
.
Cuando el valor es true, el temporizador se vuelve a activar, cuando es falso, se para.
La prioridad de timeoutAdd
es priorityDefault
de tipo Priority
.
timeoutAdd :: IO Bool -> Int -> IO HandlerId
En el ejemplo, definimos una función showPulse
, que causa que la barra
de progreso que compruebe y que siempre devuelva IO True
. El paso,
la cantidad que se moverá el indicador por la barra, se establece a 1.0 con
progressBarSetPulseStep
.
El ejemplo es un poco atípico en el uso del diálogo, ya que lo usamos para
mostrar el progreso tras la pulsación del botón "aplicar". Para cerrar la aplicación,
el diálogo debe ser destruido, destruyendo la ventana. Los botones de cerrar
y cancelar no funcionan después de que "aplicar" haya sido seleccionada.
Si han sido seleccionados antes de "aplicar", la aplicación se cierra. Esto se realiza
comprobando la respuesta de dialogRun
.
Si el widget de diálogo se destruye, se llama a mainQuit
. Como mencioné
arriba, un Dialog
consta de una ventana y dos cajas. Se debe acceder a las cajas
a través de funciones especiales, y la barra de progreso se empaqueta en la parte superior con
dialogGetUpper
. Los botones en un diálogo son visibles por defecto, pero los widgets
de la parte superior, no. Un Dialog
es una instancia de la WindowClass
,
y por tanto, podemos ponerle el título y/o definir su tamaño si queremos.
Una característica trivial a tener en cuenta: Un widget sólo puede ser visible si su
padre es visible. Así, para mostrar la barra de progreso, usamos widgetShowAll
en la caja vertical y no widgetShow
en la barra de progreso.
import Graphics.UI.Gtk main :: IO () main = do initGUI dia <- dialogNew set dia [windowTitle := "Time Flies"] dialogAddButton dia stockapply Responseapply dialogAddButton dia stockCancel ResponseCancel dialogAddButton dia stockClose ResponseClose pr <- progressBarNew progressBarSetPulseStep pr 1.0 upbox <- dialogGetUpper dia boxPackStart upbox pr PackGrow 10 widgetShowAll upbox answer <- dialogRun dia if answer == Responseapply then do tmhandle <- timeoutAdd (showPulse pr) 500 return () else widgetDestroy dia onDestroy dia mainQuit mainGUI showPulse :: ProgressBar -> IO Bool showPulse b = do progressBarPulse b return True