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