Clemapfel / Mousetrap.jl

Finally, a GUI Engine made for Julia
https://clemens-cords.com/mousetrap
GNU Lesser General Public License v3.0
410 stars 10 forks source link

Async calls #49

Closed fcdimitr closed 10 months ago

fcdimitr commented 11 months ago

Thank you for this great package!

Is it possible to have async calls within actions? Perhaps I am missing something, but I was trying something like the following:

spinner = Spinner()
set_is_spinning!(spinner, false)

action = Action("example.test_spinner", app)
set_function!(action) do x::Action
    set_is_spinning!(spinner, true)
    Threads.@spawn begin
      sleep!(2)
      set_is_spinning!(spinner, false)
      println("HEY")
    end

    return nothing

end
set_action!(button, action)

I would expect to see the spinner spinning for 2 seconds and then stop, but it never stops, and "After sleep" is never printed. If I remove the spawn, then the window freezes for 2 seconds (the spinner is never shown spinning).

Any ideas? Thank you!

Clemapfel commented 10 months ago

Yeah this will cause undefined behavior, the GTK backend uses it's own multi-threaded system so mixing those with Julia threads is not currently supported.

You can get the same behavior by using tick callbacks, like so:

using Mousetrap

main() do app::Application
    spinner = Spinner()
    set_is_spinning!(spinner, false)

    spinner_elapsed = 0 # in seconds

    delay_spinner_action = Action("delay_spinner", app) do self::Action
        println("starting spinner")
        spinner_elapsed = 0

        set_is_spinning!(spinner, true)
        # connect tick callback, this function will be called once per frame
        set_tick_callback!(spinner) do clock::FrameClock
            spinner_elapsed += as_seconds(get_time_since_last_frame(clock))
            if spinner_elapsed >= 2  
                # if 2 seconds elapsed, stop spinner and remove tick callback
                set_is_spinning!(spinner, false)
                println("stopping spinner")
                return TICK_CALLBACK_RESULT_DISCONTINUE
            else
                # else wait until 2s have passed
                return TICK_CALLBACK_RESULT_CONTINUE
            end
        end
    end
    add_shortcut!(delay_spinner_action, "<Ctrl>t")

    window = Window(app)
    set_title!(window, "Press Ctrl+T")
    set_listens_for_shortcut_action!(spinner, delay_spinner_action)
    set_child!(window, spinner)
    present!(window)
end

Here we created at an action that when triggered using Ctrl + T, will make the spinner start spinning. During each callback, the spinner_elapsed upvalue is updated to keep track of how much time has passed. After 2s have passed, the spinner will automatically stop spinning and the tick callback is disconnected.