typed_list_view_async/
typed_list_view_async.rs1use std::time::Duration;
2
3use gtk::{glib, prelude::*};
4use relm4::{
5 RelmObjectExt,
6 binding::{Binding, U8Binding},
7 prelude::*,
8 typed_view::list::{RelmListItem, TypedListView},
9};
10
11struct MyListItem {
12 value: u8,
13 binding: U8Binding,
14 handle: Option<glib::JoinHandle<()>>,
15}
16
17impl PartialEq for MyListItem {
18 fn eq(&self, other: &Self) -> bool {
19 self.value == other.value
20 }
21}
22
23impl Eq for MyListItem {}
24
25impl PartialOrd for MyListItem {
26 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
27 Some(self.cmp(other))
28 }
29}
30
31impl Ord for MyListItem {
32 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
33 self.value.cmp(&other.value)
34 }
35}
36
37impl MyListItem {
38 fn new(value: u8) -> Self {
39 Self {
40 value,
41 binding: U8Binding::new(0),
42 handle: None,
43 }
44 }
45}
46
47struct Widgets {
48 label: gtk::Label,
49 label2: gtk::Label,
50 button: gtk::CheckButton,
51}
52
53impl RelmListItem for MyListItem {
54 type Root = gtk::Box;
55 type Widgets = Widgets;
56
57 fn setup(_item: >k::ListItem) -> (gtk::Box, Widgets) {
58 relm4::view! {
59 my_box = gtk::Box {
60 #[name = "label"]
61 gtk::Label {
62 set_margin_end: 10,
63 },
64
65 #[name = "label2"]
66 gtk::Label {
67 set_margin_end: 10,
68 },
69
70 #[name = "button"]
71 gtk::CheckButton,
72 }
73 }
74
75 let widgets = Widgets {
76 label,
77 label2,
78 button,
79 };
80
81 (my_box, widgets)
82 }
83
84 fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
85 println!("Unbind {}", self.value);
86 let Widgets {
87 label,
88 label2,
89 button,
90 } = widgets;
91
92 let future_binding = self.binding.clone();
93 self.handle = Some(relm4::spawn_local(async move {
94 loop {
95 tokio::time::sleep(Duration::from_secs(1)).await;
96 let mut guard = future_binding.guard();
97 *guard = guard.wrapping_add(1);
98 }
99 }));
100
101 label.set_label(&format!("Value: {} ", self.value));
102 label2.add_write_only_binding(&self.binding, "label");
103 button.set_active(self.value % 2 == 0);
104 }
105
106 fn unbind(&mut self, _widgets: &mut Self::Widgets, _root: &mut Self::Root) {
107 self.handle
108 .take()
109 .unwrap()
110 .into_source_id()
111 .unwrap()
112 .remove();
113 *self.binding.guard() = 0;
114 }
115}
116
117struct App {
118 counter: u8,
119 list_view_wrapper: TypedListView<MyListItem, gtk::SingleSelection>,
120}
121
122#[derive(Debug)]
123enum Msg {
124 Append,
125 Remove,
126 OnlyShowEven(bool),
127}
128
129#[relm4::component]
130impl SimpleComponent for App {
131 type Init = u8;
132 type Input = Msg;
133 type Output = ();
134
135 view! {
136 gtk::Window {
137 set_title: Some("Async + idiomatic list view"),
138 set_default_size: (300, 100),
139
140 gtk::Box {
141 set_orientation: gtk::Orientation::Vertical,
142 set_spacing: 5,
143 set_margin_all: 5,
144
145 gtk::Button {
146 set_label: "Append 10 items",
147 connect_clicked => Msg::Append,
148 },
149
150 gtk::Button {
151 set_label: "Remove second item",
152 connect_clicked => Msg::Remove,
153 },
154
155 gtk::ToggleButton {
156 set_label: "Only show even numbers",
157 connect_clicked[sender] => move |btn| {
158 sender.input(Msg::OnlyShowEven(btn.is_active()));
159 }
160 },
161
162 gtk::ScrolledWindow {
163 set_vexpand: true,
164
165 #[local_ref]
166 my_view -> gtk::ListView {}
167 }
168 }
169 }
170 }
171
172 fn init(
173 counter: Self::Init,
174 root: Self::Root,
175 sender: ComponentSender<Self>,
176 ) -> ComponentParts<Self> {
177 let mut list_view_wrapper: TypedListView<MyListItem, gtk::SingleSelection> =
179 TypedListView::with_sorting();
180
181 list_view_wrapper.add_filter(|item| item.value % 2 == 0);
183 list_view_wrapper.set_filter_status(0, false);
184
185 let model = App {
186 counter,
187 list_view_wrapper,
188 };
189
190 let my_view = &model.list_view_wrapper.view;
191
192 let widgets = view_output!();
193
194 ComponentParts { model, widgets }
195 }
196
197 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
198 match msg {
199 Msg::Append => {
200 for _ in 0..10 {
202 self.counter = self.counter.wrapping_add(1);
203 self.list_view_wrapper.append(MyListItem::new(self.counter));
204 }
205 }
206 Msg::Remove => {
207 self.list_view_wrapper.remove(1);
209 }
210 Msg::OnlyShowEven(show_only_even) => {
211 self.list_view_wrapper.set_filter_status(0, show_only_even);
213 }
214 }
215 }
216}
217
218fn main() {
219 let app = RelmApp::new("relm4.example.typed-list-view-async");
220 app.run::<App>(0);
221}