1#![allow(deprecated)]
4
5use std::convert::identity;
6
7use gtk::{glib, prelude::*};
8use relm4::prelude::*;
9
10struct Header;
11
12#[relm4::component]
13impl SimpleComponent for Header {
14 type Init = ();
15 type Input = ();
16 type Output = AppMsg;
17
18 view! {
19 gtk::HeaderBar {
20 #[wrap(Some)]
21 set_title_widget = >k::Box {
22 add_css_class: relm4::css::LINKED,
23 append: group = >k::ToggleButton {
24 set_label: "View",
25 set_active: true,
26 connect_toggled[sender] => move |btn| {
27 if btn.is_active() {
28 sender.output(AppMsg::SetMode(AppMode::View)).unwrap();
29 }
30 },
31 },
32 append = >k::ToggleButton {
33 set_label: "Edit",
34 set_group: Some(&group),
35 connect_toggled[sender] => move |btn| {
36 if btn.is_active() {
37 sender.output(AppMsg::SetMode(AppMode::Edit)).unwrap();
38 }
39 },
40 },
41 append = >k::ToggleButton {
42 set_label: "Export",
43 set_group: Some(&group),
44 connect_toggled[sender] => move |btn| {
45 if btn.is_active() {
46 sender.output(AppMsg::SetMode(AppMode::Export)).unwrap();
47 }
48 },
49 },
50 }
51 }
52 }
53
54 fn init(
55 _init: Self::Init,
56 root: Self::Root,
57 sender: ComponentSender<Self>,
58 ) -> ComponentParts<Self> {
59 let model = Header;
60 let widgets = view_output!();
61
62 ComponentParts { model, widgets }
63 }
64}
65
66struct Dialog {
67 hidden: bool,
68}
69
70#[derive(Debug)]
71enum DialogMsg {
72 Show,
73 Accept,
74 Cancel,
75}
76
77struct DialogInit {
78 text: String,
79 secondary_text: Option<String>,
80 accept_text: String,
81 cancel_text: String,
82}
83
84#[relm4::component]
85impl SimpleComponent for Dialog {
86 type Init = DialogInit;
87 type Input = DialogMsg;
88 type Output = AppMsg;
89
90 view! {
91 dialog = gtk::MessageDialog {
92 set_modal: true,
93 set_text: Some(&init.text),
94 set_secondary_text: init.secondary_text.as_deref(),
95 add_button: (&init.accept_text, gtk::ResponseType::Accept),
96 add_button: (&init.cancel_text, gtk::ResponseType::Cancel),
97
98 #[watch]
99 set_visible: !model.hidden,
100
101 connect_response[sender] => move |_, resp| {
102 sender.input(if resp == gtk::ResponseType::Accept {
103 DialogMsg::Accept
104 } else {
105 DialogMsg::Cancel
106 });
107 }
108 }
109 }
110
111 fn init(
112 init: Self::Init,
113 root: Self::Root,
114 sender: ComponentSender<Self>,
115 ) -> ComponentParts<Self> {
116 let model = Dialog { hidden: true };
117 let widgets = view_output!();
118
119 ComponentParts { model, widgets }
120 }
121
122 fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
123 match msg {
124 DialogMsg::Show => self.hidden = false,
125 DialogMsg::Accept => {
126 self.hidden = true;
127 sender.output(AppMsg::Close).unwrap();
128 }
129 DialogMsg::Cancel => self.hidden = true,
130 }
131 }
132}
133
134#[derive(Debug)]
135enum AppMode {
136 View,
137 Edit,
138 Export,
139}
140
141#[derive(Debug)]
142enum AppMsg {
143 SetMode(AppMode),
144 CloseRequest,
145 Close,
146}
147
148struct App {
149 mode: AppMode,
150 dialog: Controller<Dialog>,
151 header: Controller<Header>,
152}
153
154#[relm4::component]
155impl SimpleComponent for App {
156 type Init = ();
157 type Input = AppMsg;
158 type Output = ();
159
160 view! {
161 main_window = gtk::ApplicationWindow {
162 set_default_size: (500, 250),
163 set_titlebar: Some(model.header.widget()),
164
165 #[wrap(Some)]
166 set_child = >k::Label {
167 #[watch]
168 set_label: &format!("Placeholder for {:?}", model.mode),
169 },
170 connect_close_request[sender] => move |_| {
171 sender.input(AppMsg::CloseRequest);
172 glib::Propagation::Stop
173 }
174 }
175 }
176
177 fn init(
178 _: Self::Init,
179 root: Self::Root,
180 sender: ComponentSender<Self>,
181 ) -> ComponentParts<Self> {
182 let header = Header::builder()
183 .launch(())
184 .forward(sender.input_sender(), identity);
185 let dialog = Dialog::builder()
186 .transient_for(&root)
187 .launch(DialogInit {
188 text: "Do you want to close before saving?".to_string(),
189 secondary_text: Some("All unsaved changes will be lost".to_string()),
190 accept_text: "Close".to_string(),
191 cancel_text: "Cancel".to_string(),
192 })
193 .forward(sender.input_sender(), identity);
194
195 let model = App {
196 mode: AppMode::View,
197 header,
198 dialog,
199 };
200 let widgets = view_output!();
201
202 ComponentParts { model, widgets }
203 }
204
205 fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
206 match msg {
207 AppMsg::SetMode(mode) => {
208 self.mode = mode;
209 }
210 AppMsg::CloseRequest => {
211 self.dialog.emit(DialogMsg::Show);
212 }
213 AppMsg::Close => {
214 relm4::main_application().quit();
215 }
216 }
217 }
218}
219
220fn main() {
221 let app = RelmApp::new("relm4.example.components");
222 app.run::<App>(());
223}