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

onEvent does not always update display #191

Closed joeyh closed 7 years ago

joeyh commented 7 years ago

A program using onEvent with a timer does not always update the display each time the timer fires. This example program shows the problem:

import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core

main = startGUI defaultConfig $ \window -> do
    foo <- UI.string "foo"
    getBody window #+ [ element foo ]
    timer <- UI.timer # set UI.interval 100
    poschange <- accumE (0 :: Int) $ succ <$ UI.tick timer
    witness <- UI.input
    onEvent poschange $ \x -> do
            element foo # set style
                    [ ("position", "absolute")
                    , ("left", show (x * 5) ++ "px")
                    ]
            -- get UI.checked witness
    UI.start timer

That should, I think, scroll "foo" across the top of the screen. However, it never actually moves.

Uncommenting the commented-out line makes the display update. I don't understand why.

Note that samples/DrumMachine.hs seems to only avoid this problem because it happens to call "get UI.checked box" when playing a sample.

massudaw commented 7 years ago

It's because threepenny is now batching the calls. Unfortunately it only forces when you call a function. That's why in drumMachine it uses a get UI.checked box. You can explicitly flush with flushCallBuffer after you set the element foo. I've made a pull request #192, with a workaround i use to avoid calling it explicitly

joeyh commented 7 years ago

Thanks, flushCallBuffer worked for me.

I wonder, is there any use of onEvent where you would not want to display any updates that were made? Perhaps onEvent should automically call flushCallBuffer.

-- see shy jo

massudaw commented 7 years ago

From what i understand from flushCallBuffer. You want to call it as few times as possible. Currently because all FFI Calls (that return values) need to call it to get the value, we endup flushing in every FFI call.

Ideally all sequential runEval in the server can be batched together.

Simply using onEvent for two events that trigger at the same time would lead to unnecessary flushing pushing twice to the client.

HeinrichApfelmus commented 7 years ago

The new commit above introduces a new default buffering mode, called FlushOften. It ensures that handlers for onEvent also flush the buffer. It is the new default, so the code reported in this issue should work without problems, now.