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