tracker/
tracker.rs

1use gtk::prelude::{BoxExt, ButtonExt, OrientableExt};
2use rand::prelude::IteratorRandom;
3use relm4::{ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
4
5const ICON_LIST: &[&str] = &[
6    "bookmark-new-symbolic",
7    "edit-copy-symbolic",
8    "edit-cut-symbolic",
9    "edit-find-symbolic",
10    "starred-symbolic",
11    "system-run-symbolic",
12    "emoji-objects-symbolic",
13    "emoji-nature-symbolic",
14    "display-brightness-symbolic",
15];
16
17fn random_icon_name() -> &'static str {
18    ICON_LIST
19        .iter()
20        .choose(&mut rand::rng())
21        .expect("Could not choose a random icon")
22}
23
24// Returns a random icon different from the excluded one (avoids repeats).
25fn gen_unique_icon(exclude: &'static str) -> &'static str {
26    let mut rnd = random_icon_name();
27    while rnd == exclude {
28        rnd = random_icon_name()
29    }
30    rnd
31}
32
33#[derive(Debug)]
34enum AppMsg {
35    UpdateFirst,
36    UpdateSecond,
37}
38
39// The track proc macro allows to easily track changes to different
40// fields of the model
41#[tracker::track]
42struct AppModel {
43    first_icon: &'static str,
44    second_icon: &'static str,
45    identical: bool,
46}
47
48#[relm4::component]
49impl SimpleComponent for AppModel {
50    type Init = ();
51    type Input = AppMsg;
52    type Output = ();
53
54    view! {
55        gtk::Window {
56            #[track(model.changed(AppModel::identical()))]
57            set_class_active: ("identical", model.identical),
58
59            gtk::Box {
60                set_orientation: gtk::Orientation::Horizontal,
61                set_spacing: 10,
62                set_margin_all: 10,
63
64                gtk::Box {
65                    set_orientation: gtk::Orientation::Vertical,
66                    set_spacing: 10,
67
68                    gtk::Image {
69                        set_pixel_size: 50,
70                        #[track(model.changed(AppModel::first_icon()))]
71                        set_icon_name: Some(model.first_icon),
72                    },
73
74                    gtk::Button {
75                        set_label: "New random image",
76                        connect_clicked => AppMsg::UpdateFirst,
77                    }
78                },
79
80                gtk::Box {
81                    set_orientation: gtk::Orientation::Vertical,
82                    set_spacing: 10,
83
84                    gtk::Image {
85                        set_pixel_size: 50,
86                        #[track(model.changed(AppModel::second_icon()))]
87                        set_icon_name: Some(model.second_icon),
88                    },
89
90                    gtk::Button {
91                        set_label: "New random image",
92                        connect_clicked => AppMsg::UpdateSecond,
93                    }
94                },
95            }
96        }
97    }
98
99    fn update(&mut self, msg: AppMsg, _sender: ComponentSender<Self>) {
100        // reset tracker value of the model
101        self.reset();
102
103        match msg {
104            AppMsg::UpdateFirst => {
105                self.set_first_icon(gen_unique_icon(self.first_icon));
106            }
107            AppMsg::UpdateSecond => {
108                self.set_second_icon(gen_unique_icon(self.second_icon));
109            }
110        }
111        self.set_identical(self.first_icon == self.second_icon);
112    }
113
114    fn init(
115        _: Self::Init,
116        root: Self::Root,
117        sender: ComponentSender<Self>,
118    ) -> ComponentParts<Self> {
119        let mut model = AppModel {
120            first_icon: random_icon_name(),
121            second_icon: random_icon_name(),
122            identical: false,
123            tracker: 0,
124        };
125
126        model.identical = model.first_icon == model.second_icon;
127
128        let widgets = view_output!();
129
130        ComponentParts { model, widgets }
131    }
132}
133
134fn main() {
135    let app = RelmApp::new("relm4.example.tracker");
136    relm4::set_global_css(".identical { background: #00ad5c; }");
137
138    app.run::<AppModel>(());
139}