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