1use futures::FutureExt;
16use gtk::prelude::*;
17use relm4::*;
18
19fn main() {
20 RelmApp::new("org.relm4.ProgressExample").run::<App>("Settings List Demo".into());
21}
22
23#[derive(Default)]
24pub struct App {
25 computing: bool,
27
28 task: Option<CmdOut>,
30}
31
32pub struct Widgets {
33 button: gtk::Button,
34 label: gtk::Label,
35 progress: gtk::ProgressBar,
36}
37
38#[derive(Debug)]
39pub enum Input {
40 Compute,
41}
42
43#[derive(Debug)]
44pub enum Output {
45 Clicked(u32),
46}
47
48#[derive(Debug)]
49pub enum CmdOut {
50 Progress(f32),
52 Finished(Result<String, ()>),
54}
55
56impl Component for App {
57 type Init = String;
58 type Input = Input;
59 type Output = Output;
60 type CommandOutput = CmdOut;
61 type Widgets = Widgets;
62 type Root = gtk::Window;
63
64 fn init_root() -> Self::Root {
65 gtk::Window::default()
66 }
67
68 fn init(
69 _args: Self::Init,
70 root: Self::Root,
71 sender: ComponentSender<Self>,
72 ) -> ComponentParts<Self> {
73 relm4::view! {
74 container = gtk::Box {
75 set_halign: gtk::Align::Center,
76 set_valign: gtk::Align::Center,
77 set_width_request: 300,
78 set_spacing: 12,
79 set_margin_top: 4,
80 set_margin_bottom: 4,
81 set_margin_start: 12,
82 set_margin_end: 12,
83 set_orientation: gtk::Orientation::Horizontal,
84
85 gtk::Box {
86 set_spacing: 4,
87 set_hexpand: true,
88 set_valign: gtk::Align::Center,
89 set_orientation: gtk::Orientation::Vertical,
90
91 append: label = >k::Label {
92 set_xalign: 0.0,
93 set_label: "Find the answer to life:",
94 },
95
96 append: progress = >k::ProgressBar {
97 set_visible: false,
98 },
99 },
100
101 append: button = >k::Button {
102 set_label: "Compute",
103 connect_clicked => Input::Compute,
104 }
105 }
106 }
107
108 root.set_child(Some(&container));
109
110 ComponentParts {
111 model: App::default(),
112 widgets: Widgets {
113 label,
114 button,
115 progress,
116 },
117 }
118 }
119
120 fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>, _root: &Self::Root) {
121 match message {
122 Input::Compute => {
123 self.computing = true;
124 sender.command(|out, shutdown| {
125 shutdown
126 .register(async move {
128 let mut progress = 0.0;
129
130 while progress < 1.0 {
131 out.send(CmdOut::Progress(progress)).unwrap();
132 progress += 0.1;
133 tokio::time::sleep(std::time::Duration::from_millis(333)).await;
134 }
135
136 out.send(CmdOut::Finished(Ok("42".into()))).unwrap();
137 })
138 .drop_on_shutdown()
140 .boxed()
142 });
143 }
144 }
145 }
146
147 fn update_cmd(
148 &mut self,
149 message: Self::CommandOutput,
150 _sender: ComponentSender<Self>,
151 _root: &Self::Root,
152 ) {
153 if let CmdOut::Finished(_) = message {
154 self.computing = false;
155 }
156
157 self.task = Some(message);
158 }
159
160 fn update_view(&self, widgets: &mut Self::Widgets, _sender: ComponentSender<Self>) {
161 widgets.button.set_sensitive(!self.computing);
162
163 if let Some(ref progress) = self.task {
164 match progress {
165 CmdOut::Progress(p) => {
166 widgets.label.set_label("Searching for the answer...");
167 widgets.progress.set_visible(true);
168 widgets.progress.set_fraction(*p as f64);
169 }
170 CmdOut::Finished(result) => {
171 widgets.progress.set_visible(false);
172 widgets
173 .label
174 .set_label(&format!("The answer to life is: {result:?}"));
175 }
176 }
177 }
178 }
179}