Search code examples
haskellgtkcairo

How do I draw on drawingArea widget using Cairo?


I have this little project where I try to use gi-gtk to draw on a window.

https://github.com/bigos/cairo-example/blob/b9480dc63d6fff3bc195d35c7422f193fc8ae7d4/src/Main.hs

I have figured out how to make gi-gtk work with Cairo.

https://github.com/haskell-gi/haskell-gi/wiki/Using-Cairo-with-haskell-gi-generated-bindings

But I have a problem with obtaining cairo context. In the following code I have two events. The first event works fine. But I can't figure out how to get the cairo context needed for drawing on canves when I listen to key presses on window object.

  win <- Gtk.windowNew WindowTypeToplevel
  canvas <- Gtk.drawingAreaNew
  Gtk.containerAdd win canvas

  _ <- Gtk.onWidgetDraw canvas $ \context ->
    renderWithContext context (updateCanvas canvas) >> pure True

  _ <- Gtk.onWidgetKeyPressEvent win $ \x -> do
    vvv <- Gdk.getEventKeyKeyval x
    -- How do I draw on canvas here?
    (putStrLn ("You have pressed key code " ++  (show vvv))) >> pure True

Solution

  • Simply put, you can't. You will not have access to the drawing context outside of the onWidgetDraw callback.

    What you can (and should) do is to use a global state to allow your onWidgetDraw callback to draw different things depending on what happens. For example, if you want to draw some text when the user press the right arrow:

    • In the onWidgetKeyPressEvent callback, set a value in your global state when the user presses the selected key
    • In the onWidgetDraw callback, use that value to determine if you have to draw the text or not.

      Ideally, you'd try to fit your state in a State Monad or something similar - however, in this specific case, Gtk doesn't allow injecting Monadic state itself, so this is not directly possible. You have to resort to IORefs, TVars or any equivalent.

      Good luck! :)