message_from_grid_view/
message_from_grid_view.rs

1use glib::SignalHandlerId;
2use gtk::prelude::*;
3use rand::seq::IteratorRandom;
4use relm4::gtk::glib;
5use relm4::{
6    prelude::*,
7    typed_view::grid::{RelmGridItem, TypedGridView},
8};
9
10const CONTRIBUTORS: &[&str] = &[
11    "AaronErhardt",
12    "MaksymShcherbak",
13    "mmstick",
14    "tronta",
15    "zekefast",
16    "M23SNEZ3",
17];
18
19fn random_name() -> &'static str {
20    CONTRIBUTORS
21        .iter()
22        .choose(&mut rand::rng())
23        .expect("Could not choose a random name")
24}
25
26#[derive(Debug)]
27struct MyGridItem {
28    name: &'static str,
29    sender: ComponentSender<App>,
30    button_click_handler_id: Option<SignalHandlerId>,
31}
32
33impl MyGridItem {
34    fn new(sender: ComponentSender<App>) -> Self {
35        Self {
36            name: random_name(),
37            sender,
38            button_click_handler_id: Default::default(),
39        }
40    }
41}
42
43struct Widgets {
44    button: gtk::Button,
45    label: gtk::Label,
46}
47
48impl RelmGridItem for MyGridItem {
49    type Root = gtk::Box;
50    type Widgets = Widgets;
51
52    fn setup(item: &gtk::ListItem) -> (gtk::Box, Widgets) {
53        item.set_activatable(false);
54        item.set_focusable(false);
55        relm4::view! {
56            my_box = gtk::Box {
57                set_orientation: gtk::Orientation::Horizontal,
58                set_margin_all: 2,
59                set_spacing: 5,
60
61                #[name = "button"]
62                gtk::Button {
63                    set_hexpand: true,
64                    #[name = "label"]
65                    gtk::Label {
66                        set_halign: gtk::Align::Center,
67                    }
68                }
69            }
70        }
71
72        let widgets = Widgets { label, button };
73
74        (my_box, widgets)
75    }
76
77    fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
78        widgets.label.set_label(self.name);
79        let name = self.name;
80        let button_click_handler_id = widgets.button.connect_clicked(glib::clone!(
81            #[strong(rename_to = sender)]
82            self.sender,
83            move |_btn| {
84                // Use the cloned sender to send a message
85                sender.input(Msg::Print(name));
86            }
87        ));
88        self.button_click_handler_id
89            .replace(button_click_handler_id);
90    }
91
92    fn unbind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
93        if let Some(id) = self.button_click_handler_id.take() {
94            widgets.button.disconnect(id)
95        }
96    }
97}
98
99#[derive(Debug)]
100struct App {
101    grid_view: TypedGridView<MyGridItem, gtk::NoSelection>,
102}
103
104#[derive(Debug)]
105enum Msg<'a> {
106    Add,
107    Print(&'a str),
108}
109
110#[relm4::component]
111impl SimpleComponent for App {
112    type Init = u8;
113    type Input = Msg<'static>;
114    type Output = ();
115
116    view! {
117        gtk::Window {
118            set_title: Some("Is it really possible to send messages from an item in a grid?"),
119            set_default_size: (650, 300),
120
121            gtk::Box {
122                set_orientation: gtk::Orientation::Vertical,
123                set_spacing: 5,
124                set_margin_all: 5,
125
126                gtk::Button {
127                    set_label: "Append 10 items",
128                    connect_clicked => Msg::Add,
129                },
130
131                gtk::ScrolledWindow {
132                    set_vexpand: true,
133
134                    #[local_ref]
135                    my_view -> gtk::GridView {
136                        set_orientation: gtk::Orientation::Vertical,
137                        set_max_columns: 3,
138                    }
139                }
140            }
141        }
142    }
143
144    fn init(
145        _: Self::Init,
146        root: Self::Root,
147        sender: ComponentSender<Self>,
148    ) -> ComponentParts<Self> {
149        // Initialize the GridView
150        let grid_view: TypedGridView<MyGridItem, gtk::NoSelection> = TypedGridView::new();
151
152        let model = App { grid_view };
153
154        let my_view = &model.grid_view.view;
155
156        let widgets = view_output!();
157
158        ComponentParts { model, widgets }
159    }
160
161    fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
162        match msg {
163            Msg::Add => {
164                for _ in 0..10 {
165                    self.grid_view.append(MyGridItem::new(sender.clone()));
166                }
167            }
168            Msg::Print(name) => {
169                println!("Name: {:?}", name)
170            }
171        }
172    }
173}
174
175fn main() {
176    let app = RelmApp::new("relm4.example.message_from_grid_view");
177    app.run::<App>(0);
178}