Go to the first, previous, next, last section, table of contents.

Adding interaction

Let's try to add user interactivity to the static labels and pictures that we so far have put up in windows on the screen. The main combinator for adding `life' to a component is catchDeviceEv, which wraps a filter around a user interface component:

catchDeviceEv :: DisplayHandle -> IO (InputDevice, DisplayHandle)

All input events from the user (mouse pointer movement, mouse button presses etc.) performed inside the bounds of a component are communicated via the window system to its corresponding DisplayHandle. catchDeviceEv intercepts and diverts these events into an InputDevice handle. Instead of the encapsulated component seeing the events, we can now catch events such as mouse button presses and interpret them, here's a simple push button:

myButton :: String -> String -> Component (Label,DisplayHandle)
myButton onLab offLab env =
 label offLab env            >>= \ (lab, dh) ->
 catchDeviceEv dh            >>= \ (ip, dh1) ->
 forkIO (ticker lab ip True) >>
 return (lab,dh1)
 where
  ticker lab ip flg =
   getDeviceEv ip >>= \ ev ->
   if buttonUp 1 ev then
      setLabel lab (if flg then onLab else offLab) >>
      ticker lab ip (not flg)
   else
      ticker lab ip flg

main =
 wopen ["*name: Button"] 
       (myButton "On" "Off") >>
 return ()

Simple button onSimple button off

The myButton create a Label and with the help of the catchDeviceEv combinator, listens to input events performed on the string label. To be able actively listen to the actions performed on this label, a process is created using forkIO:

forkIO :: IO () -> IO ()

forkIO is the process creation construct in Concurrent Haskell, taking an IO action to execute concurrently, returning immediately without blocking the process that called forkIO. In myButton, a small tracking process is created which for each user event destined for the label, tests to see if mouse button 1 was released. If it was, the on and off strings are toggled and the new one is displayed inside the label. To the user, the label now comes alive, clicking on it causes the labelling to change.

This is a general pattern in Haggis programs, interactive behaviour is attached to a user interface component through the use of a concurrent process. The process actively listens and handle any interaction with the user to a large degree independently from the rest of the application. Indeed, one example of this is the Button in the Haggis library of components which uses the same encapsulation mechanism to add interactive behaviour to a Glyph displaying the visual feedback of the push button.


Go to the first, previous, next, last section, table of contents.