1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
3
4/*!
5This module contains types that are public and re-exported in the slint-rs as well as the slint-interpreter crate as public API.
6*/
7
8#![warn(missing_docs)]
9
10#[cfg(target_has_atomic = "ptr")]
11pub use crate::future::*;
12use crate::input::{KeyEventType, MouseEvent};
13use crate::item_tree::ItemTreeVTable;
14use crate::window::{WindowAdapter, WindowInner};
15#[cfg(not(feature = "std"))]
16use alloc::boxed::Box;
17#[cfg(not(feature = "std"))]
18use alloc::string::String;
19
20/// A position represented in the coordinate space of logical pixels. That is the space before applying
21/// a display device specific scale factor.
22#[derive(Debug, Default, Copy, Clone, PartialEq)]
23#[repr(C)]
24pub struct LogicalPosition {
25 /// The x coordinate.
26 pub x: f32,
27 /// The y coordinate.
28 pub y: f32,
29}
30
31impl LogicalPosition {
32 /// Construct a new logical position from the given x and y coordinates, that are assumed to be
33 /// in the logical coordinate space.
34 pub const fn new(x: f32, y: f32) -> Self {
35 Self { x, y }
36 }
37
38 /// Convert a given physical position to a logical position by dividing the coordinates with the
39 /// specified scale factor.
40 pub fn from_physical(physical_pos: PhysicalPosition, scale_factor: f32) -> Self {
41 Self::new(physical_pos.x as f32 / scale_factor, physical_pos.y as f32 / scale_factor)
42 }
43
44 /// Convert this logical position to a physical position by multiplying the coordinates with the
45 /// specified scale factor.
46 pub fn to_physical(&self, scale_factor: f32) -> PhysicalPosition {
47 PhysicalPosition::from_logical(*self, scale_factor)
48 }
49
50 pub(crate) fn to_euclid(self) -> crate::lengths::LogicalPoint {
51 [self.x as _, self.y as _].into()
52 }
53 pub(crate) fn from_euclid(p: crate::lengths::LogicalPoint) -> Self {
54 Self::new(p.x as _, p.y as _)
55 }
56}
57
58/// A position represented in the coordinate space of physical device pixels. That is the space after applying
59/// a display device specific scale factor to pixels from the logical coordinate space.
60#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
61pub struct PhysicalPosition {
62 /// The x coordinate.
63 pub x: i32,
64 /// The y coordinate.
65 pub y: i32,
66}
67
68impl PhysicalPosition {
69 /// Construct a new physical position from the given x and y coordinates, that are assumed to be
70 /// in the physical coordinate space.
71 pub const fn new(x: i32, y: i32) -> Self {
72 Self { x, y }
73 }
74
75 /// Convert a given logical position to a physical position by multiplying the coordinates with the
76 /// specified scale factor.
77 pub fn from_logical(logical_pos: LogicalPosition, scale_factor: f32) -> Self {
78 Self::new((logical_pos.x * scale_factor) as i32, (logical_pos.y * scale_factor) as i32)
79 }
80
81 /// Convert this physical position to a logical position by dividing the coordinates with the
82 /// specified scale factor.
83 pub fn to_logical(&self, scale_factor: f32) -> LogicalPosition {
84 LogicalPosition::from_physical(*self, scale_factor)
85 }
86
87 #[cfg(feature = "ffi")]
88 pub(crate) fn to_euclid(&self) -> crate::graphics::euclid::default::Point2D<i32> {
89 [self.x, self.y].into()
90 }
91
92 #[cfg(feature = "ffi")]
93 pub(crate) fn from_euclid(p: crate::graphics::euclid::default::Point2D<i32>) -> Self {
94 Self::new(p.x as _, p.y as _)
95 }
96}
97
98/// The position of the window in either physical or logical pixels. This is used
99/// with [`Window::set_position`].
100#[derive(Clone, Debug, derive_more::From, PartialEq)]
101pub enum WindowPosition {
102 /// The position in physical pixels.
103 Physical(PhysicalPosition),
104 /// The position in logical pixels.
105 Logical(LogicalPosition),
106}
107
108impl WindowPosition {
109 /// Turn the `WindowPosition` into a `PhysicalPosition`.
110 pub fn to_physical(&self, scale_factor: f32) -> PhysicalPosition {
111 match self {
112 WindowPosition::Physical(pos: &PhysicalPosition) => *pos,
113 WindowPosition::Logical(pos: &LogicalPosition) => pos.to_physical(scale_factor),
114 }
115 }
116}
117
118/// A size represented in the coordinate space of logical pixels. That is the space before applying
119/// a display device specific scale factor.
120#[repr(C)]
121#[derive(Debug, Default, Copy, Clone, PartialEq)]
122pub struct LogicalSize {
123 /// The width in logical pixels.
124 pub width: f32,
125 /// The height in logical.
126 pub height: f32,
127}
128
129impl LogicalSize {
130 /// Construct a new logical size from the given width and height values, that are assumed to be
131 /// in the logical coordinate space.
132 pub const fn new(width: f32, height: f32) -> Self {
133 Self { width, height }
134 }
135
136 /// Convert a given physical size to a logical size by dividing width and height by the
137 /// specified scale factor.
138 pub fn from_physical(physical_size: PhysicalSize, scale_factor: f32) -> Self {
139 Self::new(
140 physical_size.width as f32 / scale_factor,
141 physical_size.height as f32 / scale_factor,
142 )
143 }
144
145 /// Convert this logical size to a physical size by multiplying width and height with the
146 /// specified scale factor.
147 pub fn to_physical(&self, scale_factor: f32) -> PhysicalSize {
148 PhysicalSize::from_logical(*self, scale_factor)
149 }
150
151 pub(crate) fn to_euclid(self) -> crate::lengths::LogicalSize {
152 [self.width as _, self.height as _].into()
153 }
154
155 pub(crate) fn from_euclid(p: crate::lengths::LogicalSize) -> Self {
156 Self::new(p.width as _, p.height as _)
157 }
158}
159
160/// A size represented in the coordinate space of physical device pixels. That is the space after applying
161/// a display device specific scale factor to pixels from the logical coordinate space.
162#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
163pub struct PhysicalSize {
164 /// The width in physical pixels.
165 pub width: u32,
166 /// The height in physical pixels;
167 pub height: u32,
168}
169
170impl PhysicalSize {
171 /// Construct a new physical size from the width and height values, that are assumed to be
172 /// in the physical coordinate space.
173 pub const fn new(width: u32, height: u32) -> Self {
174 Self { width, height }
175 }
176
177 /// Convert a given logical size to a physical size by multiplying width and height with the
178 /// specified scale factor.
179 pub fn from_logical(logical_size: LogicalSize, scale_factor: f32) -> Self {
180 Self::new(
181 (logical_size.width * scale_factor) as u32,
182 (logical_size.height * scale_factor) as u32,
183 )
184 }
185
186 /// Convert this physical size to a logical size by dividing width and height by the
187 /// specified scale factor.
188 pub fn to_logical(&self, scale_factor: f32) -> LogicalSize {
189 LogicalSize::from_physical(*self, scale_factor)
190 }
191
192 #[cfg(feature = "ffi")]
193 pub(crate) fn to_euclid(&self) -> crate::graphics::euclid::default::Size2D<u32> {
194 [self.width, self.height].into()
195 }
196}
197
198/// The size of a window represented in either physical or logical pixels. This is used
199/// with [`Window::set_size`].
200#[derive(Clone, Debug, derive_more::From, PartialEq)]
201pub enum WindowSize {
202 /// The size in physical pixels.
203 Physical(PhysicalSize),
204 /// The size in logical screen pixels.
205 Logical(LogicalSize),
206}
207
208impl WindowSize {
209 /// Turn the `WindowSize` into a `PhysicalSize`.
210 pub fn to_physical(&self, scale_factor: f32) -> PhysicalSize {
211 match self {
212 WindowSize::Physical(size: &PhysicalSize) => *size,
213 WindowSize::Logical(size: &LogicalSize) => size.to_physical(scale_factor),
214 }
215 }
216
217 /// Turn the `WindowSize` into a `LogicalSize`.
218 pub fn to_logical(&self, scale_factor: f32) -> LogicalSize {
219 match self {
220 WindowSize::Physical(size: &PhysicalSize) => size.to_logical(scale_factor),
221 WindowSize::Logical(size: &LogicalSize) => *size,
222 }
223 }
224}
225
226#[test]
227fn logical_physical_pos() {
228 use crate::graphics::euclid::approxeq::ApproxEq;
229
230 let phys: PhysicalPosition = PhysicalPosition::new(x:100, y:50);
231 let logical: LogicalPosition = phys.to_logical(scale_factor:2.);
232 assert!(logical.x.approx_eq(&50.));
233 assert!(logical.y.approx_eq(&25.));
234
235 assert_eq!(logical.to_physical(2.), phys);
236}
237
238#[test]
239fn logical_physical_size() {
240 use crate::graphics::euclid::approxeq::ApproxEq;
241
242 let phys: PhysicalSize = PhysicalSize::new(width:100, height:50);
243 let logical: LogicalSize = phys.to_logical(scale_factor:2.);
244 assert!(logical.width.approx_eq(&50.));
245 assert!(logical.height.approx_eq(&25.));
246
247 assert_eq!(logical.to_physical(2.), phys);
248}
249
250/// This enum describes a low-level access to specific graphics APIs used
251/// by the renderer.
252#[derive(Clone)]
253#[non_exhaustive]
254pub enum GraphicsAPI<'a> {
255 /// The rendering is done using OpenGL.
256 NativeOpenGL {
257 /// Use this function pointer to obtain access to the OpenGL implementation - similar to `eglGetProcAddress`.
258 get_proc_address: &'a dyn Fn(&core::ffi::CStr) -> *const core::ffi::c_void,
259 },
260 /// The rendering is done on a HTML Canvas element using WebGL.
261 WebGL {
262 /// The DOM element id of the HTML Canvas element used for rendering.
263 canvas_element_id: &'a str,
264 /// The drawing context type used on the HTML Canvas element for rendering. This is the argument to the
265 /// `getContext` function on the HTML Canvas element.
266 context_type: &'a str,
267 },
268}
269
270impl<'a> core::fmt::Debug for GraphicsAPI<'a> {
271 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
272 match self {
273 GraphicsAPI::NativeOpenGL { .. } => write!(f, "GraphicsAPI::NativeOpenGL"),
274 GraphicsAPI::WebGL { context_type: &&str, .. } => {
275 write!(f, "GraphicsAPI::WebGL(context_type = {})", context_type)
276 }
277 }
278 }
279}
280
281/// This enum describes the different rendering states, that will be provided
282/// to the parameter of the callback for `set_rendering_notifier` on the `slint::Window`.
283#[derive(Debug, Clone)]
284#[repr(u8)]
285#[non_exhaustive]
286pub enum RenderingState {
287 /// The window has been created and the graphics adapter/context initialized. When OpenGL
288 /// is used for rendering, the context will be current.
289 RenderingSetup,
290 /// The scene of items is about to be rendered. When OpenGL
291 /// is used for rendering, the context will be current.
292 BeforeRendering,
293 /// The scene of items was rendered, but the back buffer was not sent for display presentation
294 /// yet (for example GL swap buffers). When OpenGL is used for rendering, the context will be current.
295 AfterRendering,
296 /// The window will be destroyed and/or graphics resources need to be released due to other
297 /// constraints.
298 RenderingTeardown,
299}
300
301/// Internal trait that's used to map rendering state callbacks to either a Rust-API provided
302/// impl FnMut or a struct that invokes a C callback and implements Drop to release the closure
303/// on the C++ side.
304pub trait RenderingNotifier {
305 /// Called to notify that rendering has reached a certain state.
306 fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI);
307}
308
309impl<F: FnMut(RenderingState, &GraphicsAPI)> RenderingNotifier for F {
310 fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI) {
311 self(state, graphics_api)
312 }
313}
314
315/// This enum describes the different error scenarios that may occur when the application
316/// registers a rendering notifier on a `slint::Window`.
317#[derive(Debug, Clone)]
318#[repr(u8)]
319#[non_exhaustive]
320pub enum SetRenderingNotifierError {
321 /// The rendering backend does not support rendering notifiers.
322 Unsupported,
323 /// There is already a rendering notifier set, multiple notifiers are not supported.
324 AlreadySet,
325}
326
327/// This type represents a window towards the windowing system, that's used to render the
328/// scene of a component. It provides API to control windowing system specific aspects such
329/// as the position on the screen.
330#[repr(transparent)]
331pub struct Window(pub(crate) WindowInner);
332
333/// This enum describes whether a Window is allowed to be hidden when the user tries to close the window.
334/// It is the return type of the callback provided to [Window::on_close_requested].
335#[derive(Copy, Clone, Debug, PartialEq, Default)]
336#[repr(u8)]
337pub enum CloseRequestResponse {
338 /// The Window will be hidden (default action)
339 #[default]
340 HideWindow = 0,
341 /// The close request is rejected and the window will be kept shown.
342 KeepWindowShown = 1,
343}
344
345impl Window {
346 /// Create a new window from a window adapter
347 ///
348 /// You only need to create the window yourself when you create a [`WindowAdapter`] from
349 /// [`Platform::create_window_adapter`](crate::platform::Platform::create_window_adapter)
350 ///
351 /// Since the window adapter must own the Window, this function is meant to be used with
352 /// [`Rc::new_cyclic`](alloc::rc::Rc::new_cyclic)
353 ///
354 /// # Example
355 /// ```rust
356 /// use std::rc::Rc;
357 /// use slint::platform::{WindowAdapter, Renderer};
358 /// use slint::{Window, PhysicalSize};
359 /// struct MyWindowAdapter {
360 /// window: Window,
361 /// //...
362 /// }
363 /// impl WindowAdapter for MyWindowAdapter {
364 /// fn window(&self) -> &Window { &self.window }
365 /// fn size(&self) -> PhysicalSize { unimplemented!() }
366 /// fn renderer(&self) -> &dyn Renderer { unimplemented!() }
367 /// }
368 ///
369 /// fn create_window_adapter() -> Rc<dyn WindowAdapter> {
370 /// Rc::<MyWindowAdapter>::new_cyclic(|weak| {
371 /// MyWindowAdapter {
372 /// window: Window::new(weak.clone()),
373 /// //...
374 /// }
375 /// })
376 /// }
377 /// ```
378 pub fn new(window_adapter_weak: alloc::rc::Weak<dyn WindowAdapter>) -> Self {
379 Self(WindowInner::new(window_adapter_weak))
380 }
381
382 /// Shows the window on the screen. An additional strong reference on the
383 /// associated component is maintained while the window is visible.
384 ///
385 /// Call [`Self::hide()`] to make the window invisible again, and drop the additional
386 /// strong reference.
387 pub fn show(&self) -> Result<(), PlatformError> {
388 self.0.show()
389 }
390
391 /// Hides the window, so that it is not visible anymore. The additional strong
392 /// reference on the associated component, that was created when [`Self::show()`] was called, is
393 /// dropped.
394 pub fn hide(&self) -> Result<(), PlatformError> {
395 self.0.hide()
396 }
397
398 /// This function allows registering a callback that's invoked during the different phases of
399 /// rendering. This allows custom rendering on top or below of the scene.
400 pub fn set_rendering_notifier(
401 &self,
402 callback: impl FnMut(RenderingState, &GraphicsAPI) + 'static,
403 ) -> Result<(), SetRenderingNotifierError> {
404 self.0.window_adapter().renderer().set_rendering_notifier(Box::new(callback))
405 }
406
407 /// This function allows registering a callback that's invoked when the user tries to close a window.
408 /// The callback has to return a [CloseRequestResponse].
409 pub fn on_close_requested(&self, callback: impl FnMut() -> CloseRequestResponse + 'static) {
410 self.0.on_close_requested(callback);
411 }
412
413 /// This function issues a request to the windowing system to redraw the contents of the window.
414 pub fn request_redraw(&self) {
415 self.0.window_adapter().request_redraw()
416 }
417
418 /// This function returns the scale factor that allows converting between logical and
419 /// physical pixels.
420 pub fn scale_factor(&self) -> f32 {
421 self.0.scale_factor()
422 }
423
424 /// Returns the position of the window on the screen, in physical screen coordinates and including
425 /// a window frame (if present).
426 pub fn position(&self) -> PhysicalPosition {
427 self.0.window_adapter().position().unwrap_or_default()
428 }
429
430 /// Sets the position of the window on the screen, in physical screen coordinates and including
431 /// a window frame (if present).
432 /// Note that on some windowing systems, such as Wayland, this functionality is not available.
433 pub fn set_position(&self, position: impl Into<WindowPosition>) {
434 let position = position.into();
435 self.0.window_adapter().set_position(position)
436 }
437
438 /// Returns the size of the window on the screen, in physical screen coordinates and excluding
439 /// a window frame (if present).
440 pub fn size(&self) -> PhysicalSize {
441 self.0.window_adapter().size()
442 }
443
444 /// Resizes the window to the specified size on the screen, in physical pixels and excluding
445 /// a window frame (if present).
446 pub fn set_size(&self, size: impl Into<WindowSize>) {
447 let size = size.into();
448 crate::window::WindowAdapter::set_size(&*self.0.window_adapter(), size);
449 }
450
451 /// Returns if the window is currently fullscreen
452 pub fn is_fullscreen(&self) -> bool {
453 self.0.is_fullscreen()
454 }
455
456 /// Set or unset the window to display fullscreen.
457 pub fn set_fullscreen(&self, fullscreen: bool) {
458 self.0.set_fullscreen(fullscreen);
459 }
460
461 /// Returns if the window is currently maximized
462 pub fn is_maximized(&self) -> bool {
463 self.0.is_maximized()
464 }
465
466 /// Maximize or unmaximize the window.
467 pub fn set_maximized(&self, maximized: bool) {
468 self.0.set_maximized(maximized);
469 }
470
471 /// Returns if the window is currently minimized
472 pub fn is_minimized(&self) -> bool {
473 self.0.is_minimized()
474 }
475
476 /// Minimize or unminimze the window.
477 pub fn set_minimized(&self, minimized: bool) {
478 self.0.set_minimized(minimized);
479 }
480
481 /// Dispatch a window event to the scene.
482 ///
483 /// Use this when you're implementing your own backend and want to forward user input events.
484 ///
485 /// Any position fields in the event must be in the logical pixel coordinate system relative to
486 /// the top left corner of the window.
487 // TODO: Return a Result<(), PlatformError>
488 pub fn dispatch_event(&self, event: crate::platform::WindowEvent) {
489 match event {
490 crate::platform::WindowEvent::PointerPressed { position, button } => {
491 self.0.process_mouse_input(MouseEvent::Pressed {
492 position: position.to_euclid().cast(),
493 button,
494 click_count: 0,
495 });
496 }
497 crate::platform::WindowEvent::PointerReleased { position, button } => {
498 self.0.process_mouse_input(MouseEvent::Released {
499 position: position.to_euclid().cast(),
500 button,
501 click_count: 0,
502 });
503 }
504 crate::platform::WindowEvent::PointerMoved { position } => {
505 self.0.process_mouse_input(MouseEvent::Moved {
506 position: position.to_euclid().cast(),
507 });
508 }
509 crate::platform::WindowEvent::PointerScrolled { position, delta_x, delta_y } => {
510 self.0.process_mouse_input(MouseEvent::Wheel {
511 position: position.to_euclid().cast(),
512 delta_x: delta_x as _,
513 delta_y: delta_y as _,
514 });
515 }
516 crate::platform::WindowEvent::PointerExited => {
517 self.0.process_mouse_input(MouseEvent::Exit)
518 }
519
520 crate::platform::WindowEvent::KeyPressed { text } => {
521 self.0.process_key_input(crate::input::KeyEvent {
522 text,
523 repeat: false,
524 event_type: KeyEventType::KeyPressed,
525 ..Default::default()
526 })
527 }
528 crate::platform::WindowEvent::KeyPressRepeated { text } => {
529 self.0.process_key_input(crate::input::KeyEvent {
530 text,
531 repeat: true,
532 event_type: KeyEventType::KeyPressed,
533 ..Default::default()
534 })
535 }
536 crate::platform::WindowEvent::KeyReleased { text } => {
537 self.0.process_key_input(crate::input::KeyEvent {
538 text,
539 event_type: KeyEventType::KeyReleased,
540 ..Default::default()
541 })
542 }
543 crate::platform::WindowEvent::ScaleFactorChanged { scale_factor } => {
544 self.0.set_scale_factor(scale_factor);
545 }
546 crate::platform::WindowEvent::Resized { size } => {
547 self.0.set_window_item_geometry(size.to_euclid());
548 self.0
549 .window_adapter()
550 .renderer()
551 .resize(size.to_physical(self.scale_factor()))
552 .unwrap()
553 }
554 crate::platform::WindowEvent::CloseRequested => {
555 if self.0.request_close() {
556 self.hide().unwrap();
557 }
558 }
559 crate::platform::WindowEvent::WindowActiveChanged(bool) => self.0.set_active(bool),
560 }
561 }
562
563 /// Returns true if there is an animation currently active on any property in the Window; false otherwise.
564 pub fn has_active_animations(&self) -> bool {
565 // TODO make it really per window.
566 crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| driver.has_active_animations())
567 }
568
569 /// Returns the visibility state of the window. This function can return false even if you previously called show()
570 /// on it, for example if the user minimized the window.
571 pub fn is_visible(&self) -> bool {
572 self.0.is_visible()
573 }
574}
575
576pub use crate::SharedString;
577
578/// This trait is used to obtain references to global singletons exported in `.slint`
579/// markup. Alternatively, you can use [`ComponentHandle::global`] to obtain access.
580///
581/// This trait is implemented by the compiler for each global singleton that's exported.
582///
583/// # Example
584/// The following example of `.slint` markup defines a global singleton called `Palette`, exports
585/// it and modifies it from Rust code:
586/// ```rust
587/// # i_slint_backend_testing::init();
588/// slint::slint!{
589/// export global Palette {
590/// in property<color> foreground-color;
591/// in property<color> background-color;
592/// }
593///
594/// export component App inherits Window {
595/// background: Palette.background-color;
596/// Text {
597/// text: "Hello";
598/// color: Palette.foreground-color;
599/// }
600/// // ...
601/// }
602/// }
603/// let app = App::new().unwrap();
604/// app.global::<Palette>().set_background_color(slint::Color::from_rgb_u8(0, 0, 0));
605///
606/// // alternate way to access the global singleton:
607/// Palette::get(&app).set_foreground_color(slint::Color::from_rgb_u8(255, 255, 255));
608/// ```
609///
610#[doc = concat!("See also the [language documentation for global singletons](https://slint.dev/releases/", env!("CARGO_PKG_VERSION"), "/docs/slint/src/reference/globals.html) for more information.")]
611///
612/// **Note:** Only globals that are exported or re-exported from the main .slint file will
613/// be exposed in the API
614pub trait Global<'a, Component> {
615 /// Returns a reference that's tied to the life time of the provided component.
616 fn get(component: &'a Component) -> Self;
617}
618
619/// This trait describes the common public API of a strongly referenced Slint component.
620/// It allows creating strongly-referenced clones, a conversion into/ a weak pointer as well
621/// as other convenience functions.
622///
623/// This trait is implemented by the [generated component](index.html#generated-components)
624pub trait ComponentHandle {
625 /// The type of the generated component.
626 #[doc(hidden)]
627 type Inner;
628 /// Returns a new weak pointer.
629 fn as_weak(&self) -> Weak<Self>
630 where
631 Self: Sized;
632
633 /// Returns a clone of this handle that's a strong reference.
634 #[must_use]
635 fn clone_strong(&self) -> Self;
636
637 /// Internal function used when upgrading a weak reference to a strong one.
638 #[doc(hidden)]
639 fn from_inner(_: vtable::VRc<ItemTreeVTable, Self::Inner>) -> Self;
640
641 /// Convenience function for [`crate::Window::show()`](struct.Window.html#method.show).
642 /// This shows the window on the screen and maintains an extra strong reference while
643 /// the window is visible. To react to events from the windowing system, such as draw
644 /// requests or mouse/touch input, it is still necessary to spin the event loop,
645 /// using [`crate::run_event_loop`](fn.run_event_loop.html).
646 fn show(&self) -> Result<(), PlatformError>;
647
648 /// Convenience function for [`crate::Window::hide()`](struct.Window.html#method.hide).
649 /// Hides the window, so that it is not visible anymore. The additional strong reference
650 /// on the associated component, that was created when show() was called, is dropped.
651 fn hide(&self) -> Result<(), PlatformError>;
652
653 /// Returns the Window associated with this component. The window API can be used
654 /// to control different aspects of the integration into the windowing system,
655 /// such as the position on the screen.
656 fn window(&self) -> &Window;
657
658 /// This is a convenience function that first calls [`Self::show`], followed by [`crate::run_event_loop()`](fn.run_event_loop.html)
659 /// and [`Self::hide`].
660 fn run(&self) -> Result<(), PlatformError>;
661
662 /// This function provides access to instances of global singletons exported in `.slint`.
663 /// See [`Global`] for an example how to export and access globals from `.slint` markup.
664 fn global<'a, T: Global<'a, Self>>(&'a self) -> T
665 where
666 Self: Sized;
667}
668
669mod weak_handle {
670
671 use super::*;
672
673 /// Struct that's used to hold weak references of a [Slint component](index.html#generated-components)
674 ///
675 /// In order to create a Weak, you should use [`ComponentHandle::as_weak`].
676 ///
677 /// Strong references should not be captured by the functions given to a lambda,
678 /// as this would produce a reference loop and leak the component.
679 /// Instead, the callback function should capture a weak component.
680 ///
681 /// The Weak component also implement `Send` and can be send to another thread.
682 /// but the upgrade function will only return a valid component from the same thread
683 /// as the one it has been created from.
684 /// This is useful to use with [`invoke_from_event_loop()`] or [`Self::upgrade_in_event_loop()`].
685 pub struct Weak<T: ComponentHandle> {
686 inner: vtable::VWeak<ItemTreeVTable, T::Inner>,
687 #[cfg(feature = "std")]
688 thread: std::thread::ThreadId,
689 }
690
691 impl<T: ComponentHandle> Default for Weak<T> {
692 fn default() -> Self {
693 Self {
694 inner: vtable::VWeak::default(),
695 #[cfg(feature = "std")]
696 thread: std::thread::current().id(),
697 }
698 }
699 }
700
701 impl<T: ComponentHandle> Clone for Weak<T> {
702 fn clone(&self) -> Self {
703 Self {
704 inner: self.inner.clone(),
705 #[cfg(feature = "std")]
706 thread: self.thread,
707 }
708 }
709 }
710
711 impl<T: ComponentHandle> Weak<T> {
712 #[doc(hidden)]
713 pub fn new(rc: &vtable::VRc<ItemTreeVTable, T::Inner>) -> Self {
714 Self {
715 inner: vtable::VRc::downgrade(rc),
716 #[cfg(feature = "std")]
717 thread: std::thread::current().id(),
718 }
719 }
720
721 /// Returns a new strongly referenced component if some other instance still
722 /// holds a strong reference. Otherwise, returns None.
723 ///
724 /// This also returns None if the current thread is not the thread that created
725 /// the component
726 pub fn upgrade(&self) -> Option<T>
727 where
728 T: ComponentHandle,
729 {
730 #[cfg(feature = "std")]
731 if std::thread::current().id() != self.thread {
732 return None;
733 }
734 self.inner.upgrade().map(T::from_inner)
735 }
736
737 /// Convenience function that returns a new strongly referenced component if
738 /// some other instance still holds a strong reference and the current thread
739 /// is the thread that created this component.
740 /// Otherwise, this function panics.
741 #[track_caller]
742 pub fn unwrap(&self) -> T {
743 #[cfg(feature = "std")]
744 if std::thread::current().id() != self.thread {
745 panic!(
746 "Trying to upgrade a Weak from a different thread than the one it belongs to"
747 );
748 }
749 T::from_inner(self.inner.upgrade().expect("The Weak doesn't hold a valid component"))
750 }
751
752 /// A helper function to allow creation on `component_factory::Component` from
753 /// a `ComponentHandle`
754 pub(crate) fn inner(&self) -> vtable::VWeak<ItemTreeVTable, T::Inner> {
755 self.inner.clone()
756 }
757
758 /// Convenience function that combines [`invoke_from_event_loop()`] with [`Self::upgrade()`]
759 ///
760 /// The given functor will be added to an internal queue and will wake the event loop.
761 /// On the next iteration of the event loop, the functor will be executed with a `T` as an argument.
762 ///
763 /// If the component was dropped because there are no more strong reference to the component,
764 /// the functor will not be called.
765 ///
766 /// # Example
767 /// ```rust
768 /// # i_slint_backend_testing::init();
769 /// slint::slint! { export component MyApp inherits Window { in property <int> foo; /* ... */ } }
770 /// let handle = MyApp::new().unwrap();
771 /// let handle_weak = handle.as_weak();
772 /// let thread = std::thread::spawn(move || {
773 /// // ... Do some computation in the thread
774 /// let foo = 42;
775 /// # assert!(handle_weak.upgrade().is_none()); // note that upgrade fails in a thread
776 /// # return; // don't upgrade_in_event_loop in our examples
777 /// // now forward the data to the main thread using upgrade_in_event_loop
778 /// handle_weak.upgrade_in_event_loop(move |handle| handle.set_foo(foo));
779 /// });
780 /// # thread.join().unwrap(); return; // don't run the event loop in examples
781 /// handle.run().unwrap();
782 /// ```
783 #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))]
784 pub fn upgrade_in_event_loop(
785 &self,
786 func: impl FnOnce(T) + Send + 'static,
787 ) -> Result<(), EventLoopError>
788 where
789 T: 'static,
790 {
791 let weak_handle = self.clone();
792 super::invoke_from_event_loop(move || {
793 if let Some(h) = weak_handle.upgrade() {
794 func(h);
795 }
796 })
797 }
798 }
799
800 // Safety: we make sure in upgrade that the thread is the proper one,
801 // and the VWeak only use atomic pointer so it is safe to clone and drop in another thread
802 #[allow(unsafe_code)]
803 #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))]
804 unsafe impl<T: ComponentHandle> Send for Weak<T> {}
805}
806
807pub use weak_handle::*;
808
809/// Adds the specified function to an internal queue, notifies the event loop to wake up.
810/// Once woken up, any queued up functors will be invoked.
811///
812/// This function is thread-safe and can be called from any thread, including the one
813/// running the event loop. The provided functors will only be invoked from the thread
814/// that started the event loop.
815///
816/// You can use this to set properties or use any other Slint APIs from other threads,
817/// by collecting the code in a functor and queuing it up for invocation within the event loop.
818///
819/// See also [`Weak::upgrade_in_event_loop`]
820///
821/// # Example
822/// ```rust
823/// slint::slint! { export component MyApp inherits Window { in property <int> foo; /* ... */ } }
824/// # i_slint_backend_testing::init();
825/// let handle = MyApp::new().unwrap();
826/// let handle_weak = handle.as_weak();
827/// # return; // don't run the event loop in examples
828/// let thread = std::thread::spawn(move || {
829/// // ... Do some computation in the thread
830/// let foo = 42;
831/// // now forward the data to the main thread using invoke_from_event_loop
832/// let handle_copy = handle_weak.clone();
833/// slint::invoke_from_event_loop(move || handle_copy.unwrap().set_foo(foo));
834/// });
835/// handle.run().unwrap();
836/// ```
837pub fn invoke_from_event_loop(func: impl FnOnce() + Send + 'static) -> Result<(), EventLoopError> {
838 crate::platform::event_loop_proxy()
839 .ok_or(EventLoopError::NoEventLoopProvider)?
840 .invoke_from_event_loop(event:alloc::boxed::Box::new(func))
841}
842
843/// Schedules the main event loop for termination. This function is meant
844/// to be called from callbacks triggered by the UI. After calling the function,
845/// it will return immediately and once control is passed back to the event loop,
846/// the initial call to `slint::run_event_loop()` will return.
847///
848/// This function can be called from any thread
849pub fn quit_event_loop() -> Result<(), EventLoopError> {
850 crate&dyn EventLoopProxy::platform::event_loop_proxy()
851 .ok_or(err:EventLoopError::NoEventLoopProvider)?
852 .quit_event_loop()
853}
854
855#[derive(Debug, Clone, Eq, PartialEq)]
856#[non_exhaustive]
857/// Error returned from the [`invoke_from_event_loop()`] and [`quit_event_loop()`] function
858pub enum EventLoopError {
859 /// The event could not be sent because the event loop was terminated already
860 EventLoopTerminated,
861 /// The event could not be sent because the Slint platform abstraction was not yet initialized,
862 /// or the platform does not support event loop.
863 NoEventLoopProvider,
864}
865
866impl core::fmt::Display for EventLoopError {
867 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
868 match self {
869 EventLoopError::EventLoopTerminated => {
870 f.write_str(data:"The event loop was already terminated")
871 }
872 EventLoopError::NoEventLoopProvider => {
873 f.write_str(data:"The Slint platform does not provide an event loop")
874 }
875 }
876 }
877}
878
879/// The platform encountered a fatal error.
880///
881/// This error typically indicates an issue with initialization or connecting to the windowing system.
882///
883/// This can be constructed from a `String`:
884/// ```rust
885/// use slint::platform::PlatformError;
886/// PlatformError::from(format!("Could not load resource {}", 1234));
887/// ```
888#[derive(Debug)]
889#[non_exhaustive]
890pub enum PlatformError {
891 /// No default platform was selected, or no platform could be initialized.
892 ///
893 /// If you encounter this error, make sure to either selected trough the `backend-*` cargo features flags,
894 /// or call [`platform::set_platform()`](crate::platform::set_platform)
895 /// before running the event loop
896 NoPlatform,
897 /// The Slint Platform does not provide an event loop.
898 ///
899 /// The [`Platform::run_event_loop`](crate::platform::Platform::run_event_loop)
900 /// is not implemented for the current platform.
901 NoEventLoopProvider,
902
903 /// There is already a platform set from another thread.
904 SetPlatformError(crate::platform::SetPlatformError),
905
906 /// Another platform-specific error occurred
907 Other(String),
908 /// Another platform-specific error occurred.
909 #[cfg(feature = "std")]
910 OtherError(Box<dyn std::error::Error + Send + Sync>),
911}
912
913impl core::fmt::Display for PlatformError {
914 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
915 match self {
916 PlatformError::NoPlatform => f.write_str(
917 data:"No default Slint platform was selected, and no Slint platform was initialized",
918 ),
919 PlatformError::NoEventLoopProvider => {
920 f.write_str(data:"The Slint platform does not provide an event loop")
921 }
922 PlatformError::SetPlatformError(_) => {
923 f.write_str(data:"The Slint platform was initialized in another thread")
924 }
925 PlatformError::Other(str: &String) => f.write_str(data:str),
926 #[cfg(feature = "std")]
927 PlatformError::OtherError(error: &Box) => error.fmt(f),
928 }
929 }
930}
931
932impl From<String> for PlatformError {
933 fn from(value: String) -> Self {
934 Self::Other(value)
935 }
936}
937impl From<&str> for PlatformError {
938 fn from(value: &str) -> Self {
939 Self::Other(value.into())
940 }
941}
942
943#[cfg(feature = "std")]
944impl From<Box<dyn std::error::Error + Send + Sync>> for PlatformError {
945 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
946 Self::OtherError(error)
947 }
948}
949
950#[cfg(feature = "std")]
951impl std::error::Error for PlatformError {
952 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
953 match self {
954 PlatformError::OtherError(err: &Box) => Some(err.as_ref()),
955 _ => None,
956 }
957 }
958}
959
960#[test]
961#[cfg(feature = "std")]
962fn error_is_send() {
963 let _: Box<dyn std::error::Error + Send + Sync + 'static> = PlatformError::NoPlatform.into();
964}
965