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