1use crate::Property;
5use crate::api::PlatformError;
6use crate::graphics::Color;
7use crate::input::InternalKeyboardModifierState;
8use crate::item_tree::{ItemRc, ItemTreeRc};
9use crate::items::ColorScheme;
10use crate::lengths::LogicalLength;
11use crate::platform::{EventLoopProxy, Platform, WindowAdapter, WindowEvent};
12use alloc::boxed::Box;
13use alloc::rc::Rc;
14use core::cell::Cell;
15use core::cell::RefCell;
16use pin_weak::rc::PinWeak;
17
18pub type WindowEventHook =
21 Box<dyn Fn(&Rc<dyn WindowAdapter>, &WindowEvent, WindowEventDispatchResult)>;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum WindowEventDispatchResult {
40 Accepted,
43 Rejected,
46 Ignored,
49}
50
51crate::thread_local! {
52 pub(crate) static GLOBAL_CONTEXT : once_cell::unsync::OnceCell<SlintContext>
53 = const { once_cell::unsync::OnceCell::new() }
54}
55
56#[pin_project::pin_project]
57pub(crate) struct SlintContextInner {
58 platform: Box<dyn Platform>,
59 pub(crate) window_count: core::cell::RefCell<isize>,
60
61 #[pin]
65 pub(crate) translations_dirty: Property<usize>,
66 pub(crate) translations_bundle:
67 core::cell::RefCell<Option<alloc::vec::Vec<i_slint_common::TranslationsBundled>>>,
68 #[cfg(feature = "tr")]
69 external_translator: core::cell::RefCell<Option<Box<dyn tr::Translator>>>,
70 #[pin]
71 pub(crate) locale_decimal_separator: Property<char>,
72
73 #[pin]
77 pub(crate) color_scheme: Property<ColorScheme>,
78 #[pin]
82 pub(crate) accent_color: Property<Color>,
83 #[pin]
88 pub(crate) platform_default_font_size: Property<Option<LogicalLength>>,
89 pub(crate) window_shown_hook:
90 core::cell::RefCell<Option<Box<dyn FnMut(&Rc<dyn crate::platform::WindowAdapter>)>>>,
91 pub(crate) window_event_hook: core::cell::RefCell<Option<WindowEventHook>>,
92 pub(crate) log_message_handler: RefCell<Option<crate::debug_log::LogMessageHandler>>,
93 #[cfg(all(unix, not(target_os = "macos")))]
94 xdg_app_id: core::cell::RefCell<Option<crate::SharedString>>,
95 #[cfg(feature = "shared-parley")]
96 pub(crate) font_context: core::cell::RefCell<crate::textlayout::sharedparley::FontContext>,
97 #[cfg(feature = "shared-swash")]
98 pub(crate) swash_scale_context: core::cell::RefCell<swash::scale::ScaleContext>,
99 pub(crate) modifiers: Cell<InternalKeyboardModifierState>,
100}
101
102#[derive(Clone)]
106pub struct SlintContext(pub(crate) core::pin::Pin<Rc<SlintContextInner>>);
107
108impl SlintContext {
109 pub fn new(platform: Box<dyn Platform + 'static>) -> Self {
111 #[cfg(feature = "shared-parley")]
112 let collection = i_slint_common::sharedfontique::create_collection(true);
113
114 Self(Rc::pin(SlintContextInner {
115 platform,
116 window_count: 0.into(),
117
118 translations_dirty: Property::new_named(0, "SlintContext::translations"),
119 translations_bundle: Default::default(),
120 #[cfg(feature = "tr")]
121 external_translator: Default::default(),
122 locale_decimal_separator: Property::new_named(
123 i_slint_common::DEFAULT_DECIMAL_SEPARATOR,
124 "SlintContext::locale_decimal_separator",
125 ),
126
127 color_scheme: Property::new_named(ColorScheme::Unknown, "SlintContext::color_scheme"),
128 accent_color: Property::new_named(Color::default(), "SlintContext::accent_color"),
129 platform_default_font_size: Property::new_named(
130 None,
131 "SlintContext::platform_default_font_size",
132 ),
133 window_shown_hook: Default::default(),
134 window_event_hook: Default::default(),
135 log_message_handler: Default::default(),
136 #[cfg(all(unix, not(target_os = "macos")))]
137 xdg_app_id: Default::default(),
138 #[cfg(feature = "shared-parley")]
139 font_context: {
140 let font_context = parley::FontContext {
141 collection: collection.inner,
142 source_cache: collection.source_cache,
143 };
144 core::cell::RefCell::new(crate::textlayout::sharedparley::FontContext::new(
145 font_context,
146 ))
147 },
148 #[cfg(feature = "shared-swash")]
149 swash_scale_context: core::cell::RefCell::new(swash::scale::ScaleContext::new()),
150 modifiers: Cell::new(Default::default()),
151 }))
152 }
153
154 pub fn platform(&self) -> &dyn Platform {
156 &*self.0.platform
157 }
158
159 #[cfg(feature = "shared-parley")]
161 pub fn font_context(
162 &self,
163 ) -> &core::cell::RefCell<crate::textlayout::sharedparley::FontContext> {
164 &self.0.font_context
165 }
166
167 #[cfg(feature = "shared-swash")]
169 pub fn swash_scale_context(&self) -> &core::cell::RefCell<swash::scale::ScaleContext> {
170 &self.0.swash_scale_context
171 }
172
173 pub fn event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
176 self.0.platform.new_event_loop_proxy()
177 }
178
179 #[cfg(target_has_atomic = "ptr")]
180 pub fn spawn_local<F: core::future::Future + 'static>(
182 &self,
183 fut: F,
184 ) -> Result<crate::future::JoinHandle<F::Output>, crate::api::EventLoopError> {
185 crate::future::spawn_local_with_ctx(self, fut)
186 }
187
188 pub fn run_event_loop(&self) -> Result<(), PlatformError> {
189 self.0.platform.run_event_loop()
190 }
191
192 pub fn color_scheme(&self, root: Option<&ItemTreeRc>) -> ColorScheme {
199 if let Some(root) = root {
200 let root_item = ItemRc::new_root(root.clone());
201 if let Some(tray) = root_item.downcast::<crate::items::SystemTrayIcon>() {
202 let scheme = tray.as_pin_ref().color_scheme();
203 if scheme != ColorScheme::Unknown {
204 return scheme;
205 }
206 }
207 }
208 self.0.as_ref().project_ref().color_scheme.get()
209 }
210
211 pub fn set_color_scheme(&self, scheme: ColorScheme) {
214 self.0.as_ref().project_ref().color_scheme.set(scheme);
215 }
216
217 pub fn accent_color(&self) -> Color {
220 self.0.as_ref().project_ref().accent_color.get()
221 }
222
223 pub fn set_accent_color(&self, color: Color) {
226 self.0.as_ref().project_ref().accent_color.set(color);
227 }
228
229 pub fn platform_default_font_size(&self) -> Option<LogicalLength> {
233 self.0.as_ref().project_ref().platform_default_font_size.get()
234 }
235
236 pub fn set_platform_default_font_size(&self, size: Option<LogicalLength>) {
239 self.0.as_ref().project_ref().platform_default_font_size.set(size);
240 }
241
242 #[doc(hidden)]
243 pub fn dispatch_log_message(&self, message: crate::debug_log::LogMessage<'_>) {
244 if let Some(handler) = self.0.log_message_handler.borrow().as_ref() {
245 handler(message);
246 } else {
247 self.0.platform.debug_log(message.message_arguments());
248 }
249 }
250
251 #[doc(hidden)]
252 pub fn set_log_message_handler(
253 &self,
254 handler: Option<crate::debug_log::LogMessageHandler>,
255 ) -> Option<crate::debug_log::LogMessageHandler> {
256 let mut slot = self.0.log_message_handler.borrow_mut();
257 core::mem::replace(&mut *slot, handler)
258 }
259
260 pub(crate) fn acquire_keepalive(&self) {
264 *self.0.window_count.borrow_mut() += 1;
265 }
266
267 pub(crate) fn release_keepalive(&self) {
271 let mut count = self.0.window_count.borrow_mut();
272 *count -= 1;
273 if *count <= 0 {
274 drop(count);
275 let _ = self.event_loop_proxy().and_then(|p| p.quit_event_loop().ok());
276 }
277 }
278
279 pub fn set_xdg_app_id(&self, _app_id: crate::SharedString) {
280 #[cfg(all(unix, not(target_os = "macos")))]
281 {
282 self.0.xdg_app_id.replace(Some(_app_id));
283 }
284 }
285
286 #[cfg(all(unix, not(target_os = "macos")))]
287 pub fn xdg_app_id(&self) -> Option<crate::SharedString> {
288 self.0.xdg_app_id.borrow().clone()
289 }
290
291 #[cfg(not(all(unix, not(target_os = "macos"))))]
292 pub fn xdg_app_id(&self) -> Option<crate::SharedString> {
293 None
294 }
295
296 pub fn locale_decimal_separator(&self) -> char {
298 self.0.as_ref().project_ref().locale_decimal_separator.get()
299 }
300
301 #[cfg(feature = "std")]
303 pub fn set_locale(&self, locale: &str) {
304 self.0
305 .as_ref()
306 .project_ref()
307 .locale_decimal_separator
308 .set(i_slint_common::decimal_separator_for_locale(locale));
309 }
310
311 #[cfg(feature = "tr")]
312 pub fn set_external_translator(&self, translator: Option<Box<dyn tr::Translator>>) {
313 *self.0.external_translator.borrow_mut() = translator;
314 self.0.as_ref().project_ref().translations_dirty.mark_dirty();
315 }
316
317 #[cfg(feature = "tr")]
318 pub fn external_translator(&self) -> Option<core::cell::Ref<'_, Box<dyn tr::Translator>>> {
319 core::cell::Ref::filter_map(self.0.external_translator.borrow(), |maybe_translator| {
320 maybe_translator.as_ref()
321 })
322 .ok()
323 }
324
325 pub fn downgrade(&self) -> SlintContextWeak {
328 SlintContextWeak(PinWeak::downgrade(self.0.clone()))
329 }
330}
331
332#[derive(Clone)]
337pub struct SlintContextWeak(PinWeak<SlintContextInner>);
338
339impl SlintContextWeak {
340 pub fn upgrade(&self) -> Option<SlintContext> {
342 self.0.upgrade().map(SlintContext)
343 }
344}
345
346pub fn with_global_context<R>(
350 factory: impl FnOnce() -> Result<Box<dyn Platform + 'static>, PlatformError>,
351 f: impl FnOnce(&SlintContext) -> R,
352) -> Result<R, PlatformError> {
353 GLOBAL_CONTEXT.with(|p| match p.get() {
354 Some(ctx) => Ok(f(ctx)),
355 None => {
356 if crate::platform::with_event_loop_proxy(|proxy| proxy.is_some()) {
357 return Err(PlatformError::SetPlatformError(
358 crate::platform::SetPlatformError::AlreadySet,
359 ));
360 }
361 crate::platform::set_platform(factory()?).map_err(PlatformError::SetPlatformError)?;
362 Ok(f(p.get().unwrap()))
363 }
364 })
365}
366
367pub fn set_window_shown_hook(
370 hook: Option<Box<dyn FnMut(&Rc<dyn crate::platform::WindowAdapter>)>>,
371) -> Result<Option<Box<dyn FnMut(&Rc<dyn crate::platform::WindowAdapter>)>>, PlatformError> {
372 GLOBAL_CONTEXT.with(|p| match p.get() {
373 Some(ctx) => Ok(ctx.0.window_shown_hook.replace(hook)),
374 None => Err(PlatformError::NoPlatform),
375 })
376}
377
378pub fn set_window_event_hook(
381 hook: Option<WindowEventHook>,
382) -> Result<Option<WindowEventHook>, PlatformError> {
383 GLOBAL_CONTEXT.with(|p| match p.get() {
384 Some(ctx) => {
385 let mut slot = ctx.0.window_event_hook.try_borrow_mut().map_err(|_| {
386 PlatformError::Other(alloc::string::String::from("event hook is currently in use"))
387 })?;
388 Ok(core::mem::replace(&mut *slot, hook))
389 }
390 None => Err(PlatformError::NoPlatform),
391 })
392}