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