Crate woab[][src]

Expand description

WoAB (Widgets on Actors Bridge) is a GUI microframework for combining the widgets toolkit GTK with the actors framework Actix. It helps with:

  • Running the actors inside the GTK thread, allowing message handlers to interact with the widgets directly.
  • Routing GTK signals through the asynchronous runtime, so that the code handling them can proceed naturally to interact with the actors.
  • Mapping widgets and signals from Glade XML files to user types.

To use WoAB one would typically create a factories struct using woab::Factories and use it dissect the Glade XML file(s). Each field of the factories struct will be a woab::BuilderFactory that can:

  • Create a widgets struct using woab::WidgetsFromBuilder.
  • Route the signals defined in the builder to Actix handlers using woab::Signal messages.
  • In the Actix handler, match on the signal name (msg.name()) and use woab::params! to extract the signal parameters.

The factories can then be used to generate the GTK widgets and either connect them to a new actor which will receive the signals defined in the Glade GTK or connect them to an existing actor and tag the signals (so that multiple instances can be added - e.g. with GtkListBox - and the signal handler can know from which one the event came). The actors receive the signals as Actix messages, and the Handler returns the inhibitness decision (if the signal requires it)

To remove widget-bound actors at runtime, see woab::Remove.

To synchronize the widgets’ data with a model (or any old Rust values), see woab::PropSync.

After initializing GTK and before starting the main loop, woab::run_actix_inside_gtk_event_loop must be called. Do not run the Actix system manually!

use actix::prelude::*;
use gtk::prelude::*;

#[derive(woab::Factories)]
struct Factories {
    // The field name must be the ID from the builder XML file:
    main_window: woab::BuilderFactory,
    // Possibly other things from the builder XML file that need to be created during the program.
}

struct AppActor {
    widgets: AppWidgets,
    factories: std::rc::Rc<Factories>, // for creating more things from inside the actor.
    // More actor data
}

impl actix::Actor for AppActor {
    type Context = actix::Context<Self>;
}

#[derive(woab::WidgetsFromBuilder)]
struct AppWidgets {
    main_window: gtk::ApplicationWindow, // needed for making the window visible
    // Other widgets inside the window to interact with.
}

impl actix::Handler<woab::Signal> for AppActor {
    type Result = woab::SignalResult;

    fn handle(&mut self, msg: woab::Signal, ctx: &mut <Self as actix::Actor>::Context) -> Self::Result {
        Ok(match msg.name() {
            // These are custom signals defined in Glade's "Signals" tab.
            "sig1" => {
                // Behavior for sig1.
                None // GTK does not expect sig1 to return anything
            },
            "sig2" => {
                let woab::params!(text_buffer: gtk::TextBuffer, _) = msg.params()?;
                // Behavior for sig2 that uses the signal parameters.
                Some(gtk::Inhibit(false)) // GTK expects sig2 to return its inhibitness decision
            },
            _ => msg.cant_handle()?,
        })
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let factories = std::rc::Rc::new(Factories::read(read_builder_xml())?);
    gtk::init()?;
    woab::run_actix_inside_gtk_event_loop(); // <===== IMPORTANT!!!

    factories.main_window.instantiate().connect_with(|bld| {
        let widgets: AppWidgets = bld.widgets().unwrap();
        widgets.main_window.show_all(); // Could also be done inside the actor
        AppActor {
            widgets,
            factories: factories,
        }.start()
    });

    gtk::main();
    Ok(())
}

Pitfalls

  • When starting Actix actors from outside Tokio/Actix, woab::block_on must be used. This is a limitation of Actix that needs to be respected.
  • dialog.run() must not be used - use woab::run_dialog instead.
  • If an actor is created inside a gtk::Application::connect_activate, its started method will run after the activate signal is done. This can be a problem for methods like set_application that can segfault if they are called outside the activate signal. A solution could be to either do the startup inside connect_activate or use woab::route_signal to route the application’s activate signal to the actor and do the startup in the actor’s signal handler.

Modules

Macros

Helper macro for extracting signal parameters from woab::Signal.

Structs

Context for utilizing a gtk::Builder and connecting it to he Actix world.

Degraded version of BuilderConnector that can only be used to get widgets.

Holds instructions for generating a GTK builder.

Signal

A message for removing actors along with their GUI

The generic signal WoAB uses.

When a future cannot be woken.

Enums

Functions

Run a feature inside the Actix system GTK will be spinning.

Shut down the Actix System that runs inside the GTK thread.

Run a future outside the Actix runtime.

Route a GIO action to an Actix actor that can handle woab::Signal.

Route a GTK signal to an Actix actor that can handle woab::Signal.

Start an Actix System that runs inside the GTK thread.

Run a GTK dialog and get its response.

Run a future outside the Actix system.

Run a feature inside the Actix system GTK will be spinning.

Asynchronously wait for something to happen somewhere.

Asynchronously wait for a signal to be called.

Type Definitions

Type of a gtk signal callback function that operates on uncast glib values.

Result type for Actix handlers that handle woab::Signal.

Derive Macros

Dissect a single Glade XML file to multiple builder factories.

Generate methods for setting/getting the widgets’ data.

Make the actor remove itself and its widgets when it gets the woab::Remove message.

Represent a set of GTK widgets created by a GTK builder.