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
24fn 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#[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 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}