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