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