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}