Remove the state argument from gfxHandler'.
[matthijs/projects/fpprac.git] / FPPrac.hs
index cfbf0008654ad762246d6af423a85b102bd49130..5da734e98aebb4e9917f06b3f444a4d2f1b48afc 100644 (file)
--- a/FPPrac.hs
+++ b/FPPrac.hs
@@ -30,13 +30,14 @@ data Rect = Rect
 
 data Point = Point !Int !Int deriving (Show, Eq)
 
-type Color = Gtk.Color
+-- A color, with RGB values from 0 to 1
+data Color = Color Double Double Double deriving (Show, Eq)
 
 -- Create a Color from Red, Green and Blue values. The inputs should be
 -- between 0 and 255 (inclusive).
 rgb :: Int -> Int -> Int -> Color
-rgb r g b = Gtk.Color (conv r) (conv g) (conv b)
-       where conv = fromInteger . toInteger . (*256)
+rgb r g b = Color (conv r) (conv g) (conv b)
+       where conv = (/256) . fromIntegral
 
 -- | Some predefined colours
 red = rgb 0xff 0 0
@@ -98,10 +99,9 @@ testProg = Main
        }
 
 data IState = forall s. IS
-       { {-sFrame    :: Frame    ()
-       , sPanel    :: Panel    ()
-       , buffer    :: MemoryDC ()
-       , -}postponed :: IORef [Request]
+       { window    :: Gtk.Window
+       , buffer    :: IORef Cairo.Surface
+       , postponed :: IORef [Request]
        , usrState  :: IORef s
        , usrProg   :: TinaStep s
        }
@@ -134,17 +134,18 @@ runTina :: TinaProgram -> IO ()
 runTina Main {..} = do
        usrState  <- newIORef initialState
        postponed <- newIORef (GfxText (rgb 0 0 0) (pt 50 50) "foo" : GfxClear :initialRequests)
-       let state = IS { usrProg = eventHandler, .. }
+       let state = IS { window = undefined, buffer = undefined, usrProg = eventHandler, .. }
        runGUI windowWidth windowHeight state
 
 runGUI :: Int -> Int -> IState -> IO ()
-runGUI w h s = do
+runGUI w h (IS { .. }) = do
        -- Init GTK.
        Gtk.initGUI
        
        -- Create a window, which will make the mainloop terminated when
        -- it is closed.
        window <- Gtk.windowNew
+               
        Gtk.set window [ Gtk.containerBorderWidth := 10
                       , Gtk.windowTitle := "FP Practicum" 
                       , Gtk.windowDefaultWidth := w
@@ -152,10 +153,39 @@ runGUI w h s = do
                       ]
        Gtk.onDestroy window Gtk.mainQuit
        
+       -- Create a buffer to draw on (name the actual buffer buffer', so we
+       -- can use IS { .. } syntax below to pack the state. Using a record update
+       -- wouldn't work, probably because Cairo.Surface contains an existential
+       -- type...
+       -- We put the buffer in an IORef, so we can change it for a new one
+       -- later on (on window resizes).
+       buffer' <- Cairo.createImageSurface Cairo.FormatARGB32 w h
+       buffer <- newIORef buffer'
+
+       -- Register the expose event
+       Gtk.on window Gtk.exposeEvent $ onExpose buffer
+
+       -- Repack state
+       let state = IS { .. }
+       
+       -- Process any initial requests
+       processPostponed state
+
        -- Show the window and start the Gtk mainloop.
        Gtk.widgetShowAll window
        Gtk.mainGUI
+       
 
+-- | Copy the given surface to the exposed window on an expose event.
+onExpose :: IORef Cairo.Surface -> EventM.EventM EventM.EExpose Bool
+onExpose buffer = do
+       current_buffer <- liftIO $ readIORef buffer
+       dw <- EventM.eventWindow
+       -- Copy the buffer to the window
+       liftIO $ Gtk.renderWithDrawable dw $ do
+               Cairo.setSourceSurface current_buffer 0 0
+               Cairo.paint
+       return True -- No clue what this means
 
 {-
 runGUI s IS {..} = do
@@ -231,6 +261,13 @@ winHandler s@IS {..} (WinMenu      ms) = Just $ mkMenu >>= \ms' -> sFrame `set`
 -}
 winHandler _        _                = Nothing
 
+gfxHandler s req = case gfxHandler' req of
+       Nothing -> Nothing
+       Just render -> Just $ do
+               buf <- readIORef (buffer s)
+               Cairo.renderWith buf render
+               Gtk.widgetQueueDraw (window s)
+               
 {-
 gfxHandler IS {..} (GfxLines     col ps)    = Just $ polyline buffer ps [penColor := col] >> dirtyPts sPanel ps
 gfxHandler IS {..} (GfxPolygon   col ps)    = Just $ polygon  buffer ps [penColor := col, brushColor := col] >> dirtyPts sPanel ps
@@ -239,10 +276,21 @@ gfxHandler IS {..} (GfxRectangle col rt)    = Just $ drawRect buffer rt [penColo
 gfxHandler IS {..} (GfxEllipse   col rt)    = Just $ ellipse buffer rt [penColor := col, brushKind := BrushTransparent] >> dirtyRect sPanel rt
 gfxHandler IS {..} (GfxDisc      col rt)    = Just $ ellipse buffer rt [penColor := col, brushColor := col] >> dirtyRect sPanel rt
 gfxHandler IS {..} (GfxFont      st  sz)    = Just $ buffer `set` [ fontSize := sz, fontFace := st ]
-gfxHandler IS {..}  GfxClear                = Just $ dcClear buffer >> windowRefresh sPanel False
 gfxHandler IS {..} (GfxPicture   fd  pt)    = Just $ bitmapCreateFromFile fd >>= \bm -> drawBitmap buffer bm pt False [] >> bitmapGetSize bm >>= dirtyRect' sPanel pt
 -}
-gfxHandler _        _                       = Nothing
+gfxHandler' (GfxText col (Point x y) st) = Just $ do
+       setSourceColor col
+       Cairo.moveTo (fromIntegral x) (fromIntegral y)
+       Cairo.showText st 
+gfxHandler' GfxClear = Just $ do
+       Cairo.setSourceRGB 1 1 1
+       Cairo.paint
+gfxHandler' _ = Nothing
+
+-- | Sets the source to a pattern fill of the given color
+setSourceColor :: Color -> Cairo.Render ()
+setSourceColor (Color r g b) =
+       Cairo.setSourceRGB r g b
 
 {-
 dirtyPts :: Window a -> [Point] -> IO ()