The last few days I had a lot of fun with Relm4
The last few days I had a lot of fun with Relm4. It is an Elm inspired GTK abstraction.
The main problem I had when using gtk-rs directly was managing state. Either you use a Rc<RefCell<_>>
and pass it around or you use a background thread managing the state and channels to communicate with it.
Both ways are tedious.
Relm4 abstracts that away and helps managing state in a much better way:
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::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(>k::Box) {
set_orientation: gtk::Orientation::Vertical,
set_margin_all: 5,
set_spacing: 5,
append = >k::Button {
set_label: "Increment",
connect_clicked(sender) => move |_| {
send!(sender, AppMsg::Increment);
},
},
append = >k::Button {
set_label: "Decrement",
connect_clicked(sender) => move |_| {
send!(sender, AppMsg::Decrement);
},
},
append = >k::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();
}
I really like that approach!