relm4 0.1.0-beta.9

An idiomatic GUI library inspired by Elm and based on gtk4-rs
docs.rs failed to build relm4-0.1.0-beta.9
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: relm4-0.8.1

CI Matrix Relm4 on crates.io Relm4 docs Relm4 book Miminum Rust version 1.53

An idiomatic GUI library inspired by Elm and based on gtk4-rs. Relm4 is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.

Goals

  • ⏱️ Productivity: Writing an application should require as few overhead as possible
  • Flexibility: Anything that's possible to do with GTK4 should be possible in Relm4 as well
  • Simplicity: Writing an application should be as easy and straight forward as possible
  • 🔧 Maintainability: The Elm programming model used by Relm4 provides a simple and clear structure for app development

Documentation

Dependencies

Relm4 only depends on GTK4: How to install GTK4

Ecosystem

Relm4 has two crates that extend the core functionality:

  • relm4-macros provides a widget macro that simplifies UI creation
  • relm4-components is a collections of reusable components you can easily integrate into your application

Add this to your Cargo.toml:

gtk = { version = "0.2", package = "gtk4" }
relm4 = "0.1.0-beta.9"
relm4-macros = "0.1.0-beta.9"
relm4-components = "0.1.0-beta.9"

Features

The relm4 crate has two feature flags:

  • tokio-rt: Adds the AsyncWorker type that uses an async update function
  • libadwaita: Improved support for libadwaita

Examples

Several example applications are available at relm4-examples/.

A simple counter app

use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt};
use relm4::{send, AppUpdate, Model, RelmApp, Sender, WidgetPlus, Widgets};

#[derive(Default)]
struct AppModel {
    counter: u8,
}

enum AppMsg {
    Increment,
    Decrement,
}

impl Model for AppModel {
    type Msg = AppMsg;
    type Widgets = AppWidgets;
    type Components = ();
}

impl AppUpdate for AppModel {
    fn update(&mut self, msg: AppMsg, _components: &(), _sender: Sender<AppMsg>) -> bool {
        match msg {
            AppMsg::Increment => {
                self.counter = self.counter.wrapping_add(1);
            }
            AppMsg::Decrement => {
                self.counter = self.counter.wrapping_sub(1);
            }
        }
        true
    }
}

#[relm4_macros::widget]
impl Widgets<AppModel, ()> for AppWidgets {
    view! {
        gtk::ApplicationWindow {
            set_title: Some("Simple app"),
            set_default_width: 300,
            set_default_height: 100,
            set_child = Some(&gtk::Box) {
                set_orientation: gtk::Orientation::Vertical,
                set_margin_all: 5,
                set_spacing: 5,

                append = &gtk::Button {
                    set_label: "Increment",
                    connect_clicked(sender) => move |_| {
                        send!(sender, AppMsg::Increment);
                    },
                },
                append = &gtk::Button {
                    set_label: "Decrement",
                    connect_clicked(sender) => move |_| {
                        send!(sender, AppMsg::Decrement);
                    },
                },
                append = &gtk::Label {
                    set_margin_all: 5,
                    set_label: watch! { &format!("Counter: {}", model.counter) },
                }
            },
        }
    }
}

fn main() {
    let model = AppModel::default();
    let app = RelmApp::new(model);
    app.run();
}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Feedback and contributions are highly appreciated!