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