HeinrichApfelmus / threepenny-gui

GUI framework that uses the web browser as a display.
https://heinrichapfelmus.github.io/threepenny-gui/
Other
441 stars 77 forks source link

Question about Event Composition #211

Closed MarisaKirisame closed 6 years ago

MarisaKirisame commented 6 years ago

Hi, so I want to create something like ms paint, with a canvas where you can mousedown, drag your mouse, mouseup, and draw a penstroke.

I intended to do this by making an mouseStroke :: Element -> Event [(Int, Int)], but I cannot find anyway to start, except modify the Element (listen for these three event and call the handler of createEvent).

Is there any other way?

MarisaKirisame commented 6 years ago

I got it, except mouseStroke has type MonadIO m => Element -> m (Event [(Int, Int)]), is it possible to remove the m with unsafePerformIO (and expect everything to be fine)?

bradrn commented 6 years ago

I don't know how you would remove the m without seeing your code. However, I ran into a similar issue when making my current project: I had to handle an event which was triggered by JavaScript but used in Haskell. This is what I would do to handle the onmousedown canvas event in Haskell:

eventExample = do
    (event, trigger) <- liftIO $ newEvent :: IO (Event Value, Handler Value)
    exportedTrigger <- ffiExport $ trigger
    runFunction $ ffi "canvas.onmousedown = %1" exportedTrigger

The important line is the second: it exports the event trigger trigger so it can be used in JavaScript. This exported function exportedTrigger is then used as a handler for the JavaScript canvas.onmousedown event in the next line. Now, whenever the canvas.onmousedown event fires, it will be transferred to the Haskell side where it can be used as the event :: Event Value value. (Value refers to the aeson Value type, which is useful for accessing JavaScript objects from Haskell.)

HeinrichApfelmus commented 6 years ago

mouseStroke has type MonadIO m => Element -> m (Event [(Int, Int)])

The mouseStroke event, as you describe it, needs to keep track of some sort of state (namely whether the mouse has been pressed or not). In other words, you are probably using accumB.

It is a general rule that you cannot get rid of the m monad if your events contain state. See the section "Dynamic Event Switching" in a blog post of mine for somewhat more information.