1#![doc(html_logo_url = "https://relm4.org/icons/relm4_logo.svg")]
20#![doc(html_favicon_url = "https://relm4.org/icons/relm4_org.svg")]
21#![warn(
22 missing_debug_implementations,
23 missing_docs,
24 rust_2018_idioms,
25 unreachable_pub,
26 unused_qualifications,
27 clippy::cargo,
28 clippy::must_use_candidate,
29 clippy::used_underscore_binding
30)]
31#![allow(clippy::multiple_crate_versions)]
32#![cfg_attr(docsrs, feature(doc_cfg))]
34
35mod app;
36mod channel;
37mod extensions;
38pub(crate) mod late_initialization;
39mod runtime_util;
40
41pub mod abstractions;
42pub mod actions;
43pub mod binding;
44pub mod component;
45pub mod factory;
46pub mod loading_widgets;
47pub mod shared_state;
48pub mod typed_view;
49
50pub use channel::ComponentSender;
51pub use channel::*;
52pub use component::worker::{Worker, WorkerController, WorkerHandle};
53pub use component::{
54 Component, ComponentBuilder, ComponentController, ComponentParts, Controller, MessageBroker,
55 SimpleComponent,
56};
57pub use extensions::*;
58pub use shared_state::{AsyncReducer, AsyncReducible, Reducer, Reducible, SharedState};
59pub use shutdown::ShutdownReceiver;
60
61pub use app::RelmApp;
62pub use tokio::task::JoinHandle;
63
64use gtk::prelude::{Cast, IsA};
65use once_cell::sync::{Lazy, OnceCell};
66use runtime_util::{GuardedReceiver, RuntimeSenders, ShutdownOnDrop};
67use std::cell::Cell;
68use std::future::Future;
69use tokio::runtime::Runtime;
70
71pub static RELM_THREADS: OnceCell<usize> = OnceCell::new();
75
76pub static RELM_BLOCKING_THREADS: OnceCell<usize> = OnceCell::new();
80
81pub mod prelude;
82
83pub use gtk;
85
86#[cfg(feature = "css")]
88#[cfg_attr(docsrs, doc(cfg(feature = "css")))]
89pub use relm4_css as css;
90
91#[cfg(feature = "macros")]
93#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
94pub use relm4_macros::*;
95
96#[cfg(feature = "libadwaita")]
98#[cfg_attr(docsrs, doc(cfg(feature = "libadwaita")))]
99pub use adw;
100
101#[cfg(feature = "libpanel")]
103#[cfg_attr(docsrs, doc(cfg(feature = "libpanel")))]
104pub use panel;
105
106pub use once_cell;
107pub use tokio;
108
109thread_local! {
110 static MAIN_APPLICATION: Cell<Option<gtk::Application>> = Cell::default();
111}
112
113fn set_main_application(app: impl IsA<gtk::Application>) {
114 MAIN_APPLICATION.with(move |cell| cell.set(Some(app.upcast())));
115}
116
117fn init() {
118 gtk::init().unwrap();
119 #[cfg(feature = "libadwaita")]
120 adw::init().unwrap();
121}
122
123#[must_use]
132pub fn main_application() -> gtk::Application {
133 #[cfg(feature = "libadwaita")]
134 fn new_application() -> gtk::Application {
135 adw::Application::default().upcast()
136 }
137
138 #[cfg(not(feature = "libadwaita"))]
139 fn new_application() -> gtk::Application {
140 gtk::Application::default()
141 }
142
143 MAIN_APPLICATION.with(|cell| {
144 let app = cell.take().unwrap_or_else(new_application);
145 cell.set(Some(app.clone()));
146 app
147 })
148}
149
150#[cfg(feature = "libadwaita")]
151#[cfg_attr(docsrs, doc(cfg(feature = "libadwaita")))]
152#[must_use]
158pub fn main_adw_application() -> adw::Application {
159 main_application().downcast().unwrap()
160}
161
162pub fn spawn_local<F, Out>(func: F) -> gtk::glib::JoinHandle<Out>
164where
165 F: Future<Output = Out> + 'static,
166 Out: 'static,
167{
168 gtk::glib::MainContext::ref_thread_default().spawn_local(func)
169}
170
171pub fn spawn_local_with_priority<F, Out>(
173 priority: gtk::glib::Priority,
174 func: F,
175) -> gtk::glib::JoinHandle<Out>
176where
177 F: Future<Output = Out> + 'static,
178 Out: 'static,
179{
180 gtk::glib::MainContext::ref_thread_default().spawn_local_with_priority(priority, func)
181}
182
183static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
184 tokio::runtime::Builder::new_multi_thread()
185 .enable_all()
186 .worker_threads(*RELM_THREADS.get_or_init(|| 1))
187 .max_blocking_threads(*RELM_BLOCKING_THREADS.get_or_init(|| 512))
188 .build()
189 .unwrap()
190});
191
192pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
194where
195 F: Future + Send + 'static,
196 F::Output: Send + 'static,
197{
198 RUNTIME.spawn(future)
199}
200
201pub fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
203where
204 F: FnOnce() -> R + Send + 'static,
205 R: Send + 'static,
206{
207 RUNTIME.spawn_blocking(func)
208}
209
210pub fn set_global_css_with_priority(style_data: &str, priority: u32) {
216 let display = gtk::gdk::Display::default().unwrap();
217 let provider = gtk::CssProvider::new();
218 #[allow(deprecated)]
219 provider.load_from_data(style_data);
220
221 #[allow(deprecated)]
222 gtk::StyleContext::add_provider_for_display(&display, &provider, priority);
223}
224pub fn set_global_css(style_data: &str) {
226 set_global_css_with_priority(style_data, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
227}
228
229pub fn set_global_css_from_file_with_priority<P: AsRef<std::path::Path>>(
238 path: P,
239 priority: u32,
240) -> Result<(), std::io::Error> {
241 std::fs::read_to_string(path)
242 .map(|bytes| set_global_css_with_priority(&bytes, priority))
243 .map_err(|err| {
244 tracing::error!("Couldn't load global CSS from file: {}", err);
245 err
246 })
247}
248
249pub fn set_global_css_from_file<P: AsRef<std::path::Path>>(path: P) -> Result<(), std::io::Error> {
254 set_global_css_from_file_with_priority(path, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION)
255}