1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt};
use rand::prelude::IteratorRandom;
use relm4::{gtk, send, AppUpdate, Model, RelmApp, Sender, WidgetPlus, Widgets};

const ICON_LIST: &[&str] = &[
    "bookmark-new-symbolic",
    "edit-copy-symbolic",
    "edit-cut-symbolic",
    "edit-find-symbolic",
    "starred-symbolic",
    "system-run-symbolic",
    "emoji-objects-symbolic",
    "emoji-nature-symbolic",
    "display-brightness-symbolic",
];

fn random_icon_name() -> &'static str {
    ICON_LIST
        .iter()
        .choose(&mut rand::thread_rng())
        .expect("Could not choose a random icon")
}

enum AppMsg {
    UpdateFirst,
    UpdateSecond,
}

// The track proc macro allows to easily track changes to different
// fields of the model
#[tracker::track]
struct AppModel {
    first_icon: &'static str,
    second_icon: &'static str,
    identical: bool,
}

impl Model for AppModel {
    type Msg = AppMsg;
    type Widgets = AppWidgets;
    type Components = ();
}

impl AppUpdate for AppModel {
    fn update(&mut self, msg: AppMsg, _components: &(), _sender: Sender<AppMsg>) -> bool {
        // reset tracker value of the model
        self.reset();

        match msg {
            AppMsg::UpdateFirst => {
                self.set_first_icon(random_icon_name());
            }
            AppMsg::UpdateSecond => {
                self.set_second_icon(random_icon_name());
            }
        }
        self.set_identical(self.first_icon == self.second_icon);

        true
    }
}

#[relm4::widget]
impl Widgets<AppModel, ()> for AppWidgets {
    view! {
        main_window = gtk::ApplicationWindow {
            set_class_active: track!(model.changed(AppModel::identical()), "identical", model.identical),
            set_child = Some(&gtk::Box) {
                set_orientation: gtk::Orientation::Horizontal,
                set_spacing: 10,
                set_margin_all: 10,
                append = &gtk::Box {
                    set_orientation: gtk::Orientation::Vertical,
                    set_spacing: 10,
                    append = &gtk::Image {
                        set_pixel_size: 50,
                        set_icon_name: track!(Some(model.first_icon)),
                    },
                    append = &gtk::Button {
                        set_label: "New random image",
                        connect_clicked(sender) => move |_| {
                            send!(sender, AppMsg::UpdateFirst);
                        }
                    }
                },
                append = &gtk::Box {
                    set_orientation: gtk::Orientation::Vertical,
                    set_spacing: 10,
                    append = &gtk::Image {
                        set_pixel_size: 50,
                        set_icon_name: track!(model.changed(AppModel::second_icon()),
                            Some(model.second_icon)),
                    },
                    append = &gtk::Button {
                        set_label: "New random image",
                        connect_clicked(sender) => move |_| {
                            send!(sender, AppMsg::UpdateSecond);
                        }
                    }
                },
            }
        }
    }

    fn post_init() {
        relm4::set_global_css(b".identical { background: #00ad5c; }");
    }
}

fn main() {
    let model = AppModel {
        first_icon: random_icon_name(),
        second_icon: random_icon_name(),
        identical: false,
        tracker: 0,
    };
    let relm = RelmApp::new(model);
    relm.run();
}