Idiomatic, GTK+-based, GUI library, inspired by Elm, written in Rust

Related tags

GUI hacktoberfest
Overview

Relm

Asynchronous, GTK+-based, GUI library, inspired by Elm, written in Rust.

This library is in beta stage: it has not been thoroughly tested and its API may change at any time.

CI Relm Tutorial blueviolet relm rust documentation blue relm relm:matrix relm Donate Patreon orange

Requirements

Since relm is based on GTK+, you need this library on your system in order to use it.

See this page for information on how to install GTK+.

Usage

First, add this to your Cargo.toml:

gtk = "^0.9.0"
relm = "^0.21.0"
relm-derive = "^0.21.0"

Next, add this to your crate:

use relm::{connect, Relm, Update, Widget};
use gtk::prelude::*;
use gtk::{Window, Inhibit, WindowType};
use relm_derive::Msg;

Then, create your model:

struct Model {
    // …
}

The model contains the data related to a Widget. It may be updated by the Widget::update function.

Create your message enum:

#[derive(Msg)]
enum Msg {
    // …
    Quit,
}

Messages are sent to Widget::update to indicate that an event happened. The model can be updated when an event is received.

Create a struct which represents a Widget which contains the GTK+ widgets (in this case, the main window of the application) and the model:

struct Win {
    // …
    model: Model,
    window: Window,
}

To make this struct a relm Widget that can be shown by the library, implement the Update and Widget traits:

impl Update for Win {
    // Specify the model used for this widget.
    type Model = Model;
    // Specify the model parameter used to init the model.
    type ModelParam = ();
    // Specify the type of the messages sent to the update function.
    type Msg = Msg;

    // Return the initial model.
    fn model(_: &Relm<Self>, _: ()) -> Model {
        Model {
        }
    }

    // The model may be updated when a message is received.
    // Widgets may also be updated in this function.
    fn update(&mut self, event: Msg) {
        match event {
            Msg::Quit => gtk::main_quit(),
        }
    }
}

impl Widget for Win {
    // Specify the type of the root widget.
    type Root = Window;

    // Return the root widget.
    fn root(&self) -> Self::Root {
        self.window.clone()
    }

    // Create the widgets.
    fn view(relm: &Relm<Self>, model: Self::Model) -> Self {
        // GTK+ widgets are used normally within a `Widget`.
        let window = Window::new(WindowType::Toplevel);

        // Connect the signal `delete_event` to send the `Quit` message.
        connect!(relm, window, connect_delete_event(_, _), return (Some(Msg::Quit), Inhibit(false)));
        // There is also a `connect!()` macro for GTK+ events that do not need a
        // value to be returned in the callback.

        window.show_all();

        Win {
            model,
            window,
        }
    }
}

Finally, show this Widget by calling Win::run():

fn main() {
    Win::run(()).unwrap();
}

#[widget] attribute

A #[widget] attribute is provided to simplify the creation of a widget.

This attribute does the following:

  • Provide a view! macro to create the widget with a declarative syntax.

  • Automatically create the fn root(), type Msg, type Model, type ModelParam and type Root items.

  • Automatically insert the call to Widget::set_property() in the update() function when assigning to an attribute of the model.

  • Automatically create the Widget struct.

  • Update and Widget traits can be implemented at once.

To use this attribute, add the following code:

use relm_derive::widget;

Here is an example using this attribute:

#[derive(Msg)]
pub enum Msg {
    Decrement,
    Increment,
    Quit,
}

pub struct Model {
    counter: u32,
}

#[widget]
impl Widget for Win {
    fn model() -> Model {
        Model {
            counter: 0,
        }
    }

    fn update(&mut self, event: Msg) {
        match event {
            // A call to self.label1.set_text() is automatically inserted by the
            // attribute every time the model.counter attribute is updated.
            Msg::Decrement => self.model.counter -= 1,
            Msg::Increment => self.model.counter += 1,
            Msg::Quit => gtk::main_quit(),
        }
    }

    view! {
        gtk::Window {
            gtk::Box {
                orientation: Vertical,
                gtk::Button {
                    // By default, an event with one paramater is assumed.
                    clicked => Msg::Increment,
                    // Hence, the previous line is equivalent to:
                    // clicked(_) => Increment,
                    label: "+",
                },
                gtk::Label {
                    // Bind the text property of this Label to the counter attribute
                    // of the model.
                    // Every time the counter attribute is updated, the text property
                    // will be updated too.
                    text: &self.model.counter.to_string(),
                },
                gtk::Button {
                    clicked => Msg::Decrement,
                    label: "-",
                },
            },
            // Use a tuple when you want to both send a message and return a value to
            // the GTK+ callback.
            delete_event(_, _) => (Msg::Quit, Inhibit(false)),
        }
    }
}
Note
The struct Win is now automatically created by the attribute, as are the function root() and the associated types Model, ModelParam, Msg and Container. You can still provide the method and the associated types if needed, but you cannot create the struct.
Warning
The #[widget] makes the generated struct public: hence, the corresponding model and message types must be public too.
Warning

