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::{KeyEventType, MouseEvent};
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::sharedvector::SharedVector;
22pub use crate::{format, string::SharedString, string::ToSharedString};
23
24/// A position represented in the coordinate space of logical pixels. That is the space before applying
25/// a display device specific scale factor.
26#[derive(Debug, Default, Copy, Clone, PartialEq)]
27#[repr(C)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct LogicalPosition {
30    /// The x coordinate.
31    pub x: f32,
32    /// The y coordinate.
33    pub y: f32,
34}
35
36impl LogicalPosition {
37    /// Construct a new logical position from the given x and y coordinates, that are assumed to be
38    /// in the logical coordinate space.
39    pub const fn new(x: f32, y: f32) -> Self {
40        Self { x, y }
41    }
42
43    /// Convert a given physical position to a logical position by dividing the coordinates with the
44    /// specified scale factor.
45    pub fn from_physical(physical_pos: PhysicalPosition, scale_factor: f32) -> Self {
46        Self::new(physical_pos.x as f32 / scale_factor, physical_pos.y as f32 / scale_factor)
47    }
48
49    /// Convert this logical position to a physical position by multiplying the coordinates with the
50    /// specified scale factor.
51    pub fn to_physical(&self, scale_factor: f32) -> PhysicalPosition {
52        PhysicalPosition::from_logical(*self, scale_factor)
53    }
54
55    pub(crate) fn to_euclid(self) -> crate::lengths::LogicalPoint {
56        [self.x as _, self.y as _].into()
57    }
58    pub(crate) fn from_euclid(p: crate::lengths::LogicalPoint) -> Self {
59        Self::new(p.x as _, p.y as _)
60    }
61}
62
63/// A position represented in the coordinate space of physical device pixels. That is the space after applying
64/// a display device specific scale factor to pixels from the logical coordinate space.
65#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
67pub struct PhysicalPosition {
68    /// The x coordinate.
69    pub x: i32,
70    /// The y coordinate.
71    pub y: i32,
72}
73
74impl PhysicalPosition {
75    /// Construct a new physical position from the given x and y coordinates, that are assumed to be
76    /// in the physical coordinate space.
77    pub const fn new(x: i32, y: i32) -> Self {
78        Self { x, y }
79    }
80
81    /// Convert a given logical position to a physical position by multiplying the coordinates with the
82    /// specified scale factor.
83    pub fn from_logical(logical_pos: LogicalPosition, scale_factor: f32) -> Self {
84        Self::new((logical_pos.x * scale_factor) as i32, (logical_pos.y * scale_factor) as i32)
85    }
86
87    /// Convert this physical position to a logical position by dividing the coordinates with the
88    /// specified scale factor.
89    pub fn to_logical(&self, scale_factor: f32) -> LogicalPosition {
90        LogicalPosition::from_physical(*self, scale_factor)
91    }
92
93    #[cfg(feature = "ffi")]
94    pub(crate) fn to_euclid(&self) -> crate::graphics::euclid::default::Point2D<i32> {
95        [self.x, self.y].into()
96    }
97
98    #[cfg(feature = "ffi")]
99    pub(crate) fn from_euclid(p: crate::graphics::euclid::default::Point2D<i32>) -> Self {
100        Self::new(p.x as _, p.y as _)
101    }
102}
103
104/// The position of the window in either physical or logical pixels. This is used
105/// with [`Window::set_position`].
106#[derive(Clone, Debug, derive_more::From, PartialEq)]
107pub enum WindowPosition {
108    /// The position in physical pixels.
109    Physical(PhysicalPosition),
110    /// The position in logical pixels.
111    Logical(LogicalPosition),
112}
113
114impl WindowPosition {
115    /// Turn the `WindowPosition` into a `PhysicalPosition`.
116    pub fn to_physical(&self, scale_factor: f32) -> PhysicalPosition {
117        match self {
118            WindowPosition::Physical(pos) => *pos,
119            WindowPosition::Logical(pos) => pos.to_physical(scale_factor),
120        }
121    }
122}
123
124/// A size represented in the coordinate space of logical pixels. That is the space before applying
125/// a display device specific scale factor.
126#[repr(C)]
127#[derive(Debug, Default, Copy, Clone, PartialEq)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
129pub struct LogicalSize {
130    /// The width in logical pixels.
131    pub width: f32,
132    /// The height in logical.
133    pub height: f32,
134}
135
136impl LogicalSize {
137    /// Construct a new logical size from the given width and height values, that are assumed to be
138    /// in the logical coordinate space.
139    pub const fn new(width: f32, height: f32) -> Self {
140        Self { width, height }
141    }
142
143    /// Convert a given physical size to a logical size by dividing width and height by the
144    /// specified scale factor.
145    pub fn from_physical(physical_size: PhysicalSize, scale_factor: f32) -> Self {
146        Self::new(
147            physical_size.width as f32 / scale_factor,
148            physical_size.height as f32 / scale_factor,
149        )
150    }
151
152    /// Convert this logical size to a physical size by multiplying width and height with the
153    /// specified scale factor.
154    pub fn to_physical(&self, scale_factor: f32) -> PhysicalSize {
155        PhysicalSize::from_logical(*self, scale_factor)
156    }
157
158    pub(crate) fn to_euclid(self) -> crate::lengths::LogicalSize {
159        [self.width as _, self.height as _].into()
160    }
161
162    pub(crate) fn from_euclid(p: crate::lengths::LogicalSize) -> Self {
163        Self::new(p.width as _, p.height as _)
164    }
165}
166
167/// A size represented in the coordinate space of physical device pixels. That is the space after applying
168/// a display device specific scale factor to pixels from the logical coordinate space.
169#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
170#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
171pub struct PhysicalSize {
172    /// The width in physical pixels.
173    pub width: u32,
174    /// The height in physical pixels;
175    pub height: u32,
176}
177
178impl PhysicalSize {
179    /// Construct a new physical size from the width and height values, that are assumed to be
180    /// in the physical coordinate space.
181    pub const fn new(width: u32, height: u32) -> Self {
182        Self { width, height }
183    }
184
185    /// Convert a given logical size to a physical size by multiplying width and height with the
186    /// specified scale factor.
187    pub fn from_logical(logical_size: LogicalSize, scale_factor: f32) -> Self {
188        Self::new(
189            (logical_size.width * scale_factor) as u32,
190            (logical_size.height * scale_factor) as u32,
191        )
192    }
193
194    /// Convert this physical size to a logical size by dividing width and height by the
195    /// specified scale factor.
196    pub fn to_logical(&self, scale_factor: f32) -> LogicalSize {
197        LogicalSize::from_physical(*self, scale_factor)
198    }
199
200    #[cfg(feature = "ffi")]
201    pub(crate) fn to_euclid(&self) -> crate::graphics::euclid::default::Size2D<u32> {
202        [self.width, self.height].into()
203    }
204}
205
206/// The size of a window represented in either physical or logical pixels. This is used
207/// with [`Window::set_size`].
208#[derive(Clone, Debug, derive_more::From, PartialEq)]
209pub enum WindowSize {
210    /// The size in physical pixels.
211    Physical(PhysicalSize),
212    /// The size in logical screen pixels.
213    Logical(LogicalSize),
214}
215
216impl WindowSize {
217    /// Turn the `WindowSize` into a `PhysicalSize`.
218    pub fn to_physical(&self, scale_factor: f32) -> PhysicalSize {
219        match self {
220            WindowSize::Physical(size) => *size,
221            WindowSize::Logical(size) => size.to_physical(scale_factor),
222        }
223    }
224
225    /// Turn the `WindowSize` into a `LogicalSize`.
226    pub fn to_logical(&self, scale_factor: f32) -> LogicalSize {
227        match self {
228            WindowSize::Physical(size) => size.to_logical(scale_factor),
229            WindowSize::Logical(size) => *size,
230        }
231    }
232}
233
234#[test]
235fn logical_physical_pos() {
236    use crate::graphics::euclid::approxeq::ApproxEq;
237
238    let phys = PhysicalPosition::new(100, 50);
239    let logical = phys.to_logical(2.);
240    assert!(logical.x.approx_eq(&50.));
241    assert!(logical.y.approx_eq(&25.));
242
243    assert_eq!(logical.to_physical(2.), phys);
244}
245
246#[test]
247fn logical_physical_size() {
248    use crate::graphics::euclid::approxeq::ApproxEq;
249
250    let phys = PhysicalSize::new(100, 50);
251    let logical = phys.to_logical(2.);
252    assert!(logical.width.approx_eq(&50.));
253    assert!(logical.height.approx_eq(&25.));
254
255    assert_eq!(logical.to_physical(2.), phys);
256}
257
258#[i_slint_core_macros::slint_doc]
259/// This enum describes a low-level access to specific graphics APIs used
260/// by the renderer.
261#[derive(Clone)]
262#[non_exhaustive]
263pub enum GraphicsAPI<'a> {
264    /// The rendering is done using OpenGL.
265    NativeOpenGL {
266        /// Use this function pointer to obtain access to the OpenGL implementation - similar to `eglGetProcAddress`.
267        get_proc_address: &'a dyn Fn(&core::ffi::CStr) -> *const core::ffi::c_void,
268    },
269    /// The rendering is done on a HTML Canvas element using WebGL.
270    WebGL {
271        /// The DOM element id of the HTML Canvas element used for rendering.
272        canvas_element_id: &'a str,
273        /// The drawing context type used on the HTML Canvas element for rendering. This is the argument to the
274        /// `getContext` function on the HTML Canvas element.
275        context_type: &'a str,
276    },
277    /// The rendering is based on WGPU 27.x. Use the provided fields to submit commits to the provided
278    /// WGPU command queue.
279    ///
280    /// *Note*: This function is behind the [`unstable-wgpu-27` feature flag](slint:rust:slint/docs/cargo_features/#backends)
281    ///         and may be removed or changed in future minor releases, as new major WGPU releases become available.
282    ///
283    /// See also the [`slint::wgpu_27`](slint:rust:slint/wgpu_27) module.
284    #[cfg(feature = "unstable-wgpu-27")]
285    #[non_exhaustive]
286    WGPU27 {
287        /// The WGPU instance used for rendering.
288        instance: wgpu_27::Instance,
289        /// The WGPU device used for rendering.
290        device: wgpu_27::Device,
291        /// The WGPU queue for used for command submission.
292        queue: wgpu_27::Queue,
293    },
294    /// The rendering is based on WGPU 28.x. Use the provided fields to submit commits to the provided
295    /// WGPU command queue.
296    ///
297    /// *Note*: This function is behind the [`unstable-wgpu-28` feature flag](slint:rust:slint/docs/cargo_features/#backends)
298    ///         and may be removed or changed in future minor releases, as new major WGPU releases become available.
299    ///
300    /// See also the [`slint::wgpu_28`](slint:rust:slint/wgpu_28) module.
301    #[cfg(feature = "unstable-wgpu-28")]
302    #[non_exhaustive]
303    WGPU28 {
304        /// The WGPU instance used for rendering.
305        instance: wgpu_28::Instance,
306        /// The WGPU device used for rendering.
307        device: wgpu_28::Device,
308        /// The WGPU queue for used for command submission.
309        queue: wgpu_28::Queue,
310    },
311}
312
313impl core::fmt::Debug for GraphicsAPI<'_> {
314    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
315        match self {
316            GraphicsAPI::NativeOpenGL { .. } => write!(f, "GraphicsAPI::NativeOpenGL"),
317            GraphicsAPI::WebGL { context_type, .. } => {
318                write!(f, "GraphicsAPI::WebGL(context_type = {context_type})")
319            }
320            #[cfg(feature = "unstable-wgpu-27")]
321            GraphicsAPI::WGPU27 { .. } => write!(f, "GraphicsAPI::WGPU27"),
322            #[cfg(feature = "unstable-wgpu-28")]
323            GraphicsAPI::WGPU28 { .. } => write!(f, "GraphicsAPI::WGPU28"),
324        }
325    }
326}
327
328/// This enum describes the different rendering states, that will be provided
329/// to the parameter of the callback for `set_rendering_notifier` on the `slint::Window`.
330///
331/// When OpenGL is used for rendering, the context will be current.
332/// It's safe to call OpenGL functions, but it is crucial that the state of the context is
333/// preserved. So make sure to save and restore state such as `TEXTURE_BINDING_2D` or
334/// `ARRAY_BUFFER_BINDING` perfectly.
335#[derive(Debug, Clone)]
336#[repr(u8)]
337#[non_exhaustive]
338pub enum RenderingState {
339    /// The window has been created and the graphics adapter/context initialized.
340    RenderingSetup,
341    /// The scene of items is about to be rendered.
342    BeforeRendering,
343    /// The scene of items was rendered, but the back buffer was not sent for display presentation
344    /// yet (for example GL swap buffers).
345    AfterRendering,
346    /// The window will be destroyed and/or graphics resources need to be released due to other
347    /// constraints.
348    RenderingTeardown,
349}
350
351/// Internal trait that's used to map rendering state callbacks to either a Rust-API provided
352/// impl FnMut or a struct that invokes a C callback and implements Drop to release the closure
353/// on the C++ side.
354#[doc(hidden)]
355pub trait RenderingNotifier {
356    /// Called to notify that rendering has reached a certain state.
357    fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI);
358}
359
360impl<F: FnMut(RenderingState, &GraphicsAPI)> RenderingNotifier for F {
361    fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI) {
362        self(state, graphics_api)
363    }
364}
365
366/// This enum describes the different error scenarios that may occur when the application
367/// registers a rendering notifier on a `slint::Window`.
368#[derive(Debug, Clone)]
369#[repr(u8)]
370#[non_exhaustive]
371pub enum SetRenderingNotifierError {
372    /// The rendering backend does not support rendering notifiers.
373    Unsupported,
374    /// There is already a rendering notifier set, multiple notifiers are not supported.
375    AlreadySet,
376}
377
378impl core::fmt::Display for SetRenderingNotifierError {
379    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
380        match self {
381            Self::Unsupported => {
382                f.write_str("The rendering backend does not support rendering notifiers.")
383            }
384            Self::AlreadySet => f.write_str(
385                "There is already a rendering notifier set, multiple notifiers are not supported.",
386            ),
387        }
388    }
389}
390
391#[cfg(feature = "std")]
392impl std::error::Error for SetRenderingNotifierError {}
393
394#[cfg(feature = "raw-window-handle-06")]
395#[derive(Clone)]
396enum WindowHandleInner {
397    HandleByAdapter(alloc::rc::Rc<dyn WindowAdapter>),
398    #[cfg(feature = "std")]
399    HandleByRcRWH {
400        window_handle_provider: std::sync::Arc<dyn raw_window_handle_06::HasWindowHandle>,
401        display_handle_provider: std::sync::Arc<dyn raw_window_handle_06::HasDisplayHandle>,
402    },
403}
404
405/// This struct represents a persistent handle to a window and implements the
406/// [`raw_window_handle_06::HasWindowHandle`] and [`raw_window_handle_06::HasDisplayHandle`]
407/// traits for accessing exposing raw window and display handles.
408/// Obtain an instance of this by calling [`Window::window_handle()`].
409#[cfg(feature = "raw-window-handle-06")]
410#[derive(Clone)]
411pub struct WindowHandle {
412    inner: WindowHandleInner,
413}
414
415#[cfg(feature = "raw-window-handle-06")]
416impl raw_window_handle_06::HasWindowHandle for WindowHandle {
417    fn window_handle(
418        &self,
419    ) -> Result<raw_window_handle_06::WindowHandle<'_>, raw_window_handle_06::HandleError> {
420        match &self.inner {
421            WindowHandleInner::HandleByAdapter(adapter) => adapter.window_handle_06(),
422            #[cfg(feature = "std")]
423            WindowHandleInner::HandleByRcRWH { window_handle_provider, .. } => {
424                window_handle_provider.window_handle()
425            }
426        }
427    }
428}
429
430#[cfg(feature = "raw-window-handle-06")]
431impl raw_window_handle_06::HasDisplayHandle for WindowHandle {
432    fn display_handle(
433        &self,
434    ) -> Result<raw_window_handle_06::DisplayHandle<'_>, raw_window_handle_06::HandleError> {
435        match &self.inner {
436            WindowHandleInner::HandleByAdapter(adapter) => adapter.display_handle_06(),
437            #[cfg(feature = "std")]
438            WindowHandleInner::HandleByRcRWH { display_handle_provider, .. } => {
439                display_handle_provider.display_handle()
440            }
441        }
442    }
443}
444
445/// This type represents a window towards the windowing system, that's used to render the
446/// scene of a component. It provides API to control windowing system specific aspects such
447/// as the position on the screen.
448#[repr(transparent)]
449pub struct Window(pub(crate) WindowInner);
450
451/// This enum describes whether a Window is allowed to be hidden when the user tries to close the window.
452/// It is the return type of the callback provided to [Window::on_close_requested].
453#[derive(Copy, Clone, Debug, PartialEq, Default)]
454#[repr(u8)]
455pub enum CloseRequestResponse {
456    /// The Window will be hidden (default action)
457    #[default]
458    HideWindow = 0,
459    /// The close request is rejected and the window will be kept shown.
460    KeepWindowShown = 1,
461}
462
463impl Window {
464    /// Create a new window from a window adapter
465    ///
466    /// You only need to create the window yourself when you create a [`WindowAdapter`] from
467    /// [`Platform::create_window_adapter`](crate::platform::Platform::create_window_adapter)
468    ///
469    /// Since the window adapter must own the Window, this function is meant to be used with
470    /// [`Rc::new_cyclic`](alloc::rc::Rc::new_cyclic)
471    ///
472    /// # Example
473    /// ```rust
474    /// use std::rc::Rc;
475    /// use slint::platform::{WindowAdapter, Renderer};
476    /// use slint::{Window, PhysicalSize};
477    /// struct MyWindowAdapter {
478    ///     window: Window,
479    ///     //...
480    /// }
481    /// impl WindowAdapter for MyWindowAdapter {
482    ///    fn window(&self) -> &Window { &self.window }
483    ///    fn size(&self) -> PhysicalSize { unimplemented!() }
484    ///    fn renderer(&self) -> &dyn Renderer { unimplemented!() }
485    /// }
486    ///
487    /// fn create_window_adapter() -> Rc<dyn WindowAdapter> {
488    ///    Rc::<MyWindowAdapter>::new_cyclic(|weak| {
489    ///        MyWindowAdapter {
490    ///           window: Window::new(weak.clone()),
491    ///           //...
492    ///        }
493    ///    })
494    /// }
495    /// ```
496    pub fn new(window_adapter_weak: alloc::rc::Weak<dyn WindowAdapter>) -> Self {
497        Self(WindowInner::new(window_adapter_weak))
498    }
499
500    /// Shows the window on the screen. An additional strong reference on the
501    /// associated component is maintained while the window is visible.
502    ///
503    /// Call [`Self::hide()`] to make the window invisible again, and drop the additional
504    /// strong reference.
505    pub fn show(&self) -> Result<(), PlatformError> {
506        self.0.show()
507    }
508
509    /// Hides the window, so that it is not visible anymore. The additional strong
510    /// reference on the associated component, that was created when [`Self::show()`] was called, is
511    /// dropped.
512    pub fn hide(&self) -> Result<(), PlatformError> {
513        self.0.hide()
514    }
515
516    /// This function allows registering a callback that's invoked during the different phases of
517    /// rendering. This allows custom rendering on top or below of the scene.
518    pub fn set_rendering_notifier(
519        &self,
520        callback: impl FnMut(RenderingState, &GraphicsAPI) + 'static,
521    ) -> Result<(), SetRenderingNotifierError> {
522        self.0.window_adapter().renderer().set_rendering_notifier(Box::new(callback))
523    }
524
525    /// This function allows registering a callback that's invoked when the user tries to close a window.
526    /// The callback has to return a [CloseRequestResponse].
527    pub fn on_close_requested(&self, callback: impl FnMut() -> CloseRequestResponse + 'static) {
528        self.0.on_close_requested(callback);
529    }
530
531    /// This function issues a request to the windowing system to redraw the contents of the window.
532    pub fn request_redraw(&self) {
533        self.0.window_adapter().request_redraw()
534    }
535
536    /// This function returns the scale factor that allows converting between logical and
537    /// physical pixels.
538    pub fn scale_factor(&self) -> f32 {
539        self.0.scale_factor()
540    }
541
542    /// Returns the position of the window on the screen, in physical screen coordinates and including
543    /// a window frame (if present).
544    pub fn position(&self) -> PhysicalPosition {
545        self.0.window_adapter().position().unwrap_or_default()
546    }
547
548    /// Sets the position of the window on the screen, in physical screen coordinates and including
549    /// a window frame (if present).
550    /// Note that on some windowing systems, such as Wayland, this functionality is not available.
551    pub fn set_position(&self, position: impl Into<WindowPosition>) {
552        let position = position.into();
553        self.0.window_adapter().set_position(position)
554    }
555
556    /// Returns the size of the window on the screen, in physical screen coordinates and excluding
557    /// a window frame (if present).
558    pub fn size(&self) -> PhysicalSize {
559        self.0.window_adapter().size()
560    }
561
562    /// Resizes the window to the specified size on the screen, in physical pixels and excluding
563    /// a window frame (if present).
564    pub fn set_size(&self, size: impl Into<WindowSize>) {
565        let size = size.into();
566        crate::window::WindowAdapter::set_size(&*self.0.window_adapter(), size);
567    }
568
569    /// Returns if the window is currently fullscreen
570    pub fn is_fullscreen(&self) -> bool {
571        self.0.is_fullscreen()
572    }
573
574    /// Set or unset the window to display fullscreen.
575    pub fn set_fullscreen(&self, fullscreen: bool) {
576        self.0.set_fullscreen(fullscreen);
577    }
578
579    /// Returns if the window is currently maximized
580    pub fn is_maximized(&self) -> bool {
581        self.0.is_maximized()
582    }
583
584    /// Maximize or unmaximize the window.
585    pub fn set_maximized(&self, maximized: bool) {
586        self.0.set_maximized(maximized);
587    }
588
589    /// Returns if the window is currently minimized
590    pub fn is_minimized(&self) -> bool {
591        self.0.is_minimized()
592    }
593
594    /// Minimize or unminimze the window.
595    pub fn set_minimized(&self, minimized: bool) {
596        self.0.set_minimized(minimized);
597    }
598
599    /// The area of the window covered by the software keyboard is changing (animated).
600    #[doc(hidden)]
601    pub fn set_virtual_keyboard(
602        &self,
603        origin: LogicalPosition,
604        size: LogicalSize,
605        _: crate::InternalToken,
606    ) {
607        self.0.set_window_item_virtual_keyboard(origin.to_euclid(), size.to_euclid());
608    }
609
610    #[doc(hidden)]
611    pub fn virtual_keyboard(
612        &self,
613        _: crate::InternalToken,
614    ) -> Option<(LogicalPosition, LogicalSize)> {
615        self.0.window_item_virtual_keyboard().map(|(origin, size)| {
616            (LogicalPosition::from_euclid(origin), LogicalSize::from_euclid(size))
617        })
618    }
619
620    /// Dispatch a window event to the scene.
621    ///
622    /// Use this when you're implementing your own backend and want to forward user input events.
623    ///
624    /// Any position fields in the event must be in the logical pixel coordinate system relative to
625    /// the top left corner of the window.
626    ///
627    /// This function panics if there is an error processing the event.
628    /// Use [`Self::try_dispatch_event()`] to handle the error.
629    #[track_caller]
630    pub fn dispatch_event(&self, event: crate::platform::WindowEvent) {
631        self.try_dispatch_event(event).unwrap()
632    }
633
634    /// Dispatch a window event to the scene.
635    ///
636    /// Use this when you're implementing your own backend and want to forward user input events.
637    ///
638    /// Any position fields in the event must be in the logical pixel coordinate system relative to
639    /// the top left corner of the window.
640    pub fn try_dispatch_event(
641        &self,
642        event: crate::platform::WindowEvent,
643    ) -> Result<(), PlatformError> {
644        match event {
645            crate::platform::WindowEvent::PointerPressed { position, button } => {
646                self.0.process_mouse_input(MouseEvent::Pressed {
647                    position: position.to_euclid().cast(),
648                    button,
649                    click_count: 0,
650                    is_touch: false,
651                });
652            }
653            crate::platform::WindowEvent::PointerReleased { position, button } => {
654                self.0.process_mouse_input(MouseEvent::Released {
655                    position: position.to_euclid().cast(),
656                    button,
657                    click_count: 0,
658                    is_touch: false,
659                });
660            }
661            crate::platform::WindowEvent::PointerMoved { position } => {
662                self.0.process_mouse_input(MouseEvent::Moved {
663                    position: position.to_euclid().cast(),
664                    is_touch: false,
665                });
666            }
667            crate::platform::WindowEvent::PointerScrolled { position, delta_x, delta_y } => {
668                self.0.process_mouse_input(MouseEvent::Wheel {
669                    position: position.to_euclid().cast(),
670                    delta_x: delta_x as _,
671                    delta_y: delta_y as _,
672                });
673            }
674            crate::platform::WindowEvent::PointerExited => {
675                self.0.process_mouse_input(MouseEvent::Exit)
676            }
677
678            crate::platform::WindowEvent::KeyPressed { text } => {
679                self.0.process_key_input(crate::input::KeyEvent {
680                    text,
681                    repeat: false,
682                    event_type: KeyEventType::KeyPressed,
683                    ..Default::default()
684                });
685            }
686            crate::platform::WindowEvent::KeyPressRepeated { text } => {
687                self.0.process_key_input(crate::input::KeyEvent {
688                    text,
689                    repeat: true,
690                    event_type: KeyEventType::KeyPressed,
691                    ..Default::default()
692                });
693            }
694            crate::platform::WindowEvent::KeyReleased { text } => {
695                self.0.process_key_input(crate::input::KeyEvent {
696                    text,
697                    event_type: KeyEventType::KeyReleased,
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
807pub trait Global<'a, Component> {
808    /// Returns a reference that's tied to the life time of the provided component.
809    fn get(component: &'a Component) -> Self;
810}
811
812/// This trait describes the common public API of a strongly referenced Slint component.
813/// It allows creating strongly-referenced clones, a conversion into/ a weak pointer as well
814/// as other convenience functions.
815///
816/// This trait is implemented by the [generated component](index.html#generated-components)
817pub trait ComponentHandle {
818    /// The internal Inner type for `Weak<Self>::inner`.
819    #[doc(hidden)]
820    type WeakInner: Clone + Default;
821    /// Returns a new weak pointer.
822    fn as_weak(&self) -> Weak<Self>
823    where
824        Self: Sized;
825
826    /// Returns a clone of this handle that's a strong reference.
827    #[must_use]
828    fn clone_strong(&self) -> Self;
829
830    /// Internal function used when upgrading a weak reference to a strong one.
831    #[doc(hidden)]
832    fn upgrade_from_weak_inner(_: &Self::WeakInner) -> Option<Self>
833    where
834        Self: Sized;
835
836    /// Convenience function for [`crate::Window::show()`](struct.Window.html#method.show).
837    /// This shows the window on the screen and maintains an extra strong reference while
838    /// the window is visible. To react to events from the windowing system, such as draw
839    /// requests or mouse/touch input, it is still necessary to spin the event loop,
840    /// using [`crate::run_event_loop`](fn.run_event_loop.html).
841    fn show(&self) -> Result<(), PlatformError>;
842
843    /// Convenience function for [`crate::Window::hide()`](struct.Window.html#method.hide).
844    /// Hides the window, so that it is not visible anymore. The additional strong reference
845    /// on the associated component, that was created when show() was called, is dropped.
846    fn hide(&self) -> Result<(), PlatformError>;
847
848    /// Returns the Window associated with this component. The window API can be used
849    /// to control different aspects of the integration into the windowing system,
850    /// such as the position on the screen.
851    fn window(&self) -> &Window;
852
853    /// This is a convenience function that first calls [`Self::show`], followed by [`crate::run_event_loop()`](fn.run_event_loop.html)
854    /// and [`Self::hide`].
855    fn run(&self) -> Result<(), PlatformError>;
856
857    /// This function provides access to instances of global singletons exported in `.slint`.
858    /// See [`Global`] for an example how to export and access globals from `.slint` markup.
859    fn global<'a, T: Global<'a, Self>>(&'a self) -> T
860    where
861        Self: Sized;
862}
863
864mod weak_handle {
865
866    use super::*;
867
868    /// Struct that's used to hold weak references of a [Slint component](index.html#generated-components)
869    ///
870    /// In order to create a Weak, you should use [`ComponentHandle::as_weak`].
871    ///
872    /// Strong references should not be captured by the functions given to a lambda,
873    /// as this would produce a reference loop and leak the component.
874    /// Instead, the callback function should capture a weak component.
875    ///
876    /// The Weak component also implement `Send` and can be send to another thread.
877    /// but the upgrade function will only return a valid component from the same thread
878    /// as the one it has been created from.
879    /// This is useful to use with [`invoke_from_event_loop()`] or [`Self::upgrade_in_event_loop()`].
880    pub struct Weak<T: ComponentHandle> {
881        inner: T::WeakInner,
882        #[cfg(feature = "std")]
883        thread: std::thread::ThreadId,
884    }
885
886    impl<T: ComponentHandle> Default for Weak<T> {
887        fn default() -> Self {
888            Self {
889                inner: T::WeakInner::default(),
890                #[cfg(feature = "std")]
891                thread: std::thread::current().id(),
892            }
893        }
894    }
895
896    impl<T: ComponentHandle> Clone for Weak<T> {
897        fn clone(&self) -> Self {
898            Self {
899                inner: self.inner.clone(),
900                #[cfg(feature = "std")]
901                thread: self.thread,
902            }
903        }
904    }
905
906    impl<T: ComponentHandle> Weak<T> {
907        #[doc(hidden)]
908        pub fn new(inner: T::WeakInner) -> Self {
909            Self {
910                inner,
911                #[cfg(feature = "std")]
912                thread: std::thread::current().id(),
913            }
914        }
915
916        /// Returns a new strongly referenced component if some other instance still
917        /// holds a strong reference. Otherwise, returns None.
918        ///
919        /// This also returns None if the current thread is not the thread that created
920        /// the component
921        pub fn upgrade(&self) -> Option<T>
922        where
923            T: ComponentHandle,
924        {
925            #[cfg(feature = "std")]
926            if std::thread::current().id() != self.thread {
927                return None;
928            }
929            T::upgrade_from_weak_inner(&self.inner)
930        }
931
932        /// Convenience function that returns a new strongly referenced component if
933        /// some other instance still holds a strong reference and the current thread
934        /// is the thread that created this component.
935        /// Otherwise, this function panics.
936        #[track_caller]
937        pub fn unwrap(&self) -> T {
938            #[cfg(feature = "std")]
939            if std::thread::current().id() != self.thread {
940                panic!(
941                    "Trying to upgrade a Weak from a different thread than the one it belongs to"
942                );
943            }
944            T::upgrade_from_weak_inner(&self.inner)
945                .expect("The Weak doesn't hold a valid component")
946        }
947
948        /// A helper function to allow creation on `component_factory::Component` from
949        /// a `ComponentHandle`
950        pub(crate) fn inner(&self) -> T::WeakInner {
951            self.inner.clone()
952        }
953
954        /// Convenience function that combines [`invoke_from_event_loop()`] with [`Self::upgrade()`]
955        ///
956        /// The given functor will be added to an internal queue and will wake the event loop.
957        /// On the next iteration of the event loop, the functor will be executed with a `T` as an argument.
958        ///
959        /// If the component was dropped because there are no more strong reference to the component,
960        /// the functor will not be called.
961        ///
962        /// # Example
963        /// ```rust
964        /// # i_slint_backend_testing::init_no_event_loop();
965        /// slint::slint! { export component MyApp inherits Window { in property <int> foo; /* ... */ } }
966        /// let handle = MyApp::new().unwrap();
967        /// let handle_weak = handle.as_weak();
968        /// let thread = std::thread::spawn(move || {
969        ///     // ... Do some computation in the thread
970        ///     let foo = 42;
971        ///     # assert!(handle_weak.upgrade().is_none()); // note that upgrade fails in a thread
972        ///     # return; // don't upgrade_in_event_loop in our examples
973        ///     // now forward the data to the main thread using upgrade_in_event_loop
974        ///     handle_weak.upgrade_in_event_loop(move |handle| handle.set_foo(foo));
975        /// });
976        /// # thread.join().unwrap(); return; // don't run the event loop in examples
977        /// handle.run().unwrap();
978        /// ```
979        #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))]
980        pub fn upgrade_in_event_loop(
981            &self,
982            func: impl FnOnce(T) + Send + 'static,
983        ) -> Result<(), EventLoopError>
984        where
985            T: 'static,
986        {
987            let weak_handle = self.clone();
988            super::invoke_from_event_loop(move || {
989                if let Some(h) = weak_handle.upgrade() {
990                    func(h);
991                }
992            })
993        }
994    }
995
996    // Safety: we make sure in upgrade that the thread is the proper one,
997    // and the VWeak only use atomic pointer so it is safe to clone and drop in another thread
998    #[allow(unsafe_code)]
999    #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))]
1000    unsafe impl<T: ComponentHandle> Send for Weak<T> {}
1001    #[allow(unsafe_code)]
1002    #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))]
1003    unsafe impl<T: ComponentHandle> Sync for Weak<T> {}
1004}
1005
1006pub use weak_handle::*;
1007
1008/// Adds the specified function to an internal queue, notifies the event loop to wake up.
1009/// Once woken up, any queued up functors will be invoked.
1010///
1011/// This function is thread-safe and can be called from any thread, including the one
1012/// running the event loop. The provided functors will only be invoked from the thread
1013/// that started the event loop.
1014///
1015/// You can use this to set properties or use any other Slint APIs from other threads,
1016/// by collecting the code in a functor and queuing it up for invocation within the event loop.
1017///
1018/// If you want to capture non-Send types to run in the next event loop iteration,
1019/// you can use the `slint::spawn_local` function instead.
1020///
1021/// See also [`Weak::upgrade_in_event_loop`].
1022///
1023/// # Example
1024/// ```rust
1025/// slint::slint! { export component MyApp inherits Window { in property <int> foo; /* ... */ } }
1026/// # i_slint_backend_testing::init_no_event_loop();
1027/// let handle = MyApp::new().unwrap();
1028/// let handle_weak = handle.as_weak();
1029/// # return; // don't run the event loop in examples
1030/// let thread = std::thread::spawn(move || {
1031///     // ... Do some computation in the thread
1032///     let foo = 42;
1033///      // now forward the data to the main thread using invoke_from_event_loop
1034///     let handle_copy = handle_weak.clone();
1035///     slint::invoke_from_event_loop(move || handle_copy.unwrap().set_foo(foo));
1036/// });
1037/// handle.run().unwrap();
1038/// ```
1039pub fn invoke_from_event_loop(func: impl FnOnce() + Send + 'static) -> Result<(), EventLoopError> {
1040    crate::platform::with_event_loop_proxy(|proxy| {
1041        proxy
1042            .ok_or(EventLoopError::NoEventLoopProvider)?
1043            .invoke_from_event_loop(alloc::boxed::Box::new(func))
1044    })
1045}
1046
1047/// Schedules the main event loop for termination. This function is meant
1048/// to be called from callbacks triggered by the UI. After calling the function,
1049/// it will return immediately and once control is passed back to the event loop,
1050/// the initial call to `slint::run_event_loop()` will return.
1051///
1052/// This function can be called from any thread
1053///
1054/// Any previously queued events may or may not be processed before the loop terminates.
1055/// This is platform dependent behaviour.
1056pub fn quit_event_loop() -> Result<(), EventLoopError> {
1057    crate::platform::with_event_loop_proxy(|proxy| {
1058        proxy.ok_or(EventLoopError::NoEventLoopProvider)?.quit_event_loop()
1059    })
1060}
1061
1062#[derive(Debug, Clone, Eq, PartialEq)]
1063#[non_exhaustive]
1064/// Error returned from the [`invoke_from_event_loop()`] and [`quit_event_loop()`] function
1065pub enum EventLoopError {
1066    /// The event could not be sent because the event loop was terminated already
1067    EventLoopTerminated,
1068    /// The event could not be sent because the Slint platform abstraction was not yet initialized,
1069    /// or the platform does not support event loop.
1070    NoEventLoopProvider,
1071}
1072
1073impl core::fmt::Display for EventLoopError {
1074    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1075        match self {
1076            EventLoopError::EventLoopTerminated => {
1077                f.write_str("The event loop was already terminated")
1078            }
1079            EventLoopError::NoEventLoopProvider => {
1080                f.write_str("The Slint platform does not provide an event loop")
1081            }
1082        }
1083    }
1084}
1085
1086#[cfg(feature = "std")]
1087impl std::error::Error for EventLoopError {}
1088
1089/// The platform encountered a fatal error.
1090///
1091/// This error typically indicates an issue with initialization or connecting to the windowing system.
1092///
1093/// This can be constructed from a `String`:
1094/// ```rust
1095/// use slint::platform::PlatformError;
1096/// PlatformError::from(format!("Could not load resource {}", 1234));
1097/// ```
1098#[non_exhaustive]
1099pub enum PlatformError {
1100    /// No default platform was selected, or no platform could be initialized.
1101    ///
1102    /// If you encounter this error, make sure to either selected trough the `backend-*` cargo features flags,
1103    /// or call [`platform::set_platform()`](crate::platform::set_platform)
1104    /// before running the event loop
1105    NoPlatform,
1106    /// The Slint Platform does not provide an event loop.
1107    ///
1108    /// The [`Platform::run_event_loop`](crate::platform::Platform::run_event_loop)
1109    /// is not implemented for the current platform.
1110    NoEventLoopProvider,
1111
1112    /// There is already a platform set from another thread.
1113    SetPlatformError(crate::platform::SetPlatformError),
1114
1115    /// Another platform-specific error occurred
1116    Other(String),
1117    /// Another platform-specific error occurred.
1118    #[cfg(feature = "std")]
1119    OtherError(Box<dyn std::error::Error + Send + Sync>),
1120}
1121
1122#[cfg(target_arch = "wasm32")]
1123impl From<PlatformError> for wasm_bindgen::JsValue {
1124    fn from(err: PlatformError) -> wasm_bindgen::JsValue {
1125        wasm_bindgen::JsError::from(err).into()
1126    }
1127}
1128
1129impl core::fmt::Debug for PlatformError {
1130    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1131        core::fmt::Display::fmt(self, f)
1132    }
1133}
1134
1135impl core::fmt::Display for PlatformError {
1136    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1137        match self {
1138            PlatformError::NoPlatform => f.write_str(
1139                "No default Slint platform was selected, and no Slint platform was initialized",
1140            ),
1141            PlatformError::NoEventLoopProvider => {
1142                f.write_str("The Slint platform does not provide an event loop")
1143            }
1144            PlatformError::SetPlatformError(_) => {
1145                f.write_str("The Slint platform was initialized in another thread")
1146            }
1147            PlatformError::Other(str) => f.write_str(str),
1148            #[cfg(feature = "std")]
1149            PlatformError::OtherError(error) => error.fmt(f),
1150        }
1151    }
1152}
1153
1154impl From<String> for PlatformError {
1155    fn from(value: String) -> Self {
1156        Self::Other(value)
1157    }
1158}
1159impl From<&str> for PlatformError {
1160    fn from(value: &str) -> Self {
1161        Self::Other(value.into())
1162    }
1163}
1164
1165#[cfg(feature = "std")]
1166impl From<Box<dyn std::error::Error + Send + Sync>> for PlatformError {
1167    fn from(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
1168        Self::OtherError(error)
1169    }
1170}
1171
1172#[cfg(feature = "std")]
1173impl std::error::Error for PlatformError {
1174    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1175        match self {
1176            PlatformError::OtherError(err) => Some(err.as_ref()),
1177            _ => None,
1178        }
1179    }
1180}
1181
1182#[test]
1183#[cfg(feature = "std")]
1184fn error_is_send() {
1185    let _: Box<dyn std::error::Error + Send + Sync + 'static> = PlatformError::NoPlatform.into();
1186}
1187
1188/// Sets the application id for use on Wayland or X11 with [xdg](https://specifications.freedesktop.org/desktop-entry-spec/latest/)
1189/// compliant window managers. This must be set before the window is shown, and has only an effect on Wayland or X11.
1190pub fn set_xdg_app_id(app_id: impl Into<SharedString>) -> Result<(), PlatformError> {
1191    crate::context::with_global_context(
1192        || Err(crate::platform::PlatformError::NoPlatform),
1193        |ctx| ctx.set_xdg_app_id(app_id.into()),
1194    )
1195}