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

Provided by KDAB

Privacy Policy