nrelm 0.1.0

An idiomatic GUI library inspired by Elm and based on gtk3-rs
docs.rs failed to build nrelm-0.1.0
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.

Pipeline NeoRelm on crates.io NeoRelm docs Minimum Rust version 1.93 dependency status

An idiomatic GUI library inspired by Elm and based on gtk3-rs.

NeoRelm is a GTK3 port of Relm4 (the original Relm4 targets GTK4 and libadwaita, while this project backports the same API and design to GTK3). Why? Because gnome keeps deprecating everything in gtk4. Making apps with gtk4 without libadwaita might not be easy in future. And because I wanted to make apps for XFCE.

Documentation

Dependencies

NeoRelm depends on GTK3

Ecosystem

  • nrelm-macros - several macros for declarative UI definitions.
  • nrelm-components - a collection of reusable components.
  • nrelm-icons - icons for your application (maintained in its own repository).

Use this in your Cargo.toml:

# Core library
nrelm = "0.1"
# Optional: reusable components
nrelm-components = "0.1"
# Optional: icons (more info at https://gitlab.com/NeoRelm/NeoRelm-icons)
nrelm-icons = "0.1"

Features

The nrelm crate has two feature flags:

Flag Purpose Default
macros Enable macros by re-exporting nrelm-macros
css Enable the built-in CSS definitions of nrelm-css

Examples

Several example applications are available at examples/.

📸 Screenshots from the example apps

A simple counter app

Simple app screenshot light Simple app screenshot dark

use gtk::prelude::*;
use nrelm::prelude::*;

struct AppModel {
    counter: u8,
}

#[derive(Debug)]
enum AppMsg {
    Increment,
    Decrement,
}

#[nrelm::component]
impl SimpleComponent for AppModel {
    type Init = u8;
    type Input = AppMsg;
    type Output = ();

    view! {
        gtk::Window {
            set_title: "Simple app",
            set_default_size: (300, 100),

            gtk::Box {
                set_orientation: gtk::Orientation::Vertical,
                set_spacing: 5,
                set_margin_all: 5,

                gtk::Button {
                    set_label: "Increment",
                    connect_clicked => AppMsg::Increment,
                },

                gtk::Button {
                    set_label: "Decrement",
                    connect_clicked => AppMsg::Decrement,
                },

                gtk::Label {
                    #[watch]
                    set_label: &format!("Counter: {}", model.counter),
                    set_margin_all: 5,
                }
            }
        }
    }

    // Initialize the component.
    fn init(
        counter: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let model = AppModel { counter };

        // Insert the code generation of the view! macro here
        let widgets = view_output!();

        ComponentParts { model, widgets }
    }

    fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
        match msg {
            AppMsg::Increment => {
                self.counter = self.counter.wrapping_add(1);
            }
            AppMsg::Decrement => {
                self.counter = self.counter.wrapping_sub(1);
            }
        }
    }
}

fn main() {
    let app = RelmApp::new("nrelm.example.simple");
    app.run::<AppModel>(0);
}

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!