Random Haskell GTK GUIs

Random Haskell GTK GUIs

module Main (main) where

import System.Environment (getArgs)
import System.Random (randomIO, randomRIO)
import Control.Monad (replicateM)
import Graphics.UI.Gtk

I was slightly bored and insomniac and pining for the days of AmigaE/EasyGUI (no, not the later imitations), so I thought about making a simple wrapper around GTK to replicate some of that ease of use in Haskell. I didn't get very far before I was distracted by shiny ideas...

type W = IO Widget

layout :: BoxClass b => (Bool -> Int -> IO b) -> [W] -> W
layout box ws = do
  b <- box False 0
  mapM_ (>>= \w -> boxPackStart b w PackGrow 0) ws
  return $ toWidget b

rows, cols :: [W] -> W
rows = layout vBoxNew
cols = layout hBoxNew

button :: String -> IO () -> W
button name act = do
  b <- buttonNewWithLabel name
  _ <- b `on` buttonActivated $ act
  return $ toWidget b

...like interpreting values to turn them into real life GUIs...

data GUI = Button String | Rows [GUI] | Cols [GUI]

interpret :: GUI -> W
interpret (Button g) = button g (putStrLn g)
interpret (Rows g) = rows (map interpret g)
interpret (Cols g) = cols (map interpret g)

...and generating random values and GUIs within a given size limit:

randomGUI :: Int -> IO GUI
randomGUI n
  | n < 2 = do
      l <- randomRIO (3, 8)
      s <- replicateM l $ randomRIO ('a', 'z')
      return $ Button s
  | otherwise = do
      d <- randomRIO (1, n - 1)
      w <- randomIO
      l <- randomGUI $     d
      r <- randomGUI $ n - d - 2
      -- termination assured by (n - d - 2) + d < n
      return $ if w then Rows [l, r] else Cols [l, r]

Which just requires a minimal GTK driver to display the top-level widget...

gui :: W -> IO ()
gui wc = do
  _ <- initGUI
  w <- windowNew
  c <- wc
  set w [ containerChild := c ]
  _ <- onDestroy w mainQuit
  widgetShowAll w
  mainGUI

...and a braindead command-line argument "parser" to finish the whole program off:

main :: IO ()
main = gui . interpret =<< randomGUI =<< fmap (read . head) getArgs

Full source code without the HTML entities: RandomGUI.hs