Skip to main content

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    ClickState, FocusEvent, FocusReason, InternalKeyboardModifierState, KeyEvent, KeyEventType,
15    MouseEvent, MouseInputState, PointerEventButton, TextCursorBlinker, key_codes,
16};
17use crate::item_tree::{
18    ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeRefPin, 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` or `FemtoVGRenderer`.
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 `slint::platform::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    fn renderer(&self) -> &dyn Renderer;
131
132    /// Re-implement this function to update the properties such as window title or layout constraints.
133    ///
134    /// This function is called before `set_visible(true)`, and will be called again when the properties
135    /// that were queried on the last call are changed. If you do not query any properties, it may not
136    /// be called again.
137    fn update_window_properties(&self, _properties: WindowProperties<'_>) {}
138
139    #[doc(hidden)]
140    fn internal(&self, _: crate::InternalToken) -> Option<&dyn WindowAdapterInternal> {
141        None
142    }
143
144    /// Re-implement this to support exposing raw window handles (version 0.6).
145    #[cfg(feature = "raw-window-handle-06")]
146    fn window_handle_06(
147        &self,
148    ) -> Result<raw_window_handle_06::WindowHandle<'_>, raw_window_handle_06::HandleError> {
149        Err(raw_window_handle_06::HandleError::NotSupported)
150    }
151
152    /// Re-implement this to support exposing raw display handles (version 0.6).
153    #[cfg(feature = "raw-window-handle-06")]
154    fn display_handle_06(
155        &self,
156    ) -> Result<raw_window_handle_06::DisplayHandle<'_>, raw_window_handle_06::HandleError> {
157        Err(raw_window_handle_06::HandleError::NotSupported)
158    }
159}
160
161/// Implementation details behind [`WindowAdapter`], but since this
162/// trait is not exported in the public API, it is not possible for the
163/// users to call or re-implement these functions.
164// TODO: add events for window receiving and loosing focus
165#[doc(hidden)]
166pub trait WindowAdapterInternal: core::any::Any {
167    /// This function is called by the generated code when a component and therefore its tree of items are created.
168    fn register_item_tree(&self, _: ItemTreeRefPin) {}
169
170    /// This function is called by the generated code when a component and therefore its tree of items are destroyed. The
171    /// implementation typically uses this to free the underlying graphics resources.
172    fn unregister_item_tree(
173        &self,
174        _component: ItemTreeRef,
175        _items: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
176    ) {
177    }
178
179    /// Create a window for a popup.
180    ///
181    /// `geometry` is the location of the popup in the window coordinate
182    ///
183    /// If this function return None (the default implementation), then the
184    /// popup will be rendered within the window itself.
185    fn create_popup(&self, _geometry: LogicalRect) -> Option<Rc<dyn WindowAdapter>> {
186        None
187    }
188
189    /// Set the mouse cursor
190    // TODO: Make the enum public and make public
191    fn set_mouse_cursor(&self, _cursor: MouseCursor) {}
192
193    /// This method allow editable input field to communicate with the platform about input methods
194    fn input_method_request(&self, _: InputMethodRequest) {}
195
196    /// Handle focus change
197    // used for accessibility
198    fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {}
199
200    /// returns the color scheme used
201    fn color_scheme(&self) -> ColorScheme {
202        ColorScheme::Unknown
203    }
204
205    /// Returns whether we can have a native menu bar
206    fn supports_native_menu_bar(&self) -> bool {
207        false
208    }
209
210    fn setup_menubar(&self, _menubar: vtable::VRc<MenuVTable>) {}
211
212    fn show_native_popup_menu(
213        &self,
214        _context_menu_item: vtable::VRc<MenuVTable>,
215        _position: LogicalPosition,
216    ) -> bool {
217        false
218    }
219
220    /// Re-implement this to support exposing raw window handles (version 0.6).
221    #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
222    fn window_handle_06_rc(
223        &self,
224    ) -> Result<
225        std::sync::Arc<dyn raw_window_handle_06::HasWindowHandle>,
226        raw_window_handle_06::HandleError,
227    > {
228        Err(raw_window_handle_06::HandleError::NotSupported)
229    }
230
231    /// Re-implement this to support exposing raw display handles (version 0.6).
232    #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
233    fn display_handle_06_rc(
234        &self,
235    ) -> Result<
236        std::sync::Arc<dyn raw_window_handle_06::HasDisplayHandle>,
237        raw_window_handle_06::HandleError,
238    > {
239        Err(raw_window_handle_06::HandleError::NotSupported)
240    }
241
242    /// Brings the window to the front and focuses it.
243    fn bring_to_front(&self) -> Result<(), PlatformError> {
244        Ok(())
245    }
246
247    /// Return the inset of the safe area of the Window in physical pixels.
248    /// This is necessary to avoid overlapping system UI such as notches or system bars.
249    fn safe_area_inset(&self) -> crate::lengths::PhysicalEdges {
250        Default::default()
251    }
252}
253
254/// This is the parameter from [`WindowAdapterInternal::input_method_request()`] which lets the editable text input field
255/// communicate with the platform about input methods.
256#[non_exhaustive]
257#[derive(Debug, Clone)]
258pub enum InputMethodRequest {
259    /// Enables the input method with the specified properties.
260    Enable(InputMethodProperties),
261    /// Updates the input method with new properties.
262    Update(InputMethodProperties),
263    /// Disables the input method.
264    Disable,
265}
266
267/// This struct holds properties related to an input method.
268#[non_exhaustive]
269#[derive(Clone, Default, Debug)]
270pub struct InputMethodProperties {
271    /// The text surrounding the cursor.
272    ///
273    /// This field does not include pre-edit text or composition.
274    pub text: SharedString,
275    /// The position of the cursor in bytes within the `text`.
276    pub cursor_position: usize,
277    /// When there is a selection, this is the position of the second anchor
278    /// for the beginning (or the end) of the selection.
279    pub anchor_position: Option<usize>,
280    /// The current value of the pre-edit text as known by the input method.
281    /// This is the text currently being edited but not yet committed.
282    /// When empty, there is no pre-edit text.
283    pub preedit_text: SharedString,
284    /// When the `preedit_text` is not empty, this is the offset of the pre-edit within the `text`.
285    pub preedit_offset: usize,
286    /// The top-left corner of the cursor rectangle in window coordinates.
287    pub cursor_rect_origin: LogicalPosition,
288    /// The size of the cursor rectangle.
289    pub cursor_rect_size: crate::api::LogicalSize,
290    /// The position of the anchor (bottom). Only meaningful if anchor_position is Some
291    pub anchor_point: LogicalPosition,
292    /// The type of input for the text edit.
293    pub input_type: InputType,
294    /// The clip rect in window coordinates
295    pub clip_rect: Option<LogicalRect>,
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        let scale_factor = self.scale_factor();
550        self.set_window_item_geometry(window_adapter.size().to_logical(scale_factor).to_euclid());
551        let inset = window_adapter
552            .internal(crate::InternalToken)
553            .map(|internal| internal.safe_area_inset())
554            .unwrap_or_default();
555        self.set_window_item_safe_area(inset.to_logical(scale_factor));
556        window_adapter.request_redraw();
557        let weak = Rc::downgrade(&window_adapter);
558        crate::timers::Timer::single_shot(Default::default(), move || {
559            if let Some(window_adapter) = weak.upgrade() {
560                WindowInner::from_pub(window_adapter.window()).update_window_properties();
561            }
562        })
563    }
564
565    /// return the component.
566    /// Panics if it wasn't set.
567    pub fn component(&self) -> ItemTreeRc {
568        self.component.borrow().upgrade().unwrap()
569    }
570
571    /// returns the component or None if it isn't set.
572    pub fn try_component(&self) -> Option<ItemTreeRc> {
573        self.component.borrow().upgrade()
574    }
575
576    /// Returns a slice of the active popups.
577    pub fn active_popups(&self) -> core::cell::Ref<'_, [PopupWindow]> {
578        core::cell::Ref::map(self.active_popups.borrow(), |v| v.as_slice())
579    }
580
581    /// Receive a mouse event and pass it to the items of the component to
582    /// change their state.
583    pub fn process_mouse_input(&self, mut event: MouseEvent) {
584        crate::animations::update_animations();
585
586        // handle multiple press release
587        event = self.click_state.check_repeat(event, self.ctx.platform().click_interval());
588
589        let window_adapter = self.window_adapter();
590        let mut mouse_input_state = self.mouse_input_state.take();
591        if let Some(mut drop_event) = mouse_input_state.drag_data.clone() {
592            match &event {
593                MouseEvent::Released { position, button: PointerEventButton::Left, .. } => {
594                    if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
595                        window_adapter.set_mouse_cursor(MouseCursor::Default);
596                    }
597                    drop_event.position = crate::lengths::logical_position_to_api(*position);
598                    event = MouseEvent::Drop(drop_event);
599                    mouse_input_state.drag_data = None;
600                }
601                MouseEvent::Moved { position, .. } => {
602                    if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
603                        window_adapter.set_mouse_cursor(MouseCursor::NoDrop);
604                    }
605                    drop_event.position = crate::lengths::logical_position_to_api(*position);
606                    event = MouseEvent::DragMove(drop_event);
607                }
608                MouseEvent::Exit => {
609                    mouse_input_state.drag_data = None;
610                }
611                _ => {}
612            }
613        }
614
615        let pressed_event = matches!(event, MouseEvent::Pressed { .. });
616        let released_event = matches!(event, MouseEvent::Released { .. });
617
618        let last_top_item = mouse_input_state.top_item_including_delayed();
619        if released_event {
620            mouse_input_state =
621                crate::input::process_delayed_event(&window_adapter, mouse_input_state);
622        }
623
624        let Some(item_tree) = self.try_component() else { return };
625
626        // Try to get the root window in case `self` is the popup itself (to get the active_popups list)
627        let mut root_adapter = None;
628        ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut root_adapter);
629        let root_adapter = root_adapter.unwrap_or_else(|| window_adapter.clone());
630        let active_popups = &WindowInner::from_pub(root_adapter.window()).active_popups;
631        let native_popup_index = active_popups.borrow().iter().position(|p| {
632            if let PopupWindowLocation::TopLevel(wa) = &p.location {
633                Rc::ptr_eq(wa, &window_adapter)
634            } else {
635                false
636            }
637        });
638
639        if pressed_event {
640            self.had_popup_on_press.set(!active_popups.borrow().is_empty());
641        }
642
643        let mut popup_to_close = active_popups.borrow().last().and_then(|popup| {
644            let mouse_inside_popup = || {
645                if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
646                    event.position().is_none_or(|pos| {
647                        ItemTreeRc::borrow_pin(&popup.component)
648                            .as_ref()
649                            .item_geometry(0)
650                            .contains(pos - coordinates.to_vector())
651                    })
652                } else {
653                    native_popup_index.is_some_and(|idx| idx == active_popups.borrow().len() - 1)
654                        && event.position().is_none_or(|pos| {
655                            ItemTreeRc::borrow_pin(&item_tree)
656                                .as_ref()
657                                .item_geometry(0)
658                                .contains(pos)
659                        })
660                }
661            };
662            match popup.close_policy {
663                PopupClosePolicy::CloseOnClick => {
664                    let mouse_inside_popup = mouse_inside_popup();
665                    (mouse_inside_popup && released_event && self.had_popup_on_press.get())
666                        || (!mouse_inside_popup && pressed_event)
667                }
668                PopupClosePolicy::CloseOnClickOutside => !mouse_inside_popup() && pressed_event,
669                PopupClosePolicy::NoAutoClose => false,
670            }
671            .then_some(popup.popup_id)
672        });
673
674        mouse_input_state = if let Some(mut event) =
675            crate::input::handle_mouse_grab(&event, &window_adapter, &mut mouse_input_state)
676        {
677            let mut item_tree = self.component.borrow().upgrade();
678            let mut offset = LogicalPoint::default();
679            let mut menubar_item = None;
680            for (idx, popup) in active_popups.borrow().iter().enumerate().rev() {
681                item_tree = None;
682                menubar_item = None;
683                if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
684                    let geom = ItemTreeRc::borrow_pin(&popup.component).as_ref().item_geometry(0);
685                    let mouse_inside_popup = event
686                        .position()
687                        .is_none_or(|pos| geom.contains(pos - coordinates.to_vector()));
688                    if mouse_inside_popup {
689                        item_tree = Some(popup.component.clone());
690                        offset = *coordinates;
691                        break;
692                    }
693                } else if native_popup_index.is_some_and(|i| i == idx) {
694                    item_tree = self.component.borrow().upgrade();
695                    break;
696                }
697
698                if !popup.is_menu {
699                    break;
700                } else if popup_to_close.is_some() {
701                    // clicking outside of a popup menu should close all the menus
702                    popup_to_close = Some(popup.popup_id);
703                }
704
705                menubar_item = popup.parent_item.upgrade();
706            }
707
708            let root = match menubar_item {
709                None => item_tree.map(|item_tree| ItemRc::new(item_tree.clone(), 0)),
710                Some(menubar_item) => {
711                    event.translate(
712                        menubar_item
713                            .map_to_item_tree(Default::default(), &self.component())
714                            .to_vector(),
715                    );
716                    menubar_item.parent_item(ParentItemTraversalMode::StopAtPopups)
717                }
718            };
719
720            if let Some(root) = root {
721                event.translate(-offset.to_vector());
722                let mut new_input_state = crate::input::process_mouse_input(
723                    root,
724                    &event,
725                    &window_adapter,
726                    mouse_input_state,
727                );
728                new_input_state.offset = offset;
729                new_input_state
730            } else {
731                // When outside, send exit event
732                let mut new_input_state = MouseInputState::default();
733                crate::input::send_exit_events(
734                    &mouse_input_state,
735                    &mut new_input_state,
736                    event.position(),
737                    &window_adapter,
738                );
739                new_input_state
740            }
741        } else {
742            mouse_input_state
743        };
744
745        if last_top_item != mouse_input_state.top_item_including_delayed() {
746            self.click_state.reset();
747            self.click_state.check_repeat(event, self.ctx.platform().click_interval());
748        }
749
750        self.mouse_input_state.set(mouse_input_state);
751
752        if let Some(popup_id) = popup_to_close {
753            WindowInner::from_pub(root_adapter.window()).close_popup(popup_id);
754        }
755
756        crate::properties::ChangeTracker::run_change_handlers();
757    }
758
759    /// Called by the input code's internal timer to send an event that was delayed
760    pub(crate) fn process_delayed_event(&self) {
761        self.mouse_input_state.set(crate::input::process_delayed_event(
762            &self.window_adapter(),
763            self.mouse_input_state.take(),
764        ));
765    }
766
767    /// Receive a key event and pass it to the items of the component to
768    /// change their state.
769    ///
770    /// Arguments:
771    /// * `event`: The key event received by the windowing system.
772    pub fn process_key_input(&self, mut event: KeyEvent) -> crate::input::KeyEventResult {
773        if let Some(updated_modifier) = self
774            .modifiers
775            .get()
776            .state_update(event.event_type == KeyEventType::KeyPressed, &event.text)
777        {
778            // Updates the key modifiers depending on the key code and pressed state.
779            self.modifiers.set(updated_modifier);
780        }
781
782        event.modifiers = self.modifiers.get().into();
783
784        let mut item = self.focus_item.borrow().clone().upgrade();
785
786        if item.as_ref().is_some_and(|i| !i.is_visible()) {
787            // Reset the focus... not great, but better than keeping it.
788            self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation));
789            item = None;
790        }
791
792        let item_list = {
793            let mut tmp = Vec::new();
794            let mut item = item.clone();
795
796            while let Some(i) = item {
797                tmp.push(i.clone());
798                item = i.parent_item(ParentItemTraversalMode::StopAtPopups);
799            }
800
801            tmp
802        };
803
804        // Check capture_key_event (going from window to focused item):
805        for i in item_list.iter().rev() {
806            if i.borrow().as_ref().capture_key_event(&event, &self.window_adapter(), i)
807                == crate::input::KeyEventResult::EventAccepted
808            {
809                crate::properties::ChangeTracker::run_change_handlers();
810                return crate::input::KeyEventResult::EventAccepted;
811            }
812        }
813
814        drop(item_list);
815
816        // Deliver key_event (to focused item, going up towards the window):
817        while let Some(focus_item) = item {
818            if focus_item.borrow().as_ref().key_event(&event, &self.window_adapter(), &focus_item)
819                == crate::input::KeyEventResult::EventAccepted
820            {
821                crate::properties::ChangeTracker::run_change_handlers();
822                return crate::input::KeyEventResult::EventAccepted;
823            }
824            item = focus_item.parent_item(ParentItemTraversalMode::StopAtPopups);
825        }
826
827        // Make Tab/Backtab handle keyboard focus
828        let extra_mod = event.modifiers.control || event.modifiers.meta || event.modifiers.alt;
829        if event.text.starts_with(key_codes::Tab)
830            && !event.modifiers.shift
831            && !extra_mod
832            && event.event_type == KeyEventType::KeyPressed
833        {
834            self.focus_next_item();
835            crate::properties::ChangeTracker::run_change_handlers();
836            return crate::input::KeyEventResult::EventAccepted;
837        } else if (event.text.starts_with(key_codes::Backtab)
838            || (event.text.starts_with(key_codes::Tab) && event.modifiers.shift))
839            && event.event_type == KeyEventType::KeyPressed
840            && !extra_mod
841        {
842            self.focus_previous_item();
843            crate::properties::ChangeTracker::run_change_handlers();
844            return crate::input::KeyEventResult::EventAccepted;
845        } else if event.event_type == KeyEventType::KeyPressed
846            && event.text.starts_with(key_codes::Escape)
847        {
848            // Closes top most popup on ESC key pressed when policy is not no-auto-close
849
850            // Try to get the parent window in case `self` is the popup itself
851            let mut adapter = self.window_adapter();
852            let item_tree = self.component();
853            let mut a = None;
854            ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut a);
855            if let Some(a) = a {
856                adapter = a;
857            }
858            let window = WindowInner::from_pub(adapter.window());
859
860            let close_on_escape = if let Some(popup) = window.active_popups.borrow().last() {
861                popup.close_policy == PopupClosePolicy::CloseOnClick
862                    || popup.close_policy == PopupClosePolicy::CloseOnClickOutside
863            } else {
864                false
865            };
866
867            if close_on_escape {
868                window.close_top_popup();
869            }
870            crate::properties::ChangeTracker::run_change_handlers();
871            return crate::input::KeyEventResult::EventAccepted;
872        }
873        crate::properties::ChangeTracker::run_change_handlers();
874        crate::input::KeyEventResult::EventIgnored
875    }
876
877    /// Installs a binding on the specified property that's toggled whenever the text cursor is supposed to be visible or not.
878    pub fn set_cursor_blink_binding(&self, prop: &crate::Property<bool>) {
879        let existing_blinker = self.cursor_blinker.borrow().clone();
880
881        let blinker = existing_blinker.upgrade().unwrap_or_else(|| {
882            let new_blinker = TextCursorBlinker::new();
883            *self.cursor_blinker.borrow_mut() =
884                pin_weak::rc::PinWeak::downgrade(new_blinker.clone());
885            new_blinker
886        });
887
888        TextCursorBlinker::set_binding(blinker, prop, self.ctx.platform().cursor_flash_cycle());
889    }
890
891    /// Sets the focus to the item pointed to by item_ptr. This will remove the focus from any
892    /// currently focused item. If set_focus is false, the focus is cleared.
893    pub fn set_focus_item(&self, new_focus_item: &ItemRc, set_focus: bool, reason: FocusReason) {
894        if self.prevent_focus_change.get() {
895            return;
896        }
897
898        let popup_wa = self.active_popups.borrow().last().and_then(|p| match &p.location {
899            PopupWindowLocation::TopLevel(wa) => Some(wa.clone()),
900            PopupWindowLocation::ChildWindow(..) => None,
901        });
902        if let Some(popup_wa) = popup_wa {
903            // Set the focus item on the popup's Window instead
904            popup_wa.window().0.set_focus_item(new_focus_item, set_focus, reason);
905            return;
906        }
907
908        let current_focus_item = self.focus_item.borrow().clone();
909        if let Some(current_focus_item_rc) = current_focus_item.upgrade() {
910            if set_focus {
911                if current_focus_item_rc == *new_focus_item {
912                    // don't send focus out and in even to the same item if focus doesn't change
913                    return;
914                }
915            } else if current_focus_item_rc != *new_focus_item {
916                // can't clear focus unless called with currently focused item.
917                return;
918            }
919        }
920
921        let old = self.take_focus_item(&FocusEvent::FocusOut(reason));
922        let new = if set_focus {
923            self.move_focus(new_focus_item.clone(), next_focus_item, reason)
924        } else {
925            None
926        };
927        let window_adapter = self.window_adapter();
928        if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
929            window_adapter.handle_focus_change(old, new);
930        }
931    }
932
933    /// Take the focus_item out of this Window
934    ///
935    /// This sends the event which must be either FocusOut or WindowLostFocus for popups
936    fn take_focus_item(&self, event: &FocusEvent) -> Option<ItemRc> {
937        let focus_item = self.focus_item.take();
938        assert!(matches!(event, FocusEvent::FocusOut(_)));
939
940        if let Some(focus_item_rc) = focus_item.upgrade() {
941            focus_item_rc.borrow().as_ref().focus_event(
942                event,
943                &self.window_adapter(),
944                &focus_item_rc,
945            );
946            Some(focus_item_rc)
947        } else {
948            None
949        }
950    }
951
952    /// Publish the new focus_item to this Window and return the FocusEventResult
953    ///
954    /// This sends a FocusIn event!
955    fn publish_focus_item(
956        &self,
957        item: &Option<ItemRc>,
958        reason: FocusReason,
959    ) -> crate::input::FocusEventResult {
960        match item {
961            Some(item) => {
962                *self.focus_item.borrow_mut() = item.downgrade();
963                let result = item.borrow().as_ref().focus_event(
964                    &FocusEvent::FocusIn(reason),
965                    &self.window_adapter(),
966                    item,
967                );
968                // Reveal offscreen item when it gains focus
969                if result == crate::input::FocusEventResult::FocusAccepted {
970                    item.try_scroll_into_visible();
971                }
972
973                result
974            }
975            None => {
976                *self.focus_item.borrow_mut() = Default::default();
977                crate::input::FocusEventResult::FocusAccepted // We were removing the focus, treat that as OK
978            }
979        }
980    }
981
982    fn move_focus(
983        &self,
984        start_item: ItemRc,
985        forward: impl Fn(ItemRc) -> ItemRc,
986        reason: FocusReason,
987    ) -> Option<ItemRc> {
988        let mut current_item = start_item;
989        let mut visited = Vec::new();
990
991        loop {
992            if (current_item.is_visible() || reason == FocusReason::Programmatic)
993                && self.publish_focus_item(&Some(current_item.clone()), reason)
994                    == crate::input::FocusEventResult::FocusAccepted
995            {
996                return Some(current_item); // Item was just published.
997            }
998            visited.push(current_item.clone());
999            current_item = forward(current_item);
1000
1001            if visited.contains(&current_item) {
1002                return None; // Nothing to do: We took the focus_item already
1003            }
1004        }
1005    }
1006
1007    /// Move keyboard focus to the next item
1008    pub fn focus_next_item(&self) {
1009        let start_item = self
1010            .take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation))
1011            .map(next_focus_item)
1012            .unwrap_or_else(|| {
1013                ItemRc::new(
1014                    self.active_popups
1015                        .borrow()
1016                        .last()
1017                        .map_or_else(|| self.component(), |p| p.component.clone()),
1018                    0,
1019                )
1020            });
1021        let end_item =
1022            self.move_focus(start_item.clone(), next_focus_item, FocusReason::TabNavigation);
1023        let window_adapter = self.window_adapter();
1024        if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1025            window_adapter.handle_focus_change(Some(start_item), end_item);
1026        }
1027    }
1028
1029    /// Move keyboard focus to the previous item.
1030    pub fn focus_previous_item(&self) {
1031        let start_item = previous_focus_item(
1032            self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation)).unwrap_or_else(
1033                || {
1034                    ItemRc::new(
1035                        self.active_popups
1036                            .borrow()
1037                            .last()
1038                            .map_or_else(|| self.component(), |p| p.component.clone()),
1039                        0,
1040                    )
1041                },
1042            ),
1043        );
1044        let end_item =
1045            self.move_focus(start_item.clone(), previous_focus_item, FocusReason::TabNavigation);
1046        let window_adapter = self.window_adapter();
1047        if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1048            window_adapter.handle_focus_change(Some(start_item), end_item);
1049        }
1050    }
1051
1052    /// Marks the window to be the active window. This typically coincides with the keyboard
1053    /// focus. One exception though is when a popup is shown, in which case the window may
1054    /// remain active but temporarily loose focus to the popup.
1055    ///
1056    /// This results in WindowFocusReceived and WindowFocusLost events.
1057    pub fn set_active(&self, have_focus: bool) {
1058        self.pinned_fields.as_ref().project_ref().active.set(have_focus);
1059
1060        let event = if have_focus {
1061            FocusEvent::FocusIn(FocusReason::WindowActivation)
1062        } else {
1063            FocusEvent::FocusOut(FocusReason::WindowActivation)
1064        };
1065
1066        if let Some(focus_item) = self.focus_item.borrow().upgrade() {
1067            focus_item.borrow().as_ref().focus_event(&event, &self.window_adapter(), &focus_item);
1068        }
1069
1070        // If we lost focus due to for example a global shortcut, then when we regain focus
1071        // should not assume that the modifiers are in the same state.
1072        if !have_focus {
1073            self.modifiers.take();
1074        }
1075    }
1076
1077    /// Returns true of the window is the active window. That typically implies having the
1078    /// keyboard focus, except when a popup is shown and temporarily takes the focus.
1079    pub fn active(&self) -> bool {
1080        self.pinned_fields.as_ref().project_ref().active.get()
1081    }
1082
1083    /// If the component's root item is a Window element, then this function synchronizes its properties, such as the title
1084    /// for example, with the properties known to the windowing system.
1085    pub fn update_window_properties(&self) {
1086        let window_adapter = self.window_adapter();
1087
1088        // No `if !dirty { return; }` check here because the backend window may be newly mapped and not up-to-date, so force
1089        // an evaluation.
1090        self.pinned_fields
1091            .as_ref()
1092            .project_ref()
1093            .window_properties_tracker
1094            .evaluate_as_dependency_root(|| {
1095                window_adapter.update_window_properties(WindowProperties(self));
1096            });
1097    }
1098
1099    /// Calls the render_components to render the main component and any sub-window components, tracked by a
1100    /// property dependency tracker.
1101    /// Returns None if no component is set yet.
1102    pub fn draw_contents<T>(
1103        &self,
1104        render_components: impl FnOnce(&[(ItemTreeWeak, LogicalPoint)]) -> T,
1105    ) -> Option<T> {
1106        let component_weak = ItemTreeRc::downgrade(&self.try_component()?);
1107        Some(self.pinned_fields.as_ref().project_ref().redraw_tracker.evaluate_as_dependency_root(
1108            || {
1109                if !self
1110                    .active_popups
1111                    .borrow()
1112                    .iter()
1113                    .any(|p| matches!(p.location, PopupWindowLocation::ChildWindow(..)))
1114                {
1115                    render_components(&[(component_weak, LogicalPoint::default())])
1116                } else {
1117                    let borrow = self.active_popups.borrow();
1118                    let mut item_trees = Vec::with_capacity(borrow.len() + 1);
1119                    item_trees.push((component_weak, LogicalPoint::default()));
1120                    for popup in borrow.iter() {
1121                        if let PopupWindowLocation::ChildWindow(location) = &popup.location {
1122                            item_trees.push((ItemTreeRc::downgrade(&popup.component), *location));
1123                        }
1124                    }
1125                    drop(borrow);
1126                    render_components(&item_trees)
1127                }
1128            },
1129        ))
1130    }
1131
1132    /// Registers the window with the windowing system, in order to render the component's items and react
1133    /// to input events once the event loop spins.
1134    pub fn show(&self) -> Result<(), PlatformError> {
1135        if let Some(component) = self.try_component() {
1136            let was_visible = self.strong_component_ref.replace(Some(component)).is_some();
1137            if !was_visible {
1138                *(self.ctx.0.window_count.borrow_mut()) += 1;
1139            }
1140        }
1141
1142        self.update_window_properties();
1143        self.window_adapter().set_visible(true)?;
1144        // Make sure that the window's inner size is in sync with the root window item's
1145        // width/height.
1146        let size = self.window_adapter().size();
1147        let scale_factor = self.scale_factor();
1148        self.set_window_item_geometry(size.to_logical(scale_factor).to_euclid());
1149        let inset = self
1150            .window_adapter()
1151            .internal(crate::InternalToken)
1152            .map(|internal| internal.safe_area_inset())
1153            .unwrap_or_default();
1154        self.set_window_item_safe_area(inset.to_logical(scale_factor));
1155        self.window_adapter().renderer().resize(size).unwrap();
1156        if let Some(hook) = self.ctx.0.window_shown_hook.borrow_mut().as_mut() {
1157            hook(&self.window_adapter());
1158        }
1159        Ok(())
1160    }
1161
1162    /// De-registers the window with the windowing system.
1163    pub fn hide(&self) -> Result<(), PlatformError> {
1164        let result = self.window_adapter().set_visible(false);
1165        let was_visible = self.strong_component_ref.borrow_mut().take().is_some();
1166        if was_visible {
1167            let mut count = self.ctx.0.window_count.borrow_mut();
1168            *count -= 1;
1169            if *count <= 0 {
1170                drop(count);
1171                let _ = self.ctx.event_loop_proxy().and_then(|p| p.quit_event_loop().ok());
1172            }
1173        }
1174        result
1175    }
1176
1177    /// returns the color theme used
1178    pub fn color_scheme(&self) -> ColorScheme {
1179        self.window_adapter()
1180            .internal(crate::InternalToken)
1181            .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1182    }
1183
1184    /// Return whether the platform supports native menu bars
1185    pub fn supports_native_menu_bar(&self) -> bool {
1186        self.window_adapter()
1187            .internal(crate::InternalToken)
1188            .is_some_and(|x| x.supports_native_menu_bar())
1189    }
1190
1191    /// Setup the native menu bar
1192    pub fn setup_menubar(&self, menubar: vtable::VRc<MenuVTable>) {
1193        if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1194            x.setup_menubar(menubar);
1195        }
1196    }
1197
1198    /// Show a popup at the given position relative to the `parent_item` and returns its ID.
1199    /// The returned ID will always be non-zero.
1200    /// `is_menu` specifies whether the popup is a popup menu.
1201    pub fn show_popup(
1202        &self,
1203        popup_componentrc: &ItemTreeRc,
1204        position: LogicalPosition,
1205        close_policy: PopupClosePolicy,
1206        parent_item: &ItemRc,
1207        is_menu: bool,
1208    ) -> NonZeroU32 {
1209        let position = parent_item
1210            .map_to_window(parent_item.geometry().origin + position.to_euclid().to_vector());
1211        let root_of = |mut item_tree: ItemTreeRc| loop {
1212            if ItemRc::new(item_tree.clone(), 0).downcast::<crate::items::WindowItem>().is_some() {
1213                return item_tree;
1214            }
1215            let mut r = crate::item_tree::ItemWeak::default();
1216            ItemTreeRc::borrow_pin(&item_tree).as_ref().parent_node(&mut r);
1217            match r.upgrade() {
1218                None => return item_tree,
1219                Some(x) => item_tree = x.item_tree().clone(),
1220            }
1221        };
1222
1223        let parent_root_item_tree = root_of(parent_item.item_tree().clone());
1224        let (parent_window_adapter, position) = if let Some(parent_popup) = self
1225            .active_popups
1226            .borrow()
1227            .iter()
1228            .find(|p| ItemTreeRc::ptr_eq(&p.component, &parent_root_item_tree))
1229        {
1230            match &parent_popup.location {
1231                PopupWindowLocation::TopLevel(wa) => (wa.clone(), position),
1232                PopupWindowLocation::ChildWindow(offset) => {
1233                    (self.window_adapter(), position + offset.to_vector())
1234                }
1235            }
1236        } else {
1237            (self.window_adapter(), position)
1238        };
1239
1240        let popup_component = ItemTreeRc::borrow_pin(popup_componentrc);
1241        let popup_root = popup_component.as_ref().get_item_ref(0);
1242
1243        let (mut w, mut h) = if let Some(window_item) =
1244            ItemRef::downcast_pin::<crate::items::WindowItem>(popup_root)
1245        {
1246            (window_item.width(), window_item.height())
1247        } else {
1248            (LogicalLength::zero(), LogicalLength::zero())
1249        };
1250
1251        let layout_info_h =
1252            popup_component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
1253        let layout_info_v =
1254            popup_component.as_ref().layout_info(crate::layout::Orientation::Vertical);
1255
1256        if w <= LogicalLength::zero() {
1257            w = LogicalLength::new(layout_info_h.preferred);
1258        }
1259        if h <= LogicalLength::zero() {
1260            h = LogicalLength::new(layout_info_v.preferred);
1261        }
1262        w = w.max(LogicalLength::new(layout_info_h.min)).min(LogicalLength::new(layout_info_h.max));
1263        h = h.max(LogicalLength::new(layout_info_v.min)).min(LogicalLength::new(layout_info_v.max));
1264
1265        let size = crate::lengths::LogicalSize::from_lengths(w, h);
1266
1267        if let Some(window_item) = ItemRef::downcast_pin(popup_root) {
1268            let width_property =
1269                crate::items::WindowItem::FIELD_OFFSETS.width.apply_pin(window_item);
1270            let height_property =
1271                crate::items::WindowItem::FIELD_OFFSETS.height.apply_pin(window_item);
1272            width_property.set(size.width_length());
1273            height_property.set(size.height_length());
1274        };
1275
1276        let popup_id = self.next_popup_id.get();
1277        self.next_popup_id.set(self.next_popup_id.get().checked_add(1).unwrap());
1278
1279        // Close active popups before creating a new one.
1280        let siblings: Vec<_> = self
1281            .active_popups
1282            .borrow()
1283            .iter()
1284            .filter(|p| p.parent_item == parent_item.downgrade())
1285            .map(|p| p.popup_id)
1286            .collect();
1287
1288        for sibling in siblings {
1289            self.close_popup(sibling);
1290        }
1291
1292        let location = match parent_window_adapter
1293            .internal(crate::InternalToken)
1294            .and_then(|x| x.create_popup(LogicalRect::new(position, size)))
1295        {
1296            None => {
1297                let clip = LogicalRect::new(
1298                    LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1299                    self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
1300                );
1301                let rect = popup::place_popup(
1302                    popup::Placement::Fixed(LogicalRect::new(position, size)),
1303                    &Some(clip),
1304                );
1305                self.window_adapter().request_redraw();
1306                PopupWindowLocation::ChildWindow(rect.origin)
1307            }
1308            Some(window_adapter) => {
1309                WindowInner::from_pub(window_adapter.window()).set_component(popup_componentrc);
1310                PopupWindowLocation::TopLevel(window_adapter)
1311            }
1312        };
1313
1314        let focus_item = self
1315            .take_focus_item(&FocusEvent::FocusOut(FocusReason::PopupActivation))
1316            .map(|item| item.downgrade())
1317            .unwrap_or_default();
1318
1319        self.active_popups.borrow_mut().push(PopupWindow {
1320            popup_id,
1321            location,
1322            component: popup_componentrc.clone(),
1323            close_policy,
1324            focus_item_in_parent: focus_item,
1325            parent_item: parent_item.downgrade(),
1326            is_menu,
1327        });
1328
1329        popup_id
1330    }
1331
1332    /// Attempt to show a native popup menu
1333    ///
1334    /// context_menu_item is an instance of a ContextMenu
1335    ///
1336    /// Returns false if the native platform doesn't support it
1337    pub fn show_native_popup_menu(
1338        &self,
1339        context_menu_item: vtable::VRc<MenuVTable>,
1340        position: LogicalPosition,
1341        parent_item: &ItemRc,
1342    ) -> bool {
1343        if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1344            let position = parent_item
1345                .map_to_window(parent_item.geometry().origin + position.to_euclid().to_vector());
1346            let position = crate::lengths::logical_position_to_api(position);
1347            x.show_native_popup_menu(context_menu_item, position)
1348        } else {
1349            false
1350        }
1351    }
1352
1353    // Close the popup associated with the given popup window.
1354    fn close_popup_impl(&self, current_popup: &PopupWindow) {
1355        match &current_popup.location {
1356            PopupWindowLocation::ChildWindow(offset) => {
1357                // Refresh the area that was previously covered by the popup.
1358                let popup_region = crate::properties::evaluate_no_tracking(|| {
1359                    let popup_component = ItemTreeRc::borrow_pin(&current_popup.component);
1360                    popup_component.as_ref().item_geometry(0)
1361                })
1362                .translate(offset.to_vector());
1363
1364                if !popup_region.is_empty() {
1365                    let window_adapter = self.window_adapter();
1366                    window_adapter.renderer().mark_dirty_region(popup_region.into());
1367                    window_adapter.request_redraw();
1368                }
1369            }
1370            PopupWindowLocation::TopLevel(adapter) => {
1371                let _ = adapter.set_visible(false);
1372            }
1373        }
1374        if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
1375            self.set_focus_item(&focus, true, FocusReason::PopupActivation);
1376        }
1377    }
1378
1379    /// Removes the popup matching the given ID.
1380    pub fn close_popup(&self, popup_id: NonZeroU32) {
1381        let mut active_popups = self.active_popups.borrow_mut();
1382        let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
1383
1384        if let Some(popup_index) = maybe_index {
1385            let p = active_popups.remove(popup_index);
1386            drop(active_popups);
1387            self.close_popup_impl(&p);
1388            if p.is_menu {
1389                // close all sub-menus
1390                while self.active_popups.borrow().get(popup_index).is_some_and(|p| p.is_menu) {
1391                    let p = self.active_popups.borrow_mut().remove(popup_index);
1392                    self.close_popup_impl(&p);
1393                }
1394            }
1395        }
1396    }
1397
1398    /// Close all active popups.
1399    pub fn close_all_popups(&self) {
1400        for popup in self.active_popups.take() {
1401            self.close_popup_impl(&popup);
1402        }
1403    }
1404
1405    /// Close the top-most popup.
1406    pub fn close_top_popup(&self) {
1407        let popup = self.active_popups.borrow_mut().pop();
1408        if let Some(popup) = popup {
1409            self.close_popup_impl(&popup);
1410        }
1411    }
1412
1413    /// Returns the scale factor set on the window, as provided by the windowing system.
1414    pub fn scale_factor(&self) -> f32 {
1415        self.pinned_fields.as_ref().project_ref().scale_factor.get()
1416    }
1417
1418    /// Sets the scale factor for the window. This is set by the backend or for testing.
1419    pub(crate) fn set_scale_factor(&self, factor: f32) {
1420        if !self.pinned_fields.scale_factor.is_constant() {
1421            self.pinned_fields.scale_factor.set(factor)
1422        }
1423    }
1424
1425    /// Sets the scale factor for the window.
1426    /// From that point on, the scale factor is constant and cannot be changed anymore.
1427    pub fn set_const_scale_factor(&self, factor: f32) {
1428        if !self.pinned_fields.scale_factor.is_constant() {
1429            self.pinned_fields.scale_factor.set(factor);
1430            self.pinned_fields.scale_factor.set_constant();
1431        }
1432    }
1433
1434    /// Reads the global property `TextInputInterface.text-input-focused`
1435    pub fn text_input_focused(&self) -> bool {
1436        self.pinned_fields.as_ref().project_ref().text_input_focused.get()
1437    }
1438
1439    /// Sets the global property `TextInputInterface.text-input-focused`
1440    pub fn set_text_input_focused(&self, value: bool) {
1441        self.pinned_fields.text_input_focused.set(value)
1442    }
1443
1444    /// Returns true if the window is visible
1445    pub fn is_visible(&self) -> bool {
1446        self.strong_component_ref.borrow().is_some()
1447    }
1448
1449    /// Returns the window item that is the first item in the component. When Some()
1450    /// is returned, it's guaranteed to be safe to downcast to `WindowItem`.
1451    pub fn window_item_rc(&self) -> Option<ItemRc> {
1452        self.try_component().and_then(|component_rc| {
1453            let item_rc = ItemRc::new(component_rc, 0);
1454            if item_rc.downcast::<crate::items::WindowItem>().is_some() {
1455                Some(item_rc)
1456            } else {
1457                None
1458            }
1459        })
1460    }
1461
1462    /// Returns the window item that is the first item in the component.
1463    pub fn window_item(&self) -> Option<VRcMapped<ItemTreeVTable, crate::items::WindowItem>> {
1464        self.try_component().and_then(|component_rc| {
1465            ItemRc::new(component_rc, 0).downcast::<crate::items::WindowItem>()
1466        })
1467    }
1468
1469    /// Sets the size of the window item. This method is typically called in response to receiving a
1470    /// window resize event from the windowing system.
1471    pub(crate) fn set_window_item_geometry(&self, size: crate::lengths::LogicalSize) {
1472        if let Some(component_rc) = self.try_component() {
1473            let component = ItemTreeRc::borrow_pin(&component_rc);
1474            let root_item = component.as_ref().get_item_ref(0);
1475            if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1476            {
1477                window_item.width.set(size.width_length());
1478                window_item.height.set(size.height_length());
1479            }
1480        }
1481    }
1482
1483    /// The safe area of the window has changed.
1484    pub fn set_window_item_safe_area(&self, inset: crate::lengths::LogicalEdges) {
1485        if let Some(component_rc) = self.try_component() {
1486            let component = ItemTreeRc::borrow_pin(&component_rc);
1487            let root_item = component.as_ref().get_item_ref(0);
1488            if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1489            {
1490                window_item.safe_area_insets.set(inset);
1491            }
1492        }
1493    }
1494
1495    pub(crate) fn set_window_item_virtual_keyboard(
1496        &self,
1497        origin: crate::lengths::LogicalPoint,
1498        size: crate::lengths::LogicalSize,
1499    ) {
1500        let Some(component_rc) = self.try_component() else {
1501            return;
1502        };
1503        let component = ItemTreeRc::borrow_pin(&component_rc);
1504        let root_item = component.as_ref().get_item_ref(0);
1505        let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item) else {
1506            return;
1507        };
1508        window_item.virtual_keyboard_position.set(origin);
1509        window_item.virtual_keyboard_size.set(size);
1510        if let Some(focus_item) = self.focus_item.borrow().upgrade() {
1511            focus_item.try_scroll_into_visible();
1512        }
1513    }
1514
1515    pub(crate) fn window_item_virtual_keyboard(
1516        &self,
1517    ) -> Option<(crate::lengths::LogicalPoint, crate::lengths::LogicalSize)> {
1518        let component_rc = self.try_component()?;
1519        let component = ItemTreeRc::borrow_pin(&component_rc);
1520        let root_item = component.as_ref().get_item_ref(0);
1521        let window_item = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)?;
1522        Some((window_item.virtual_keyboard_position(), window_item.virtual_keyboard_size()))
1523    }
1524
1525    /// Sets the close_requested callback. The callback will be run when the user tries to close a window.
1526    pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
1527        self.close_requested.set_handler(move |()| callback());
1528    }
1529
1530    /// Runs the close_requested callback.
1531    /// If the callback returns KeepWindowShown, this function returns false. That should prevent the Window from closing.
1532    /// Otherwise it returns true, which allows the Window to hide.
1533    pub fn request_close(&self) -> bool {
1534        match self.close_requested.call(&()) {
1535            CloseRequestResponse::HideWindow => true,
1536            CloseRequestResponse::KeepWindowShown => false,
1537        }
1538    }
1539
1540    /// Returns if the window is currently maximized
1541    pub fn is_fullscreen(&self) -> bool {
1542        if let Some(window_item) = self.window_item() {
1543            window_item.as_pin_ref().full_screen()
1544        } else {
1545            false
1546        }
1547    }
1548
1549    /// Set or unset the window to display fullscreen.
1550    pub fn set_fullscreen(&self, enabled: bool) {
1551        if let Some(window_item) = self.window_item() {
1552            window_item.as_pin_ref().full_screen.set(enabled);
1553            self.update_window_properties()
1554        }
1555    }
1556
1557    /// Returns if the window is currently maximized
1558    pub fn is_maximized(&self) -> bool {
1559        self.maximized.get()
1560    }
1561
1562    /// Set the window as maximized or unmaximized
1563    pub fn set_maximized(&self, maximized: bool) {
1564        self.maximized.set(maximized);
1565        self.update_window_properties()
1566    }
1567
1568    /// Returns if the window is currently minimized
1569    pub fn is_minimized(&self) -> bool {
1570        self.minimized.get()
1571    }
1572
1573    /// Set the window as minimized or unminimized
1574    pub fn set_minimized(&self, minimized: bool) {
1575        self.minimized.set(minimized);
1576        self.update_window_properties()
1577    }
1578
1579    /// Returns the (context global) xdg app id for use with wayland and x11.
1580    pub fn xdg_app_id(&self) -> Option<SharedString> {
1581        self.ctx.xdg_app_id()
1582    }
1583
1584    /// Returns the upgraded window adapter
1585    pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
1586        self.window_adapter_weak.upgrade().unwrap()
1587    }
1588
1589    /// Private access to the WindowInner for a given window.
1590    pub fn from_pub(window: &crate::api::Window) -> &Self {
1591        &window.0
1592    }
1593
1594    /// Provides access to the Windows' Slint context.
1595    pub fn context(&self) -> &crate::SlintContext {
1596        &self.ctx
1597    }
1598}
1599
1600/// Internal alias for `Rc<dyn WindowAdapter>`.
1601pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
1602
1603/// This module contains the functions needed to interface with the event loop and window traits
1604/// from outside the Rust language.
1605#[cfg(feature = "ffi")]
1606pub mod ffi {
1607    #![allow(unsafe_code)]
1608    #![allow(clippy::missing_safety_doc)]
1609    #![allow(missing_docs)]
1610
1611    use super::*;
1612    use crate::SharedVector;
1613    use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
1614    use crate::graphics::Size;
1615    use crate::graphics::{IntSize, Rgba8Pixel};
1616    use crate::items::WindowItem;
1617
1618    /// This enum describes a low-level access to specific graphics APIs used
1619    /// by the renderer.
1620    #[repr(u8)]
1621    pub enum GraphicsAPI {
1622        /// The rendering is done using OpenGL.
1623        NativeOpenGL,
1624        /// The rendering is done using APIs inaccessible from C++, such as WGPU.
1625        Inaccessible,
1626    }
1627
1628    #[allow(non_camel_case_types)]
1629    type c_void = ();
1630
1631    /// Same layout as WindowAdapterRc
1632    #[repr(C)]
1633    pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
1634
1635    /// Releases the reference to the windowrc held by handle.
1636    #[unsafe(no_mangle)]
1637    pub unsafe extern "C" fn slint_windowrc_drop(handle: *mut WindowAdapterRcOpaque) {
1638        unsafe {
1639            assert_eq!(
1640                core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1641                core::mem::size_of::<WindowAdapterRcOpaque>()
1642            );
1643            assert_eq!(
1644                core::mem::size_of::<Option<Rc<dyn WindowAdapter>>>(),
1645                core::mem::size_of::<WindowAdapterRcOpaque>()
1646            );
1647            drop(core::ptr::read(handle as *mut Option<Rc<dyn WindowAdapter>>));
1648        }
1649    }
1650
1651    /// Releases the reference to the component window held by handle.
1652    #[unsafe(no_mangle)]
1653    pub unsafe extern "C" fn slint_windowrc_clone(
1654        source: *const WindowAdapterRcOpaque,
1655        target: *mut WindowAdapterRcOpaque,
1656    ) {
1657        unsafe {
1658            assert_eq!(
1659                core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1660                core::mem::size_of::<WindowAdapterRcOpaque>()
1661            );
1662            let window = &*(source as *const Rc<dyn WindowAdapter>);
1663            core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window.clone());
1664        }
1665    }
1666
1667    /// Spins an event loop and renders the items of the provided component in this window.
1668    #[unsafe(no_mangle)]
1669    pub unsafe extern "C" fn slint_windowrc_show(handle: *const WindowAdapterRcOpaque) {
1670        unsafe {
1671            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1672
1673            window_adapter.window().show().unwrap();
1674        }
1675    }
1676
1677    /// Spins an event loop and renders the items of the provided component in this window.
1678    #[unsafe(no_mangle)]
1679    pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) {
1680        unsafe {
1681            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1682            window_adapter.window().hide().unwrap();
1683        }
1684    }
1685
1686    /// Returns the visibility state of the window. This function can return false even if you previously called show()
1687    /// on it, for example if the user minimized the window.
1688    #[unsafe(no_mangle)]
1689    pub unsafe extern "C" fn slint_windowrc_is_visible(
1690        handle: *const WindowAdapterRcOpaque,
1691    ) -> bool {
1692        unsafe {
1693            let window = &*(handle as *const Rc<dyn WindowAdapter>);
1694            window.window().is_visible()
1695        }
1696    }
1697
1698    /// Returns the window scale factor.
1699    #[unsafe(no_mangle)]
1700    pub unsafe extern "C" fn slint_windowrc_get_scale_factor(
1701        handle: *const WindowAdapterRcOpaque,
1702    ) -> f32 {
1703        unsafe {
1704            assert_eq!(
1705                core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1706                core::mem::size_of::<WindowAdapterRcOpaque>()
1707            );
1708            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1709            WindowInner::from_pub(window_adapter.window()).scale_factor()
1710        }
1711    }
1712
1713    /// Sets the window scale factor, merely for testing purposes.
1714    #[unsafe(no_mangle)]
1715    pub unsafe extern "C" fn slint_windowrc_set_const_scale_factor(
1716        handle: *const WindowAdapterRcOpaque,
1717        value: f32,
1718    ) {
1719        unsafe {
1720            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1721            WindowInner::from_pub(window_adapter.window()).set_const_scale_factor(value)
1722        }
1723    }
1724
1725    /// Returns the text-input-focused property value.
1726    #[unsafe(no_mangle)]
1727    pub unsafe extern "C" fn slint_windowrc_get_text_input_focused(
1728        handle: *const WindowAdapterRcOpaque,
1729    ) -> bool {
1730        unsafe {
1731            assert_eq!(
1732                core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1733                core::mem::size_of::<WindowAdapterRcOpaque>()
1734            );
1735            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1736            WindowInner::from_pub(window_adapter.window()).text_input_focused()
1737        }
1738    }
1739
1740    /// Set the text-input-focused property.
1741    #[unsafe(no_mangle)]
1742    pub unsafe extern "C" fn slint_windowrc_set_text_input_focused(
1743        handle: *const WindowAdapterRcOpaque,
1744        value: bool,
1745    ) {
1746        unsafe {
1747            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1748            WindowInner::from_pub(window_adapter.window()).set_text_input_focused(value)
1749        }
1750    }
1751
1752    /// Sets the focus item.
1753    #[unsafe(no_mangle)]
1754    pub unsafe extern "C" fn slint_windowrc_set_focus_item(
1755        handle: *const WindowAdapterRcOpaque,
1756        focus_item: &ItemRc,
1757        set_focus: bool,
1758        reason: FocusReason,
1759    ) {
1760        unsafe {
1761            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1762            WindowInner::from_pub(window_adapter.window())
1763                .set_focus_item(focus_item, set_focus, reason)
1764        }
1765    }
1766
1767    /// Associates the window with the given component.
1768    #[unsafe(no_mangle)]
1769    pub unsafe extern "C" fn slint_windowrc_set_component(
1770        handle: *const WindowAdapterRcOpaque,
1771        component: &ItemTreeRc,
1772    ) {
1773        unsafe {
1774            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1775            WindowInner::from_pub(window_adapter.window()).set_component(component)
1776        }
1777    }
1778
1779    /// Show a popup and return its ID. The returned ID will always be non-zero.
1780    #[unsafe(no_mangle)]
1781    pub unsafe extern "C" fn slint_windowrc_show_popup(
1782        handle: *const WindowAdapterRcOpaque,
1783        popup: &ItemTreeRc,
1784        position: LogicalPosition,
1785        close_policy: PopupClosePolicy,
1786        parent_item: &ItemRc,
1787        is_menu: bool,
1788    ) -> NonZeroU32 {
1789        unsafe {
1790            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1791            WindowInner::from_pub(window_adapter.window()).show_popup(
1792                popup,
1793                position,
1794                close_policy,
1795                parent_item,
1796                is_menu,
1797            )
1798        }
1799    }
1800
1801    /// Close the popup by the given ID.
1802    #[unsafe(no_mangle)]
1803    pub unsafe extern "C" fn slint_windowrc_close_popup(
1804        handle: *const WindowAdapterRcOpaque,
1805        popup_id: NonZeroU32,
1806    ) {
1807        unsafe {
1808            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1809            WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
1810        }
1811    }
1812
1813    /// C binding to the set_rendering_notifier() API of Window
1814    #[unsafe(no_mangle)]
1815    pub unsafe extern "C" fn slint_windowrc_set_rendering_notifier(
1816        handle: *const WindowAdapterRcOpaque,
1817        callback: extern "C" fn(
1818            rendering_state: RenderingState,
1819            graphics_api: GraphicsAPI,
1820            user_data: *mut c_void,
1821        ),
1822        drop_user_data: extern "C" fn(user_data: *mut c_void),
1823        user_data: *mut c_void,
1824        error: *mut SetRenderingNotifierError,
1825    ) -> bool {
1826        unsafe {
1827            struct CNotifier {
1828                callback: extern "C" fn(
1829                    rendering_state: RenderingState,
1830                    graphics_api: GraphicsAPI,
1831                    user_data: *mut c_void,
1832                ),
1833                drop_user_data: extern "C" fn(*mut c_void),
1834                user_data: *mut c_void,
1835            }
1836
1837            impl Drop for CNotifier {
1838                fn drop(&mut self) {
1839                    (self.drop_user_data)(self.user_data)
1840                }
1841            }
1842
1843            impl RenderingNotifier for CNotifier {
1844                fn notify(
1845                    &mut self,
1846                    state: RenderingState,
1847                    graphics_api: &crate::api::GraphicsAPI,
1848                ) {
1849                    let cpp_graphics_api = match graphics_api {
1850                        crate::api::GraphicsAPI::NativeOpenGL { .. } => GraphicsAPI::NativeOpenGL,
1851                        crate::api::GraphicsAPI::WebGL { .. } => unreachable!(), // We don't support wasm with C++
1852                        #[cfg(feature = "unstable-wgpu-27")]
1853                        crate::api::GraphicsAPI::WGPU27 { .. } => GraphicsAPI::Inaccessible, // There is no C++ API for wgpu (maybe wgpu c in the future?)
1854                        #[cfg(feature = "unstable-wgpu-28")]
1855                        crate::api::GraphicsAPI::WGPU28 { .. } => GraphicsAPI::Inaccessible, // There is no C++ API for wgpu (maybe wgpu c in the future?)
1856                    };
1857                    (self.callback)(state, cpp_graphics_api, self.user_data)
1858                }
1859            }
1860
1861            let window = &*(handle as *const Rc<dyn WindowAdapter>);
1862            match window.renderer().set_rendering_notifier(Box::new(CNotifier {
1863                callback,
1864                drop_user_data,
1865                user_data,
1866            })) {
1867                Ok(()) => true,
1868                Err(err) => {
1869                    *error = err;
1870                    false
1871                }
1872            }
1873        }
1874    }
1875
1876    /// C binding to the on_close_requested() API of Window
1877    #[unsafe(no_mangle)]
1878    pub unsafe extern "C" fn slint_windowrc_on_close_requested(
1879        handle: *const WindowAdapterRcOpaque,
1880        callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
1881        drop_user_data: extern "C" fn(user_data: *mut c_void),
1882        user_data: *mut c_void,
1883    ) {
1884        unsafe {
1885            struct WithUserData {
1886                callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
1887                drop_user_data: extern "C" fn(*mut c_void),
1888                user_data: *mut c_void,
1889            }
1890
1891            impl Drop for WithUserData {
1892                fn drop(&mut self) {
1893                    (self.drop_user_data)(self.user_data)
1894                }
1895            }
1896
1897            impl WithUserData {
1898                fn call(&self) -> CloseRequestResponse {
1899                    (self.callback)(self.user_data)
1900                }
1901            }
1902
1903            let with_user_data = WithUserData { callback, drop_user_data, user_data };
1904
1905            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1906            window_adapter.window().on_close_requested(move || with_user_data.call());
1907        }
1908    }
1909
1910    /// This function issues a request to the windowing system to redraw the contents of the window.
1911    #[unsafe(no_mangle)]
1912    pub unsafe extern "C" fn slint_windowrc_request_redraw(handle: *const WindowAdapterRcOpaque) {
1913        unsafe {
1914            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1915            window_adapter.request_redraw();
1916        }
1917    }
1918
1919    /// Returns the position of the window on the screen, in physical screen coordinates and including
1920    /// a window frame (if present).
1921    #[unsafe(no_mangle)]
1922    pub unsafe extern "C" fn slint_windowrc_position(
1923        handle: *const WindowAdapterRcOpaque,
1924        pos: &mut euclid::default::Point2D<i32>,
1925    ) {
1926        unsafe {
1927            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1928            *pos = window_adapter.position().unwrap_or_default().to_euclid()
1929        }
1930    }
1931
1932    /// Sets the position of the window on the screen, in physical screen coordinates and including
1933    /// a window frame (if present).
1934    /// Note that on some windowing systems, such as Wayland, this functionality is not available.
1935    #[unsafe(no_mangle)]
1936    pub unsafe extern "C" fn slint_windowrc_set_physical_position(
1937        handle: *const WindowAdapterRcOpaque,
1938        pos: &euclid::default::Point2D<i32>,
1939    ) {
1940        unsafe {
1941            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1942            window_adapter.set_position(crate::api::PhysicalPosition::new(pos.x, pos.y).into());
1943        }
1944    }
1945
1946    /// Sets the position of the window on the screen, in physical screen coordinates and including
1947    /// a window frame (if present).
1948    /// Note that on some windowing systems, such as Wayland, this functionality is not available.
1949    #[unsafe(no_mangle)]
1950    pub unsafe extern "C" fn slint_windowrc_set_logical_position(
1951        handle: *const WindowAdapterRcOpaque,
1952        pos: &euclid::default::Point2D<f32>,
1953    ) {
1954        unsafe {
1955            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1956            window_adapter.set_position(LogicalPosition::new(pos.x, pos.y).into());
1957        }
1958    }
1959
1960    /// Returns the size of the window on the screen, in physical screen coordinates and excluding
1961    /// a window frame (if present).
1962    #[unsafe(no_mangle)]
1963    pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
1964        unsafe {
1965            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1966            window_adapter.size().to_euclid().cast()
1967        }
1968    }
1969
1970    /// Resizes the window to the specified size on the screen, in physical pixels and excluding
1971    /// a window frame (if present).
1972    #[unsafe(no_mangle)]
1973    pub unsafe extern "C" fn slint_windowrc_set_physical_size(
1974        handle: *const WindowAdapterRcOpaque,
1975        size: &IntSize,
1976    ) {
1977        unsafe {
1978            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1979            window_adapter
1980                .window()
1981                .set_size(crate::api::PhysicalSize::new(size.width, size.height));
1982        }
1983    }
1984
1985    /// Resizes the window to the specified size on the screen, in physical pixels and excluding
1986    /// a window frame (if present).
1987    #[unsafe(no_mangle)]
1988    pub unsafe extern "C" fn slint_windowrc_set_logical_size(
1989        handle: *const WindowAdapterRcOpaque,
1990        size: &Size,
1991    ) {
1992        unsafe {
1993            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1994            window_adapter.window().set_size(crate::api::LogicalSize::new(size.width, size.height));
1995        }
1996    }
1997
1998    /// Return whether the style is using a dark theme
1999    #[unsafe(no_mangle)]
2000    pub unsafe extern "C" fn slint_windowrc_color_scheme(
2001        handle: *const WindowAdapterRcOpaque,
2002    ) -> ColorScheme {
2003        unsafe {
2004            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2005            window_adapter
2006                .internal(crate::InternalToken)
2007                .map_or(ColorScheme::Unknown, |x| x.color_scheme())
2008        }
2009    }
2010
2011    /// Return whether the platform supports native menu bars
2012    #[unsafe(no_mangle)]
2013    pub unsafe extern "C" fn slint_windowrc_supports_native_menu_bar(
2014        handle: *const WindowAdapterRcOpaque,
2015    ) -> bool {
2016        unsafe {
2017            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2018            window_adapter
2019                .internal(crate::InternalToken)
2020                .is_some_and(|x| x.supports_native_menu_bar())
2021        }
2022    }
2023
2024    /// Setup the native menu bar
2025    #[unsafe(no_mangle)]
2026    pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
2027        handle: *const WindowAdapterRcOpaque,
2028        menu_instance: &vtable::VRc<MenuVTable>,
2029    ) {
2030        unsafe {
2031            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2032            if let Some(x) = window_adapter.internal(crate::InternalToken) {
2033                x.setup_menubar(menu_instance.clone())
2034            }
2035        }
2036    }
2037
2038    /// Show a native context menu
2039    #[unsafe(no_mangle)]
2040    pub unsafe extern "C" fn slint_windowrc_show_native_popup_menu(
2041        handle: *const WindowAdapterRcOpaque,
2042        context_menu: &vtable::VRc<MenuVTable>,
2043        position: LogicalPosition,
2044        parent_item: &ItemRc,
2045    ) -> bool {
2046        unsafe {
2047            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2048            WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(
2049                context_menu.clone(),
2050                position,
2051                parent_item,
2052            )
2053        }
2054    }
2055
2056    /// Return the default-font-size property of the WindowItem
2057    #[unsafe(no_mangle)]
2058    pub unsafe extern "C" fn slint_windowrc_resolved_default_font_size(
2059        item_tree: &ItemTreeRc,
2060    ) -> f32 {
2061        WindowItem::resolved_default_font_size(item_tree.clone()).get()
2062    }
2063
2064    /// Dispatch a key pressed or release event
2065    #[unsafe(no_mangle)]
2066    pub unsafe extern "C" fn slint_windowrc_dispatch_key_event(
2067        handle: *const WindowAdapterRcOpaque,
2068        event_type: crate::input::KeyEventType,
2069        text: &SharedString,
2070        repeat: bool,
2071    ) {
2072        unsafe {
2073            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2074            window_adapter.window().0.process_key_input(crate::items::KeyEvent {
2075                text: text.clone(),
2076                repeat,
2077                event_type,
2078                ..Default::default()
2079            });
2080        }
2081    }
2082
2083    /// Dispatch a mouse event
2084    #[unsafe(no_mangle)]
2085    pub unsafe extern "C" fn slint_windowrc_dispatch_pointer_event(
2086        handle: *const WindowAdapterRcOpaque,
2087        event: &crate::input::MouseEvent,
2088    ) {
2089        unsafe {
2090            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2091            window_adapter.window().0.process_mouse_input(event.clone());
2092        }
2093    }
2094
2095    /// Dispatch a window event
2096    #[unsafe(no_mangle)]
2097    pub unsafe extern "C" fn slint_windowrc_dispatch_event(
2098        handle: *const WindowAdapterRcOpaque,
2099        event: &crate::platform::WindowEvent,
2100    ) {
2101        unsafe {
2102            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2103            window_adapter.window().dispatch_event(event.clone());
2104        }
2105    }
2106
2107    #[unsafe(no_mangle)]
2108    pub unsafe extern "C" fn slint_windowrc_is_fullscreen(
2109        handle: *const WindowAdapterRcOpaque,
2110    ) -> bool {
2111        unsafe {
2112            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2113            window_adapter.window().is_fullscreen()
2114        }
2115    }
2116
2117    #[unsafe(no_mangle)]
2118    pub unsafe extern "C" fn slint_windowrc_is_minimized(
2119        handle: *const WindowAdapterRcOpaque,
2120    ) -> bool {
2121        unsafe {
2122            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2123            window_adapter.window().is_minimized()
2124        }
2125    }
2126
2127    #[unsafe(no_mangle)]
2128    pub unsafe extern "C" fn slint_windowrc_is_maximized(
2129        handle: *const WindowAdapterRcOpaque,
2130    ) -> bool {
2131        unsafe {
2132            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2133            window_adapter.window().is_maximized()
2134        }
2135    }
2136
2137    #[unsafe(no_mangle)]
2138    pub unsafe extern "C" fn slint_windowrc_set_fullscreen(
2139        handle: *const WindowAdapterRcOpaque,
2140        value: bool,
2141    ) {
2142        unsafe {
2143            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2144            window_adapter.window().set_fullscreen(value)
2145        }
2146    }
2147
2148    #[unsafe(no_mangle)]
2149    pub unsafe extern "C" fn slint_windowrc_set_minimized(
2150        handle: *const WindowAdapterRcOpaque,
2151        value: bool,
2152    ) {
2153        unsafe {
2154            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2155            window_adapter.window().set_minimized(value)
2156        }
2157    }
2158
2159    #[unsafe(no_mangle)]
2160    pub unsafe extern "C" fn slint_windowrc_set_maximized(
2161        handle: *const WindowAdapterRcOpaque,
2162        value: bool,
2163    ) {
2164        unsafe {
2165            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2166            window_adapter.window().set_maximized(value)
2167        }
2168    }
2169
2170    /// Takes a snapshot of the window contents and returns it as RGBA8 encoded pixel buffer.
2171    #[unsafe(no_mangle)]
2172    pub unsafe extern "C" fn slint_windowrc_take_snapshot(
2173        handle: *const WindowAdapterRcOpaque,
2174        data: &mut SharedVector<Rgba8Pixel>,
2175        width: &mut u32,
2176        height: &mut u32,
2177    ) -> bool {
2178        unsafe {
2179            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2180            if let Ok(snapshot) = window_adapter.window().take_snapshot() {
2181                *data = snapshot.data.clone();
2182                *width = snapshot.width();
2183                *height = snapshot.height();
2184                true
2185            } else {
2186                false
2187            }
2188        }
2189    }
2190}
2191
2192/// This module contains the functions needed to interface with window handles from outside the Rust language.
2193#[cfg(all(feature = "ffi", feature = "raw-window-handle-06"))]
2194pub mod ffi_window {
2195    #![allow(unsafe_code)]
2196    #![allow(clippy::missing_safety_doc)]
2197
2198    use super::ffi::WindowAdapterRcOpaque;
2199    use super::*;
2200    use std::ffi::c_void;
2201    use std::ptr::null_mut;
2202    use std::sync::Arc;
2203
2204    /// Helper to grab the `HasWindowHandle` for the `WindowAdapter` behind `handle`.
2205    fn has_window_handle(
2206        handle: *const WindowAdapterRcOpaque,
2207    ) -> Option<Arc<dyn raw_window_handle_06::HasWindowHandle>> {
2208        let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2209        let window_adapter = window_adapter.internal(crate::InternalToken)?;
2210        window_adapter.window_handle_06_rc().ok()
2211    }
2212
2213    /// Helper to grab the `HasDisplayHandle` for the `WindowAdapter` behind `handle`.
2214    fn has_display_handle(
2215        handle: *const WindowAdapterRcOpaque,
2216    ) -> Option<Arc<dyn raw_window_handle_06::HasDisplayHandle>> {
2217        let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2218        let window_adapter = window_adapter.internal(crate::InternalToken)?;
2219        window_adapter.display_handle_06_rc().ok()
2220    }
2221
2222    /// Returns the `HWND` associated with this window, or null if it doesn't exist or isn't created yet.
2223    #[unsafe(no_mangle)]
2224    pub unsafe extern "C" fn slint_windowrc_hwnd_win32(
2225        handle: *const WindowAdapterRcOpaque,
2226    ) -> *mut c_void {
2227        use raw_window_handle_06::HasWindowHandle;
2228
2229        if let Some(has_window_handle) = has_window_handle(handle)
2230            && let Ok(window_handle) = has_window_handle.window_handle()
2231            && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
2232        {
2233            isize::from(win32.hwnd) as *mut c_void
2234        } else {
2235            null_mut()
2236        }
2237    }
2238
2239    /// Returns the `HINSTANCE` associated with this window, or null if it doesn't exist or isn't created yet.
2240    #[unsafe(no_mangle)]
2241    pub unsafe extern "C" fn slint_windowrc_hinstance_win32(
2242        handle: *const WindowAdapterRcOpaque,
2243    ) -> *mut c_void {
2244        use raw_window_handle_06::HasWindowHandle;
2245
2246        if let Some(has_window_handle) = has_window_handle(handle)
2247            && let Ok(window_handle) = has_window_handle.window_handle()
2248            && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
2249        {
2250            win32
2251                .hinstance
2252                .map(|hinstance| isize::from(hinstance) as *mut c_void)
2253                .unwrap_or_default()
2254        } else {
2255            null_mut()
2256        }
2257    }
2258
2259    /// Returns the `wl_surface` associated with this window, or null if it doesn't exist or isn't created yet.
2260    #[unsafe(no_mangle)]
2261    pub unsafe extern "C" fn slint_windowrc_wlsurface_wayland(
2262        handle: *const WindowAdapterRcOpaque,
2263    ) -> *mut c_void {
2264        use raw_window_handle_06::HasWindowHandle;
2265
2266        if let Some(has_window_handle) = has_window_handle(handle)
2267            && let Ok(window_handle) = has_window_handle.window_handle()
2268            && let raw_window_handle_06::RawWindowHandle::Wayland(wayland) = window_handle.as_raw()
2269        {
2270            wayland.surface.as_ptr()
2271        } else {
2272            null_mut()
2273        }
2274    }
2275
2276    /// Returns the `wl_display` associated with this window, or null if it doesn't exist or isn't created yet.
2277    #[unsafe(no_mangle)]
2278    pub unsafe extern "C" fn slint_windowrc_wldisplay_wayland(
2279        handle: *const WindowAdapterRcOpaque,
2280    ) -> *mut c_void {
2281        use raw_window_handle_06::HasDisplayHandle;
2282
2283        if let Some(has_display_handle) = has_display_handle(handle)
2284            && let Ok(display_handle) = has_display_handle.display_handle()
2285            && let raw_window_handle_06::RawDisplayHandle::Wayland(wayland) =
2286                display_handle.as_raw()
2287        {
2288            wayland.display.as_ptr()
2289        } else {
2290            null_mut()
2291        }
2292    }
2293
2294    /// Returns the `NSView` associated with this window, or null if it doesn't exist or isn't created yet.
2295    #[unsafe(no_mangle)]
2296    pub unsafe extern "C" fn slint_windowrc_nsview_appkit(
2297        handle: *const WindowAdapterRcOpaque,
2298    ) -> *mut c_void {
2299        use raw_window_handle_06::HasWindowHandle;
2300
2301        if let Some(has_window_handle) = has_window_handle(handle)
2302            && let Ok(window_handle) = has_window_handle.window_handle()
2303            && let raw_window_handle_06::RawWindowHandle::AppKit(appkit) = window_handle.as_raw()
2304        {
2305            appkit.ns_view.as_ptr()
2306        } else {
2307            null_mut()
2308        }
2309    }
2310}