Note: this page is under construction. Feel free to help out!
As with any tutorial, certain assumptions are inherent in the code and instructions presented below. In this case the primary assumptions are:
- You are reasonably familiar with Haskell and in particular, writing code using the IO Monad.
- You have successfully installed Gtk2Hs.
- You believe using Glade to design your user interface is a good idea.
Bon chance, mes amis!
2 "Hello World" with Gtk2Hs
Start with the most basic working hello world app. Then we compile it...
Here's the most basic Gtk2Hs prog:
import Graphics.UI.Gtk main = do initGUI window <- windowNew widgetShow window mainGUI
2.1 Compiling "Hello World"
How to compile it with GHC on linux/mac/win32
> ghc --make Hello.hs -o hello
2.2 Stepping through "Hello World"
(Meanwhile, between the above and below is a customary place to change settings of the window and add stuff to it, although you could also do them later.)
The loop is required to process input, for example mouse movements and clicks, key presses and releases, windows getting moved or resized, losing focus, gaining focus, requests for redraws. The loop receives all this input and takes corresponding default actions; it can also call functions you write (in addition or instead). More on this in the next section.
Here we modify the basic program to allow for quitting: closing the window quits the program.
import Graphics.UI.Gtk import Control.Monad.Trans(liftIO) main = do initGUI window <- windowNew window `on` deleteEvent $ liftIO mainQuit >> return False -- i.e., on window deleteEvent (liftIO mainQuit >> return False) widgetShow window mainGUI
And now the detailed theory:
3.1 Event dispatch loop
Input from GUI is asynchronous and more importantly arbitrarily ordered. The console paradigm of “wait for name, then wait for phone number” does not apply; the user may very well type in a bit of name, then a bit of phone number, then a bit of name again. In the early days when concurrent programming was not popular, the forefathers decided to use this event dispatch loop: wait for any input event at all (key press, mouse move), classify its type (is it key press?) and destination (is it for name?), use those to look up handlers to call, call them, loop back. Now a handler just needs to update some buffer and the screen. You, as programmer, may also register your own handlers to be called. Thus you work in the paradigm of event-driven programming and callbacks.
(Nowadays this may be better organized as several loops in several threads so each thread-loop minds one logical aspect. But mutual exclusions concerning input device queries and screen updates justify the perpetuation of a mother loop.)
A twist is added to aid high-level programming. For example you would rather not carefully track the detailed event sequence of “mouse button is down over the OK button”, “now mouse button is up over the OK button”; you prefer one single “the OK button is clicked” to register your handler with. (Not to mention that there are also ways to “click” the OK button by a keyboard shortcut and numerous accessibility aids. You can't possibly track them all.) As another example, you also prefer one single “user wants to close” event, which we use above. This is facilitated by default handlers of GTK+ that do the tedious tracking and generate the derived events “the OK button is clicked”, “user wants to close”. So we usually register with these derived events and not raw input events.The event dispatch loop of Gtk2Hs is started by
3.2 Signals, events, handlersIn GTK+ and Gtk2Hs terminology, events are called signals. Gtk2Hs adds static type safety over GTK+ in that
focus :: WidgetClass self => Signal self (DirectionType -> IO Bool)
window `on` focus $ \dirtype -> putStrLn "focused!" >> return False
showSignal :: WidgetClass self => Signal self (IO ())
window `on` showSignal $ putStrLn "shown!"
There is a special but dominating group of signals in Gtk2Hs documentation called “events”. This refers to the signal type
Signal self (EventM e Bool)
After expanding a type synonym, it is really
Signal self (ReaderT (Ptr e) IO Bool)
I.e., valid handlers take on a rather special type. That is all. But this special case is pervasive in Gtk2Hs, so we want to see its examples.As an example, the type of
deleteEvent :: WidgetClass self => Signal self (EventM EAny Bool)
window `on` deleteEvent $ do liftIO (putStrLn "closing") liftIO mainQuit -- could merge the above two return False
Here is another example, a signal for resize and/or move:
configureEvent :: WidgetClass self => Signal self (EventM EConfigure Bool)
eventSize :: EventM EConfigure (Int, Int)
This query has the perfect type! So an example of registering a handler to snoop but not interfere goes like:
window `on` configureEvent $ do (width, height) <- eventSize liftIO (putStrLn (show width ++ " x " ++ show height)) return False
4 Widget types
The way that the class heiriaharcy is represented in Haskell using type classes. Breif mention of up casting & downcasting. We'll come back to downcasting in the glade section.
Show the "set object [ attr := value ]" style syntax. Reccomend it over the more verbose "objectSetAttr object value" style.
6 Using Glade
We want to encourage people to use Glade to do their widget layout rather than doing it all in code. We can explain manual widget layout as a more advanced topic next.
Redo the hello world program using Glade.
7 Widget layout
Doing widget layout in code rather than with Glade.
8 Basic concepts
Brief explanation of basic and general concepts. With references to the API functions whenever possible. (probably in form of examples?).
- signal - event - window - widget - container - box - layout - button - label
The idea is to get the user with as general and useful concepts as possible so it makes it easier for him to surf the API since the beginning and write simple code.