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// cSpell: ignore backtab
5
6#![warn(missing_docs)]
7//! Exposed Window API
8
9use crate::api::{
10 CloseRequestResponse, LogicalPosition, PhysicalPosition, PhysicalSize, PlatformError, Window,
11 WindowPosition, WindowSize,
12};
13use crate::input::{
14 key_codes, ClickState, FocusEvent, InternalKeyboardModifierState, KeyEvent, KeyEventType,
15 MouseEvent, MouseInputState, TextCursorBlinker,
16};
17use crate::item_tree::{ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeVTable, ItemTreeWeak, ItemWeak};
18use crate::items::{ColorScheme, InputType, ItemRef, MouseCursor, PopupClosePolicy};
19use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, SizeLengths};
20use crate::menus::MenuVTable;
21use crate::properties::{Property, PropertyTracker};
22use crate::renderer::Renderer;
23use crate::{Callback, Coord, SharedString};
24use alloc::boxed::Box;
25use alloc::rc::{Rc, Weak};
26use alloc::vec::Vec;
27use core::cell::{Cell, RefCell};
28use core::num::NonZeroU32;
29use core::pin::Pin;
30use euclid::num::Zero;
31use vtable::VRcMapped;
32
33pub mod popup;
34
35fn next_focus_item(item: ItemRc) -> ItemRc {
36 item.next_focus_item()
37}
38
39fn previous_focus_item(item: ItemRc) -> ItemRc {
40 item.previous_focus_item()
41}
42
43/// This trait represents the adaptation layer between the [`Window`] API and then
44/// windowing specific window representation, such as a Win32 `HWND` handle or a `wayland_surface_t`.
45///
46/// Implement this trait to establish the link between the two, and pass messages in both
47/// directions:
48///
49/// - When receiving messages from the windowing system about state changes, such as the window being resized,
50/// the user requested the window to be closed, input being received, etc. you need to create a
51/// [`WindowEvent`](crate::platform::WindowEvent) and send it to Slint via [`Window::try_dispatch_event()`].
52///
53/// - Slint sends requests to change visibility, position, size, etc. via functions such as [`Self::set_visible`],
54/// [`Self::set_size`], [`Self::set_position`], or [`Self::update_window_properties()`]. Re-implement these functions
55/// and delegate the requests to the windowing system.
56///
57/// If the implementation of this bi-directional message passing protocol is incomplete, the user may
58/// experience unexpected behavior, or the intention of the developer calling functions on the [`Window`]
59/// API may not be fulfilled.
60///
61/// Your implementation must hold a renderer, such as [`SoftwareRenderer`](crate::software_renderer::SoftwareRenderer).
62/// In the [`Self::renderer()`] function, you must return a reference to it.
63///
64/// It is also required to hold a [`Window`] and return a reference to it in your
65/// implementation of [`Self::window()`].
66///
67/// See also [`MinimalSoftwareWindow`](crate::software_renderer::MinimalSoftwareWindow)
68/// for a minimal implementation of this trait using the software renderer
69pub trait WindowAdapter {
70 /// Returns the window API.
71 fn window(&self) -> &Window;
72
73 /// Show the window if the argument is true, hide otherwise.
74 fn set_visible(&self, _visible: bool) -> Result<(), PlatformError> {
75 Ok(())
76 }
77
78 /// Returns the position of the window on the screen, in physical screen coordinates and including
79 /// a window frame (if present).
80 ///
81 /// The default implementation returns `None`
82 ///
83 /// Called from [`Window::position()`]
84 fn position(&self) -> Option<PhysicalPosition> {
85 None
86 }
87 /// Sets the position of the window on the screen, in physical screen coordinates and including
88 /// a window frame (if present).
89 ///
90 /// The default implementation does nothing
91 ///
92 /// Called from [`Window::set_position()`]
93 fn set_position(&self, _position: WindowPosition) {}
94
95 /// Request a new size for the window to the specified size on the screen, in physical or logical pixels
96 /// and excluding a window frame (if present).
97 ///
98 /// This is called from [`Window::set_size()`]
99 ///
100 /// The default implementation does nothing
101 ///
102 /// This function should sent the size to the Windowing system. If the window size actually changes, you
103 /// should dispatch a [`WindowEvent::Resized`](crate::platform::WindowEvent::Resized) using
104 /// [`Window::dispatch_event()`] to propagate the new size to the slint view
105 fn set_size(&self, _size: WindowSize) {}
106
107 /// Return the size of the Window on the screen
108 fn size(&self) -> PhysicalSize;
109
110 /// Issues a request to the windowing system to re-render the contents of the window.
111 ///
112 /// This request is typically asynchronous.
113 /// It is called when a property that was used during window rendering is marked as dirty.
114 ///
115 /// An implementation should repaint the window in a subsequent iteration of the event loop,
116 /// throttled to the screen refresh rate if possible.
117 /// It is important not to query any Slint properties to avoid introducing a dependency loop in the properties,
118 /// including the use of the render function, which itself queries properties.
119 ///
120 /// See also [`Window::request_redraw()`]
121 fn request_redraw(&self) {}
122
123 /// Return the renderer.
124 ///
125 /// The `Renderer` trait is an internal trait that you are not expected to implement.
126 /// In your implementation you should return a reference to an instance of one of the renderers provided by Slint.
127 ///
128 /// Currently, the only public struct that implement renderer is [`SoftwareRenderer`](crate::software_renderer::SoftwareRenderer).
129 fn renderer(&self) -> &dyn Renderer;
130
131 /// Re-implement this function to update the properties such as window title or layout constraints.
132 ///
133 /// This function is called before `set_visible(true)`, and will be called again when the properties
134 /// that were queried on the last call are changed. If you do not query any properties, it may not
135 /// be called again.
136 fn update_window_properties(&self, _properties: WindowProperties<'_>) {}
137
138 #[doc(hidden)]
139 fn internal(&self, _: crate::InternalToken) -> Option<&dyn WindowAdapterInternal> {
140 None
141 }
142
143 /// Re-implement this to support exposing raw window handles (version 0.6).
144 #[cfg(feature = "raw-window-handle-06")]
145 fn window_handle_06(
146 &self,
147 ) -> Result<raw_window_handle_06::WindowHandle<'_>, raw_window_handle_06::HandleError> {
148 Err(raw_window_handle_06::HandleError::NotSupported)
149 }
150
151 /// Re-implement this to support exposing raw display handles (version 0.6).
152 #[cfg(feature = "raw-window-handle-06")]
153 fn display_handle_06(
154 &self,
155 ) -> Result<raw_window_handle_06::DisplayHandle<'_>, raw_window_handle_06::HandleError> {
156 Err(raw_window_handle_06::HandleError::NotSupported)
157 }
158}
159
160/// Implementation details behind [`WindowAdapter`], but since this
161/// trait is not exported in the public API, it is not possible for the
162/// users to call or re-implement these functions.
163// TODO: add events for window receiving and loosing focus
164#[doc(hidden)]
165pub trait WindowAdapterInternal {
166 /// This function is called by the generated code when a component and therefore its tree of items are created.
167 fn register_item_tree(&self) {}
168
169 /// This function is called by the generated code when a component and therefore its tree of items are destroyed. The
170 /// implementation typically uses this to free the underlying graphics resources cached via [`crate::graphics::RenderingCache`].
171 fn unregister_item_tree(
172 &self,
173 _component: ItemTreeRef,
174 _items: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
175 ) {
176 }
177
178 /// Create a window for a popup.
179 ///
180 /// `geometry` is the location of the popup in the window coordinate
181 ///
182 /// If this function return None (the default implementation), then the
183 /// popup will be rendered within the window itself.
184 fn create_popup(&self, _geometry: LogicalRect) -> Option<Rc<dyn WindowAdapter>> {
185 None
186 }
187
188 /// Set the mouse cursor
189 // TODO: Make the enum public and make public
190 fn set_mouse_cursor(&self, _cursor: MouseCursor) {}
191
192 /// This method allow editable input field to communicate with the platform about input methods
193 fn input_method_request(&self, _: InputMethodRequest) {}
194
195 /// Return self as any so the backend can upcast
196 // TODO: consider using the as_any crate, or deriving the traint from Any to provide a better default
197 fn as_any(&self) -> &dyn core::any::Any {
198 &()
199 }
200
201 /// Handle focus change
202 // used for accessibility
203 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {}
204
205 /// returns the color scheme used
206 fn color_scheme(&self) -> ColorScheme {
207 ColorScheme::Unknown
208 }
209
210 /// Returns whether we can have a native menu bar
211 fn supports_native_menu_bar(&self) -> bool {
212 false
213 }
214
215 fn setup_menubar(&self, _menubar: vtable::VBox<MenuVTable>) {}
216
217 /// Re-implement this to support exposing raw window handles (version 0.6).
218 #[cfg(feature = "raw-window-handle-06")]
219 fn window_handle_06_rc(
220 &self,
221 ) -> Result<Rc<dyn raw_window_handle_06::HasWindowHandle>, raw_window_handle_06::HandleError>
222 {
223 Err(raw_window_handle_06::HandleError::NotSupported)
224 }
225
226 /// Re-implement this to support exposing raw display handles (version 0.6).
227 #[cfg(feature = "raw-window-handle-06")]
228 fn display_handle_06_rc(
229 &self,
230 ) -> Result<Rc<dyn raw_window_handle_06::HasDisplayHandle>, raw_window_handle_06::HandleError>
231 {
232 Err(raw_window_handle_06::HandleError::NotSupported)
233 }
234
235 /// Brings the window to the front and focuses it.
236 fn bring_to_front(&self) -> Result<(), PlatformError> {
237 Ok(())
238 }
239}
240
241/// This is the parameter from [`WindowAdapterInternal::input_method_request()`] which lets the editable text input field
242/// communicate with the platform about input methods.
243#[non_exhaustive]
244#[derive(Debug, Clone)]
245pub enum InputMethodRequest {
246 /// Enables the input method with the specified properties.
247 Enable(InputMethodProperties),
248 /// Updates the input method with new properties.
249 Update(InputMethodProperties),
250 /// Disables the input method.
251 Disable,
252}
253
254/// This struct holds properties related to an input method.
255#[non_exhaustive]
256#[derive(Clone, Default, Debug)]
257pub struct InputMethodProperties {
258 /// The text surrounding the cursor.
259 ///
260 /// This field does not include pre-edit text or composition.
261 pub text: SharedString,
262 /// The position of the cursor in bytes within the `text`.
263 pub cursor_position: usize,
264 /// When there is a selection, this is the position of the second anchor
265 /// for the beginning (or the end) of the selection.
266 pub anchor_position: Option<usize>,
267 /// The current value of the pre-edit text as known by the input method.
268 /// This is the text currently being edited but not yet committed.
269 /// When empty, there is no pre-edit text.
270 pub preedit_text: SharedString,
271 /// When the `preedit_text` is not empty, this is the offset of the pre-edit within the `text`.
272 pub preedit_offset: usize,
273 /// The top-left corner of the cursor rectangle in window coordinates.
274 pub cursor_rect_origin: LogicalPosition,
275 /// The size of the cursor rectangle.
276 pub cursor_rect_size: crate::api::LogicalSize,
277 /// The position of the anchor (bottom). Only meaningful if anchor_position is Some
278 pub anchor_point: LogicalPosition,
279 /// The type of input for the text edit.
280 pub input_type: InputType,
281}
282
283/// This struct describes layout constraints of a resizable element, such as a window.
284#[non_exhaustive]
285#[derive(Copy, Clone, Debug, PartialEq, Default)]
286pub struct LayoutConstraints {
287 /// The minimum size.
288 pub min: Option<crate::api::LogicalSize>,
289 /// The maximum size.
290 pub max: Option<crate::api::LogicalSize>,
291 /// The preferred size.
292 pub preferred: crate::api::LogicalSize,
293}
294
295/// This struct contains getters that provide access to properties of the `Window`
296/// element, and is used with [`WindowAdapter::update_window_properties`].
297pub struct WindowProperties<'a>(&'a WindowInner);
298
299impl WindowProperties<'_> {
300 /// Returns the Window's title
301 pub fn title(&self) -> SharedString {
302 self.0.window_item().map(|w| w.as_pin_ref().title()).unwrap_or_default()
303 }
304
305 /// The background color or brush of the Window
306 pub fn background(&self) -> crate::Brush {
307 self.0
308 .window_item()
309 .map(|w: VRcMapped<ItemTreeVTable, crate::items::WindowItem>| {
310 w.as_pin_ref().background()
311 })
312 .unwrap_or_default()
313 }
314
315 /// Returns the layout constraints of the window
316 pub fn layout_constraints(&self) -> LayoutConstraints {
317 let component = self.0.component();
318 let component = ItemTreeRc::borrow_pin(&component);
319 let h = component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
320 let v = component.as_ref().layout_info(crate::layout::Orientation::Vertical);
321 let (min, max) = crate::layout::min_max_size_for_layout_constraints(h, v);
322 LayoutConstraints {
323 min,
324 max,
325 preferred: crate::api::LogicalSize::new(
326 h.preferred_bounded() as f32,
327 v.preferred_bounded() as f32,
328 ),
329 }
330 }
331
332 /// Returns true if the window should be shown fullscreen; false otherwise.
333 #[deprecated(note = "Please use `is_fullscreen` instead")]
334 pub fn fullscreen(&self) -> bool {
335 self.is_fullscreen()
336 }
337
338 /// Returns true if the window should be shown fullscreen; false otherwise.
339 pub fn is_fullscreen(&self) -> bool {
340 self.0.is_fullscreen()
341 }
342
343 /// true if the window is in a maximized state, otherwise false
344 pub fn is_maximized(&self) -> bool {
345 self.0.maximized.get()
346 }
347
348 /// true if the window is in a minimized state, otherwise false
349 pub fn is_minimized(&self) -> bool {
350 self.0.minimized.get()
351 }
352}
353
354struct WindowPropertiesTracker {
355 window_adapter_weak: Weak<dyn WindowAdapter>,
356}
357
358impl crate::properties::PropertyDirtyHandler for WindowPropertiesTracker {
359 fn notify(self: Pin<&Self>) {
360 let win: Weak = self.window_adapter_weak.clone();
361 crate::timers::Timer::single_shot(duration:Default::default(), callback:move || {
362 if let Some(window_adapter: Rc) = win.upgrade() {
363 WindowInner::from_pub(window_adapter.window()).update_window_properties();
364 };
365 })
366 }
367}
368
369struct WindowRedrawTracker {
370 window_adapter_weak: Weak<dyn WindowAdapter>,
371}
372
373impl crate::properties::PropertyDirtyHandler for WindowRedrawTracker {
374 fn notify(self: Pin<&Self>) {
375 if let Some(window_adapter: Rc) = self.window_adapter_weak.upgrade() {
376 window_adapter.request_redraw();
377 };
378 }
379}
380
381/// This enum describes the different ways a popup can be rendered by the back-end.
382#[derive(Clone)]
383pub enum PopupWindowLocation {
384 /// The popup is rendered in its own top-level window that is know to the windowing system.
385 TopLevel(Rc<dyn WindowAdapter>),
386 /// The popup is rendered as an embedded child window at the given position.
387 ChildWindow(LogicalPoint),
388}
389
390/// This structure defines a graphical element that is designed to pop up from the surrounding
391/// UI content, for example to show a context menu.
392#[derive(Clone)]
393pub struct PopupWindow {
394 /// The ID of the associated popup.
395 pub popup_id: NonZeroU32,
396 /// The location defines where the pop up is rendered.
397 pub location: PopupWindowLocation,
398 /// The component that is responsible for providing the popup content.
399 pub component: ItemTreeRc,
400 /// Defines the close behaviour of the popup.
401 pub close_policy: PopupClosePolicy,
402 /// the item that had the focus in the parent window when the popup was opened
403 focus_item_in_parent: ItemWeak,
404 /// The item from where the Popup was invoked from
405 pub parent_item: ItemWeak,
406 /// Whether the popup is a popup menu.
407 /// Popup menu allow the mouse event to be propagated on their parent menu/menubar
408 is_menu: bool,
409}
410
411#[pin_project::pin_project]
412struct WindowPinnedFields {
413 #[pin]
414 redraw_tracker: PropertyTracker<WindowRedrawTracker>,
415 /// Gets dirty when the layout restrictions, or some other property of the windows change
416 #[pin]
417 window_properties_tracker: PropertyTracker<WindowPropertiesTracker>,
418 #[pin]
419 scale_factor: Property<f32>,
420 #[pin]
421 active: Property<bool>,
422 #[pin]
423 text_input_focused: Property<bool>,
424}
425
426/// Inner datastructure for the [`crate::api::Window`]
427pub struct WindowInner {
428 window_adapter_weak: Weak<dyn WindowAdapter>,
429 component: RefCell<ItemTreeWeak>,
430 /// When the window is visible, keep a strong reference
431 strong_component_ref: RefCell<Option<ItemTreeRc>>,
432 mouse_input_state: Cell<MouseInputState>,
433 pub(crate) modifiers: Cell<InternalKeyboardModifierState>,
434
435 /// ItemRC that currently have the focus. (possibly a, instance of TextInput)
436 pub focus_item: RefCell<crate::item_tree::ItemWeak>,
437 /// The last text that was sent to the input method
438 pub(crate) last_ime_text: RefCell<SharedString>,
439 /// Don't let ComponentContainers's instantiation change the focus.
440 /// This is a workaround for a recursion when instantiating ComponentContainer because the
441 /// init code for the component might have code that sets the focus, but we don't want that
442 /// for the ComponentContainer
443 pub(crate) prevent_focus_change: Cell<bool>,
444 cursor_blinker: RefCell<pin_weak::rc::PinWeak<crate::input::TextCursorBlinker>>,
445
446 pinned_fields: Pin<Box<WindowPinnedFields>>,
447 maximized: Cell<bool>,
448 minimized: Cell<bool>,
449
450 /// Stack of currently active popups
451 active_popups: RefCell<Vec<PopupWindow>>,
452 next_popup_id: Cell<NonZeroU32>,
453 had_popup_on_press: Cell<bool>,
454 close_requested: Callback<(), CloseRequestResponse>,
455 click_state: ClickState,
456 pub(crate) ctx: once_cell::unsync::Lazy<crate::SlintContext>,
457}
458
459impl Drop for WindowInner {
460 fn drop(&mut self) {
461 if let Some(existing_blinker: Pin>) = self.cursor_blinker.borrow().upgrade() {
462 existing_blinker.stop();
463 }
464 }
465}
466
467impl WindowInner {
468 /// Create a new instance of the window, given the window_adapter factory fn
469 pub fn new(window_adapter_weak: Weak<dyn WindowAdapter>) -> Self {
470 #![allow(unused_mut)]
471
472 let mut window_properties_tracker =
473 PropertyTracker::new_with_dirty_handler(WindowPropertiesTracker {
474 window_adapter_weak: window_adapter_weak.clone(),
475 });
476
477 let mut redraw_tracker = PropertyTracker::new_with_dirty_handler(WindowRedrawTracker {
478 window_adapter_weak: window_adapter_weak.clone(),
479 });
480
481 #[cfg(slint_debug_property)]
482 {
483 window_properties_tracker
484 .set_debug_name("i_slint_core::Window::window_properties_tracker".into());
485 redraw_tracker.set_debug_name("i_slint_core::Window::redraw_tracker".into());
486 }
487
488 Self {
489 window_adapter_weak,
490 component: Default::default(),
491 strong_component_ref: Default::default(),
492 mouse_input_state: Default::default(),
493 modifiers: Default::default(),
494 pinned_fields: Box::pin(WindowPinnedFields {
495 redraw_tracker,
496 window_properties_tracker,
497 scale_factor: Property::new_named(1., "i_slint_core::Window::scale_factor"),
498 active: Property::new_named(false, "i_slint_core::Window::active"),
499 text_input_focused: Property::new_named(
500 false,
501 "i_slint_core::Window::text_input_focused",
502 ),
503 }),
504 maximized: Cell::new(false),
505 minimized: Cell::new(false),
506 focus_item: Default::default(),
507 last_ime_text: Default::default(),
508 cursor_blinker: Default::default(),
509 active_popups: Default::default(),
510 next_popup_id: Cell::new(NonZeroU32::MIN),
511 had_popup_on_press: Default::default(),
512 close_requested: Default::default(),
513 click_state: ClickState::default(),
514 prevent_focus_change: Default::default(),
515 // The ctx is lazy so that a Window can be initialized before the backend.
516 // (for example in test_empty_window)
517 ctx: once_cell::unsync::Lazy::new(|| {
518 crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().unwrap().clone())
519 }),
520 }
521 }
522
523 /// Associates this window with the specified component. Further event handling and rendering, etc. will be
524 /// done with that component.
525 pub fn set_component(&self, component: &ItemTreeRc) {
526 self.close_all_popups();
527 self.focus_item.replace(Default::default());
528 self.mouse_input_state.replace(Default::default());
529 self.modifiers.replace(Default::default());
530 self.component.replace(ItemTreeRc::downgrade(component));
531 self.pinned_fields.window_properties_tracker.set_dirty(); // component changed, layout constraints for sure must be re-calculated
532 let window_adapter = self.window_adapter();
533 window_adapter.renderer().set_window_adapter(&window_adapter);
534 {
535 let component = ItemTreeRc::borrow_pin(component);
536 let root_item = component.as_ref().get_item_ref(0);
537 let window_item = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item).unwrap();
538
539 let default_font_size_prop =
540 crate::items::WindowItem::FIELD_OFFSETS.default_font_size.apply_pin(window_item);
541 if default_font_size_prop.get().get() <= 0 as Coord {
542 default_font_size_prop.set(window_adapter.renderer().default_font_size());
543 }
544 }
545 self.set_window_item_geometry(
546 window_adapter.size().to_logical(self.scale_factor()).to_euclid(),
547 );
548 window_adapter.request_redraw();
549 let weak = Rc::downgrade(&window_adapter);
550 crate::timers::Timer::single_shot(Default::default(), move || {
551 if let Some(window_adapter) = weak.upgrade() {
552 WindowInner::from_pub(window_adapter.window()).update_window_properties();
553 }
554 })
555 }
556
557 /// return the component.
558 /// Panics if it wasn't set.
559 pub fn component(&self) -> ItemTreeRc {
560 self.component.borrow().upgrade().unwrap()
561 }
562
563 /// returns the component or None if it isn't set.
564 pub fn try_component(&self) -> Option<ItemTreeRc> {
565 self.component.borrow().upgrade()
566 }
567
568 /// Returns a slice of the active poppups.
569 pub fn active_popups(&self) -> core::cell::Ref<'_, [PopupWindow]> {
570 core::cell::Ref::map(self.active_popups.borrow(), |v| v.as_slice())
571 }
572
573 /// Receive a mouse event and pass it to the items of the component to
574 /// change their state.
575 pub fn process_mouse_input(&self, mut event: MouseEvent) {
576 crate::animations::update_animations();
577
578 // handle multiple press release
579 event = self.click_state.check_repeat(event, self.ctx.platform().click_interval());
580
581 let pressed_event = matches!(event, MouseEvent::Pressed { .. });
582 let released_event = matches!(event, MouseEvent::Released { .. });
583
584 let window_adapter = self.window_adapter();
585 let mut mouse_input_state = self.mouse_input_state.take();
586 let last_top_item = mouse_input_state.top_item_including_delayed();
587 if released_event {
588 mouse_input_state =
589 crate::input::process_delayed_event(&window_adapter, mouse_input_state);
590 }
591
592 let Some(item_tree) = self.try_component() else { return };
593
594 // Try to get the root window in case `self` is the popup itself (to get the active_popups list)
595 let mut root_adapter = None;
596 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut root_adapter);
597 let root_adapter = root_adapter.unwrap_or_else(|| window_adapter.clone());
598 let active_popups = &WindowInner::from_pub(root_adapter.window()).active_popups;
599 let native_popup_index = active_popups.borrow().iter().position(|p| {
600 if let PopupWindowLocation::TopLevel(wa) = &p.location {
601 Rc::ptr_eq(wa, &window_adapter)
602 } else {
603 false
604 }
605 });
606
607 if pressed_event {
608 self.had_popup_on_press.set(!active_popups.borrow().is_empty());
609 }
610
611 let mut popup_to_close = active_popups.borrow().last().and_then(|popup| {
612 let mouse_inside_popup = || {
613 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
614 event.position().is_none_or(|pos| {
615 ItemTreeRc::borrow_pin(&popup.component)
616 .as_ref()
617 .item_geometry(0)
618 .contains(pos - coordinates.to_vector())
619 })
620 } else {
621 native_popup_index.is_some_and(|idx| idx == active_popups.borrow().len() - 1)
622 && event.position().is_none_or(|pos| {
623 ItemTreeRc::borrow_pin(&item_tree)
624 .as_ref()
625 .item_geometry(0)
626 .contains(pos)
627 })
628 }
629 };
630 match popup.close_policy {
631 PopupClosePolicy::CloseOnClick => {
632 let mouse_inside_popup = mouse_inside_popup();
633 (mouse_inside_popup && released_event && self.had_popup_on_press.get())
634 || (!mouse_inside_popup && pressed_event)
635 }
636 PopupClosePolicy::CloseOnClickOutside => !mouse_inside_popup() && pressed_event,
637 PopupClosePolicy::NoAutoClose => false,
638 }
639 .then_some(popup.popup_id)
640 });
641
642 mouse_input_state = if let Some(mut event) =
643 crate::input::handle_mouse_grab(event, &window_adapter, &mut mouse_input_state)
644 {
645 let mut item_tree = self.component.borrow().upgrade();
646 let mut offset = LogicalPoint::default();
647 let mut menubar_item = None;
648 for (idx, popup) in active_popups.borrow().iter().enumerate().rev() {
649 item_tree = None;
650 menubar_item = None;
651 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
652 let geom = ItemTreeRc::borrow_pin(&popup.component).as_ref().item_geometry(0);
653 let mouse_inside_popup = event
654 .position()
655 .is_none_or(|pos| geom.contains(pos - coordinates.to_vector()));
656 if mouse_inside_popup {
657 item_tree = Some(popup.component.clone());
658 offset = *coordinates;
659 break;
660 }
661 } else if native_popup_index.is_some_and(|i| i == idx) {
662 item_tree = self.component.borrow().upgrade();
663 break;
664 }
665
666 if !popup.is_menu {
667 break;
668 } else if popup_to_close.is_some() {
669 // clicking outside of a popup menu should close all the menus
670 popup_to_close = Some(popup.popup_id);
671 }
672
673 menubar_item = popup.parent_item.upgrade();
674 }
675
676 let root = match menubar_item {
677 None => item_tree.map(|item_tree| ItemRc::new(item_tree.clone(), 0)),
678 Some(menubar_item) => {
679 assert_ne!(menubar_item.index(), 0, "ContextMenuInternal cannot be root");
680 event.translate(
681 menubar_item
682 .map_to_item_tree(Default::default(), &self.component())
683 .to_vector(),
684 );
685 menubar_item.parent_item()
686 }
687 };
688
689 if let Some(root) = root {
690 event.translate(-offset.to_vector());
691 let mut new_input_state = crate::input::process_mouse_input(
692 root,
693 event,
694 &window_adapter,
695 mouse_input_state,
696 );
697 new_input_state.offset = offset;
698 new_input_state
699 } else {
700 // When outside, send exit event
701 let mut new_input_state = MouseInputState::default();
702 crate::input::send_exit_events(
703 &mouse_input_state,
704 &mut new_input_state,
705 event.position(),
706 &window_adapter,
707 );
708 new_input_state
709 }
710 } else {
711 mouse_input_state
712 };
713
714 if last_top_item != mouse_input_state.top_item_including_delayed() {
715 self.click_state.reset();
716 self.click_state.check_repeat(event, self.ctx.platform().click_interval());
717 }
718
719 self.mouse_input_state.set(mouse_input_state);
720
721 if let Some(popup_id) = popup_to_close {
722 WindowInner::from_pub(root_adapter.window()).close_popup(popup_id);
723 }
724
725 crate::properties::ChangeTracker::run_change_handlers();
726 }
727
728 /// Called by the input code's internal timer to send an event that was delayed
729 pub(crate) fn process_delayed_event(&self) {
730 self.mouse_input_state.set(crate::input::process_delayed_event(
731 &self.window_adapter(),
732 self.mouse_input_state.take(),
733 ));
734 }
735
736 /// Receive a key event and pass it to the items of the component to
737 /// change their state.
738 ///
739 /// Arguments:
740 /// * `event`: The key event received by the windowing system.
741 /// * `component`: The Slint compiled component that provides the tree of items.
742 pub fn process_key_input(&self, mut event: KeyEvent) {
743 if let Some(updated_modifier) = self
744 .modifiers
745 .get()
746 .state_update(event.event_type == KeyEventType::KeyPressed, &event.text)
747 {
748 // Updates the key modifiers depending on the key code and pressed state.
749 self.modifiers.set(updated_modifier);
750 }
751
752 event.modifiers = self.modifiers.get().into();
753
754 let mut item = self.focus_item.borrow().clone().upgrade();
755
756 if item.as_ref().is_some_and(|i| !i.is_visible()) {
757 // Reset the focus... not great, but better than keeping it.
758 self.take_focus_item(&FocusEvent::FocusOut);
759 item = None;
760 }
761
762 while let Some(focus_item) = item {
763 if focus_item.borrow().as_ref().key_event(&event, &self.window_adapter(), &focus_item)
764 == crate::input::KeyEventResult::EventAccepted
765 {
766 crate::properties::ChangeTracker::run_change_handlers();
767 return;
768 }
769 item = focus_item.parent_item();
770 }
771
772 // Make Tab/Backtab handle keyboard focus
773 let extra_mod = event.modifiers.control || event.modifiers.meta || event.modifiers.alt;
774 if event.text.starts_with(key_codes::Tab)
775 && !event.modifiers.shift
776 && !extra_mod
777 && event.event_type == KeyEventType::KeyPressed
778 {
779 self.focus_next_item();
780 } else if (event.text.starts_with(key_codes::Backtab)
781 || (event.text.starts_with(key_codes::Tab) && event.modifiers.shift))
782 && event.event_type == KeyEventType::KeyPressed
783 && !extra_mod
784 {
785 self.focus_previous_item();
786 } else if event.event_type == KeyEventType::KeyPressed
787 && event.text.starts_with(key_codes::Escape)
788 {
789 // Closes top most popup on esc key pressed when policy is not no-auto-close
790
791 // Try to get the parent window in case `self` is the popup itself
792 let mut adapter = self.window_adapter();
793 let item_tree = self.component();
794 let mut a = None;
795 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut a);
796 if let Some(a) = a {
797 adapter = a;
798 }
799 let window = WindowInner::from_pub(adapter.window());
800
801 let close_on_escape = if let Some(popup) = window.active_popups.borrow().last() {
802 popup.close_policy == PopupClosePolicy::CloseOnClick
803 || popup.close_policy == PopupClosePolicy::CloseOnClickOutside
804 } else {
805 false
806 };
807
808 if close_on_escape {
809 window.close_top_popup();
810 }
811 }
812 crate::properties::ChangeTracker::run_change_handlers();
813 }
814
815 /// Installs a binding on the specified property that's toggled whenever the text cursor is supposed to be visible or not.
816 pub fn set_cursor_blink_binding(&self, prop: &crate::Property<bool>) {
817 let existing_blinker = self.cursor_blinker.borrow().clone();
818
819 let blinker = existing_blinker.upgrade().unwrap_or_else(|| {
820 let new_blinker = TextCursorBlinker::new();
821 *self.cursor_blinker.borrow_mut() =
822 pin_weak::rc::PinWeak::downgrade(new_blinker.clone());
823 new_blinker
824 });
825
826 TextCursorBlinker::set_binding(blinker, prop);
827 }
828
829 /// Sets the focus to the item pointed to by item_ptr. This will remove the focus from any
830 /// currently focused item. If set_focus is false, the focus is cleared.
831 pub fn set_focus_item(&self, new_focus_item: &ItemRc, set_focus: bool) {
832 if self.prevent_focus_change.get() {
833 return;
834 }
835
836 let popup_wa = self.active_popups.borrow().last().and_then(|p| match &p.location {
837 PopupWindowLocation::TopLevel(wa) => Some(wa.clone()),
838 PopupWindowLocation::ChildWindow(..) => None,
839 });
840 if let Some(popup_wa) = popup_wa {
841 // Set the focus item on the popup's Window instead
842 popup_wa.window().0.set_focus_item(new_focus_item, set_focus);
843 return;
844 }
845
846 let current_focus_item = self.focus_item.borrow().clone();
847 if let Some(current_focus_item_rc) = current_focus_item.upgrade() {
848 if set_focus {
849 if current_focus_item_rc == *new_focus_item {
850 // don't send focus out and in even to the same item if focus doesn't change
851 return;
852 }
853 } else if current_focus_item_rc != *new_focus_item {
854 // can't clear focus unless called with currently focused item.
855 return;
856 }
857 }
858
859 let old = self.take_focus_item(&FocusEvent::FocusOut);
860 let new =
861 if set_focus { self.move_focus(new_focus_item.clone(), next_focus_item) } else { None };
862 let window_adapter = self.window_adapter();
863 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
864 window_adapter.handle_focus_change(old, new);
865 }
866 }
867
868 /// Take the focus_item out of this Window
869 ///
870 /// This sends the event whiwh must be either FocusOut or WindowLostFocus for popups
871 fn take_focus_item(&self, event: &FocusEvent) -> Option<ItemRc> {
872 let focus_item = self.focus_item.take();
873 assert!(matches!(event, FocusEvent::FocusOut | FocusEvent::WindowLostFocus));
874
875 if let Some(focus_item_rc) = focus_item.upgrade() {
876 focus_item_rc.borrow().as_ref().focus_event(
877 event,
878 &self.window_adapter(),
879 &focus_item_rc,
880 );
881 Some(focus_item_rc)
882 } else {
883 None
884 }
885 }
886
887 /// Publish the new focus_item to this Window and return the FocusEventResult
888 ///
889 /// This sends a FocusIn event!
890 fn publish_focus_item(&self, item: &Option<ItemRc>) -> crate::input::FocusEventResult {
891 match item {
892 Some(item) => {
893 *self.focus_item.borrow_mut() = item.downgrade();
894 item.borrow().as_ref().focus_event(
895 &FocusEvent::FocusIn,
896 &self.window_adapter(),
897 item,
898 )
899 }
900 None => {
901 *self.focus_item.borrow_mut() = Default::default();
902 crate::input::FocusEventResult::FocusAccepted // We were removing the focus, treat that as OK
903 }
904 }
905 }
906
907 fn move_focus(&self, start_item: ItemRc, forward: impl Fn(ItemRc) -> ItemRc) -> Option<ItemRc> {
908 let mut current_item = start_item;
909 let mut visited = Vec::new();
910
911 loop {
912 if current_item.is_visible()
913 && self.publish_focus_item(&Some(current_item.clone()))
914 == crate::input::FocusEventResult::FocusAccepted
915 {
916 return Some(current_item); // Item was just published.
917 }
918 visited.push(current_item.clone());
919 current_item = forward(current_item);
920
921 if visited.iter().any(|i| *i == current_item) {
922 return None; // Nothing to do: We took the focus_item already
923 }
924 }
925 }
926
927 /// Move keyboard focus to the next item
928 pub fn focus_next_item(&self) {
929 let start_item =
930 self.take_focus_item(&FocusEvent::FocusOut).map(next_focus_item).unwrap_or_else(|| {
931 ItemRc::new(
932 self.active_popups
933 .borrow()
934 .last()
935 .map_or_else(|| self.component(), |p| p.component.clone()),
936 0,
937 )
938 });
939 let end_item = self.move_focus(start_item.clone(), next_focus_item);
940 let window_adapter = self.window_adapter();
941 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
942 window_adapter.handle_focus_change(Some(start_item), end_item);
943 }
944 }
945
946 /// Move keyboard focus to the previous item.
947 pub fn focus_previous_item(&self) {
948 let start_item =
949 previous_focus_item(self.take_focus_item(&FocusEvent::FocusOut).unwrap_or_else(|| {
950 ItemRc::new(
951 self.active_popups
952 .borrow()
953 .last()
954 .map_or_else(|| self.component(), |p| p.component.clone()),
955 0,
956 )
957 }));
958 let end_item = self.move_focus(start_item.clone(), previous_focus_item);
959 let window_adapter = self.window_adapter();
960 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
961 window_adapter.handle_focus_change(Some(start_item), end_item);
962 }
963 }
964
965 /// Marks the window to be the active window. This typically coincides with the keyboard
966 /// focus. One exception though is when a popup is shown, in which case the window may
967 /// remain active but temporarily loose focus to the popup.
968 ///
969 /// This results in WindowFocusReceived and WindowFocusLost events.
970 pub fn set_active(&self, have_focus: bool) {
971 self.pinned_fields.as_ref().project_ref().active.set(have_focus);
972
973 let event =
974 if have_focus { FocusEvent::WindowReceivedFocus } else { FocusEvent::WindowLostFocus };
975
976 if let Some(focus_item) = self.focus_item.borrow().upgrade() {
977 focus_item.borrow().as_ref().focus_event(&event, &self.window_adapter(), &focus_item);
978 }
979
980 // If we lost focus due to for example a global shortcut, then when we regain focus
981 // should not assume that the modifiers are in the same state.
982 if !have_focus {
983 self.modifiers.take();
984 }
985 }
986
987 /// Returns true of the window is the active window. That typically implies having the
988 /// keyboard focus, except when a popup is shown and temporarily takes the focus.
989 pub fn active(&self) -> bool {
990 self.pinned_fields.as_ref().project_ref().active.get()
991 }
992
993 /// If the component's root item is a Window element, then this function synchronizes its properties, such as the title
994 /// for example, with the properties known to the windowing system.
995 pub fn update_window_properties(&self) {
996 let window_adapter = self.window_adapter();
997
998 // No `if !dirty { return; }` check here because the backend window may be newly mapped and not up-to-date, so force
999 // an evaluation.
1000 self.pinned_fields
1001 .as_ref()
1002 .project_ref()
1003 .window_properties_tracker
1004 .evaluate_as_dependency_root(|| {
1005 window_adapter.update_window_properties(WindowProperties(self));
1006 });
1007 }
1008
1009 /// Calls the render_components to render the main component and any sub-window components, tracked by a
1010 /// property dependency tracker.
1011 /// Returns None if no component is set yet.
1012 pub fn draw_contents<T>(
1013 &self,
1014 render_components: impl FnOnce(&[(&ItemTreeRc, LogicalPoint)]) -> T,
1015 ) -> Option<T> {
1016 let component_rc = self.try_component()?;
1017 Some(self.pinned_fields.as_ref().project_ref().redraw_tracker.evaluate_as_dependency_root(
1018 || {
1019 if !self
1020 .active_popups
1021 .borrow()
1022 .iter()
1023 .any(|p| matches!(p.location, PopupWindowLocation::ChildWindow(..)))
1024 {
1025 render_components(&[(&component_rc, LogicalPoint::default())])
1026 } else {
1027 let borrow = self.active_popups.borrow();
1028 let mut cmps = Vec::with_capacity(borrow.len() + 1);
1029 cmps.push((&component_rc, LogicalPoint::default()));
1030 for popup in borrow.iter() {
1031 if let PopupWindowLocation::ChildWindow(location) = &popup.location {
1032 cmps.push((&popup.component, *location));
1033 }
1034 }
1035 render_components(&cmps)
1036 }
1037 },
1038 ))
1039 }
1040
1041 /// Registers the window with the windowing system, in order to render the component's items and react
1042 /// to input events once the event loop spins.
1043 pub fn show(&self) -> Result<(), PlatformError> {
1044 if let Some(component) = self.try_component() {
1045 let was_visible = self.strong_component_ref.replace(Some(component)).is_some();
1046 if !was_visible {
1047 *(self.ctx.0.window_count.borrow_mut()) += 1;
1048 }
1049 }
1050
1051 self.update_window_properties();
1052 self.window_adapter().set_visible(true)?;
1053 // Make sure that the window's inner size is in sync with the root window item's
1054 // width/height.
1055 let size = self.window_adapter().size();
1056 self.set_window_item_geometry(size.to_logical(self.scale_factor()).to_euclid());
1057 self.window_adapter().renderer().resize(size).unwrap();
1058 if let Some(hook) = self.ctx.0.window_shown_hook.borrow_mut().as_mut() {
1059 hook(&self.window_adapter());
1060 }
1061 Ok(())
1062 }
1063
1064 /// De-registers the window with the windowing system.
1065 pub fn hide(&self) -> Result<(), PlatformError> {
1066 let result = self.window_adapter().set_visible(false);
1067 let was_visible = self.strong_component_ref.borrow_mut().take().is_some();
1068 if was_visible {
1069 let mut count = self.ctx.0.window_count.borrow_mut();
1070 *count -= 1;
1071 if *count <= 0 {
1072 drop(count);
1073 let _ = self.ctx.event_loop_proxy().and_then(|p| p.quit_event_loop().ok());
1074 }
1075 }
1076 result
1077 }
1078
1079 /// returns the color theme used
1080 pub fn color_scheme(&self) -> ColorScheme {
1081 self.window_adapter()
1082 .internal(crate::InternalToken)
1083 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1084 }
1085
1086 /// Return whether the platform supports native menu bars
1087 pub fn supports_native_menu_bar(&self) -> bool {
1088 self.window_adapter()
1089 .internal(crate::InternalToken)
1090 .is_some_and(|x| x.supports_native_menu_bar())
1091 }
1092
1093 /// Setup the native menu bar
1094 pub fn setup_menubar(&self, menubar: vtable::VBox<MenuVTable>) {
1095 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1096 x.setup_menubar(menubar);
1097 }
1098 }
1099
1100 /// Show a popup at the given position relative to the `parent_item` and returns its ID.
1101 /// The returned ID will always be non-zero.
1102 /// `is_menu` specifies whether the popup is a popup menu.
1103 pub fn show_popup(
1104 &self,
1105 popup_componentrc: &ItemTreeRc,
1106 position: LogicalPosition,
1107 close_policy: PopupClosePolicy,
1108 parent_item: &ItemRc,
1109 is_menu: bool,
1110 ) -> NonZeroU32 {
1111 let position = parent_item
1112 .map_to_window(parent_item.geometry().origin + position.to_euclid().to_vector());
1113 let root_of = |mut item_tree: ItemTreeRc| loop {
1114 if ItemRc::new(item_tree.clone(), 0).downcast::<crate::items::WindowItem>().is_some() {
1115 return item_tree;
1116 }
1117 let mut r = crate::item_tree::ItemWeak::default();
1118 ItemTreeRc::borrow_pin(&item_tree).as_ref().parent_node(&mut r);
1119 match r.upgrade() {
1120 None => return item_tree,
1121 Some(x) => item_tree = x.item_tree().clone(),
1122 }
1123 };
1124 let parent_root_item_tree = root_of(parent_item.item_tree().clone());
1125 let (parent_window_adapter, position) = if let Some(parent_popup) = self
1126 .active_popups
1127 .borrow()
1128 .iter()
1129 .find(|p| ItemTreeRc::ptr_eq(&p.component, &parent_root_item_tree))
1130 {
1131 match &parent_popup.location {
1132 PopupWindowLocation::TopLevel(wa) => (wa.clone(), position),
1133 PopupWindowLocation::ChildWindow(offset) => {
1134 (self.window_adapter(), position + offset.to_vector())
1135 }
1136 }
1137 } else {
1138 (self.window_adapter(), position)
1139 };
1140
1141 let popup_component = ItemTreeRc::borrow_pin(popup_componentrc);
1142 let popup_root = popup_component.as_ref().get_item_ref(0);
1143
1144 let (mut w, mut h) = if let Some(window_item) =
1145 ItemRef::downcast_pin::<crate::items::WindowItem>(popup_root)
1146 {
1147 (window_item.width(), window_item.height())
1148 } else {
1149 (LogicalLength::zero(), LogicalLength::zero())
1150 };
1151
1152 let layout_info_h =
1153 popup_component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
1154 let layout_info_v =
1155 popup_component.as_ref().layout_info(crate::layout::Orientation::Vertical);
1156
1157 if w <= LogicalLength::zero() {
1158 w = LogicalLength::new(layout_info_h.preferred);
1159 }
1160 if h <= LogicalLength::zero() {
1161 h = LogicalLength::new(layout_info_v.preferred);
1162 }
1163 w = w.max(LogicalLength::new(layout_info_h.min)).min(LogicalLength::new(layout_info_h.max));
1164 h = h.max(LogicalLength::new(layout_info_v.min)).min(LogicalLength::new(layout_info_v.max));
1165
1166 let size = crate::lengths::LogicalSize::from_lengths(w, h);
1167
1168 if let Some(window_item) = ItemRef::downcast_pin(popup_root) {
1169 let width_property =
1170 crate::items::WindowItem::FIELD_OFFSETS.width.apply_pin(window_item);
1171 let height_property =
1172 crate::items::WindowItem::FIELD_OFFSETS.height.apply_pin(window_item);
1173 width_property.set(size.width_length());
1174 height_property.set(size.height_length());
1175 };
1176
1177 let popup_id = self.next_popup_id.get();
1178 self.next_popup_id.set(self.next_popup_id.get().checked_add(1).unwrap());
1179
1180 let location = match parent_window_adapter
1181 .internal(crate::InternalToken)
1182 .and_then(|x| x.create_popup(LogicalRect::new(position, size)))
1183 {
1184 None => {
1185 let clip = LogicalRect::new(
1186 LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1187 self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
1188 );
1189 let rect = popup::place_popup(
1190 popup::Placement::Fixed(LogicalRect::new(position, size)),
1191 &Some(clip),
1192 );
1193 self.window_adapter().request_redraw();
1194 PopupWindowLocation::ChildWindow(rect.origin)
1195 }
1196 Some(window_adapter) => {
1197 WindowInner::from_pub(window_adapter.window()).set_component(popup_componentrc);
1198 PopupWindowLocation::TopLevel(window_adapter)
1199 }
1200 };
1201
1202 let focus_item = self
1203 .take_focus_item(&FocusEvent::WindowLostFocus)
1204 .map(|item| item.downgrade())
1205 .unwrap_or_default();
1206
1207 self.active_popups.borrow_mut().push(PopupWindow {
1208 popup_id,
1209 location,
1210 component: popup_componentrc.clone(),
1211 close_policy,
1212 focus_item_in_parent: focus_item,
1213 parent_item: parent_item.downgrade(),
1214 is_menu,
1215 });
1216
1217 popup_id
1218 }
1219
1220 /// Attempt to show a native popup menu
1221 ///
1222 /// context_menu_item is an instance of a ContextMenu
1223 ///
1224 /// Returns false if the native platform doesn't support it
1225 pub fn show_native_popup_menu(
1226 &self,
1227 _context_menu_item: &ItemRc,
1228 _position: LogicalPosition,
1229 ) -> bool {
1230 // TODO
1231 false
1232 }
1233
1234 // Close the popup associated with the given popup window.
1235 fn close_popup_impl(&self, current_popup: &PopupWindow) {
1236 match &current_popup.location {
1237 PopupWindowLocation::ChildWindow(offset) => {
1238 // Refresh the area that was previously covered by the popup.
1239 let popup_region = crate::properties::evaluate_no_tracking(|| {
1240 let popup_component = ItemTreeRc::borrow_pin(&current_popup.component);
1241 popup_component.as_ref().item_geometry(0)
1242 })
1243 .translate(offset.to_vector());
1244
1245 if !popup_region.is_empty() {
1246 let window_adapter = self.window_adapter();
1247 window_adapter.renderer().mark_dirty_region(popup_region.into());
1248 window_adapter.request_redraw();
1249 }
1250 }
1251 PopupWindowLocation::TopLevel(adapter) => {
1252 let _ = adapter.set_visible(false);
1253 }
1254 }
1255 if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
1256 self.set_focus_item(&focus, true);
1257 }
1258 }
1259
1260 /// Removes the popup matching the given ID.
1261 pub fn close_popup(&self, popup_id: NonZeroU32) {
1262 let mut active_popups = self.active_popups.borrow_mut();
1263 let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
1264
1265 if let Some(popup_index) = maybe_index {
1266 let p = active_popups.remove(popup_index);
1267 drop(active_popups);
1268 self.close_popup_impl(&p);
1269 if p.is_menu {
1270 // close all sub-menus
1271 while self.active_popups.borrow().get(popup_index).is_some_and(|p| p.is_menu) {
1272 let p = self.active_popups.borrow_mut().remove(popup_index);
1273 self.close_popup_impl(&p);
1274 }
1275 }
1276 }
1277 }
1278
1279 /// Close all active popups.
1280 pub fn close_all_popups(&self) {
1281 for popup in self.active_popups.take() {
1282 self.close_popup_impl(&popup);
1283 }
1284 }
1285
1286 /// Close the top-most popup.
1287 pub fn close_top_popup(&self) {
1288 let popup = self.active_popups.borrow_mut().pop();
1289 if let Some(popup) = popup {
1290 self.close_popup_impl(&popup);
1291 }
1292 }
1293
1294 /// Returns the scale factor set on the window, as provided by the windowing system.
1295 pub fn scale_factor(&self) -> f32 {
1296 self.pinned_fields.as_ref().project_ref().scale_factor.get()
1297 }
1298
1299 /// Sets the scale factor for the window. This is set by the backend or for testing.
1300 pub(crate) fn set_scale_factor(&self, factor: f32) {
1301 self.pinned_fields.scale_factor.set(factor)
1302 }
1303
1304 /// Reads the global property `TextInputInterface.text-input-focused`
1305 pub fn text_input_focused(&self) -> bool {
1306 self.pinned_fields.as_ref().project_ref().text_input_focused.get()
1307 }
1308
1309 /// Sets the global property `TextInputInterface.text-input-focused`
1310 pub fn set_text_input_focused(&self, value: bool) {
1311 self.pinned_fields.text_input_focused.set(value)
1312 }
1313
1314 /// Returns true if the window is visible
1315 pub fn is_visible(&self) -> bool {
1316 self.strong_component_ref.borrow().is_some()
1317 }
1318
1319 /// Returns the window item that is the first item in the component. When Some()
1320 /// is returned, it's guaranteed to be safe to downcast to `WindowItem`.
1321 pub fn window_item_rc(&self) -> Option<ItemRc> {
1322 self.try_component().and_then(|component_rc| {
1323 let item_rc = ItemRc::new(component_rc, 0);
1324 if item_rc.downcast::<crate::items::WindowItem>().is_some() {
1325 Some(item_rc)
1326 } else {
1327 None
1328 }
1329 })
1330 }
1331
1332 /// Returns the window item that is the first item in the component.
1333 pub fn window_item(&self) -> Option<VRcMapped<ItemTreeVTable, crate::items::WindowItem>> {
1334 self.try_component().and_then(|component_rc| {
1335 ItemRc::new(component_rc, 0).downcast::<crate::items::WindowItem>()
1336 })
1337 }
1338
1339 /// Sets the size of the window item. This method is typically called in response to receiving a
1340 /// window resize event from the windowing system.
1341 pub(crate) fn set_window_item_geometry(&self, size: crate::lengths::LogicalSize) {
1342 if let Some(component_rc) = self.try_component() {
1343 let component = ItemTreeRc::borrow_pin(&component_rc);
1344 let root_item = component.as_ref().get_item_ref(0);
1345 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1346 {
1347 window_item.width.set(size.width_length());
1348 window_item.height.set(size.height_length());
1349 }
1350 }
1351 }
1352
1353 /// Sets the close_requested callback. The callback will be run when the user tries to close a window.
1354 pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
1355 self.close_requested.set_handler(move |()| callback());
1356 }
1357
1358 /// Runs the close_requested callback.
1359 /// If the callback returns KeepWindowShown, this function returns false. That should prevent the Window from closing.
1360 /// Otherwise it returns true, which allows the Window to hide.
1361 pub fn request_close(&self) -> bool {
1362 match self.close_requested.call(&()) {
1363 CloseRequestResponse::HideWindow => true,
1364 CloseRequestResponse::KeepWindowShown => false,
1365 }
1366 }
1367
1368 /// Returns if the window is currently maximized
1369 pub fn is_fullscreen(&self) -> bool {
1370 if let Some(window_item) = self.window_item() {
1371 window_item.as_pin_ref().full_screen()
1372 } else {
1373 false
1374 }
1375 }
1376
1377 /// Set or unset the window to display fullscreen.
1378 pub fn set_fullscreen(&self, enabled: bool) {
1379 if let Some(window_item) = self.window_item() {
1380 window_item.as_pin_ref().full_screen.set(enabled);
1381 self.update_window_properties()
1382 }
1383 }
1384
1385 /// Returns if the window is currently maximized
1386 pub fn is_maximized(&self) -> bool {
1387 self.maximized.get()
1388 }
1389
1390 /// Set the window as maximized or unmaximized
1391 pub fn set_maximized(&self, maximized: bool) {
1392 self.maximized.set(maximized);
1393 self.update_window_properties()
1394 }
1395
1396 /// Returns if the window is currently minimized
1397 pub fn is_minimized(&self) -> bool {
1398 self.minimized.get()
1399 }
1400
1401 /// Set the window as minimized or unminimized
1402 pub fn set_minimized(&self, minimized: bool) {
1403 self.minimized.set(minimized);
1404 self.update_window_properties()
1405 }
1406
1407 /// Returns the (context global) xdg app id for use with wayland and x11.
1408 pub fn xdg_app_id(&self) -> Option<SharedString> {
1409 self.ctx.xdg_app_id()
1410 }
1411
1412 /// Returns the upgraded window adapter
1413 pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
1414 self.window_adapter_weak.upgrade().unwrap()
1415 }
1416
1417 /// Private access to the WindowInner for a given window.
1418 pub fn from_pub(window: &crate::api::Window) -> &Self {
1419 &window.0
1420 }
1421
1422 /// Provides access to the Windows' Slint context.
1423 pub fn context(&self) -> &crate::SlintContext {
1424 &self.ctx
1425 }
1426}
1427
1428/// Internal alias for `Rc<dyn WindowAdapter>`.
1429pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
1430
1431/// This module contains the functions needed to interface with the event loop and window traits
1432/// from outside the Rust language.
1433#[cfg(feature = "ffi")]
1434pub mod ffi {
1435 #![allow(unsafe_code)]
1436 #![allow(clippy::missing_safety_doc)]
1437 #![allow(missing_docs)]
1438
1439 use super::*;
1440 use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
1441 use crate::graphics::Size;
1442 use crate::graphics::{IntSize, Rgba8Pixel};
1443 use crate::SharedVector;
1444 use core::ptr::NonNull;
1445
1446 /// This enum describes a low-level access to specific graphics APIs used
1447 /// by the renderer.
1448 #[repr(u8)]
1449 pub enum GraphicsAPI {
1450 /// The rendering is done using OpenGL.
1451 NativeOpenGL,
1452 }
1453
1454 #[allow(non_camel_case_types)]
1455 type c_void = ();
1456
1457 /// Same layout as WindowAdapterRc
1458 #[repr(C)]
1459 pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
1460
1461 /// Releases the reference to the windowrc held by handle.
1462 #[no_mangle]
1463 pub unsafe extern "C" fn slint_windowrc_drop(handle: *mut WindowAdapterRcOpaque) {
1464 assert_eq!(
1465 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1466 core::mem::size_of::<WindowAdapterRcOpaque>()
1467 );
1468 assert_eq!(
1469 core::mem::size_of::<Option<Rc<dyn WindowAdapter>>>(),
1470 core::mem::size_of::<WindowAdapterRcOpaque>()
1471 );
1472 drop(core::ptr::read(handle as *mut Option<Rc<dyn WindowAdapter>>));
1473 }
1474
1475 /// Releases the reference to the component window held by handle.
1476 #[no_mangle]
1477 pub unsafe extern "C" fn slint_windowrc_clone(
1478 source: *const WindowAdapterRcOpaque,
1479 target: *mut WindowAdapterRcOpaque,
1480 ) {
1481 assert_eq!(
1482 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1483 core::mem::size_of::<WindowAdapterRcOpaque>()
1484 );
1485 let window = &*(source as *const Rc<dyn WindowAdapter>);
1486 core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window.clone());
1487 }
1488
1489 /// Spins an event loop and renders the items of the provided component in this window.
1490 #[no_mangle]
1491 pub unsafe extern "C" fn slint_windowrc_show(handle: *const WindowAdapterRcOpaque) {
1492 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1493
1494 window_adapter.window().show().unwrap();
1495 }
1496
1497 /// Spins an event loop and renders the items of the provided component in this window.
1498 #[no_mangle]
1499 pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) {
1500 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1501 window_adapter.window().hide().unwrap();
1502 }
1503
1504 /// Returns the visibility state of the window. This function can return false even if you previously called show()
1505 /// on it, for example if the user minimized the window.
1506 #[no_mangle]
1507 pub unsafe extern "C" fn slint_windowrc_is_visible(
1508 handle: *const WindowAdapterRcOpaque,
1509 ) -> bool {
1510 let window = &*(handle as *const Rc<dyn WindowAdapter>);
1511 window.window().is_visible()
1512 }
1513
1514 /// Returns the window scale factor.
1515 #[no_mangle]
1516 pub unsafe extern "C" fn slint_windowrc_get_scale_factor(
1517 handle: *const WindowAdapterRcOpaque,
1518 ) -> f32 {
1519 assert_eq!(
1520 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1521 core::mem::size_of::<WindowAdapterRcOpaque>()
1522 );
1523 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1524 WindowInner::from_pub(window_adapter.window()).scale_factor()
1525 }
1526
1527 /// Sets the window scale factor, merely for testing purposes.
1528 #[no_mangle]
1529 pub unsafe extern "C" fn slint_windowrc_set_scale_factor(
1530 handle: *const WindowAdapterRcOpaque,
1531 value: f32,
1532 ) {
1533 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1534 WindowInner::from_pub(window_adapter.window()).set_scale_factor(value)
1535 }
1536
1537 /// Returns the text-input-focused property value.
1538 #[no_mangle]
1539 pub unsafe extern "C" fn slint_windowrc_get_text_input_focused(
1540 handle: *const WindowAdapterRcOpaque,
1541 ) -> bool {
1542 assert_eq!(
1543 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1544 core::mem::size_of::<WindowAdapterRcOpaque>()
1545 );
1546 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1547 WindowInner::from_pub(window_adapter.window()).text_input_focused()
1548 }
1549
1550 /// Set the text-input-focused property.
1551 #[no_mangle]
1552 pub unsafe extern "C" fn slint_windowrc_set_text_input_focused(
1553 handle: *const WindowAdapterRcOpaque,
1554 value: bool,
1555 ) {
1556 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1557 WindowInner::from_pub(window_adapter.window()).set_text_input_focused(value)
1558 }
1559
1560 /// Sets the focus item.
1561 #[no_mangle]
1562 pub unsafe extern "C" fn slint_windowrc_set_focus_item(
1563 handle: *const WindowAdapterRcOpaque,
1564 focus_item: &ItemRc,
1565 set_focus: bool,
1566 ) {
1567 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1568 WindowInner::from_pub(window_adapter.window()).set_focus_item(focus_item, set_focus)
1569 }
1570
1571 /// Associates the window with the given component.
1572 #[no_mangle]
1573 pub unsafe extern "C" fn slint_windowrc_set_component(
1574 handle: *const WindowAdapterRcOpaque,
1575 component: &ItemTreeRc,
1576 ) {
1577 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1578 WindowInner::from_pub(window_adapter.window()).set_component(component)
1579 }
1580
1581 /// Show a popup and return its ID. The returned ID will always be non-zero.
1582 #[no_mangle]
1583 pub unsafe extern "C" fn slint_windowrc_show_popup(
1584 handle: *const WindowAdapterRcOpaque,
1585 popup: &ItemTreeRc,
1586 position: LogicalPosition,
1587 close_policy: PopupClosePolicy,
1588 parent_item: &ItemRc,
1589 is_menu: bool,
1590 ) -> NonZeroU32 {
1591 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1592 WindowInner::from_pub(window_adapter.window()).show_popup(
1593 popup,
1594 position,
1595 close_policy,
1596 parent_item,
1597 is_menu,
1598 )
1599 }
1600
1601 /// Close the popup by the given ID.
1602 #[no_mangle]
1603 pub unsafe extern "C" fn slint_windowrc_close_popup(
1604 handle: *const WindowAdapterRcOpaque,
1605 popup_id: NonZeroU32,
1606 ) {
1607 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1608 WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
1609 }
1610
1611 /// C binding to the set_rendering_notifier() API of Window
1612 #[no_mangle]
1613 pub unsafe extern "C" fn slint_windowrc_set_rendering_notifier(
1614 handle: *const WindowAdapterRcOpaque,
1615 callback: extern "C" fn(
1616 rendering_state: RenderingState,
1617 graphics_api: GraphicsAPI,
1618 user_data: *mut c_void,
1619 ),
1620 drop_user_data: extern "C" fn(user_data: *mut c_void),
1621 user_data: *mut c_void,
1622 error: *mut SetRenderingNotifierError,
1623 ) -> bool {
1624 struct CNotifier {
1625 callback: extern "C" fn(
1626 rendering_state: RenderingState,
1627 graphics_api: GraphicsAPI,
1628 user_data: *mut c_void,
1629 ),
1630 drop_user_data: extern "C" fn(*mut c_void),
1631 user_data: *mut c_void,
1632 }
1633
1634 impl Drop for CNotifier {
1635 fn drop(&mut self) {
1636 (self.drop_user_data)(self.user_data)
1637 }
1638 }
1639
1640 impl RenderingNotifier for CNotifier {
1641 fn notify(&mut self, state: RenderingState, graphics_api: &crate::api::GraphicsAPI) {
1642 let cpp_graphics_api = match graphics_api {
1643 crate::api::GraphicsAPI::NativeOpenGL { .. } => GraphicsAPI::NativeOpenGL,
1644 crate::api::GraphicsAPI::WebGL { .. } => unreachable!(), // We don't support wasm with C++
1645 };
1646 (self.callback)(state, cpp_graphics_api, self.user_data)
1647 }
1648 }
1649
1650 let window = &*(handle as *const Rc<dyn WindowAdapter>);
1651 match window.renderer().set_rendering_notifier(Box::new(CNotifier {
1652 callback,
1653 drop_user_data,
1654 user_data,
1655 })) {
1656 Ok(()) => true,
1657 Err(err) => {
1658 *error = err;
1659 false
1660 }
1661 }
1662 }
1663
1664 /// C binding to the on_close_requested() API of Window
1665 #[no_mangle]
1666 pub unsafe extern "C" fn slint_windowrc_on_close_requested(
1667 handle: *const WindowAdapterRcOpaque,
1668 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
1669 drop_user_data: extern "C" fn(user_data: *mut c_void),
1670 user_data: *mut c_void,
1671 ) {
1672 struct WithUserData {
1673 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
1674 drop_user_data: extern "C" fn(*mut c_void),
1675 user_data: *mut c_void,
1676 }
1677
1678 impl Drop for WithUserData {
1679 fn drop(&mut self) {
1680 (self.drop_user_data)(self.user_data)
1681 }
1682 }
1683
1684 impl WithUserData {
1685 fn call(&self) -> CloseRequestResponse {
1686 (self.callback)(self.user_data)
1687 }
1688 }
1689
1690 let with_user_data = WithUserData { callback, drop_user_data, user_data };
1691
1692 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1693 window_adapter.window().on_close_requested(move || with_user_data.call());
1694 }
1695
1696 /// This function issues a request to the windowing system to redraw the contents of the window.
1697 #[no_mangle]
1698 pub unsafe extern "C" fn slint_windowrc_request_redraw(handle: *const WindowAdapterRcOpaque) {
1699 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1700 window_adapter.request_redraw();
1701 }
1702
1703 /// Returns the position of the window on the screen, in physical screen coordinates and including
1704 /// a window frame (if present).
1705 #[no_mangle]
1706 pub unsafe extern "C" fn slint_windowrc_position(
1707 handle: *const WindowAdapterRcOpaque,
1708 pos: &mut euclid::default::Point2D<i32>,
1709 ) {
1710 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1711 *pos = window_adapter.position().unwrap_or_default().to_euclid()
1712 }
1713
1714 /// Sets the position of the window on the screen, in physical screen coordinates and including
1715 /// a window frame (if present).
1716 /// Note that on some windowing systems, such as Wayland, this functionality is not available.
1717 #[no_mangle]
1718 pub unsafe extern "C" fn slint_windowrc_set_physical_position(
1719 handle: *const WindowAdapterRcOpaque,
1720 pos: &euclid::default::Point2D<i32>,
1721 ) {
1722 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1723 window_adapter.set_position(crate::api::PhysicalPosition::new(pos.x, pos.y).into());
1724 }
1725
1726 /// Sets the position of the window on the screen, in physical screen coordinates and including
1727 /// a window frame (if present).
1728 /// Note that on some windowing systems, such as Wayland, this functionality is not available.
1729 #[no_mangle]
1730 pub unsafe extern "C" fn slint_windowrc_set_logical_position(
1731 handle: *const WindowAdapterRcOpaque,
1732 pos: &euclid::default::Point2D<f32>,
1733 ) {
1734 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1735 window_adapter.set_position(LogicalPosition::new(pos.x, pos.y).into());
1736 }
1737
1738 /// Returns the size of the window on the screen, in physical screen coordinates and excluding
1739 /// a window frame (if present).
1740 #[no_mangle]
1741 pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
1742 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1743 window_adapter.size().to_euclid().cast()
1744 }
1745
1746 /// Resizes the window to the specified size on the screen, in physical pixels and excluding
1747 /// a window frame (if present).
1748 #[no_mangle]
1749 pub unsafe extern "C" fn slint_windowrc_set_physical_size(
1750 handle: *const WindowAdapterRcOpaque,
1751 size: &IntSize,
1752 ) {
1753 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1754 window_adapter.window().set_size(crate::api::PhysicalSize::new(size.width, size.height));
1755 }
1756
1757 /// Resizes the window to the specified size on the screen, in physical pixels and excluding
1758 /// a window frame (if present).
1759 #[no_mangle]
1760 pub unsafe extern "C" fn slint_windowrc_set_logical_size(
1761 handle: *const WindowAdapterRcOpaque,
1762 size: &Size,
1763 ) {
1764 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1765 window_adapter.window().set_size(crate::api::LogicalSize::new(size.width, size.height));
1766 }
1767
1768 /// Return whether the style is using a dark theme
1769 #[no_mangle]
1770 pub unsafe extern "C" fn slint_windowrc_color_scheme(
1771 handle: *const WindowAdapterRcOpaque,
1772 ) -> ColorScheme {
1773 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1774 window_adapter
1775 .internal(crate::InternalToken)
1776 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1777 }
1778
1779 /// Return whether the platform supports native menu bars
1780 #[no_mangle]
1781 pub unsafe extern "C" fn slint_windowrc_supports_native_menu_bar(
1782 handle: *const WindowAdapterRcOpaque,
1783 ) -> bool {
1784 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1785 window_adapter.internal(crate::InternalToken).is_some_and(|x| x.supports_native_menu_bar())
1786 }
1787
1788 /// Setup the native menu bar
1789 #[no_mangle]
1790 pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
1791 handle: *const WindowAdapterRcOpaque,
1792 vtable: NonNull<MenuVTable>,
1793 menu_instance: NonNull<c_void>,
1794 ) {
1795 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1796 window_adapter
1797 .internal(crate::InternalToken)
1798 .map(|x| x.setup_menubar(vtable::VBox::from_raw(vtable, menu_instance.cast())));
1799 }
1800
1801 /// Return the default-font-size property of the WindowItem
1802 #[no_mangle]
1803 pub unsafe extern "C" fn slint_windowrc_default_font_size(
1804 handle: *const WindowAdapterRcOpaque,
1805 ) -> f32 {
1806 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1807 window_adapter.window().0.window_item().unwrap().as_pin_ref().default_font_size().get()
1808 }
1809
1810 /// Dispatch a key pressed or release event
1811 #[no_mangle]
1812 pub unsafe extern "C" fn slint_windowrc_dispatch_key_event(
1813 handle: *const WindowAdapterRcOpaque,
1814 event_type: crate::input::KeyEventType,
1815 text: &SharedString,
1816 repeat: bool,
1817 ) {
1818 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1819 window_adapter.window().0.process_key_input(crate::items::KeyEvent {
1820 text: text.clone(),
1821 repeat,
1822 event_type,
1823 ..Default::default()
1824 });
1825 }
1826
1827 /// Dispatch a mouse event
1828 #[no_mangle]
1829 pub unsafe extern "C" fn slint_windowrc_dispatch_pointer_event(
1830 handle: *const WindowAdapterRcOpaque,
1831 event: crate::input::MouseEvent,
1832 ) {
1833 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1834 window_adapter.window().0.process_mouse_input(event);
1835 }
1836
1837 /// Dispatch a window event
1838 #[no_mangle]
1839 pub unsafe extern "C" fn slint_windowrc_dispatch_event(
1840 handle: *const WindowAdapterRcOpaque,
1841 event: &crate::platform::WindowEvent,
1842 ) {
1843 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1844 window_adapter.window().dispatch_event(event.clone());
1845 }
1846
1847 #[no_mangle]
1848 pub unsafe extern "C" fn slint_windowrc_is_fullscreen(
1849 handle: *const WindowAdapterRcOpaque,
1850 ) -> bool {
1851 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1852 window_adapter.window().is_fullscreen()
1853 }
1854
1855 #[no_mangle]
1856 pub unsafe extern "C" fn slint_windowrc_is_minimized(
1857 handle: *const WindowAdapterRcOpaque,
1858 ) -> bool {
1859 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1860 window_adapter.window().is_minimized()
1861 }
1862
1863 #[no_mangle]
1864 pub unsafe extern "C" fn slint_windowrc_is_maximized(
1865 handle: *const WindowAdapterRcOpaque,
1866 ) -> bool {
1867 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1868 window_adapter.window().is_maximized()
1869 }
1870
1871 #[no_mangle]
1872 pub unsafe extern "C" fn slint_windowrc_set_fullscreen(
1873 handle: *const WindowAdapterRcOpaque,
1874 value: bool,
1875 ) {
1876 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1877 window_adapter.window().set_fullscreen(value)
1878 }
1879
1880 #[no_mangle]
1881 pub unsafe extern "C" fn slint_windowrc_set_minimized(
1882 handle: *const WindowAdapterRcOpaque,
1883 value: bool,
1884 ) {
1885 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1886 window_adapter.window().set_minimized(value)
1887 }
1888
1889 #[no_mangle]
1890 pub unsafe extern "C" fn slint_windowrc_set_maximized(
1891 handle: *const WindowAdapterRcOpaque,
1892 value: bool,
1893 ) {
1894 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1895 window_adapter.window().set_maximized(value)
1896 }
1897
1898 /// Takes a snapshot of the window contents and returns it as RGBA8 encoded pixel buffer.
1899 #[no_mangle]
1900 pub unsafe extern "C" fn slint_windowrc_take_snapshot(
1901 handle: *const WindowAdapterRcOpaque,
1902 data: &mut SharedVector<Rgba8Pixel>,
1903 width: &mut u32,
1904 height: &mut u32,
1905 ) -> bool {
1906 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1907 if let Ok(snapshot) = window_adapter.window().take_snapshot() {
1908 *data = snapshot.data.clone();
1909 *width = snapshot.width();
1910 *height = snapshot.height();
1911 true
1912 } else {
1913 false
1914 }
1915 }
1916}
1917
1918#[cfg(feature = "software-renderer")]
1919#[test]
1920fn test_empty_window() {
1921 // Test that when creating an empty window without a component, we don't panic when render() is called.
1922 // This isn't typically done intentionally, but for example if we receive a paint event in Qt before a component
1923 // is set, this may happen. Concretely as per #2799 this could happen with popups where the call to
1924 // QWidget::show() with egl delivers an immediate paint event, before we've had a chance to call set_component.
1925 // Let's emulate this scenario here using public platform API.
1926
1927 let msw = crate::software_renderer::MinimalSoftwareWindow::new(
1928 crate::software_renderer::RepaintBufferType::NewBuffer,
1929 );
1930 msw.window().request_redraw();
1931 let mut region = None;
1932 let render_called = msw.draw_if_needed(|renderer| {
1933 let mut buffer =
1934 crate::graphics::SharedPixelBuffer::<crate::graphics::Rgb8Pixel>::new(100, 100);
1935 let stride = buffer.width() as usize;
1936 region = Some(renderer.render(buffer.make_mut_slice(), stride));
1937 });
1938 assert!(render_called);
1939 let region = region.unwrap();
1940 assert_eq!(region.bounding_box_size(), PhysicalSize::default());
1941 assert_eq!(region.bounding_box_origin(), PhysicalPosition::default());
1942}
1943