worker/
worker.rs

1use std::convert::identity;
2use std::time::Duration;
3
4use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt};
5use relm4::{
6    Component, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent, Worker,
7    WorkerController,
8};
9
10struct AsyncHandler;
11
12#[derive(Debug)]
13enum AsyncHandlerMsg {
14    DelayedIncrement,
15    DelayedDecrement,
16}
17
18struct App {
19    counter: u8,
20    worker: WorkerController<AsyncHandler>,
21}
22
23#[derive(Debug)]
24enum AppMsg {
25    Increment,
26    Decrement,
27}
28
29impl Worker for AsyncHandler {
30    type Init = ();
31    type Input = AsyncHandlerMsg;
32    type Output = AppMsg;
33
34    fn init(_init: Self::Init, _sender: ComponentSender<Self>) -> Self {
35        Self
36    }
37
38    // This is blocking on purpose.
39    // Only one message can be processed at the time.
40    // If you don't want to block during processing, look for commands.
41    // You'll find a good reference in the "non_blocking_async" example.
42    fn update(&mut self, msg: AsyncHandlerMsg, sender: ComponentSender<Self>) {
43        std::thread::sleep(Duration::from_secs(1));
44
45        match msg {
46            AsyncHandlerMsg::DelayedIncrement => sender.output(AppMsg::Increment).unwrap(),
47            AsyncHandlerMsg::DelayedDecrement => sender.output(AppMsg::Decrement).unwrap(),
48        }
49    }
50}
51
52#[relm4::component]
53impl SimpleComponent for App {
54    type Init = ();
55    type Input = AppMsg;
56    type Output = ();
57
58    view! {
59        gtk::Window {
60            set_title: Some("Worker Counter"),
61            set_default_size: (300, 100),
62
63            gtk::Box {
64                set_orientation: gtk::Orientation::Vertical,
65                set_margin_all: 5,
66                set_spacing: 5,
67
68                gtk::Button {
69                    set_label: "Increment",
70                    connect_clicked[sender = model.worker.sender().clone()] => move |_| {
71                        sender.send(AsyncHandlerMsg::DelayedIncrement).unwrap();
72                    },
73                },
74                gtk::Button::with_label("Decrement") {
75                    connect_clicked[sender = model.worker.sender().clone()] => move |_| {
76                        sender.send(AsyncHandlerMsg::DelayedDecrement).unwrap();
77                    },
78                },
79                gtk::Label {
80                    set_margin_all: 5,
81                    #[watch]
82                    set_label: &format!("Counter: {}", model.counter),
83                },
84            },
85        }
86    }
87
88    fn init(
89        _: Self::Init,
90        root: Self::Root,
91        sender: ComponentSender<Self>,
92    ) -> ComponentParts<Self> {
93        let model = App {
94            counter: 0,
95            worker: AsyncHandler::builder()
96                .detach_worker(())
97                .forward(sender.input_sender(), identity),
98        };
99
100        let widgets = view_output!();
101
102        ComponentParts { model, widgets }
103    }
104
105    fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
106        match msg {
107            AppMsg::Increment => {
108                self.counter = self.counter.wrapping_add(1);
109            }
110            AppMsg::Decrement => {
111                self.counter = self.counter.wrapping_sub(1);
112            }
113        }
114    }
115}
116
117fn main() {
118    let app = RelmApp::new("relm4.example.worker");
119    app.run::<App>(());
120}