Skip to main content

i_slint_core/
api.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
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
10use crate::input::{InternalKeyEvent, KeyEventType, MouseEvent, TouchPhase};
11use crate::window::{WindowAdapter, WindowInner};
12use alloc::boxed::Box;
13use alloc::string::String;
14
15#[cfg(target_has_atomic = "ptr")]
16pub use crate::future::*;
17pub use crate::graphics::{
18    Brush, Color, Image, LoadImageError, OklchColor, Rgb8Pixel, Rgba8Pixel, RgbaColor,
19    SharedPixelBuffer,
20};
21pub use crate::input::Keys;
22pub use crate::sharedvector::SharedVector;
23pub use crate::{format, string::SharedString, string::ToSharedString};
24
25/// A position represented in the coordinate space of logical pixels. That is the space before applying
26/// a display device specific scale factor.
27#[derive(Debug, Default, Copy, Clone, PartialEq)]
28#[repr(C)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct LogicalPosition {
31    /// The x coordinate.
32    pub x: f32,
33    /// The y coordinate.
34    pub y: f32,
35}
36
37impl LogicalPosition {
38    /// Construct a new logical position from the given x and y coordinates, that are assumed to be
39    /// in the logical coordinate space.
40    pub const fn new(x: f32, y: f32) -> Self {
41        Self { x, y }
42    }
43
44    /// Convert a given physical position to a logical position by dividing the coordinates with the
45    /// specified scale factor.
46    pub fn from_physical(physical_pos: PhysicalPosition, scale_factor: f32) -> Self {
47        Self::new(physical_pos.x as f32 / scale_factor, physical_pos.y as f32 / scale_factor)
48    }
49
50    /// Convert this logical position to a physical position by multiplying the coordinates with the
51    /// specified scale factor.
52    pub fn to_physical(&self, scale_factor: f32) -> PhysicalPosition {
53        PhysicalPosition::from_logical(*self, scale_factor)
54    }
55
56    pub(crate) fn to_euclid(self) -> crate::lengths::LogicalPoint {
57        [self.x as _, self.y as _].into()
58    }
59    pub(crate) fn from_euclid(p: crate::lengths::LogicalPoint) -> Self {
60        Self::new(p.x as _, p.y as _)
61    }
62}
63
64/// A position represented in the coordinate space of physical device pixels. That is the space after applying
65/// a display device specific scale factor to pixels from the logical coordinate space.
66#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68pub struct PhysicalPosition {
69    /// The x coordinate.
70    pub x: i32,
71    /// The y coordinate.
72    pub y: i32,
73}
74
75impl PhysicalPosition {
76    /// Construct a new physical position from the given x and y coordinates, that are assumed to be
77    /// in the physical coordinate space.
78    pub const fn new(x: i32, y: i32) -> Self {
79        Self { x, y }
80    }
81
82    /// Convert a given logical position to a physical position by multiplying the coordinates with the
83    /// specified scale factor.
84    pub fn from_logical(logical_pos: LogicalPosition, scale_factor: f32) -> Self {
85        Self::new((logical_pos.x * scale_factor) as i32, (logical_pos.y * scale_factor) as i32)
86    }
87
88    /// Convert this physical position to a logical position by dividing the coordinates with the
89    /// specified scale factor.
90    pub fn to_logical(&self, scale_factor: f32) -> LogicalPosition {
91        LogicalPosition::from_physical(*self, scale_factor)
92    }
93
94    #[cfg(feature = "ffi")]
95    pub(crate) fn to_euclid(self) -> crate::graphics::euclid::default::Point2D<i32> {
96        [self.x, self.y].into()
97    }
98
99    #[cfg(feature = "ffi")]
100    pub(crate) fn from_euclid(p: crate::graphics::euclid::default::Point2D<i32>) -> Self {
101        Self::new(p.x as _, p.y as _)
102    }
103}
104
105/// The position of the window in either physical or logical pixels. This is used
106/// with [`Window::set_position`].
107#[derive(Clone, Debug, derive_more::From, PartialEq)]
108pub enum WindowPosition {
109    /// The position in physical pixels.
110    Physical(PhysicalPosition),
111    /// The position in logical pixels.
112    Logical(LogicalPosition),
113}
114
115impl WindowPosition {
116    /// Turn the `WindowPosition` into a `PhysicalPosition`.
117    pub fn to_physical(&self, scale_factor: f32) -> PhysicalPosition {
118        match self {
119            WindowPosition::Physical(pos) => *pos,
120            WindowPosition::Logical(pos) => pos.to_physical(scale_factor),
121        }
122    }
123}
124
125/// A size represented in the coordinate space of logical pixels. That is the space before applying
126/// a display device specific scale factor.
127#[repr(C)]
128#[derive(Debug, Default, Copy, Clone, PartialEq)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
130pub struct LogicalSize {
131    /// The width in logical pixels.
132    pub width: f32,
133    /// The height in logical.
134    pub height: f32,
135}
136
137impl LogicalSize {
138    /// Construct a new logical size from the given width and height values, that are assumed to be
139    /// in the logical coordinate space.
140    pub const fn new(width: f32, height: f32) -> Self {
141        Self { width, height }
142    }
143
144    /// Convert a given physical size to a logical size by dividing width and height by the
145    /// specified scale factor.
146    pub fn from_physical(physical_size: PhysicalSize, scale_factor: f32) -> Self {
147        Self::new(
148            physical_size.width as f32 / scale_factor,
149            physical_size.height as f32 / scale_factor,
150        )
151    }
152
153    /// Convert this logical size to a physical size by multiplying width and height with the
154    /// specified scale factor.
155    pub fn to_physical(&self, scale_factor: f32) -> PhysicalSize {
156        PhysicalSize::from_logical(*self, scale_factor)
157    }
158
159    pub(crate) fn to_euclid(self) -> crate::lengths::LogicalSize {
160        [self.width as _, self.height as _].into()
161    }
162
163    pub(crate) fn from_euclid(p: crate::lengths::LogicalSize) -> Self {
164        Self::new(p.width as _, p.height as _)
165    }
166}
167
168/// A size represented in the coordinate space of physical device pixels. That is the space after applying
169/// a display device specific scale factor to pixels from the logical coordinate space.
170#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172pub struct PhysicalSize {
173    /// The width in physical pixels.
174    pub width: u32,
175    /// The height in physical pixels;
176    pub height: u32,
177}
178
179impl PhysicalSize {
180    /// Construct a new physical size from the width and height values, that are assumed to be
181    /// in the physical coordinate space.
182    pub const fn new(width: u32, height: u32) -> Self {
183        Self { width, height }
184    }
185
186    /// Convert a given logical size to a physical size by multiplying width and height with the
187    /// specified scale factor.
188    pub fn from_logical(logical_size: LogicalSize, scale_factor: f32) -> Self {
189        Self::new(
190            (logical_size.width * scale_factor) as u32,
191            (logical_size.height * scale_factor) as u32,
192        )
193    }
194
195    /// Convert this physical size to a logical size by dividing width and height by the
196    /// specified scale factor.
197    pub fn to_logical(&self, scale_factor: f32) -> LogicalSize {
198        LogicalSize::from_physical(*self, scale_factor)
199    }
200
201    #[cfg(feature = "ffi")]
202    pub(crate) fn to_euclid(self) -> crate::graphics::euclid::default::Size2D<u32> {
203        [self.width, self.height].into()
204    }
205}
206
207/// The size of a window represented in either physical or logical pixels. This is used
208/// with [`Window::set_size`].
209#[derive(Clone, Debug, derive_more::From, PartialEq)]
210pub enum WindowSize {
211    /// The size in physical pixels.
212    Physical(PhysicalSize),
213    /// The size in logical screen pixels.
214    Logical(LogicalSize),
215}
216
217impl WindowSize {
218    /// Turn the `WindowSize` into a `PhysicalSize`.
219    pub fn to_physical(&self, scale_factor: f32) -> PhysicalSize {
220        match self {
221            WindowSize::Physical(size) => *size,
222            WindowSize::Logical(size) => size.to_physical(scale_factor),
223        }
224    }
225
226    /// Turn the `WindowSize` into a `LogicalSize`.
227    pub fn to_logical(&self, scale_factor: f32) -> LogicalSize {
228        match self {
229            WindowSize::Physical(size) => size.to_logical(scale_factor),
230            WindowSize::Logical(size) => *size,
231        }
232    }
233}
234
235#[test]
236fn logical_physical_pos() {
237    use crate::graphics::euclid::approxeq::ApproxEq;
238
239    let phys = PhysicalPosition::new(100, 50);
240    let logical = phys.to_logical(2.);
241    assert!(logical.x.approx_eq(&50.));
242    assert!(logical.y.approx_eq(&25.));
243
244    assert_eq!(logical.to_physical(2.), phys);
245}
246
247#[test]
248fn logical_physical_size() {
249    use crate::graphics::euclid::approxeq::ApproxEq;
250
251    let phys = PhysicalSize::new(100, 50);
252    let logical = phys.to_logical(2.);
253    assert!(logical.width.approx_eq(&50.));
254    assert!(logical.height.approx_eq(&25.));
255
256    assert_eq!(logical.to_physical(2.), phys);
257}
258
259#[i_slint_core_macros::slint_doc]
260/// This enum describes a low-level access to specific graphics APIs used
261/// by the renderer.
262#[derive(Clone)]
263#[non_exhaustive]
264pub enum GraphicsAPI<'a> {
265    /// The rendering is done using OpenGL.
266    NativeOpenGL {
267        /// Use this function pointer to obtain access to the OpenGL implementation - similar to `eglGetProcAddress`.
268        get_proc_address: &'a dyn Fn(&core::ffi::CStr) -> *const core::ffi::c_void,
269    },
270    /// The rendering is done on a HTML Canvas element using WebGL.
271    WebGL {
272        /// The DOM element id of the HTML Canvas element used for rendering.
273        canvas_element_id: &'a str,
274        /// The drawing context type used on the HTML Canvas element for rendering. This is the argument to the
275        /// `getContext` function on the HTML Canvas element.
276        context_type: &'a str,
277    },
278    /// The rendering is based on WGPU 27.x. Use the provided fields to submit commits to the provided
279    /// WGPU command queue.
280    ///
281    /// *Note*: This function is behind the [`unstable-wgpu-27` feature flag](slint:rust:slint/docs/cargo_features/#backends)
282    ///         and may be removed or changed in future minor releases, as new major WGPU releases become available.
283    ///
284    /// See also the [`slint::wgpu_27`](slint:rust:slint/wgpu_27) module.
285    #[cfg(feature = "unstable-wgpu-27")]
286    #[non_exhaustive]
287    WGPU27 {
288        /// The WGPU instance used for rendering.
289        instance: wgpu_27::Instance,
290        /// The WGPU device used for rendering.
291        device: wgpu_27::Device,
292        /// The WGPU queue for used for command submission.
293        queue: wgpu_27::Queue,
294    },
295    /// The rendering is based on WGPU 28.x. Use the provided fields to submit commits to the provided
296    /// WGPU command queue.
297    ///
298    /// *Note*: This function is behind the [`unstable-wgpu-28` feature flag](slint:rust:slint/docs/cargo_features/#backends)
299    ///         and may be removed or changed in future minor releases, as new major WGPU releases become available.
300    ///
301    /// See also the [`slint::wgpu_28`](slint:rust:slint/wgpu_28) module.
302    #[cfg(feature = "unstable-wgpu-28")]
303    #[non_exhaustive]
304    WGPU28 {
305        /// The WGPU instance used for rendering.
306        instance: wgpu_28::Instance,
307        /// The WGPU device used for rendering.
308        device: wgpu_28::Device,
309        /// The WGPU queue for used for command submission.
310        queue: wgpu_28::Queue,
311    },
312}
313
314impl core::fmt::Debug for GraphicsAPI<'_> {
315    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
316        match self {
317            GraphicsAPI::NativeOpenGL { .. } => write!(f, "GraphicsAPI::NativeOpenGL"),
318            GraphicsAPI::WebGL { context_type, .. } => {
319                write!(f, "GraphicsAPI::WebGL(context_type = {context_type})")
320            }
321            #[cfg(feature = "unstable-wgpu-27")]
322            GraphicsAPI::WGPU27 { .. } => write!(f, "GraphicsAPI::WGPU27"),
323            #[cfg(feature = "unstable-wgpu-28")]
324            GraphicsAPI::WGPU28 { .. } => write!(f, "GraphicsAPI::WGPU28"),
325        }
326    }
327}
328
329/// This enum describes the different rendering states, that will be provided
330/// to the parameter of the callback for `set_rendering_notifier` on the `slint::Window`.
331///
332/// When OpenGL is used for rendering, the context will be current.
333/// It's safe to call OpenGL functions, but it is crucial that the state of the context is
334/// preserved. So make sure to save and restore state such as `TEXTURE_BINDING_2D` or
335/// `ARRAY_BUFFER_BINDING` perfectly.
336#[derive(Debug, Clone)]
337#[repr(u8)]
338#[non_exhaustive]
339pub enum RenderingState {
340    /// The window has been created and the graphics adapter/context initialized.
341    RenderingSetup,
342    /// The scene of items is about to be rendered.
343    BeforeRendering,
344    /// The scene of items was rendered, but the back buffer was not sent for display presentation
345    /// yet (for example GL swap buffers).
346    AfterRendering,
347    /// The window will be destroyed and/or graphics resources need to be released due to other
348    /// constraints.
349    RenderingTeardown,
350}
351
352/// Internal trait that's used to map rendering state callbacks to either a Rust-API provided
353/// impl FnMut or a struct that invokes a C callback and implements Drop to release the closure
354/// on the C++ side.
355#[doc(hidden)]
356pub trait RenderingNotifier {
357    /// Called to notify that rendering has reached a certain state.
358    fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI);
359}
360
361impl<F: FnMut(RenderingState, &GraphicsAPI)> RenderingNotifier for F {
362    fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI) {
363        self(state, graphics_api)
364    }
365}
366
367/// This enum describes the different error scenarios that may occur when the application
368/// registers a rendering notifier on a `slint::Window`.
369#[derive(Debug, Clone)]
370#[repr(u8)]
371#[non_exhaustive]
372pub enum SetRenderingNotifierError {
373    /// The rendering backend does not support rendering notifiers.
374    Unsupported,
375    /// There is already a rendering notifier set, multiple notifiers are not supported.
376    AlreadySet,
377}
378
379impl core::fmt::Display for SetRenderingNotifierError {
380    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
381        match self {
382            Self::Unsupported => {
383                f.write_str("The rendering backend does not support rendering notifiers.")
384            }
385            Self::AlreadySet => f.write_str(
386                "There is already a rendering notifier set, multiple notifiers are not supported.",
387            ),
388        }
389    }
390}
391
392#[cfg(feature = "std")]
393impl std::error::Error for SetRenderingNotifierError {}
394
395#[cfg(feature = "raw-window-handle-06")]
396#[derive(Clone)]
397enum WindowHandleInner {
398    HandleByAdapter(alloc::rc::Rc<dyn WindowAdapter>),
399    #[cfg(feature = "std")]
400    HandleByRcRWH {
401        window_handle_provider: std::sync::Arc<dyn raw_window_handle_06::HasWindowHandle>,
402        display_handle_provider: std::sync::Arc<dyn raw_window_handle_06::HasDisplayHandle>,
403    },
404}
405
406/// This struct represents a persistent handle to a window and implements the
407/// [`raw_window_handle_06::HasWindowHandle`] and [`raw_window_handle_06::HasDisplayHandle`]
408/// traits for accessing exposing raw window and display handles.
409/// Obtain an instance of this by calling [`Window::window_handle()`].
410#[cfg(feature = "raw-window-handle-06")]
411#[derive(Clone)]
412pub struct WindowHandle {
413    inner: WindowHandleInner,
414}
415
416#[cfg(feature = "raw-window-handle-06")]
417impl raw_window_handle_06::HasWindowHandle for WindowHandle {
418    fn window_handle(
419        &self,
420    ) -> Result<raw_window_handle_06::WindowHandle<'_>, raw_window_handle_06::HandleError> {
421        match &self.inner {
422            WindowHandleInner::HandleByAdapter(adapter) => adapter.window_handle_06(),
423            #[cfg(feature = "std")]
424            WindowHandleInner::HandleByRcRWH { window_handle_provider, .. } => {
425                window_handle_provider.window_handle()
426            }
427        }
428    }
429}
430
431#[cfg(feature = "raw-window-handle-06")]
432impl raw_window_handle_06::HasDisplayHandle for WindowHandle {
433    fn display_handle(
434        &self,
435    ) -> Result<raw_window_handle_06::DisplayHandle<'_>, raw_window_handle_06::HandleError> {
436        match &self.inner {
437            WindowHandleInner::HandleByAdapter(adapter) => adapter.display_handle_06(),
438            #[cfg(feature = "std")]
439            WindowHandleInner::HandleByRcRWH { display_handle_provider, .. } => {
440                display_handle_provider.display_handle()
441            }
442        }
443    }
444}
445
446/// This type represents a window towards the windowing system, that's used to render the
447/// scene of a component. It provides API to control windowing system specific aspects such
448/// as the position on the screen.
449#[repr(transparent)]
450pub struct Window(pub(crate) WindowInner);
451
452/// This enum describes whether a Window is allowed to be hidden when the user tries to close the window.
453/// It is the return type of the callback provided to [Window::on_close_requested].
454#[derive(Copy, Clone, Debug, PartialEq, Default)]
455#[repr(u8)]
456pub enum CloseRequestResponse {
457    /// The Window will be hidden (default action)
458    #[default]
459    HideWindow = 0,
460    /// The close request is rejected and the window will be kept shown.
461    KeepWindowShown = 1,
462}
463
464impl Window {
465    /// Create a new window from a window adapter
466    ///
467    /// You only need to create the window yourself when you create a [`WindowAdapter`] from
468    /// [`Platform::create_window_adapter`](crate::platform::Platform::create_window_adapter)
469    ///
470    /// Since the window adapter must own the Window, this function is meant to be used with
471    /// [`Rc::new_cyclic`](alloc::rc::Rc::new_cyclic)
472    ///
473    /// # Example
474    /// ```rust
475    /// use std::rc::Rc;
476    /// use slint::platform::{WindowAdapter, Renderer};
477    /// use slint::{Window, PhysicalSize};
478    /// struct MyWindowAdapter {
479    ///     window: Window,
480    ///     //...
481    /// }
482    /// impl WindowAdapter for MyWindowAdapter {
483    ///    fn window(&self) -> &Window { &self.window }
484    ///    fn size(&self) -> PhysicalSize { unimplemented!() }
485    ///    fn renderer(&self) -> &dyn Renderer { unimplemented!() }
486    /// }
487    ///
488    /// fn create_window_adapter() -> Rc<dyn WindowAdapter> {
489    ///    Rc::<MyWindowAdapter>::new_cyclic(|weak| {
490    ///        MyWindowAdapter {
491    ///           window: Window::new(weak.clone()),
492    ///           //...
493    ///        }
494    ///    })
495    /// }
496    /// ```
497    pub fn new(window_adapter_weak: alloc::rc::Weak<dyn WindowAdapter>) -> Self {
498        Self(WindowInner::new(window_adapter_weak))
499    }
500
501    /// Shows the window on the screen. An additional strong reference on the
502    /// associated component is maintained while the window is visible.
503    ///
504    /// Call [`Self::hide()`] to make the window invisible again, and drop the additional
505    /// strong reference.
506    pub fn show(&self) -> Result<(), PlatformError> {
507        self.0.show()
508    }
509
510    /// Hides the window, so that it is not visible anymore. The additional strong
511    /// reference on the associated component, that was created when [`Self::show()`] was called, is
512    /// dropped.
513    pub fn hide(&self) -> Result<(), PlatformError> {
514        self.0.hide()
515    }
516
517    /// This function allows registering a callback that's invoked during the different phases of
518    /// rendering. This allows custom rendering on top or below of the scene.
519    pub fn set_rendering_notifier(
520        &self,
521        callback: impl FnMut(RenderingState, &GraphicsAPI) + 'static,
522    ) -> Result<(), SetRenderingNotifierError> {
523        self.0.window_adapter().renderer().set_rendering_notifier(Box::new(callback))
524    }
525
526    /// This function allows registering a callback that's invoked when the user tries to close a window.
527    /// The callback has to return a [CloseRequestResponse].
528    pub fn on_close_requested(&self, callback: impl FnMut() -> CloseRequestResponse + 'static) {
529        self.0.on_close_requested(callback);
530    }
531
532    /// This function issues a request to the windowing system to redraw the contents of the window.
533    pub fn request_redraw(&self) {
534        self.0.window_adapter().request_redraw()
535    }
536
537    /// This function returns the scale factor that allows converting between logical and
538    /// physical pixels.
539    pub fn scale_factor(&self) -> f32 {
540        self.0.scale_factor()
541    }
542
543    /// Returns the position of the window on the screen, in physical screen coordinates and including
544    /// a window frame (if present).
545    pub fn position(&self) -> PhysicalPosition {
546        self.0.window_adapter().position().unwrap_or_default()
547    }
548
549    /// Sets the position of the window on the screen, in physical screen coordinates and including
550    /// a window frame (if present).
551    /// Note that on some windowing systems, such as Wayland, this functionality is not available.
552    pub fn set_position(&self, position: impl Into<WindowPosition>) {
553        let position = position.into();
554        self.0.window_adapter().set_position(position)
555    }
556
557    /// Returns the size of the window on the screen, in physical screen coordinates and excluding
558    /// a window frame (if present).
559    pub fn size(&self) -> PhysicalSize {
560        self.0.window_adapter().size()
561    }
562
563    /// Resizes the window to the specified size on the screen, in physical pixels and excluding
564    /// a window frame (if present).
565    pub fn set_size(&self, size: impl Into<WindowSize>) {
566        let size = size.into();
567        crate::window::WindowAdapter::set_size(&*self.0.window_adapter(), size);
568    }
569
570    /// Returns if the window is currently fullscreen
571    pub fn is_fullscreen(&self) -> bool {
572        self.0.is_fullscreen()
573    }
574
575    /// Set or unset the window to display fullscreen.
576    pub fn set_fullscreen(&self, fullscreen: bool) {
577        self.0.set_fullscreen(fullscreen);
578    }
579
580    /// Returns if the window is currently maximized
581    pub fn is_maximized(&self) -> bool {
582        self.0.is_maximized()
583    }
584
585    /// Maximize or unmaximize the window.
586    pub fn set_maximized(&self, maximized: bool) {
587        self.0.set_maximized(maximized);
588    }
589
590    /// Returns if the window is currently minimized
591    pub fn is_minimized(&self) -> bool {
592        self.0.is_minimized()
593    }
594
595    /// Minimize or unminimze the window.
596    pub fn set_minimized(&self, minimized: bool) {
597        self.0.set_minimized(minimized);
598    }
599
600    /// The area of the window covered by the software keyboard is changing (animated).
601    #[doc(hidden)]
602    pub fn set_virtual_keyboard(
603        &self,
604        origin: LogicalPosition,
605        size: LogicalSize,
606        _: crate::InternalToken,
607    ) {
608        self.0.set_window_item_virtual_keyboard(origin.to_euclid(), size.to_euclid());
609    }
610
611    #[doc(hidden)]
612    pub fn virtual_keyboard(
613        &self,
614        _: crate::InternalToken,
615    ) -> Option<(LogicalPosition, LogicalSize)> {
616        self.0.window_item_virtual_keyboard().map(|(origin, size)| {
617            (LogicalPosition::from_euclid(origin), LogicalSize::from_euclid(size))
618        })
619    }
620
621    /// Dispatch a window event to the scene.
622    ///
623    /// Use this when you're implementing your own backend and want to forward user input events.
624    ///
625    /// Any position fields in the event must be in the logical pixel coordinate system relative to
626    /// the top left corner of the window.
627    ///
628    /// This function panics if there is an error processing the event.
629    /// Use [`Self::try_dispatch_event()`] to handle the error.
630    #[track_caller]
631    pub fn dispatch_event(&self, event: crate::platform::WindowEvent) {
632        self.try_dispatch_event(event).unwrap()
633    }
634
635    /// Dispatch a window event to the scene.
636    ///
637    /// Use this when you're implementing your own backend and want to forward user input events.
638    ///
639    /// Any position fields in the event must be in the logical pixel coordinate system relative to
640    /// the top left corner of the window.
641    pub fn try_dispatch_event(
642        &self,
643        event: crate::platform::WindowEvent,
644    ) -> Result<(), PlatformError> {
645        match event {
646            crate::platform::WindowEvent::PointerPressed { position, button } => {
647                self.0.process_mouse_input(MouseEvent::Pressed {
648                    position: position.to_euclid().cast(),
649                    button,
650                    click_count: 0,
651                    is_touch: false,
652                });
653            }
654            crate::platform::WindowEvent::PointerReleased { position, button } => {
655                self.0.process_mouse_input(MouseEvent::Released {
656                    position: position.to_euclid().cast(),
657                    button,
658                    click_count: 0,
659                    is_touch: false,
660                });
661            }
662            crate::platform::WindowEvent::PointerMoved { position } => {
663                self.0.process_mouse_input(MouseEvent::Moved {
664                    position: position.to_euclid().cast(),
665                    is_touch: false,
666                });
667            }
668            crate::platform::WindowEvent::PointerScrolled { position, delta_x, delta_y } => {
669                self.0.process_mouse_input(MouseEvent::Wheel {
670                    position: position.to_euclid().cast(),
671                    delta_x: delta_x as _,
672                    delta_y: delta_y as _,
673                    phase: TouchPhase::Cancelled,
674                });
675            }
676            crate::platform::WindowEvent::PointerExited => {
677                self.0.process_mouse_input(MouseEvent::Exit)
678            }
679
680            crate::platform::WindowEvent::KeyPressed { text } => {
681                self.0.process_key_input(InternalKeyEvent {
682                    event_type: KeyEventType::KeyPressed,
683                    key_event: crate::input::KeyEvent { text, ..Default::default() },
684                    ..Default::default()
685                });
686            }
687            crate::platform::WindowEvent::KeyPressRepeated { text } => {
688                self.0.process_key_input(InternalKeyEvent {
689                    event_type: KeyEventType::KeyPressed,
690                    key_event: crate::input::KeyEvent { text, repeat: true, ..Default::default() },
691                    ..Default::default()
692                });
693            }
694            crate::platform::WindowEvent::KeyReleased { text } => {
695                self.0.process_key_input(InternalKeyEvent {
696                    event_type: KeyEventType::KeyReleased,
697                    key_event: crate::input::KeyEvent { text, ..Default::default() },
698                    ..Default::default()
699                });
700            }
701            crate::platform::WindowEvent::ScaleFactorChanged { scale_factor } => {
702                self.0.set_scale_factor(scale_factor);
703            }
704            crate::platform::WindowEvent::Resized { size } => {
705                self.0.set_window_item_geometry(size.to_euclid());
706                self.0.window_adapter().renderer().resize(size.to_physical(self.scale_factor()))?;
707                if let Some(item_rc) = self.0.focus_item.borrow().upgrade() {
708                    item_rc.try_scroll_into_visible();
709                }
710            }
711            crate::platform::WindowEvent::CloseRequested => {
712                if self.0.request_close() {
713                    self.hide()?;
714                }
715            }
716            crate::platform::WindowEvent::WindowActiveChanged(bool) => self.0.set_active(bool),
717        };
718        Ok(())
719    }
720
721    /// Returns true if there is an animation currently active on any property in the Window; false otherwise.
722    pub fn has_active_animations(&self) -> bool {
723        // TODO make it really per window.
724        crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| driver.has_active_animations())
725    }
726
727    /// Returns the visibility state of the window. This function can return false even if you previously called show()
728    /// on it, for example if the user minimized the window.
729    pub fn is_visible(&self) -> bool {
730        self.0.is_visible()
731    }
732
733    /// Returns a struct that implements the raw window handle traits to access the windowing system specific window
734    /// and display handles.
735    ///
736    /// Note that the window handle may only become available after the window has been created by the window manager,
737    /// which typically occurs after at least one iteration of the event loop following a call to `show()`.
738    ///
739    /// Support for this function depends on the platform backend.
740    ///
741    /// This function is only accessible if you enable the `raw-window-handle-06` crate feature.
742    #[cfg(feature = "raw-window-handle-06")]
743    pub fn window_handle(&self) -> WindowHandle {
744        let adapter = self.0.window_adapter();
745        #[cfg(feature = "std")]
746        if let Some((window_handle_provider, display_handle_provider)) =
747            adapter.internal(crate::InternalToken).and_then(|internal| {
748                internal.window_handle_06_rc().ok().zip(internal.display_handle_06_rc().ok())
749            })
750        {
751            return WindowHandle {
752                inner: WindowHandleInner::HandleByRcRWH {
753                    window_handle_provider,
754                    display_handle_provider,
755                },
756            };
757        }
758
759        WindowHandle { inner: WindowHandleInner::HandleByAdapter(adapter) }
760    }
761
762    /// Takes a snapshot of the window contents and returns it as RGBA8 encoded pixel buffer.
763    ///
764    /// Note that this function may be slow to call as it may need to re-render the scene.
765    pub fn take_snapshot(&self) -> Result<SharedPixelBuffer<Rgba8Pixel>, PlatformError> {
766        self.0.window_adapter().renderer().take_snapshot()
767    }
768}
769
770#[i_slint_core_macros::slint_doc]
771/// This trait is used to obtain references to global singletons exported in `.slint`
772/// markup. Alternatively, you can use [`ComponentHandle::global`] to obtain access.
773///
774/// This trait is implemented by the compiler for each global singleton that's exported.
775///
776/// # Example
777/// The following example of `.slint` markup defines a global singleton called `Palette`, exports
778/// it and modifies it from Rust code:
779/// ```rust
780/// # i_slint_backend_testing::init_no_event_loop();
781/// slint::slint!{
782/// export global Palette {
783///     in property<color> foreground-color;
784///     in property<color> background-color;
785/// }
786///
787/// export component App inherits Window {
788///    background: Palette.background-color;
789///    Text {
790///       text: "Hello";
791///       color: Palette.foreground-color;
792///    }
793///    // ...
794/// }
795/// }
796/// let app = App::new().unwrap();
797/// app.global::<Palette>().set_background_color(slint::Color::from_rgb_u8(0, 0, 0));
798///
799/// // alternate way to access the global singleton:
800/// Palette::get(&app).set_foreground_color(slint::Color::from_rgb_u8(255, 255, 255));
801/// ```
802///
803/// See also the [language documentation for global singletons](slint:globals) for more information.
804///
805/// **Note:** Only globals that are exported or re-exported from the main .slint file will
806/// be exposed in the API
807///
808/// # Storing References to Globals
809///
810/// Globals are strong references to the window they are attached to, unless stored in a `Weak`
811/// reference (see the [`StrongHandle`] trait).
812/// This means that if you store a reference to a global, it will keep the entire window alive
813/// and prevent it from being dropped.
814///
815/// To make this less error-prone, when accessing a global from a window, it is initially bound to
816/// the lifetime of the Window it belongs to.
817/// This prevents you from accidentally capturing the global in a callback closure, which
818/// would result in the window never being dropped.
819///
820/// To store references to a global in a callback or Rust struct, you can convert it into
821/// a weak reference using the [`Global::as_weak`] function.
822/// This will also extend the lifetime of the global to `'static`.
823///
824/// Once the window is dropped, upgrading the weak reference will return `None`.
825///
826/// ## Example
827///
828/// ```rust
829/// # i_slint_backend_testing::init_no_event_loop();
830/// slint::slint!{
831/// export global Palette {
832///     in property<color> foreground-color;
833///     in property<color> background-color;
834/// }
835///
836/// export component App inherits Window {
837///    background: Palette.background-color;
838///    // ...
839/// }
840/// }
841///
842/// struct PaletteBackend {
843///     global: slint::Weak<Palette<'static>>,
844/// }
845///
846/// impl PaletteBackend {
847///     fn global(&self) -> Palette<'static> {
848///         self.global.upgrade().expect("The window was dropped, the global is no longer available")
849///     }
850/// }
851///
852/// let app = App::new().unwrap();
853///
854/// let palette_backend = PaletteBackend { global: app.global::<Palette>().as_weak() };
855/// ```
856pub trait Global<'a, Component> {
857    /// The `Self` type, with a `'static` lifetime.
858    type StaticSelf: 'static + StrongHandle;
859
860    /// Returns a reference to the global.
861    fn get(component: &'a Component) -> Self;
862
863    /// Convert this Global reference into a weak reference.
864    ///
865    /// This will also extend the lifetime of this global to `'static`, to allow storing
866    /// the Weak reference in a struct that does not have a lifetime itself.
867    fn as_weak(&self) -> Weak<Self::StaticSelf>;
868}
869
870/// This trait marks types that hold a strong reference to a Slint component.
871///
872/// The Slint compiler automatically implements this trait for [generated components](index.html#generated-components) and the `'static` variant of [generated Globals](index.html#exported-global-singletons).
873/// Do not try to implement it manually.
874///
875/// All types that implement this trait can be used in a [`Weak`] reference.
876///
877/// > ⚠️ Strong references should not be captured by a lambda given to a callback,
878/// > as this would produce a reference loop and leak the component.
879/// > Instead, the callback function should capture a [`Weak`] reference.
880///
881/// **Example:**
882/// ```
883/// # i_slint_backend_testing::init_no_event_loop();
884/// slint::slint!{
885///     export component App inherits Window {
886///         in-out property <int> counter: 0;
887///         callback do_something;
888///     }
889/// }
890///
891/// let app = App::new().unwrap();
892/// // ⚠️ Incorrect: This will capture a strong reference to the app in the closure,
893/// // which will never be released and leak the app!
894/// app.on_do_something({
895///     let app = app.clone_strong();
896///     move || {
897///         app.set_counter(app.get_counter() + 1);
898///     }
899/// });
900///
901/// // Correct: Use a weak reference to the app, which will be released
902/// // when the app is dropped.
903/// app.on_do_something({
904///     let app = app.as_weak();
905///     move || {
906///         let Some(app) = app.upgrade() else {
907///             return;
908///         };
909///         app.set_counter(app.get_counter() + 1);
910///     }
911/// });
912/// ```
913///
914/// # Common issues
915///
916/// To use a global with a [`Weak`] reference, you need to use the `'static` variant of the Global.
917///
918/// **Example:**
919/// ```
920/// # i_slint_backend_testing::init_no_event_loop();
921/// slint::slint!{
922///    export global MyGlobal {}
923///
924///    export component App inherits Window {}
925/// }
926/// struct MyStruct {
927///    // Use the 'static variant of MyGlobal, which implements
928///    // StrongHandle and can be used in a Weak reference.
929///    global: slint::Weak<MyGlobal<'static>>,
930/// }
931///
932/// let app = App::new().unwrap();
933/// let my_global: MyGlobal = app.global();
934///
935/// let my_struct = MyStruct {
936///     // Calling as_weak() on the global automatically converts it to 'static
937///     global: my_global.as_weak()
938/// };
939/// ```
940///
941/// Otherwise you may encounter issues like this:
942///
943/// ```text
944/// error[E0106]: missing lifetime specifier
945///   --> /path/to/file.rs:10:19
946///    |
947/// 10 |         global: Weak<MyGlobal>,
948///    |                      ^^^^^^^^ expected named lifetime parameter
949///    |
950/// help: consider introducing a named lifetime parameter
951///    |
952///  9 ~     struct MyStruct<'a> {
953/// 10 ~         global: Weak<MyGlobal<'a>>,
954/// ```
955///
956/// The compiler suggests to introduce a lifetime parameter for the struct,
957/// This is not correct - use a `'static` lifetime instead!
958///
959/// Otherwise you will run into the following error:
960///
961/// ```text
962/// error: incompatible lifetime on type
963///   --> /path/to/file.rs:9:10
964///    |
965///  9 |     global: slint::Weak<MyGlobal<'a>>,
966///    |             ^^^^^^^^^^^^^^^^^^^^^^^^^
967///    |
968///note: because this has an unmet lifetime requirement
969///   --> slint/internal/core/api.rs:954:24
970///    |
971///954 |     pub struct Weak<T: StrongHandle> {
972///    |                        ^^^^^^^^^^^^ introduces a `'static` lifetime requirement
973///note: the lifetime `'a` as defined here...
974///   --> /path/to/file.rs:8:17
975///    |
976///  8 | struct MyStruct<'a> {
977///    |                 ^^
978///note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
979///   --> /path/to/file.rs:246:6
980///    |
981///246 |      impl slint :: StrongHandle for r#MyGlobal < 'static > {
982/// ```
983pub trait StrongHandle {
984    /// The internal Inner type for `Weak<Self>::inner`.
985    #[doc(hidden)]
986    type WeakInner: Clone + Default;
987
988    /// Internal function used when upgrading a weak reference to a strong one.
989    #[doc(hidden)]
990    fn upgrade_from_weak_inner(_: &Self::WeakInner) -> Option<Self>
991    where
992        Self: Sized;
993}
994
995/// This trait describes the common public API of a strongly referenced Slint component.
996/// It allows creating strongly-referenced clones, a conversion into a weak pointer as well
997/// as other convenience functions.
998///
999/// This trait is implemented by the [generated component](index.html#generated-components)
1000pub trait ComponentHandle: StrongHandle {
1001    /// Returns a new weak pointer.
1002    // Note: It would be great if we could move this function into the StrongHandle trait. But
1003    // that would be a backwards-incompatible change.
1004    fn as_weak(&self) -> Weak<Self>
1005    where
1006        Self: Sized;
1007
1008    /// Returns a clone of this handle that's a strong reference.
1009    #[must_use]
1010    fn clone_strong(&self) -> Self;
1011
1012    /// Convenience function for [`crate::Window::show()`](struct.Window.html#method.show).
1013    /// This shows the window on the screen and maintains an extra strong reference while
1014    /// the window is visible. To react to events from the windowing system, such as draw
1015    /// requests or mouse/touch input, it is still necessary to spin the event loop,
1016    /// using [`crate::run_event_loop`](fn.run_event_loop.html).
1017    fn show(&self) -> Result<(), PlatformError>;
1018
1019    /// Convenience function for [`crate::Window::hide()`](struct.Window.html#method.hide).
1020    /// Hides the window, so that it is not visible anymore. The additional strong reference
1021    /// on the associated component, that was created when show() was called, is dropped.
1022    fn hide(&self) -> Result<(), PlatformError>;
1023
1024    /// Returns the Window associated with this component. The window API can be used
1025    /// to control different aspects of the integration into the windowing system,
1026    /// such as the position on the screen.
1027    fn window(&self) -> &Window;
1028
1029    /// This is a convenience function that first calls [`Self::show`], followed by [`crate::run_event_loop()`](fn.run_event_loop.html)
1030    /// and [`Self::hide`].
1031    fn run(&self) -> Result<(), PlatformError>;
1032
1033    /// This function provides access to instances of global singletons exported in `.slint`.
1034    /// See [`Global`] for an example how to export and access globals from `.slint` markup.
1035    fn global<'a, T: Global<'a, Self>>(&'a self) -> T
1036    where
1037        Self: Sized;
1038}
1039
1040mod weak_handle {
1041
1042    use super::*;
1043
1044    /// Struct that's used to hold weak references of a [Slint component or global](index.html#generated-components)
1045    ///
1046    /// In order to create a Weak, you should use [`ComponentHandle::as_weak`] or
1047    /// [`Global::as_weak`].
1048    ///
1049    /// Strong references should not be captured by the functions given to a lambda,
1050    /// as this would produce a reference loop and leak the component.
1051    /// Instead, the callback function should capture a weak component.
1052    ///
1053    /// The Weak component also implement `Send` and can be send to another thread.
1054    /// but the upgrade function will only return a valid component from the same thread
1055    /// as the one it has been created from.
1056    /// This is useful to use with [`invoke_from_event_loop()`] or [`Self::upgrade_in_event_loop()`].
1057    pub struct Weak<T: StrongHandle> {
1058        inner: T::WeakInner,
1059        #[cfg(feature = "std")]
1060        thread: std::thread::ThreadId,
1061    }
1062
1063    impl<T: StrongHandle> Default for Weak<T> {
1064        fn default() -> Self {
1065            Self {
1066                inner: T::WeakInner::default(),
1067                #[cfg(feature = "std")]
1068                thread: std::thread::current().id(),
1069            }
1070        }
1071    }
1072
1073    impl<T: StrongHandle> Clone for Weak<T> {
1074        fn clone(&self) -> Self {
1075            Self {
1076                inner: self.inner.clone(),
1077                #[cfg(feature = "std")]
1078                thread: self.thread,
1079            }
1080        }
1081    }
1082
1083    impl<T: StrongHandle> Weak<T> {
1084        #[doc(hidden)]
1085        pub fn new(inner: T::WeakInner) -> Self {
1086            Self {
1087                inner,
1088                #[cfg(feature = "std")]
1089                thread: std::thread::current().id(),
1090            }
1091        }
1092
1093        /// Returns a new strongly referenced component if some other instance still
1094        /// holds a strong reference. Otherwise, returns None.
1095        ///
1096        /// This also returns None if the current thread is not the thread that created
1097        /// the component
1098        pub fn upgrade(&self) -> Option<T> {
1099            #[cfg(feature = "std")]
1100            if std::thread::current().id() != self.thread {
1101                return None;
1102            }
1103            T::upgrade_from_weak_inner(&self.inner)
1104        }
1105
1106        /// Convenience function that returns a new strongly referenced component if
1107        /// some other instance still holds a strong reference and the current thread
1108        /// is the thread that created this component.
1109        /// Otherwise, this function panics.
1110        #[track_caller]
1111        pub fn unwrap(&self) -> T {
1112            #[cfg(feature = "std")]
1113            if std::thread::current().id() != self.thread {
1114                panic!(
1115                    "Trying to upgrade a Weak from a different thread than the one it belongs to"
1116                );
1117            }
1118            T::upgrade_from_weak_inner(&self.inner)
1119                .expect("The Weak doesn't hold a valid component")
1120        }
1121
1122        /// A helper function to allow creation on `component_factory::Component` from
1123        /// a `ComponentHandle`
1124        pub(crate) fn inner(&self) -> T::WeakInner {
1125            self.inner.clone()
1126        }
1127
1128        /// Convenience function that combines [`invoke_from_event_loop()`] with [`Self::upgrade()`]
1129        ///
1130        /// The given functor will be added to an internal queue and will wake the event loop.
1131        /// On the next iteration of the event loop, the functor will be executed with a `T` as an argument.
1132        ///
1133        /// If the component was dropped because there are no more strong reference to the component,
1134        /// the functor will not be called.
1135        ///
1136        /// # Example
1137        /// ```rust
1138        /// # i_slint_backend_testing::init_no_event_loop();
1139        /// slint::slint! { export component MyApp inherits Window { in property <int> foo; /* ... */ } }
1140        /// let handle = MyApp::new().unwrap();
1141        /// let handle_weak = handle.as_weak();
1142        /// let thread = std::thread::spawn(move || {
1143        ///     // ... Do some computation in the thread
1144        ///     let foo = 42;
1145        ///     # assert!(handle_weak.upgrade().is_none()); // note that upgrade fails in a thread
1146        ///     # return; // don't upgrade_in_event_loop in our examples
1147        ///     // now forward the data to the main thread using upgrade_in_event_loop
1148        ///     handle_weak.upgrade_in_event_loop(move |handle| handle.set_foo(foo));
1149        /// });
1150        /// # thread.join().unwrap(); return; // don't run the event loop in examples
1151        /// handle.run().unwrap();
1152        /// ```
1153        #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))]
1154        pub fn upgrade_in_event_loop(
1155            &self,
1156            func: impl FnOnce(T) + Send + 'static,
1157        ) -> Result<(), EventLoopError>
1158        where
1159            T: 'static,
1160        {
1161            let weak_handle = self.clone();
1162            super::invoke_from_event_loop(move || {
1163                if let Some(h) = weak_handle.upgrade() {
1164                    func(h);
1165                }
1166            })
1167        }
1168    }
1169
1170    // Safety: we make sure in upgrade that the thread is the proper one,
1171    // and the VWeak only use atomic pointer so it is safe to clone and drop in another thread
1172    #[allow(unsafe_code)]
1173    #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))]
1174    unsafe impl<T: StrongHandle> Send for Weak<T> {}
1175    #[allow(unsafe_code)]
1176    #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))]
1177    unsafe impl<T: StrongHandle> Sync for Weak<T> {}
1178}
1179
1180pub use weak_handle::*;
1181
1182/// This trait provides the necessary functionality for allowing creating strongly-referenced
1183/// clones and conversion into a weak pointer for a Global slint component.
1184///
1185/// This trait is implemented by the [generated component](index.html#generated-components)
1186/// Adds the specified function to an internal queue, notifies the event loop to wake up.
1187/// Once woken up, any queued up functors will be invoked.
1188///
1189/// This function is thread-safe and can be called from any thread, including the one
1190/// running the event loop. The provided functors will only be invoked from the thread
1191/// that started the event loop.
1192///
1193/// You can use this to set properties or use any other Slint APIs from other threads,
1194/// by collecting the code in a functor and queuing it up for invocation within the event loop.
1195///
1196/// If you want to capture non-Send types to run in the next event loop iteration,
1197/// you can use the `slint::spawn_local` function instead.
1198///
1199/// See also [`Weak::upgrade_in_event_loop`].
1200///
1201/// # Example
1202/// ```rust
1203/// slint::slint! { export component MyApp inherits Window { in property <int> foo; /* ... */ } }
1204/// # i_slint_backend_testing::init_no_event_loop();
1205/// let handle = MyApp::new().unwrap();
1206/// let handle_weak = handle.as_weak();
1207/// # return; // don't run the event loop in examples
1208/// let thread = std::thread::spawn(move || {
1209///     // ... Do some computation in the thread
1210///     let foo = 42;
1211///      // now forward the data to the main thread using invoke_from_event_loop
1212///     let handle_copy = handle_weak.clone();
1213///     slint::invoke_from_event_loop(move || handle_copy.unwrap().set_foo(foo));
1214/// });
1215/// handle.run().unwrap();
1216/// ```
1217pub fn invoke_from_event_loop(func: impl FnOnce() + Send + 'static) -> Result<(), EventLoopError> {
1218    crate::platform::with_event_loop_proxy(|proxy| {
1219        proxy
1220            .ok_or(EventLoopError::NoEventLoopProvider)?
1221            .invoke_from_event_loop(alloc::boxed::Box::new(func))
1222    })
1223}
1224
1225/// Schedules the main event loop for termination. This function is meant
1226/// to be called from callbacks triggered by the UI. After calling the function,
1227/// it will return immediately and once control is passed back to the event loop,
1228/// the initial call to `slint::run_event_loop()` will return.
1229///
1230/// This function can be called from any thread
1231///
1232/// Any previously queued events may or may not be processed before the loop terminates.
1233/// This is platform dependent behaviour.
1234pub fn quit_event_loop() -> Result<(), EventLoopError> {
1235    crate::platform::with_event_loop_proxy(|proxy| {
1236        proxy.ok_or(EventLoopError::NoEventLoopProvider)?.quit_event_loop()
1237    })
1238}
1239
1240#[derive(Debug, Clone, Eq, PartialEq)]
1241#[non_exhaustive]
1242/// Error returned from the [`invoke_from_event_loop()`] and [`quit_event_loop()`] function
1243pub enum EventLoopError {
1244    /// The event could not be sent because the event loop was terminated already
1245    EventLoopTerminated,
1246    /// The event could not be sent because the Slint platform abstraction was not yet initialized,
1247    /// or the platform does not support event loop.
1248    NoEventLoopProvider,
1249}
1250
1251impl core::fmt::Display for EventLoopError {
1252    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1253        match self {
1254            EventLoopError::EventLoopTerminated => {
1255                f.write_str("The event loop was already terminated")
1256            }
1257            EventLoopError::NoEventLoopProvider => {
1258                f.write_str("The Slint platform does not provide an event loop")
1259            }
1260        }
1261    }
1262}
1263
1264#[cfg(feature = "std")]
1265impl std::error::Error for EventLoopError {}
1266
1267/// The platform encountered a fatal error.
1268///
1269/// This error typically indicates an issue with initialization or connecting to the windowing system.
1270///
1271/// This can be constructed from a `String`:
1272/// ```rust
1273/// use slint::platform::PlatformError;
1274/// PlatformError::from(format!("Could not load resource {}", 1234));
1275/// ```
1276#[non_exhaustive]
1277pub enum PlatformError {
1278    /// No default platform was selected, or no platform could be initialized.
1279    ///
1280    /// If you encounter this error, make sure to either selected trough the `backend-*` cargo features flags,
1281    /// or call [`platform::set_platform()`](crate::platform::set_platform)
1282    /// before running the event loop
1283    NoPlatform,
1284    /// The Slint Platform does not provide an event loop.
1285    ///
1286    /// The [`Platform::run_event_loop`](crate::platform::Platform::run_event_loop)
1287    /// is not implemented for the current platform.
1288    NoEventLoopProvider,
1289
1290    /// There is already a platform set from another thread.
1291    SetPlatformError(crate::platform::SetPlatformError),
1292
1293    /// The operation is not supported by the current platform.
1294    Unsupported,
1295
1296    /// Another platform-specific error occurred
1297    Other(String),
1298    /// Another platform-specific error occurred.
1299    #[cfg(feature = "std")]
1300    OtherError(Box<dyn std::error::Error + Send + Sync>),
1301}
1302
1303#[cfg(target_arch = "wasm32")]
1304impl From<PlatformError> for wasm_bindgen::JsValue {
1305    fn from(err: PlatformError) -> wasm_bindgen::JsValue {
1306        wasm_bindgen::JsError::from(err).into()
1307    }
1308}
1309
1310impl core::fmt::Debug for PlatformError {
1311    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1312        core::fmt::Display::fmt(self, f)
1313    }
1314}
1315
1316impl core::fmt::Display for PlatformError {
1317    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1318        match self {
1319            PlatformError::NoPlatform => f.write_str(
1320                "No default Slint platform was selected, and no Slint platform was initialized",
1321            ),
1322            PlatformError::NoEventLoopProvider => {
1323                f.write_str("The Slint platform does not provide an event loop")
1324            }
1325            PlatformError::SetPlatformError(_) => {
1326                f.write_str("The Slint platform was initialized in another thread")
1327            }
1328            PlatformError::Unsupported => {
1329                f.write_str("The operation is not supported by the current platform")
1330            }
1331            PlatformError::Other(str) => f.write_str(str),
1332            #[cfg(feature = "std")]
1333            PlatformError::OtherError(error) => error.fmt(f),
1334        }
1335    }
1336}
1337
1338impl From<String> for PlatformError {
1339    fn from(value: String) -> Self {
1340        Self::Other(value)
1341    }
1342}
1343impl From<&str> for PlatformError {
1344    fn from(value: &str) -> Self {
1345        Self::Other(value.into())
1346    }
1347}
1348
1349#[cfg(feature = "std")]
1350impl From<Box<dyn std::error::Error + Send + Sync>> for PlatformError {
1351    fn from(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
1352        Self::OtherError(error)
1353    }
1354}
1355
1356#[cfg(feature = "std")]
1357impl std::error::Error for PlatformError {
1358    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1359        match self {
1360            PlatformError::OtherError(err) => Some(err.as_ref()),
1361            _ => None,
1362        }
1363    }
1364}
1365
1366#[test]
1367#[cfg(feature = "std")]
1368fn error_is_send() {
1369    let _: Box<dyn std::error::Error + Send + Sync + 'static> = PlatformError::NoPlatform.into();
1370}
1371
1372/// Sets the application id for use on Wayland or X11 with [xdg](https://specifications.freedesktop.org/desktop-entry-spec/latest/)
1373/// compliant window managers. This must be set before the window is shown, and has only an effect on Wayland or X11.
1374pub fn set_xdg_app_id(app_id: impl Into<SharedString>) -> Result<(), PlatformError> {
1375    crate::context::with_global_context(
1376        || Err(crate::platform::PlatformError::NoPlatform),
1377        |ctx| ctx.set_xdg_app_id(app_id.into()),
1378    )
1379}