i_slint_core/
window.rs

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