Your program might be slower when using this attribute because the code generation is simple. For instance, the following code

fn update(&mut self, event: Msg) {
    for _ in 0..100 {
        self.model.counter += 1;
    }
}

will generate this function:

fn update(&mut self, event: Msg) {
    for _ in 0..100 {
        self.model.counter += 1;
        self.label1.set_text(&self.model.counter.to_string());
    }
}
Warning

Also, the set_property() calls are currently only inserted when assigning to an attribute of the model. For instance, the following code

fn update(&mut self, event: Msg) {
    self.model.text.push_str("Text");
}

will not work as expected.

Please use the following variation if needed.

fn update(&mut self, event: Msg) {
    self.model.text += "Text";
}

For more information about how you can use relm, you can take a look at the examples.

Donations

If you appreciate this project and want new features to be implemented, please support me on Patreon.

become a patron button

Issues
  • compilation errors are reported on the

    compilation errors are reported on the "widget" annotation not at the correct location

    I'm not sure whether that one is in fact fixable, but it is quite annoying when I hit it (which is relatively often). I get compilation errors like that:

    error[E0282]: type annotations needed
       --> projectpad/src/widgets/server_item_list_item.rs:112:1
        |
    112 | #[widget]
        | ^^^^^^^^^ this method call resolves to `&T`
        |
        = note: type must be known at this point
    

    So the error is reported on the #[widget] annotation and not at the real location of the error. It can get very annoying, sometimes I had errors like "types don't match" (without any extra info) reported at this location :(

    But due to macro use i'm not sure whether it's fixable in relm. If you're not aware of this issue and need a small reproduction, I'll try to produce one.

    bug 
    opened by emmanueltouzery 23
  • libgtkflow with relm?

    libgtkflow with relm?

    https://github.com/grindhold/libgtkflow would be really nice to use with relm. How can I? I will do whatever work necessary but I don't know where to start.

    opened by lf94 19
  • Think about how to avoid having to write wrapper methods for gtk properties of relm widget

    Think about how to avoid having to write wrapper methods for gtk properties of relm widget

    For instance, the following:

    View {
        visible: self.model.visible,
    }
    

    requires to write a set_visible() method calling the set_visible() method from gtk.

    Might be possible by calling Widget::root().

    easy 
    opened by antoyo 14
  • Better error reporting

    Better error reporting

    That would require the ability to set positions (spans) in the proc macro and the ability to spans errors.

    medium 
    opened by antoyo 13
  • Question: how to update model from outside widget and update widget afterward?

    Question: how to update model from outside widget and update widget afterward?

    Hi, I'm trying to write an app with Relm and I think I understand well the different examples but they all show the case when the model is created by the widget.

    I have data read from the hard drive that I want to display and I'm wondering how to inject them in the widget and make the widget refresh

    Thank you for any insight :)

    question 
    opened by Geobert 12
  • "Clicked" on Label

    How would one detect clicks on Label? There's no connect_clicked and there isn't one in the EventBox either.

    Is there another widget I could use to detect the button press on a Label?

    opened by MGlolenstine 12
  • refs #25: ability to set the widget style class through the view DSL

    refs #25: ability to set the widget style class through the view DSL

    I've wanted this feature in relm for a while, in my relm apps i have a bunch of get_style_context().add_class() calls in my init_view implementations.

    The new style_class attribute must enable to specify multiple style classes for a widget.. currently I made it that you must repeat the attribute multiple times in the DSL. We could also change it so that the user can type #[style_class=("destructive-action", "linked")]. Let me know if you'd prefer that.

    I also had to handle style classes specially in the Attributes struct, because with a Map of attributes, we couldn't list multiple times the style_class attribute. Again other approaches are possible, if you'd rather I do it another way, let me know!

    opened by emmanueltouzery 11
  • Using Hyper and Relm? (http examples broken)

    Using Hyper and Relm? (http examples broken)

    Is it possible to use Hyper with Relm? The example in relm-easyfibers appears to be outdated as the Relm.handle() method seems to be gone, apparently from moving from tokio to futures-glib. Is the a current working solution to Http futures from Relm? I need to be able to call some Http endpoints and update the Relm UI with the results.

    opened by iBelieve 9
  • Add a list of applications using relm

    Add a list of applications using relm

    https://github.com/sanpii/yellow-pitaya

    opened by antoyo 9
  • Provide a prelude module

    Provide a prelude module

    To import all the gtk::*Ext traits that does not conflicts.

    enhancement easy 
    opened by antoyo 8
  • examples: introduce glade_gladis example

    examples: introduce glade_gladis example

    Gladis is a crate that aims at simplifying the use of Glade/UI files for Rust GTK crates. It allows to avoid the manual calls to Gtk::Builder by automatically importing the widgets from the declared structure that uses the #[derive(Gladis)] statement.

    I have added a Relm example in the Gladis crate, but I think it would also make sense to use Gladis in the Relm crate that is demoing how to use Glade UI files.

    Thanks in advance for reviewing.

    opened by MicroJoe 2
  • extract widget macro parsing from expansion

    extract widget macro parsing from expansion

    This commit separates the parsing step from the expansion step in the widget macro. It makes it clearer which items are required vs optional, and improves the error messages.

    opened by euclio 0
  • gather crates into workspace

    gather crates into workspace

    This enables running commands across all the crates at once, as well as making the crates share a Cargo.lock and an output directory.

    opened by euclio 3
  • How does one use relm with a TextView?

    How does one use relm with a TextView?

    I just went through the mini tutorial in the README.md, and I really like this library to provide structure to my GTK-rs applications, but I need to use a gtk::TextView and when I tried to make it work with the view! macro it failed.

    Code:

    pub struct Model{
        file_contents: String,
        // … more things
    }
    
    //in view!
    gtk::TextView{
        text: {
            let text_buffer = TextBuffer::new(None);
            text_buffer.set_text(self.model.file_contents);
            Some(text_buffer)
        }
    }
    

    Error:

    error: expected identifier
      --> src/main.rs:60:25
       |
    60 |                         let text_buffer = TextBuffer::new(None);
       |                         ^^^
    

    Versions of my dependencies:

    [dependencies]
    relm = "0.21.0"
    relm-derive = "0.21.0"
    gtk = "0.9.2"
    

    MCVE (might have some other trivial errors and isn't complete (I wanted to make a trivial notepad clone to try out the library), but it demonstrates the issue):

    use std::path::PathBuf;
    use gtk::Orientation;
    use relm_derive::{widget, Msg};
    use relm::Widget;
    use gtk::prelude::*;
    
    #[derive(Msg)]
    pub enum Msg{
        OpenButton,
        OpenFile,
        SaveButton
    }
    
    pub struct Model{
        path: Option<PathBuf>,
        file_contents: String
    }
    
    #[widget]
    impl Widget for Win{
    
        fn model() -> Model{
            Model{
                path: None,
                file_contents: "".into()
            }
        }
    
        fn update(&mut self, event: Msg) {
            match event{
                Msg::OpenButton => {},
                Msg::OpenFile => {},
                Msg::SaveButton => {}
            }
        }
    
        view!{
            gtk::Window {
                gtk::Box {
                    orientation: Orientation::Vertical,
                    gtk::Box {
                        orientation: Orientation::Horizontal,
                        gtk::Button {
                            clicked => Msg::OpenButton,
                            label: "Open"
                        },
                        gtk::Button {
                            clicked => Msg::SaveButton,
                            label: "Save"
                        },
                        gtk::Label {
                            text: match &self.model.path {
                                Some(path) => &path.into_os_string().into_string().unwrap_or("Improper OS string in path".into()),
                                None => "File Not Saved".into()
                            }
                        }
                    },
                    gtk::TextView{
                        text: {
                            let text_buffer = TextBuffer::new(None);
                            text_buffer.set_text(self.model.file_contents);
                            Some(text_buffer)
                        }
                    }
                }
            }
        }
    
    }
    
    
    fn main() {
        Win::run(()).unwrap();
    }
    

    I assume that this means that the view! macro doesn't support blocks, but given this, I don't see how I can use a gtk::TextView.

    opened by john01dav 2
  • relm's event don't work when in a busy loop in the gtk GUI thread even if calling process_events

    relm's event don't work when in a busy loop in the gtk GUI thread even if calling process_events

    see reproduction here: https://github.com/emmanueltouzery/relm_process_events

    the code adds 100k rows in a gtk treeview. It does so through a busy loop in the gtk GUI thread. BUT it doesn't freeze the GUI thread because it regularly calls:

                    while gtk::events_pending() {
                        gtk::main_iteration();
                    }
    

    however that is not enough apparently to enable relm's event processing. It seems relm components' update() methods are called only after such a busy loop. In this repro, I listen to double clicks on grid rows. When there is such a double click, I make a println!()... this works during the busy loop:

    [src/main.rs:46] i = 65398 [src/main.rs:46] i = 65399 [src/main.rs:46] i = 65400 emitting relm event [src/main.rs:46] i = 65401 [src/main.rs:46] i = 65402

    however the update() itself, that should be called as a result of the stream().emit() being invoked, is not invoked until after the busy loop finishes:

        fn update(&mut self, event: Msg) {
            dbg!(event);
        }
    

    [src/main.rs:46] i = 99997 [src/main.rs:46] i = 99998 [src/main.rs:46] i = 99999 emitting relm event [src/main.rs:69] event = RowClicked emitting relm event [src/main.rs:69] event = RowClicked emitting relm event [src/main.rs:69] event = RowClicked

    opened by emmanueltouzery 1
  • unexpected behavior of nested views in embedded separated component

    unexpected behavior of nested views in embedded separated component

    relm 21 nested views are amazing!

    I've used them in a greenfield project to add titlebar menus without any imperative code. in that project I have the titlebar directly in the main window component. This works without problems.

    I now wanted to use the same pattern also in another project where the titlebar component is split from the window component, and it does not work well.

    I made a repro github: https://github.com/emmanueltouzery/relm11issue

    This is the main thing:

            gtk::Window {
                titlebar: Some(self.model.titlebar.widget())
            }
    

    In the project where this works fine, I have directly the titlebar component nested within the window view, as is now possible with relm 21. But in this legacy project the titlebar component is so large that I'll keep it separate from the window component. But in this configuration I have trouble with nested views in the separate titlebar component.

    If I run the project from this github, the titlebar is translucent, and I get the following GTK errors on the command-line:

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.630: gtk_window_get_default_widget: assertion 'GTK_IS_WINDOW (window)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.630: gtk_window_set_default: assertion 'GTK_IS_WINDOW (window)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.648: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.648: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.648: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.649: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.649: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.650: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.651: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:03.651: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.557: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.557: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.557: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.572: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.572: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.572: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.589: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.589: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.589: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.606: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.606: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.606: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.623: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.623: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.623: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.638: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.638: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.638: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.655: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.655: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.655: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.671: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.672: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.672: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.689: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.689: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.689: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.706: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.706: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.706: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.722: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.722: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.722: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.738: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.738: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.738: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.754: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.754: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.754: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.771: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.771: gtk_render_frame_gap: assertion 'xy1_gap <= height' failed

    (relm11issue:55675): Gtk-CRITICAL **: 22:03:04.771: gtk_widget_translate_coordinates: assertion 'GTK_IS_WIDGET (dest_widget)' failed

    opened by emmanueltouzery 2
  • EventStream panics on emit from another thread

    EventStream panics on emit from another thread

    This is probably not what we want.

    There should be a way to check if the stream is closed.

    opened by antoyo 1
  • support for virtualized widget lists?

    support for virtualized widget lists?

    i'm having a great time writing relm apps, but my #1 issue is the memory use when adding many relm widget to a gtk ListBox. I actually once drew my big list in a DrawingArea to work around this. This is not specifically a relm problem, that's a gtk problem, and it seems they've fixed it in gtk4: https://blog.gtk.org/2020/06/07/scalable-lists-in-gtk-4/

    So I've been expecting for a while that this issue gets fixed when relm supports gtk4, but now I started to worry that it's not enough that gtk4 offers that, it might be quite some work for relm to also support the concept of virtualized widget lists, where components get recycled as the user scrolls the list? I am curious, do you think it's achievable for relm without big changes?

    opened by emmanueltouzery 4
  • Comparison to vgtk

    Comparison to vgtk

    This crate and vgtk seem pretty similar on a first glance—they're both GUI libraries with a "modern" Elm/React-style API. It'd be nice if there was some documentation somewhere describing the differences and pros/cons of each.

    Some preliminary notes from a quick look at each crate:

    • vgtk uses an HTML-like syntax for defining UIs, while relm's syntax looks more like normal Rust. (Both syntaxes are implemented as macros usable from within normal Rust files.)
    • vgtk uses Futures, while relm doesn't.
    • vgtk uses a Virtual DOM-style approach, while relm works by inserting code to update the UI after state updates at compile time. The tradeoff here seems to be that relm's approach is more efficient, but vgtk's has fewer gotchas.
    • relm is more popular and more actively maintained.
    opened by Gaelan 1
  • Handle messages on exit

    Handle messages on exit

    Fix #266.

    opened by antoyo 0
A cross-platform GUI library for Rust, inspired by Elm

Iced A cross-platform GUI library for Rust focused on simplicity and type-safety. Inspired by Elm. Features Simple, easy-to-use, batteries-included AP

Héctor Ramón 12k Nov 24, 2021
Simple and portable (but not inflexible) GUI library in C that uses the native GUI technologies of each platform it supports.

libui: a portable GUI library for C This README is being written. Status It has come to my attention that I have not been particularly clear about how

Pietro Gagliardi 10.1k Nov 21, 2021
Highly customizable finder with high performance. Written in Rust and uses GTK

Findex Highly customizable finder with high performance. Written in Rust and uses GTK Installation Automatic Binary Clone from https://aur.archlinux.o

MD Gaziur Rahman Noor 253 Nov 30, 2021
GUI based tool to sort and categorize images written in Rust

ImageSieve GUI based tool to sort out images based on similarity, categorize them according to their creation date and archive them in a target folder

Florian Fetz 4 Nov 21, 2021
A simple GUI version of the pH calibration tool written in egui, based on the eframe template.

caliphui A simple GUI version of the pH calibration tool written in egui, based on the eframe template. Usage Native binaries are provided under relea

Peter Dunne 0 Nov 22, 2021
Provides Rust bindings for GTK libraries

The gtk-rs organization aims to provide safe Rust binding over GObject-based libraries

null 279 Nov 23, 2021
An example of searching iBeacon using gtk-rs and btleplug.

Beacon Searcher Screenshot Compile & Run Install GTK 3 dev packages: macOS: $ brew install gtk+3 $ brew install gnome-icon-theme Debian / Ubuntu: $ s

Ling, Wei-Cheng 1 Nov 28, 2021
An easy-to-use, 2D GUI library written entirely in Rust.

Conrod An easy-to-use, 2D GUI library written entirely in Rust. Guide What is Conrod? A Brief Summary Screenshots and Videos Feature Overview Availabl

PistonDevelopers 3.2k Nov 28, 2021
SwiftUI Inspired UI Library written in rust

Mule (Definitely a Work in Progress) The night I started this project I was on the couch drinking a Moscow Mule.

null 37 Aug 22, 2021
Rust bindings for the FLTK GUI library.

fltk-rs Rust bindings for the FLTK Graphical User Interface library. The FLTK crate is a crossplatform lightweight gui library which can be statically

Mohammed Alyousef 550 Nov 22, 2021
Clear Coat is a Rust wrapper for the IUP GUI library.

Clear Coat Clear Coat is a Rust wrapper for the IUP GUI library. IUP uses native controls and has Windows and GTK backends. A macOS backend has been o

Jordan Miner 18 Feb 13, 2021
A cross-platform GUI library for Rust focused on simplicity and type-safety

A cross-platform GUI library for Rust, inspired by Elm

Héctor Ramón 12k Nov 29, 2021
A single-header ANSI C immediate mode cross-platform GUI library

Nuklear This is a minimal-state, immediate-mode graphical user interface toolkit written in ANSI C and licensed under public domain. It was designed a

Immediate Mode UIs, Nuklear, etc. 5k Nov 22, 2021
The bindings to the Nuklear 2D immediate GUI library.

nuklear-rust The bindings to the Nuklear 2D immediate GUI library. Currently beta. Drawing backends: gfx-pre-ll for GFX 3D drawing engine (examples: O

Serhii Plyhun 324 Nov 17, 2021
A react-inspired UI library for building multimedia desktop apps with rust and vulkan.

narui A react-inspired UI library for building multimedia desktop apps with rust and vulkan. declarative UI with Ergonomics similar to React with hook

apertus° - open source cinema 13 Nov 23, 2021
A simple, cross-platform GUI automation module for Rust.

AutoPilot AutoPilot is a Rust port of the Python C extension AutoPy, a simple, cross-platform GUI automation library for Python. For more information,

null 206 Nov 22, 2021
Truly cross platform, truly native. multiple backend GUI for rust

WIP: Sauron-native a rust UI library that conquers all platforms ranging from desktop to mobile devices. An attempt to create a truly native, truly cr

Jovansonlee Cesar 617 Nov 19, 2021
A simple news reading GUI app built in Rust

Headlines [WIP] A native GUI app built with Rust using egui. Uses newsapi.org as the source to fetch news articles. This is a WIP and the current stat

creativcoder 12 Nov 15, 2021
Build GUI applications with minimal dependencies in Rust

winapi-app-windows A crate to build applications' windows in Windows using WinAPI. This would be less confusing if the operating system was called som

Lonami 3 Nov 4, 2021