AparokshaUI / adwaita-swift

A framework for creating user interfaces for GNOME with an API similar to SwiftUI
https://aparokshaui.github.io/adwaita-swift/
MIT License
749 stars 16 forks source link

Scheduling for concurrent tasks that update the UI #27

Closed neutrino2211 closed 1 month ago

neutrino2211 commented 2 months ago

Is your feature request related to a problem? Please describe.

For example, when running a thread that updates a progress bar, the UI crashes because updates are made out of sync with the GTK UI main thread

Describe the solution you'd like

I think an inbuilt scheduler that calls g_idle_add , g_main_context_invoke or g_timeout_add to schedule functions on the main UI thread even from other threads would be neat. Preferably with an API similar to the following

import Adwaita
import CAdw

struct MyProgressBar: Widget {
       // ...

       public func update(...) {
               if updateProperties {
                       Adwaita.dispatch {
                                 gtk_progress_bar_set_fraction(storage.pointer, fraction)
                       }
               }
       }

       public func progress(fraction: Double) -> Self {
               var newSelf = self
               newSelf.fraction = fraction

               return newSelf
       }
}

Describe alternatives you've considered

If this is not possible then some sort of documentation of how to do this would be appreciated, even if "hacky" as I believe any hacky implementation could be wrapped by a proper widget implementation.

Additional context

This is a screenshot of my specific use-case

Screenshot from 2024-05-01 22-13-03

And here is the error.

Screenshot from 2024-05-01 22-14-55

I did not want to report this as a bug as this is outside what libadwaita as a UI toolkit does.

Plus this last part is not context per se but I really appreciate the work you've put in to this library and it's documentation as I have been a user since January and have seen how it has evolved.

david-swift commented 1 month ago

Thanks so much for opening the issue! I added the Idle struct that can be used in a similar way as Task.

For example:

Task {
    try await Task.sleep(for: .seconds(0.5))
    Idle {
        someState = false
    }
}

There's an example in the demo app, showcasing the usage of the initializer for tasks that run one time and repeating tasks.

Your app project looks very interesting! I'll definitely try it once it's fully functional.