1use gtk::glib;
2use gtk::prelude::{ApplicationExt, ApplicationExtManual, Cast, GtkApplicationExt, IsA, WidgetExt};
3use std::fmt::Debug;
4
5use crate::component::{AsyncComponent, AsyncComponentBuilder, AsyncComponentController};
6use crate::runtime_util::shutdown_all;
7use crate::{Component, ComponentBuilder, ComponentController, MessageBroker, RUNTIME};
8
9use std::cell::Cell;
10
11#[derive(Debug)]
13pub struct RelmApp<M: Debug + 'static> {
14 app: gtk::Application,
17 broker: Option<&'static MessageBroker<M>>,
18 args: Option<Vec<String>>,
19 visible: bool,
22}
23
24impl<M: Debug + 'static> RelmApp<M> {
25 #[must_use]
33 pub fn new(app_id: &str) -> Self {
34 crate::init();
35 let app = crate::main_application();
36 app.set_application_id(Some(app_id));
37
38 Self {
39 app,
40 broker: None,
41 args: None,
42 visible: true,
43 }
44 }
45
46 pub fn from_app(app: impl IsA<gtk::Application>) -> Self {
48 let app = app.upcast();
49 crate::set_main_application(app.clone());
50
51 Self {
52 app,
53 broker: None,
54 args: None,
55 visible: true,
56 }
57 }
58
59 #[must_use]
61 pub fn with_broker(mut self, broker: &'static MessageBroker<M>) -> Self {
62 self.broker = Some(broker);
63 self
64 }
65
66 #[must_use]
68 pub fn with_args(mut self, args: Vec<String>) -> Self {
69 self.args = Some(args);
70 self
71 }
72
73 #[must_use]
82 pub fn visible_on_activate(mut self, visible: bool) -> Self {
83 self.visible = visible;
84 self
85 }
86
87 pub fn allow_multiple_instances(&self, allow: bool) {
94 let mut flags = self.app.flags();
95 if allow {
96 flags |= gtk::gio::ApplicationFlags::NON_UNIQUE;
97 } else {
98 flags &= !gtk::gio::ApplicationFlags::NON_UNIQUE;
99 }
100 self.app.set_flags(flags);
101 }
102
103 #[deprecated(note = "Use `relm4::set_global_css_with_priority` instead")]
109 pub fn set_global_css_with_priority(&self, style_data: &str, priority: u32) {
110 crate::set_global_css_with_priority(style_data, priority);
111 }
112 #[deprecated(note = "Use `relm4::set_global_css` instead")]
114 pub fn set_global_css(&self, style_data: &str) {
115 crate::set_global_css(style_data);
116 }
117
118 #[deprecated(note = "Use `relm4::set_global_css_from_file_with_priority` instead")]
127 pub fn set_global_css_from_file_with_priority<P: AsRef<std::path::Path>>(
128 &self,
129 path: P,
130 priority: u32,
131 ) -> Result<(), std::io::Error> {
132 crate::set_global_css_from_file_with_priority(path, priority)
133 }
134
135 #[deprecated(note = "Use `relm4::set_global_css_from_file` instead")]
140 pub fn set_global_css_from_file<P: AsRef<std::path::Path>>(
141 &self,
142 path: P,
143 ) -> Result<(), std::io::Error> {
144 crate::set_global_css_from_file(path)
145 }
146
147 pub fn run<C>(self, payload: C::Init)
149 where
150 C: Component<Input = M>,
151 C::Root: AsRef<gtk::Window>,
152 {
153 let Self {
154 app,
155 broker,
156 args,
157 visible,
158 } = self;
159
160 let payload = Cell::new(Some(payload));
161
162 app.connect_startup(move |app| {
163 if let Some(payload) = payload.take() {
164 let builder = ComponentBuilder::<C>::default();
165
166 let connector = match broker {
167 Some(broker) => builder.launch_with_broker(payload, broker),
168 None => builder.launch(payload),
169 };
170
171 crate::late_initialization::run_late_init();
173
174 let mut controller = connector.detach();
175 let window = controller.widget();
176 app.add_window(window.as_ref());
177
178 controller.detach_runtime();
179 }
180 });
181
182 app.connect_activate(move |app| {
183 if let Some(window) = app.active_window()
184 && visible
185 {
186 window.set_visible(true);
187 }
188 });
189
190 let _guard = RUNTIME.enter();
191 if let Some(args) = args {
192 app.run_with_args(&args);
193 } else {
194 app.run();
195 }
196
197 shutdown_all();
199 glib::MainContext::ref_thread_default().iteration(true);
200 }
201
202 pub fn run_async<C>(self, payload: C::Init)
204 where
205 C: AsyncComponent<Input = M>,
206 C::Root: AsRef<gtk::Window>,
207 {
208 let Self {
209 app,
210 broker,
211 args,
212 visible: set_visible,
213 } = self;
214
215 let payload = Cell::new(Some(payload));
216
217 app.connect_startup(move |app| {
218 if let Some(payload) = payload.take() {
219 let builder = AsyncComponentBuilder::<C>::default();
220
221 let connector = match broker {
222 Some(broker) => builder.launch_with_broker(payload, broker),
223 None => builder.launch(payload),
224 };
225
226 crate::late_initialization::run_late_init();
228
229 let mut controller = connector.detach();
230 let window = controller.widget();
231 app.add_window(window.as_ref());
232
233 controller.detach_runtime();
234 }
235 });
236
237 app.connect_activate(move |app| {
238 if let Some(window) = app.active_window()
239 && set_visible
240 {
241 window.set_visible(true);
242 }
243 });
244
245 let _guard = RUNTIME.enter();
246 if let Some(args) = args {
247 app.run_with_args(&args);
248 } else {
249 app.run();
250 }
251
252 shutdown_all();
254 glib::MainContext::ref_thread_default().iteration(true);
255 }
256}