haskellfrpthreepenny-gui

Threepenny GUI - Using sink with a canvas


I'm learning how to use Threepenny GUI, I've hooked up to a click event stream from a canvas, mapped that event stream to a stream of Point and applied accumB to [] and that Event Point to get a Behaviour [Point]

where Point:

data Point = Point {
  x :: Int,
  y :: Int
}

And the code to map the click Event stream to the Behaviour [Point]:

let pointStream = uncurry Point <$> UI.mousedown canvas

let pointsStream = fmap (:) pointStream

let pointsAccumB = accumB [] pointsStream

pointsBehaviour <- pointsAccumB

I would then like to draw these points to a canvas element which I have added to the body of the HTML in the setup method.

I've managed to draw the points from the behaviour like so:

on UI.click drawButton $ const $ do
  UI.clearCanvas canvas

  points <- currentValue pointsBehaviour

  -- Draw the points here

But the problem is I would like to have the points be drawn to the canvas each time the user clicks the canvas, without requiring the user to go and click a button. Or, in other words, I would like to combine a draw point function into the point stream.

From my research, it looks like sink may be the function that I need to use to tie together the behaviour and the mutation of the UI in an FRP style.

But, I'm struggling to understand how to piece this together (i.e. what would the sink application look like for this given use case?). Is it even possible to do this with Threepenny currently?


Solution

  • Wrap your point-drawing code in a WriteAttr and pass it to sink. The attribute might be defined like this:

    drawnPoints :: WriteAttr Canvas [Point]
    drawnPoints = mkWriteAttr $ \canvas point -> do
      UI.clearCanvas canvas
      -- Draw the points here
    

    You can then connect your actual points and canvas using sink:

    element canvas # sink drawnPoints pointsBehaviour