A dialog is an example of a composite widget. It consists of a window, an upper part which is a vertical box, and an action area which is a horizontal box. By default, both parts are separated by a horizontal separator.
The Dialog
widget can be used for pop-up messages to the user, and
other similar tasks. The basic functions needed are:
dialogNew :: IO Dialog dialogRun :: DialogClass self => self -> IO ResponseID
You add buttons into the action area with:
dialogAddButton :: DialogClass self => self -> String -> ResponseId -> IO Button
Any widget can be added in a similar way with
dialogAddActionWidget
.
The String
in dialogAddButton
can be the text of the
button, but since dialogs are mostly used for standard situations a
StockItem
will usually be more appropriate.
StockItem
s are resources which are known throughout Gtk2Hs, such
as standard IconSet
s. You can define your own, but many useful
ones are listed in the Graphics.UI.Gtk.General.StockItems
module.
They have an identifier StockId
, which is a type synonym for
String
. From this identifier a widget (usually a button) with the
appropriate standard text and icon is automatically selected.
If you use a StockId
when adding a button to a dialog, you can
also use a pre-defined ResponseId
constructor with the buttons.
(ResponseId
is not a String
.) Customized responses
may be constructed with ResponseUser Int
.
Whenever a dialog button is pressed, its response is passed to the calling
application through dialogRun
. According to the Gtk2Hs API
documentation dialogRun
blocks in a recursive main loop until the
dialog either emits the response signal, or is destroyed. The default mode is
modal, which means the user cannot access any other window while
dialogRun
is waiting for a response.
Progress bars are used to show the status of an ongoing operation.
progressBarNew :: IO ProgressBar
Though there is only one type, there are two distinct ways to use a progress bar. If it is known how much of the task has been completed, the fraction (between 0.0 and 1.0 inclusive) can be set with:
progressBarSetFraction :: ProgressBarClass self => self -> Double -> IO ()
This causes the progress bar to be filled in with the specified amount (between 0.0 and 1.0). To trace the progress this function should be called at regular times during the operation.
When it is not known how much of the operation has been completed, the bar can be moved back and forth with:
progressBarPulse :: ProgressBarClass self => self -> IO ()
This function must also be called repeatedly, to show that the activity is going on. There are several other functions to control the display of a progress bar, like orientation, additional text etc.; they are fairly trivial.
Application, however, is not trivial because progress bars are usually applied with timeouts or other such functions to give the illusion of multitasking. With concurrent Haskell you can also use threads and communication between threads.
In the following example we'll simulate an activity using
timeoutAdd
, which runs a function repeatedly at the interval
specified, in milliseconds. The function is passed to timeoutAdd
and must return a type of IO Bool
. When true the timeout is run
again, when false it stops. The priority of timeoutAdd
is
priorityDefault
of type Priority
.
timeoutAdd :: IO Bool -> Int -> IO HandlerId
In the example we define the function showPulse
, which causes the
progress bar to pulse and always returns IO True
. The pulse step,
the amount which the indicator moves through the bar, is set to 1.0 with
progressBarSetPulseStep
.
The example is somewhat atypical of the use of a dialog, since we keep it to
show the progress after the user has pressed the apply button. To close the
application the dialog must be destroyed by destroying the window. The close
and cancel buttons don't work after apply has been selected. If selected,
instead of Apply, the first time, the application will close. This is
done by testing the response from dialogRun
.
If the dialog widget is destroyed, mainQuit
is called. As
mentioned above, a Dialog
consists of a window and two boxes. The
boxes must be accessed through special functions, and the progress bar is
packed into the upper part using dialogGetUpper
. The buttons in a
dialog are visible by default, but the widgets in the upper part are not. A
Dialog
is an instance of the WindowClass
, and so we
can set the title and/or default length and height if we want.
A trivial feature to watch out for: A widget can only be made visible if its
parent is visible. So, to show the progress bar, we use
widgetShowAll
on the vertical box and not widgetShow
on the progress bar.
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