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 componentrc datastructure subelements unmaximized unminimized
5
6#![warn(missing_docs)]
7//! Exposed Window API
8use crate::api::{
9    CloseRequestResponse, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize,
10    PlatformError, Window, WindowPosition, WindowSize,
11};
12use crate::input::{
13    ClickState, DragData, FocusEvent, FocusReason, InternalKeyEvent, KeyEventResult, KeyEventType,
14    Keys, MouseEvent, MouseInputState, PointerEventButton, TextCursorBlinker, TouchPhase,
15    TouchState, key_codes,
16};
17use crate::item_tree::{
18    ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeRefPin, ItemTreeVTable, ItemTreeWeak, ItemWeak,
19    ParentItemTraversalMode,
20};
21use crate::items::{InputType, ItemRef, MenuEntry, MouseCursor, PopupClosePolicy};
22use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalVector, SizeLengths};
23use crate::menus::MenuVTable;
24use crate::properties::{Property, PropertyTracker};
25use crate::renderer::Renderer;
26use crate::{Callback, Coord, SharedString, SharedVector};
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::{VRc, 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/// The window kind when creating a new child window
47#[repr(C)]
48pub enum WindowKind {
49    /// Tooltip
50    ToolTip,
51    /// Popup Window
52    Popup,
53    /// Popup Menu
54    Menu,
55}
56
57/// This trait represents the adaptation layer between the [`Window`] API and then
58/// windowing specific window representation, such as a Win32 `HWND` handle or a `wayland_surface_t`.
59///
60/// Implement this trait to establish the link between the two, and pass messages in both
61/// directions:
62///
63/// - When receiving messages from the windowing system about state changes, such as the window being resized,
64///   the user requested the window to be closed, input being received, etc. you need to create a
65///   [`WindowEvent`](crate::platform::WindowEvent) and send it to Slint via [`Window::try_dispatch_event()`].
66///
67/// - Slint sends requests to change visibility, position, size, etc. via functions such as [`Self::set_visible`],
68///   [`Self::set_size`], [`Self::set_position`], or [`Self::update_window_properties()`]. Re-implement these functions
69///   and delegate the requests to the windowing system.
70///
71/// If the implementation of this bi-directional message passing protocol is incomplete, the user may
72/// experience unexpected behavior, or the intention of the developer calling functions on the [`Window`]
73/// API may not be fulfilled.
74///
75/// Your implementation must hold a renderer, such as `SoftwareRenderer` or `FemtoVGRenderer`.
76/// In the [`Self::renderer()`] function, you must return a reference to it.
77///
78/// It is also required to hold a [`Window`] and return a reference to it in your
79/// implementation of [`Self::window()`].
80///
81/// See also `slint::platform::software_renderer::MinimalSoftwareWindow`
82/// for a minimal implementation of this trait using the software renderer
83pub trait WindowAdapter {
84    /// Returns the window API.
85    fn window(&self) -> &Window;
86
87    /// Show the window if the argument is true, hide otherwise.
88    fn set_visible(&self, _visible: bool) -> Result<(), PlatformError> {
89        Ok(())
90    }
91
92    /// Returns the position of the window on the screen, in physical screen coordinates and including
93    /// a window frame (if present).
94    ///
95    /// The default implementation returns `None`
96    ///
97    /// Called from [`Window::position()`]
98    fn position(&self) -> Option<PhysicalPosition> {
99        None
100    }
101    /// Sets the position of the window on the screen, in physical screen coordinates and including
102    /// a window frame (if present).
103    ///
104    /// The default implementation does nothing
105    ///
106    /// Called from [`Window::set_position()`]
107    fn set_position(&self, _position: WindowPosition) {}
108
109    /// Request a new size for the window to the specified size on the screen, in physical or logical pixels
110    /// and excluding a window frame (if present).
111    ///
112    /// This is called from [`Window::set_size()`]
113    ///
114    /// The default implementation does nothing
115    ///
116    /// This function should sent the size to the Windowing system. If the window size actually changes, you
117    /// should dispatch a [`WindowEvent::Resized`](crate::platform::WindowEvent::Resized) using
118    /// [`Window::dispatch_event()`] to propagate the new size to the slint view
119    fn set_size(&self, _size: WindowSize) {}
120
121    /// Return the size of the Window on the screen
122    fn size(&self) -> PhysicalSize;
123
124    /// Issues a request to the windowing system to re-render the contents of the window.
125    ///
126    /// This request is typically asynchronous.
127    /// It is called when a property that was used during window rendering is marked as dirty.
128    ///
129    /// An implementation should repaint the window in a subsequent iteration of the event loop,
130    /// throttled to the screen refresh rate if possible.
131    /// It is important not to query any Slint properties to avoid introducing a dependency loop in the properties,
132    /// including the use of the render function, which itself queries properties.
133    ///
134    /// See also [`Window::request_redraw()`]
135    fn request_redraw(&self) {}
136
137    /// Return the renderer.
138    ///
139    /// The `Renderer` trait is an internal trait that you are not expected to implement.
140    /// In your implementation you should return a reference to an instance of one of the renderers provided by Slint.
141    fn renderer(&self) -> &dyn Renderer;
142
143    /// Re-implement this function to update the properties such as window title or layout constraints.
144    ///
145    /// This function is called before `set_visible(true)`, and will be called again when the properties
146    /// that were queried on the last call are changed. If you do not query any properties, it may not
147    /// be called again.
148    fn update_window_properties(&self, _properties: WindowProperties<'_>) {}
149
150    #[doc(hidden)]
151    fn internal(&self, _: crate::InternalToken) -> Option<&dyn WindowAdapterInternal> {
152        None
153    }
154
155    /// Re-implement this to support exposing raw window handles (version 0.6).
156    #[cfg(feature = "raw-window-handle-06")]
157    fn window_handle_06(
158        &self,
159    ) -> Result<raw_window_handle_06::WindowHandle<'_>, raw_window_handle_06::HandleError> {
160        Err(raw_window_handle_06::HandleError::NotSupported)
161    }
162
163    /// Re-implement this to support exposing raw display handles (version 0.6).
164    #[cfg(feature = "raw-window-handle-06")]
165    fn display_handle_06(
166        &self,
167    ) -> Result<raw_window_handle_06::DisplayHandle<'_>, raw_window_handle_06::HandleError> {
168        Err(raw_window_handle_06::HandleError::NotSupported)
169    }
170}
171
172/// Implementation details behind [`WindowAdapter`], but since this
173/// trait is not exported in the public API, it is not possible for the
174/// users to call or re-implement these functions.
175// TODO: add events for window receiving and loosing focus
176#[doc(hidden)]
177pub trait WindowAdapterInternal: core::any::Any {
178    /// This function is called by the generated code when a component and therefore its tree of items are created.
179    fn register_item_tree(&self, _: ItemTreeRefPin) {}
180
181    /// This function is called by the generated code when a component and therefore its tree of items are destroyed. The
182    /// implementation typically uses this to free the underlying graphics resources.
183    fn unregister_item_tree(
184        &self,
185        _component: ItemTreeRef,
186        _items: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
187    ) {
188    }
189
190    /// Get the parent window adapter of this window adapter
191    fn get_parent(&self) -> Option<Rc<dyn WindowAdapter>> {
192        None
193    }
194
195    /// Create a window for a popup.
196    /// This function will create only the window adapter but does not show the popup it self
197    /// Use this window adapter to create a new popup window and show it with `show_popup()`
198    ///
199    /// If this function return None (the default implementation), then the
200    /// popup will be rendered within the window itself.
201    fn create_child_window_adapter(
202        &self,
203        _window_kind: WindowKind,
204    ) -> Option<Rc<dyn WindowAdapter>> {
205        None
206    }
207
208    /// Set the mouse cursor
209    // TODO: Make the enum public and make public
210    fn set_mouse_cursor(&self, _cursor: MouseCursor) {}
211
212    /// This method allow editable input field to communicate with the platform about input methods
213    fn input_method_request(&self, _: InputMethodRequest) {}
214
215    /// Handle focus change
216    // used for accessibility
217    fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {}
218
219    /// Returns whether we can have a native menu bar
220    fn supports_native_menu_bar(&self) -> bool {
221        false
222    }
223
224    fn setup_menubar(&self, _menubar: vtable::VRc<MenuVTable>) {}
225
226    fn show_native_popup_menu(
227        &self,
228        _context_menu_item: vtable::VRc<MenuVTable>,
229        _position: LogicalPosition,
230    ) -> bool {
231        false
232    }
233
234    /// Re-implement this to support exposing raw window handles (version 0.6).
235    #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
236    fn window_handle_06_rc(
237        &self,
238    ) -> Result<
239        std::sync::Arc<dyn raw_window_handle_06::HasWindowHandle>,
240        raw_window_handle_06::HandleError,
241    > {
242        Err(raw_window_handle_06::HandleError::NotSupported)
243    }
244
245    /// Re-implement this to support exposing raw display handles (version 0.6).
246    #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
247    fn display_handle_06_rc(
248        &self,
249    ) -> Result<
250        std::sync::Arc<dyn raw_window_handle_06::HasDisplayHandle>,
251        raw_window_handle_06::HandleError,
252    > {
253        Err(raw_window_handle_06::HandleError::NotSupported)
254    }
255
256    /// Brings the window to the front and focuses it.
257    fn bring_to_front(&self) -> Result<(), PlatformError> {
258        Ok(())
259    }
260
261    /// Return the inset of the safe area of the Window in physical pixels.
262    /// This is necessary to avoid overlapping system UI such as notches or system bars.
263    fn safe_area_inset(&self) -> crate::lengths::PhysicalEdges {
264        Default::default()
265    }
266}
267
268/// This is the parameter from [`WindowAdapterInternal::input_method_request()`] which lets the editable text input field
269/// communicate with the platform about input methods.
270#[non_exhaustive]
271#[derive(Debug, Clone)]
272pub enum InputMethodRequest {
273    /// Enables the input method with the specified properties.
274    Enable(InputMethodProperties),
275    /// Updates the input method with new properties.
276    Update(InputMethodProperties),
277    /// Disables the input method.
278    Disable,
279}
280
281/// This struct holds properties related to an input method.
282#[non_exhaustive]
283#[derive(Clone, Default, Debug)]
284pub struct InputMethodProperties {
285    /// The text surrounding the cursor.
286    ///
287    /// This field does not include pre-edit text or composition.
288    pub text: SharedString,
289    /// The position of the cursor in bytes within the `text`.
290    pub cursor_position: usize,
291    /// When there is a selection, this is the position of the second anchor
292    /// for the beginning (or the end) of the selection.
293    pub anchor_position: Option<usize>,
294    /// The current value of the pre-edit text as known by the input method.
295    /// This is the text currently being edited but not yet committed.
296    /// When empty, there is no pre-edit text.
297    pub preedit_text: SharedString,
298    /// When the `preedit_text` is not empty, this is the offset of the pre-edit within the `text`.
299    pub preedit_offset: usize,
300    /// The top-left corner of the cursor rectangle in window coordinates.
301    pub cursor_rect_origin: LogicalPosition,
302    /// The size of the cursor rectangle.
303    pub cursor_rect_size: crate::api::LogicalSize,
304    /// The position of the anchor (bottom). Only meaningful if anchor_position is Some
305    pub anchor_point: LogicalPosition,
306    /// The type of input for the text edit.
307    pub input_type: InputType,
308    /// The clip rect in window coordinates
309    pub clip_rect: Option<LogicalRect>,
310}
311
312/// This struct describes layout constraints of a resizable element, such as a window.
313#[non_exhaustive]
314#[derive(Copy, Clone, Debug, PartialEq, Default)]
315pub struct LayoutConstraints {
316    /// The minimum size.
317    pub min: Option<crate::api::LogicalSize>,
318    /// The maximum size.
319    pub max: Option<crate::api::LogicalSize>,
320    /// The preferred size.
321    pub preferred: crate::api::LogicalSize,
322}
323
324/// This struct contains getters that provide access to properties of the `Window`
325/// element, and is used with [`WindowAdapter::update_window_properties`].
326pub struct WindowProperties<'a>(&'a WindowInner);
327
328impl WindowProperties<'_> {
329    /// Returns the Window's title
330    pub fn title(&self) -> SharedString {
331        self.0.window_item().map(|w| w.as_pin_ref().title()).unwrap_or_default()
332    }
333
334    /// The background color or brush of the Window
335    pub fn background(&self) -> crate::Brush {
336        self.0
337            .window_item()
338            .map(|w: VRcMapped<ItemTreeVTable, crate::items::WindowItem>| {
339                w.as_pin_ref().background()
340            })
341            .unwrap_or_default()
342    }
343
344    /// Returns the layout constraints of the window
345    pub fn layout_constraints(&self) -> LayoutConstraints {
346        let component = self.0.component();
347        let component = ItemTreeRc::borrow_pin(&component);
348        let h = component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
349        let v = component.as_ref().layout_info(crate::layout::Orientation::Vertical);
350        let (min, max) = crate::layout::min_max_size_for_layout_constraints(h, v);
351        LayoutConstraints {
352            min,
353            max,
354            preferred: crate::api::LogicalSize::new(
355                h.preferred_bounded() as f32,
356                v.preferred_bounded() as f32,
357            ),
358        }
359    }
360
361    /// Returns true if the window should be shown fullscreen; false otherwise.
362    #[deprecated(note = "Please use `is_fullscreen` instead")]
363    pub fn fullscreen(&self) -> bool {
364        self.is_fullscreen()
365    }
366
367    /// Returns true if the window should be shown fullscreen; false otherwise.
368    pub fn is_fullscreen(&self) -> bool {
369        self.0.is_fullscreen()
370    }
371
372    /// true if the window is in a maximized state, otherwise false
373    pub fn is_maximized(&self) -> bool {
374        self.0.is_maximized()
375    }
376
377    /// true if the window is in a minimized state, otherwise false
378    pub fn is_minimized(&self) -> bool {
379        self.0.is_minimized()
380    }
381}
382
383struct WindowPropertiesTracker {
384    window_adapter_weak: Weak<dyn WindowAdapter>,
385}
386
387impl crate::properties::PropertyDirtyHandler for WindowPropertiesTracker {
388    fn notify(self: Pin<&Self>) {
389        let win = self.window_adapter_weak.clone();
390        crate::timers::Timer::single_shot(Default::default(), move || {
391            if let Some(window_adapter) = win.upgrade() {
392                WindowInner::from_pub(window_adapter.window()).update_window_properties();
393            };
394        })
395    }
396}
397
398pub(crate) struct PopupWindowPropertiesTracker {
399    /// Weak reference to the parent window that owns the active_popups list
400    parent_window_adapter_weak: Weak<dyn WindowAdapter>,
401    /// ID of the popup this tracker belongs to, used to re-evaluate after notification
402    popup_id: NonZeroU32,
403}
404
405impl crate::properties::PropertyDirtyHandler for PopupWindowPropertiesTracker {
406    fn notify(self: Pin<&Self>) {
407        let parent = self.parent_window_adapter_weak.clone();
408        let popup_id = self.popup_id;
409        // Use a timer here, so if we change multiple properties at the same time not multiple notifications are send
410        // This timer will delay for the next evaluation
411        crate::timers::Timer::single_shot(Default::default(), move || {
412            if let Some(parent_adapter) = parent.upgrade() {
413                WindowInner::from_pub(parent_adapter.window()).update_popup_properties(popup_id);
414            }
415        });
416    }
417}
418
419struct WindowRedrawTracker {
420    window_adapter_weak: Weak<dyn WindowAdapter>,
421}
422
423impl crate::properties::PropertyDirtyHandler for WindowRedrawTracker {
424    fn notify(self: Pin<&Self>) {
425        if let Some(window_adapter) = self.window_adapter_weak.upgrade() {
426            window_adapter.request_redraw();
427        };
428    }
429}
430
431/// This enum describes the different ways a popup can be rendered by the back-end.
432pub enum PopupWindowLocation {
433    /// The popup is rendered in its own top-level window that is know to the windowing system.
434    TopLevel(Rc<dyn WindowAdapter>),
435    /// The popup is rendered as an embedded child window at the given position.
436    ChildWindow(LogicalPoint),
437}
438
439/// This structure defines a graphical element that is designed to pop up from the surrounding
440/// UI content, for example to show a context menu.
441pub struct PopupWindow {
442    /// The ID of the associated popup.
443    pub popup_id: NonZeroU32,
444    /// The location defines where the pop up is rendered.
445    pub location: PopupWindowLocation,
446    /// The component that is responsible for providing the popup content.
447    pub component: ItemTreeRc,
448    /// Defines the close behavior of the popup.
449    pub close_policy: PopupClosePolicy,
450    /// the item that had the focus in the parent window when the popup was opened
451    focus_item_in_parent: ItemWeak,
452    /// The item from where the Popup was invoked from
453    pub parent_item: ItemWeak,
454    /// Overlay tooltip: no focus steal, unclamped placement, skipped in main mouse routing.
455    /// Context / popup menu: participates in menu-chain hit testing and cascading close.
456    pub window_kind: WindowKind,
457    /// Callback that returns the current desired logical position of the popup.
458    /// Called during re-evaluation of the position tracker to re-subscribe to dependencies.
459    /// IMPORTANT: This position is relative to the parent
460    position_access: Box<dyn Fn() -> LogicalPosition>,
461    /// Keeps the parent component's `PopupWindow::is-open` property in sync. Provided to
462    /// [`WindowInner::show_popup`], invoked with `true` when the popup is shown and with `false` when
463    /// this `PopupWindow` is dropped (see the `Drop` impl below). It is a no-op for popups whose
464    /// parent does not read `is-open` (menus and tooltips).
465    is_open_setter: Box<dyn Fn(bool)>,
466    // tracks all relevant properties and reacts on changes
467    properties_tracker: Pin<Box<PropertyTracker<true, PopupWindowPropertiesTracker>>>,
468}
469
470impl Drop for PopupWindow {
471    fn drop(&mut self) {
472        // Dropping the `PopupWindow` is the single choke point that every close path funnels through
473        // (click-outside, selection, programmatic `close()`, sibling replacement, window change,
474        // Escape, and tearing down the window itself), so flip the parent's `is-open` back to false
475        // here rather than in any individual close function.
476        (self.is_open_setter)(false);
477    }
478}
479
480#[pin_project::pin_project]
481struct WindowPinnedFields {
482    #[pin]
483    redraw_tracker: PropertyTracker<false, WindowRedrawTracker>,
484    /// Gets dirty when the layout restrictions, or some other property of the windows change
485    #[pin]
486    window_properties_tracker: PropertyTracker<true, WindowPropertiesTracker>,
487    #[pin]
488    scale_factor: Property<f32>,
489    #[pin]
490    active: Property<bool>,
491    #[pin]
492    text_input_focused: Property<bool>,
493    #[pin]
494    menubar_shortcuts: Property<SharedVector<MenuEntry>>,
495}
496
497/// The outcome of dispatching a [`MouseEvent`] through [`WindowInner::process_mouse_input`].
498#[derive(Copy, Clone, Debug)]
499pub struct MouseDispatchResult {
500    /// For `MouseEvent::DragMove` / `MouseEvent::Drop` events, the action negotiated with
501    /// the accepting `DropArea` (or `None` if no `DropArea` accepted). Always `None` for
502    /// other event kinds.
503    pub drag_action: Option<crate::items::DragAction>,
504    /// `true` if an item consumed the event (`EventAccepted`, `GrabMouse`, `StartDrag`, or
505    /// a `DropArea` accepting a drag/drop). `false` if the event fell through without a taker.
506    pub accepted: bool,
507}
508
509/// Inner datastructure for the [`crate::api::Window`]
510pub struct WindowInner {
511    window_adapter_weak: Weak<dyn WindowAdapter>,
512    component: RefCell<ItemTreeWeak>,
513    /// When the window is visible, keep a strong reference
514    strong_component_ref: RefCell<Option<ItemTreeRc>>,
515    mouse_input_state: Cell<MouseInputState>,
516    touch_state: RefCell<TouchState>,
517
518    /// ItemRC that currently have the focus (possibly an instance of TextInput)
519    pub focus_item: RefCell<crate::item_tree::ItemWeak>,
520    /// The last text that was sent to the input method
521    pub(crate) last_ime_text: RefCell<SharedString>,
522    /// Don't let ComponentContainers's instantiation change the focus.
523    /// This is a workaround for a recursion when instantiating ComponentContainer because the
524    /// init code for the component might have code that sets the focus, but we don't want that
525    /// for the ComponentContainer
526    pub(crate) prevent_focus_change: Cell<bool>,
527    cursor_blinker: RefCell<pin_weak::rc::PinWeak<crate::input::TextCursorBlinker>>,
528
529    pinned_fields: Pin<Box<WindowPinnedFields>>,
530
531    menubar: RefCell<Option<vtable::VWeak<MenuVTable>>>,
532
533    /// Stack of currently active popups
534    pub active_popups: RefCell<Vec<PopupWindow>>,
535    next_popup_id: Cell<NonZeroU32>,
536    had_popup_on_press: Cell<bool>,
537    close_requested: Callback<(), CloseRequestResponse>,
538    click_state: ClickState,
539    ctx: core::cell::OnceCell<crate::SlintContext>,
540}
541
542impl Drop for WindowInner {
543    fn drop(&mut self) {
544        if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() {
545            existing_blinker.stop();
546        }
547    }
548}
549
550impl WindowInner {
551    /// Create a new instance of the window, given the window_adapter factory fn
552    pub fn new(window_adapter_weak: Weak<dyn WindowAdapter>) -> Self {
553        #![allow(unused_mut)]
554
555        let mut window_properties_tracker =
556            PropertyTracker::new_with_dirty_handler(WindowPropertiesTracker {
557                window_adapter_weak: window_adapter_weak.clone(),
558            });
559
560        let mut redraw_tracker = PropertyTracker::new_with_dirty_handler(WindowRedrawTracker {
561            window_adapter_weak: window_adapter_weak.clone(),
562        });
563
564        #[cfg(slint_debug_property)]
565        {
566            window_properties_tracker
567                .set_debug_name("i_slint_core::Window::window_properties_tracker".into());
568            redraw_tracker.set_debug_name("i_slint_core::Window::redraw_tracker".into());
569        }
570
571        Self {
572            window_adapter_weak,
573            component: Default::default(),
574            strong_component_ref: Default::default(),
575            mouse_input_state: Default::default(),
576            touch_state: Default::default(),
577            pinned_fields: Box::pin(WindowPinnedFields {
578                redraw_tracker,
579                window_properties_tracker,
580                scale_factor: Property::new_named(1., "i_slint_core::Window::scale_factor"),
581                active: Property::new_named(false, "i_slint_core::Window::active"),
582                text_input_focused: Property::new_named(
583                    false,
584                    "i_slint_core::Window::text_input_focused",
585                ),
586                menubar_shortcuts: Property::new_named(
587                    SharedVector::default(),
588                    "i_slint_core::Window::menubar_shortcuts",
589                ),
590            }),
591            focus_item: Default::default(),
592            last_ime_text: Default::default(),
593            cursor_blinker: Default::default(),
594            active_popups: Default::default(),
595            next_popup_id: Cell::new(NonZeroU32::MIN),
596            had_popup_on_press: Default::default(),
597            close_requested: Default::default(),
598            click_state: ClickState::default(),
599            prevent_focus_change: Default::default(),
600            ctx: Default::default(),
601            menubar: Default::default(),
602        }
603    }
604
605    /// Associates this window with the specified component. Further event handling and rendering, etc. will be
606    /// done with that component.
607    pub fn set_component(&self, component: &ItemTreeRc) {
608        self.close_all_popups();
609        self.focus_item.replace(Default::default());
610        self.mouse_input_state.replace(Default::default());
611        self.touch_state.replace(Default::default());
612        self.component.replace(ItemTreeRc::downgrade(component));
613        self.pinned_fields.window_properties_tracker.set_dirty(); // component changed, layout constraints for sure must be re-calculated
614        let window_adapter = self.window_adapter();
615        window_adapter.renderer().set_window_adapter(&window_adapter);
616        let scale_factor = self.scale_factor();
617        self.set_window_item_geometry(window_adapter.size().to_logical(scale_factor).to_euclid());
618        let inset = window_adapter
619            .internal(crate::InternalToken)
620            .map(|internal| internal.safe_area_inset())
621            .unwrap_or_default();
622        self.set_window_item_safe_area(inset.to_logical(scale_factor));
623        window_adapter.request_redraw();
624        let weak = Rc::downgrade(&window_adapter);
625        crate::timers::Timer::single_shot(Default::default(), move || {
626            if let Some(window_adapter) = weak.upgrade() {
627                WindowInner::from_pub(window_adapter.window()).update_window_properties();
628            }
629        })
630    }
631
632    /// return the component.
633    /// Panics if it wasn't set.
634    pub fn component(&self) -> ItemTreeRc {
635        self.component.borrow().upgrade().unwrap()
636    }
637
638    /// returns the component or None if it isn't set.
639    pub fn try_component(&self) -> Option<ItemTreeRc> {
640        self.component.borrow().upgrade()
641    }
642
643    /// Walk the component tree and every active popup to materialize every
644    /// Repeater, Conditional and ComponentContainer.  Runs change handlers
645    /// and the instantiation pass in a loop because init callbacks may set
646    /// properties that trigger change handlers, and change handlers may
647    /// make new conditionals/repeaters dirty.
648    pub fn ensure_tree_instantiated(&self) {
649        // Instantiation runs first so that ListView's ensure_updated_listview
650        // sees the model property before any change handler can reset it.
651        for _ in 0..10 {
652            let mut changed = false;
653            if let Some(component) = self.try_component() {
654                changed |= crate::item_tree::ensure_item_tree_instantiated(&component);
655            }
656            for popup in self.active_popups.borrow().iter() {
657                changed |= crate::item_tree::ensure_item_tree_instantiated(&popup.component);
658            }
659            changed |= crate::properties::ChangeTracker::run_change_handlers_once();
660            if !changed {
661                return;
662            }
663        }
664        crate::debug_log!("Slint: long callback/instantiation chain detected");
665    }
666
667    /// Returns a slice of the active popups.
668    pub fn active_popups(&self) -> core::cell::Ref<'_, [PopupWindow]> {
669        core::cell::Ref::map(self.active_popups.borrow(), |v| v.as_slice())
670    }
671
672    /// Receive a mouse event and pass it to the items of the component to
673    /// change their state.
674    ///
675    /// Returns `None` when there is no component to dispatch to; otherwise returns a
676    /// [`MouseDispatchResult`] carrying:
677    /// - `accepted`: whether an item consumed the event, and
678    /// - `drag_action`: for `DragMove`/`Drop` events, the negotiated
679    ///   [`DragAction`](crate::items::DragAction) (or `None` if no `DropArea` accepted).
680    ///
681    /// Note: when a drag is in flight, the runtime rewrites a `Released` into either a
682    /// `Drop` (if a `DropArea` had previously accepted the matching `DragMove`) or an
683    /// `Exit` (if not). The reported `accepted` reflects the rewritten event, so a
684    /// `Released` that completes a drop on a non-accepting target reports `accepted = false`.
685    pub fn process_mouse_input(&self, mut event: MouseEvent) -> Option<MouseDispatchResult> {
686        crate::animations::update_animations();
687
688        let item_tree = self.try_component()?;
689        self.ensure_tree_instantiated();
690
691        // handle multiple press release
692        event = self.click_state.check_repeat(event, self.context().platform().click_interval());
693
694        let window_adapter = self.window_adapter();
695        let mut mouse_input_state = self.mouse_input_state.take();
696
697        let was_dragging = mouse_input_state.drag_data.is_some();
698        let old_cursor = core::mem::replace(&mut mouse_input_state.cursor, MouseCursor::Default);
699
700        // drag-finished firing is deferred until after dispatch so the DropArea has had
701        // a chance to fire its own `dropped` callback first; that callback returns the
702        // final action, which the runtime then forwards to the source.
703        let mut pending_drag_finished: Option<(
704            crate::item_tree::ItemWeak,
705            Option<crate::item_tree::ItemWeak>,
706        )> = None;
707
708        if let Some(DragData { event: mut drop_event, allowed }) =
709            mouse_input_state.drag_data.clone()
710        {
711            match &event {
712                MouseEvent::Released { position, button: PointerEventButton::Left, .. } => {
713                    mouse_input_state.drag_data = None;
714                    let source = mouse_input_state.drag_source.take();
715                    if let Some(target_weak) = mouse_input_state.drop_target.take() {
716                        // Seed `proposed-action` for the dropped callback with the action the
717                        // target last chose during hover; the callback's return value will
718                        // become the final action reported to the source.
719                        let hovered = target_weak
720                            .upgrade()
721                            .and_then(|t| t.downcast::<crate::items::DropArea>())
722                            .map(|d| d.as_pin_ref().current_action())
723                            .unwrap_or(crate::items::DragAction::None);
724                        drop_event.proposed_action = hovered;
725                        drop_event.position = crate::lengths::logical_position_to_api(*position);
726                        event = MouseEvent::Drop { event: drop_event, allowed };
727                        if let Some(s) = source {
728                            pending_drag_finished = Some((s, Some(target_weak)));
729                        }
730                    } else {
731                        // No DropArea accepted the most recent DragMove. Tear the drag
732                        // down via Exit instead of converting to Drop so a non-accepting
733                        // DropArea under the cursor doesn't fire `dropped`, and so the
734                        // underlying Release doesn't reach hit-tested items as a
735                        // spurious click.
736                        event = MouseEvent::Exit;
737                        if let Some(s) = source {
738                            pending_drag_finished = Some((s, None));
739                        }
740                    }
741                }
742                MouseEvent::Moved { position, .. } => {
743                    drop_event.position = crate::lengths::logical_position_to_api(*position);
744                    // Recompute the proposed action from current modifier state so the target's
745                    // `can-drop` callback sees an up-to-date `event.proposed-action`.
746                    drop_event.proposed_action = crate::items::compute_proposed_action(
747                        self.context().0.modifiers.get().into(),
748                        allowed,
749                    );
750                    // Mirror the position and proposed action into the persistent state so the
751                    // renderer can place the drag-image overlay without re-deriving the cursor
752                    // location, and so a subsequent synthetic Moved (e.g. fired from a modifier
753                    // key press) starts from the right position.
754                    if let Some(d) = mouse_input_state.drag_data.as_mut() {
755                        d.event.position = drop_event.position;
756                        d.event.proposed_action = drop_event.proposed_action;
757                    }
758                    mouse_input_state.cursor = MouseCursor::NoDrop;
759                    event = MouseEvent::DragMove { event: drop_event, allowed };
760                }
761                MouseEvent::Exit => {
762                    mouse_input_state.drag_data = None;
763                    mouse_input_state.drop_target = None;
764                    if let Some(s) = mouse_input_state.drag_source.take() {
765                        pending_drag_finished = Some((s, None));
766                    }
767                }
768                _ => {}
769            }
770        }
771
772        let pressed_event = matches!(event, MouseEvent::Pressed { .. });
773        let released_event = matches!(event, MouseEvent::Released { .. });
774        let had_delay = mouse_input_state.has_delayed_event();
775
776        let last_top_item = mouse_input_state.top_item_including_delayed();
777        if released_event {
778            mouse_input_state =
779                crate::input::process_delayed_event(&window_adapter, mouse_input_state);
780        }
781
782        let parent_adapter = window_adapter
783            .internal(crate::InternalToken)
784            .and_then(|internal| internal.get_parent())
785            .unwrap_or_else(|| window_adapter.clone());
786        let active_popups = &WindowInner::from_pub(parent_adapter.window()).active_popups;
787        let native_popup_index = active_popups.borrow().iter().position(|p| {
788            if let PopupWindowLocation::TopLevel(wa) = &p.location {
789                Rc::ptr_eq(wa, &window_adapter)
790            } else {
791                false
792            }
793        });
794
795        if pressed_event {
796            self.had_popup_on_press.set(!active_popups.borrow().is_empty());
797        }
798
799        let mut popup_to_close = active_popups.borrow().last().and_then(|popup| {
800            let mouse_inside_popup = || {
801                if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
802                    event.position().is_none_or(|pos| {
803                        ItemTreeRc::borrow_pin(&popup.component)
804                            .as_ref()
805                            .item_geometry(0)
806                            .contains(pos - coordinates.to_vector())
807                    })
808                } else {
809                    native_popup_index.is_some_and(|idx| idx == active_popups.borrow().len() - 1)
810                        && event.position().is_none_or(|pos| {
811                            ItemTreeRc::borrow_pin(&item_tree)
812                                .as_ref()
813                                .item_geometry(0)
814                                .contains(pos)
815                        })
816                }
817            };
818            match popup.close_policy {
819                PopupClosePolicy::CloseOnClick => {
820                    let mouse_inside_popup = mouse_inside_popup();
821                    (mouse_inside_popup && released_event && self.had_popup_on_press.get())
822                        || (!mouse_inside_popup && pressed_event)
823                }
824                PopupClosePolicy::CloseOnClickOutside => !mouse_inside_popup() && pressed_event,
825                PopupClosePolicy::NoAutoClose => false,
826            }
827            .then_some(popup.popup_id)
828        });
829
830        let grab_result =
831            crate::input::handle_mouse_grab(&event, &window_adapter, &mut mouse_input_state);
832        let grab_accepted = grab_result.accepted;
833
834        let mut dispatch_accepted = false;
835        mouse_input_state = if let Some(mut event) = grab_result.event {
836            // The grab handler may have fired callbacks that modified models or
837            // other state, so materialize any pending repeater/conditional
838            // changes before hit-testing with the returned event.
839            self.ensure_tree_instantiated();
840            let mut item_tree = self.component.borrow().upgrade();
841            let mut offset = LogicalPoint::default();
842            let mut menubar_item = None;
843            for (idx, popup) in active_popups.borrow().iter().enumerate().rev() {
844                if matches!(popup.window_kind, WindowKind::ToolTip) {
845                    continue;
846                }
847                item_tree = None;
848                menubar_item = None;
849                if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
850                    let geom = ItemTreeRc::borrow_pin(&popup.component).as_ref().item_geometry(0);
851                    let mouse_inside_popup = event
852                        .position()
853                        .is_none_or(|pos| geom.contains(pos - coordinates.to_vector()));
854                    if mouse_inside_popup {
855                        item_tree = Some(popup.component.clone());
856                        offset = *coordinates;
857                        break;
858                    }
859                } else if native_popup_index.is_some_and(|i| i == idx) {
860                    item_tree = self.component.borrow().upgrade();
861                    break;
862                }
863
864                if !matches!(popup.window_kind, WindowKind::Menu) {
865                    break;
866                } else if popup_to_close.is_some() {
867                    // clicking outside of a popup menu should close all the menus
868                    popup_to_close = Some(popup.popup_id);
869                }
870
871                menubar_item = popup.parent_item.upgrade();
872            }
873
874            let root = match menubar_item {
875                None => item_tree.map(|item_tree| ItemRc::new_root(item_tree.clone())),
876                Some(menubar_item) => {
877                    event.translate(
878                        menubar_item
879                            .map_to_item_tree(Default::default(), &self.component())
880                            .to_vector(),
881                    );
882                    menubar_item.parent_item(ParentItemTraversalMode::StopAtPopups)
883                }
884            };
885
886            if let Some(root) = root {
887                event.translate(-offset.to_vector());
888                let crate::input::MouseInputResult { mut state, accepted } =
889                    crate::input::process_mouse_input(
890                        root,
891                        &event,
892                        &window_adapter,
893                        mouse_input_state,
894                    );
895                state.offset = offset;
896                dispatch_accepted = accepted;
897                state
898            } else {
899                // When outside, send exit event
900                let mut new_input_state = MouseInputState::default();
901                crate::input::send_exit_events(
902                    &mouse_input_state,
903                    &mut new_input_state,
904                    event.position(),
905                    &window_adapter,
906                );
907                new_input_state
908            }
909        } else {
910            mouse_input_state
911        };
912
913        let accepted = dispatch_accepted | grab_accepted;
914
915        if last_top_item != mouse_input_state.top_item_including_delayed() {
916            self.click_state.reset();
917            self.click_state.check_repeat(event, self.context().platform().click_interval());
918        }
919
920        if !had_delay && mouse_input_state.has_delayed_event() {
921            // A delay was just set up, preserve the old cursor
922            mouse_input_state.cursor = old_cursor;
923        } else if old_cursor != mouse_input_state.cursor
924            && let Some(window_adapter) = window_adapter.internal(crate::InternalToken)
925        {
926            window_adapter.set_mouse_cursor(mouse_input_state.cursor);
927        }
928
929        let is_dragging = mouse_input_state.drag_data.is_some();
930        let drag_action = mouse_input_state.drop_target_action();
931        self.mouse_input_state.set(mouse_input_state);
932
933        // The drag-image overlay follows the cursor and lives outside any item tree, so
934        // partial renderers won't otherwise know to repaint it on mouse motion or after
935        // the drag ends. `render_drag_image_overlay` marks its painted rect dirty itself,
936        // we just need to schedule the redraw.
937        if was_dragging || is_dragging {
938            window_adapter.request_redraw();
939        }
940
941        if let Some((source_weak, target_weak)) = pending_drag_finished
942            && let Some(source) = source_weak.upgrade()
943            && let Some(drag_area) = source.downcast::<crate::items::DragArea>()
944        {
945            // The action `dropped` returned is now sitting on the target's `current_action`.
946            // For a cancelled drag (no target) we just report None.
947            let target = target_weak
948                .and_then(|w| w.upgrade())
949                .and_then(|i| i.downcast::<crate::items::DropArea>());
950            let action = target
951                .as_ref()
952                .map(|d| d.as_pin_ref().current_action())
953                .unwrap_or(crate::items::DragAction::None);
954            let drag_area = drag_area.as_pin_ref();
955            drag_area.dragging.set(false);
956            crate::items::DragArea::FIELD_OFFSETS
957                .drag_finished()
958                .apply_pin(drag_area)
959                .call(&(action,));
960            // The drag is over: reset the target's `current_action` so it matches
961            // `has_drag` and the docstring ("none when no drag is hovering").
962            if let Some(target) = target {
963                target.as_pin_ref().current_action.set(crate::items::DragAction::None);
964            }
965        }
966
967        if let Some(popup_id) = popup_to_close {
968            WindowInner::from_pub(parent_adapter.window()).close_popup(popup_id);
969        }
970
971        self.ensure_tree_instantiated();
972
973        Some(MouseDispatchResult { drag_action, accepted })
974    }
975
976    /// Receive a raw touch event from a backend and either forward it as a mouse
977    /// event (single finger) or synthesize `PinchGesture`/`RotationGesture` events
978    /// (two fingers), producing the same events as platform gesture recognition.
979    ///
980    /// `position` must be in **logical coordinates** (i.e., already divided by the
981    /// scale factor). Passing physical coordinates will produce incorrect gesture
982    /// geometry and hit-testing.
983    ///
984    /// `drag_action` is taken from the *last* sub-event (including `None`), because
985    /// `drag_action` reflects the current drop-target negotiation, not a per-event
986    /// verdict to aggregate. For touch sequences that never produce a `DragMove`/`Drop`
987    /// (the common case), this stays `None` throughout.
988    pub fn process_touch_input(
989        &self,
990        id: i32,
991        position: LogicalPoint,
992        phase: TouchPhase,
993    ) -> Option<MouseDispatchResult> {
994        let events = self.touch_state.borrow_mut().process(id, position, phase);
995        let mut aggregate: Option<MouseDispatchResult> = None;
996        for event in events.into_iter() {
997            if let Some(r) = self.process_mouse_input(event) {
998                let agg = aggregate
999                    .get_or_insert(MouseDispatchResult { drag_action: None, accepted: false });
1000                agg.accepted |= r.accepted;
1001                agg.drag_action = r.drag_action;
1002            }
1003        }
1004        aggregate
1005    }
1006
1007    /// Called by the input code's internal timer to send an event that was delayed
1008    pub(crate) fn process_delayed_event(&self) {
1009        self.mouse_input_state.set(crate::input::process_delayed_event(
1010            &self.window_adapter(),
1011            self.mouse_input_state.take(),
1012        ));
1013    }
1014
1015    /// Receive a key event and pass it to the items of the component to
1016    /// change their state.
1017    ///
1018    /// Arguments:
1019    /// * `event`: The key event received by the windowing system.
1020    pub fn process_key_input(
1021        &self,
1022        mut internal_key_event: InternalKeyEvent,
1023    ) -> crate::input::KeyEventResult {
1024        self.ensure_tree_instantiated();
1025        // NFC-normalize the event text so that shortcut matching works consistently
1026        // regardless of the composed/decomposed form the backend provides
1027        // (e.g. é as U+00E9 vs e + U+0301).
1028        // Note: icu_normalizer is currently only enabled if parley is enabled
1029        #[cfg(feature = "shared-parley")]
1030        {
1031            let normalizer = icu_normalizer::ComposingNormalizer::new_nfc();
1032            let normalized = normalizer.normalize(&internal_key_event.key_event.text);
1033            // Only replace the event text if normalization actually changed it,
1034            // to avoid unnecessary allocations.
1035            if let alloc::borrow::Cow::Owned(normalized) = normalized {
1036                internal_key_event.key_event.text = normalized.into();
1037            }
1038        }
1039
1040        if let Some(updated_modifier) = self.context().0.modifiers.get().state_update(
1041            internal_key_event.event_type == KeyEventType::KeyPressed,
1042            &internal_key_event.key_event.text,
1043        ) {
1044            // Updates the key modifiers depending on the key code and pressed state.
1045            self.context().0.modifiers.set(updated_modifier);
1046
1047            // If a drag is in flight, synthesize a Moved at the last drag position so
1048            // the new modifier state flows into `event.proposed-action` and the target's
1049            // `can-drop` re-runs — letting the user change copy/move/link with Ctrl/Shift
1050            // without having to move the mouse first.
1051            let drag_pos = {
1052                let state = self.mouse_input_state.take();
1053                let pos = state.drag_data.as_ref().map(|d| d.event.position);
1054                self.mouse_input_state.replace(state);
1055                pos
1056            };
1057            if let Some(pos) = drag_pos {
1058                self.process_mouse_input(MouseEvent::Moved {
1059                    position: crate::lengths::logical_point_from_api(pos),
1060                    touch_finger_id: 0,
1061                });
1062            }
1063        }
1064
1065        internal_key_event.key_event.modifiers =
1066            self.context().0.modifiers.get().modifiers_for(&internal_key_event);
1067
1068        // Emulate macOS menubar behavior: The OS consumes the event before it reaches any
1069        // Slint widgets. Therefore we process the menubar shortcuts here first and abort event
1070        // propagation if a shortcut matches.
1071        if self.process_menubar_shortcuts(&internal_key_event) == KeyEventResult::EventAccepted {
1072            self.ensure_tree_instantiated();
1073            return crate::input::KeyEventResult::EventAccepted;
1074        }
1075
1076        let mut item = self.focus_item.borrow().clone().upgrade();
1077
1078        if item.as_ref().is_some_and(|i| !i.is_visible()) {
1079            // Reset the focus... not great, but better than keeping it.
1080            self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation));
1081            item = None;
1082        }
1083
1084        let item_list = {
1085            let mut tmp = Vec::new();
1086            let mut item = item.clone();
1087
1088            while let Some(i) = item {
1089                tmp.push(i.clone());
1090                item = i.parent_item(ParentItemTraversalMode::StopAtPopups);
1091            }
1092
1093            tmp
1094        };
1095
1096        // Check capture_key_event (going from window to focused item):
1097        for i in item_list.iter().rev() {
1098            if i.borrow().as_ref().capture_key_event(&internal_key_event, &self.window_adapter(), i)
1099                == crate::input::KeyEventResult::EventAccepted
1100            {
1101                self.ensure_tree_instantiated();
1102                return crate::input::KeyEventResult::EventAccepted;
1103            }
1104        }
1105
1106        drop(item_list);
1107
1108        // Deliver key_event (to focused item, going up towards the window):
1109        while let Some(focus_item) = item {
1110            if focus_item.borrow().as_ref().key_event(
1111                &internal_key_event,
1112                &self.window_adapter(),
1113                &focus_item,
1114            ) == crate::input::KeyEventResult::EventAccepted
1115            {
1116                self.ensure_tree_instantiated();
1117                return crate::input::KeyEventResult::EventAccepted;
1118            }
1119            item = focus_item.parent_item(ParentItemTraversalMode::StopAtPopups);
1120        }
1121
1122        // Make Tab/Backtab handle keyboard focus
1123        let extra_mod = internal_key_event.key_event.modifiers.control
1124            || internal_key_event.key_event.modifiers.meta
1125            || internal_key_event.key_event.modifiers.alt;
1126        if internal_key_event.key_event.text.starts_with(key_codes::Tab)
1127            && !internal_key_event.key_event.modifiers.shift
1128            && !extra_mod
1129            && internal_key_event.event_type == KeyEventType::KeyPressed
1130        {
1131            self.focus_next_item();
1132            self.ensure_tree_instantiated();
1133            return crate::input::KeyEventResult::EventAccepted;
1134        } else if (internal_key_event.key_event.text.starts_with(key_codes::Backtab)
1135            || (internal_key_event.key_event.text.starts_with(key_codes::Tab)
1136                && internal_key_event.key_event.modifiers.shift))
1137            && internal_key_event.event_type == KeyEventType::KeyPressed
1138            && !extra_mod
1139        {
1140            self.focus_previous_item();
1141            self.ensure_tree_instantiated();
1142            return crate::input::KeyEventResult::EventAccepted;
1143        } else if internal_key_event.event_type == KeyEventType::KeyPressed
1144            && internal_key_event.key_event.text.starts_with(key_codes::Escape)
1145        {
1146            // Closes top most popup on ESC key pressed when policy is not no-auto-close
1147
1148            // Try to get the parent window in case `self` is the popup itself
1149            let mut adapter = self.window_adapter();
1150            let item_tree = self.component();
1151            let mut a = None;
1152            ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut a);
1153            if let Some(a) = a {
1154                adapter = a;
1155            }
1156            let window = WindowInner::from_pub(adapter.window());
1157
1158            let close_on_escape = if let Some(popup) = window.active_popups.borrow().last() {
1159                popup.close_policy == PopupClosePolicy::CloseOnClick
1160                    || popup.close_policy == PopupClosePolicy::CloseOnClickOutside
1161            } else {
1162                false
1163            };
1164
1165            if close_on_escape {
1166                window.close_top_popup();
1167            }
1168            self.ensure_tree_instantiated();
1169            return crate::input::KeyEventResult::EventAccepted;
1170        }
1171
1172        self.ensure_tree_instantiated();
1173        crate::input::KeyEventResult::EventIgnored
1174    }
1175
1176    fn process_menubar_shortcuts(
1177        &self,
1178        internal_key_event: &InternalKeyEvent,
1179    ) -> crate::input::KeyEventResult {
1180        let event_type = internal_key_event.event_type;
1181        let menubar = self.menubar.borrow().as_ref().and_then(vtable::VWeak::upgrade);
1182
1183        if (event_type == KeyEventType::KeyReleased || event_type == KeyEventType::KeyPressed)
1184            && let Some(menubar) = menubar
1185        {
1186            let shortcuts = self.pinned_fields.as_ref().project_ref().menubar_shortcuts.get();
1187            let mut matches = shortcuts
1188                .into_iter()
1189                .filter(|entry| entry.shortcut.matches(&internal_key_event.key_event));
1190            if let Some(entry) = matches.next() {
1191                if internal_key_event.event_type == KeyEventType::KeyPressed {
1192                    VRc::borrow(&menubar).activate(&entry);
1193                    if matches.next().is_some() {
1194                        crate::debug_log!(
1195                            "Warning: Ambiguous menubar shortcut: {}",
1196                            entry.shortcut
1197                        );
1198                    }
1199                }
1200                return crate::input::KeyEventResult::EventAccepted;
1201            }
1202        }
1203        crate::input::KeyEventResult::EventIgnored
1204    }
1205
1206    /// Installs a binding on the specified property that's toggled whenever the text cursor is supposed to be visible or not.
1207    pub fn set_cursor_blink_binding(&self, prop: &crate::Property<bool>) {
1208        let existing_blinker = self.cursor_blinker.borrow().clone();
1209
1210        let blinker = existing_blinker.upgrade().unwrap_or_else(|| {
1211            let new_blinker = TextCursorBlinker::new();
1212            *self.cursor_blinker.borrow_mut() =
1213                pin_weak::rc::PinWeak::downgrade(new_blinker.clone());
1214            new_blinker
1215        });
1216
1217        TextCursorBlinker::set_binding(
1218            blinker,
1219            prop,
1220            self.context().platform().cursor_flash_cycle(),
1221        );
1222    }
1223
1224    /// Sets the focus to the item pointed to by item_ptr. This will remove the focus from any
1225    /// currently focused item. If set_focus is false, the focus is cleared.
1226    pub fn set_focus_item(&self, new_focus_item: &ItemRc, set_focus: bool, reason: FocusReason) {
1227        if self.prevent_focus_change.get() {
1228            return;
1229        }
1230
1231        let popup_wa = self.active_popups.borrow().last().and_then(|p| match &p.location {
1232            PopupWindowLocation::TopLevel(wa) => Some(wa.clone()),
1233            PopupWindowLocation::ChildWindow(..) => None,
1234        });
1235        if let Some(popup_wa) = popup_wa {
1236            // Set the focus item on the popup's Window instead
1237            popup_wa.window().0.set_focus_item(new_focus_item, set_focus, reason);
1238            return;
1239        }
1240
1241        let current_focus_item = self.focus_item.borrow().clone();
1242        if let Some(current_focus_item_rc) = current_focus_item.upgrade() {
1243            if set_focus {
1244                if current_focus_item_rc == *new_focus_item {
1245                    // don't send focus out and in even to the same item if focus doesn't change
1246                    return;
1247                }
1248            } else if current_focus_item_rc != *new_focus_item {
1249                // can't clear focus unless called with currently focused item.
1250                return;
1251            }
1252        }
1253
1254        let old = self.take_focus_item(&FocusEvent::FocusOut(reason));
1255        let new = if set_focus {
1256            self.move_focus(new_focus_item.clone(), next_focus_item, reason)
1257        } else {
1258            None
1259        };
1260        let window_adapter = self.window_adapter();
1261        if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1262            window_adapter.handle_focus_change(old, new);
1263        }
1264    }
1265
1266    /// Take the focus_item out of this Window
1267    ///
1268    /// This sends the event which must be either FocusOut or WindowLostFocus for popups
1269    fn take_focus_item(&self, event: &FocusEvent) -> Option<ItemRc> {
1270        let focus_item = self.focus_item.take();
1271        assert!(matches!(event, FocusEvent::FocusOut(_)));
1272
1273        if let Some(focus_item_rc) = focus_item.upgrade() {
1274            focus_item_rc.borrow().as_ref().focus_event(
1275                event,
1276                &self.window_adapter(),
1277                &focus_item_rc,
1278            );
1279            Some(focus_item_rc)
1280        } else {
1281            None
1282        }
1283    }
1284
1285    /// Publish the new focus_item to this Window and return the FocusEventResult
1286    ///
1287    /// This sends a FocusIn event!
1288    fn publish_focus_item(
1289        &self,
1290        item: &Option<ItemRc>,
1291        reason: FocusReason,
1292    ) -> crate::input::FocusEventResult {
1293        match item {
1294            Some(item) => {
1295                *self.focus_item.borrow_mut() = item.downgrade();
1296                let result = item.borrow().as_ref().focus_event(
1297                    &FocusEvent::FocusIn(reason),
1298                    &self.window_adapter(),
1299                    item,
1300                );
1301                // Reveal offscreen item when it gains focus
1302                if result == crate::input::FocusEventResult::FocusAccepted {
1303                    item.try_scroll_into_visible();
1304                }
1305
1306                result
1307            }
1308            None => {
1309                *self.focus_item.borrow_mut() = Default::default();
1310                crate::input::FocusEventResult::FocusAccepted // We were removing the focus, treat that as OK
1311            }
1312        }
1313    }
1314
1315    fn move_focus(
1316        &self,
1317        start_item: ItemRc,
1318        forward: impl Fn(ItemRc) -> ItemRc,
1319        reason: FocusReason,
1320    ) -> Option<ItemRc> {
1321        let mut current_item = start_item;
1322        let mut visited = Vec::new();
1323
1324        loop {
1325            let can_receive_focus = match reason {
1326                FocusReason::Programmatic => true,
1327                FocusReason::TabNavigation => current_item.is_visible_or_clipped_by_flickable(),
1328                _ => current_item.is_visible(),
1329            };
1330            if can_receive_focus
1331                && self.publish_focus_item(&Some(current_item.clone()), reason)
1332                    == crate::input::FocusEventResult::FocusAccepted
1333            {
1334                return Some(current_item); // Item was just published.
1335            }
1336            visited.push(current_item.clone());
1337            current_item = forward(current_item);
1338
1339            if visited.contains(&current_item) {
1340                return None; // Nothing to do: We took the focus_item already
1341            }
1342        }
1343    }
1344
1345    /// Move keyboard focus to the next item
1346    pub fn focus_next_item(&self) {
1347        let start_item = self
1348            .take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation))
1349            .map(next_focus_item)
1350            .unwrap_or_else(|| {
1351                ItemRc::new(
1352                    self.active_popups
1353                        .borrow()
1354                        .last()
1355                        .map_or_else(|| self.component(), |p| p.component.clone()),
1356                    0,
1357                )
1358            });
1359        let end_item =
1360            self.move_focus(start_item.clone(), next_focus_item, FocusReason::TabNavigation);
1361        let window_adapter = self.window_adapter();
1362        if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1363            window_adapter.handle_focus_change(Some(start_item), end_item);
1364        }
1365    }
1366
1367    /// Move keyboard focus to the previous item.
1368    pub fn focus_previous_item(&self) {
1369        let start_item = previous_focus_item(
1370            self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation)).unwrap_or_else(
1371                || {
1372                    ItemRc::new(
1373                        self.active_popups
1374                            .borrow()
1375                            .last()
1376                            .map_or_else(|| self.component(), |p| p.component.clone()),
1377                        0,
1378                    )
1379                },
1380            ),
1381        );
1382        let end_item =
1383            self.move_focus(start_item.clone(), previous_focus_item, FocusReason::TabNavigation);
1384        let window_adapter = self.window_adapter();
1385        if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1386            window_adapter.handle_focus_change(Some(start_item), end_item);
1387        }
1388    }
1389
1390    /// Marks the window to be the active window. This typically coincides with the keyboard
1391    /// focus. One exception though is when a popup is shown, in which case the window may
1392    /// remain active but temporarily loose focus to the popup.
1393    ///
1394    /// This results in WindowFocusReceived and WindowFocusLost events.
1395    pub fn set_active(&self, have_focus: bool) {
1396        self.pinned_fields.as_ref().project_ref().active.set(have_focus);
1397
1398        let event = if have_focus {
1399            FocusEvent::FocusIn(FocusReason::WindowActivation)
1400        } else {
1401            FocusEvent::FocusOut(FocusReason::WindowActivation)
1402        };
1403
1404        if let Some(focus_item) = self.focus_item.borrow().upgrade() {
1405            focus_item.borrow().as_ref().focus_event(&event, &self.window_adapter(), &focus_item);
1406        }
1407
1408        // If we lost focus due to for example a global shortcut, then when we regain focus
1409        // should not assume that the modifiers are in the same state.
1410        if !have_focus {
1411            self.context().0.modifiers.take();
1412        }
1413    }
1414
1415    /// Returns true of the window is the active window. That typically implies having the
1416    /// keyboard focus, except when a popup is shown and temporarily takes the focus.
1417    pub fn active(&self) -> bool {
1418        self.pinned_fields.as_ref().project_ref().active.get()
1419    }
1420
1421    /// If the component's root item is a Window element, then this function synchronizes its properties, such as the title
1422    /// for example, with the properties known to the windowing system.
1423    pub fn update_window_properties(&self) {
1424        let window_adapter = self.window_adapter();
1425
1426        // No `if !dirty { return; }` check here because the backend window may be newly mapped and not up-to-date, so force
1427        // an evaluation.
1428        self.pinned_fields
1429            .as_ref()
1430            .project_ref()
1431            .window_properties_tracker
1432            .evaluate_as_dependency_root(|| {
1433                window_adapter.update_window_properties(WindowProperties(self));
1434            });
1435    }
1436
1437    /// Re-evaluates the position tracker for the popup with the given ID, re-subscribing to its
1438    /// property dependencies so subsequent changes continue to trigger notifications.
1439    fn update_popup_properties(&self, popup_id: NonZeroU32) {
1440        let offset = {
1441            let active_popups = self.active_popups.borrow();
1442            let Some(popup) = active_popups.iter().find(|p| p.popup_id == popup_id) else { return };
1443            if let Some(parent) = popup.parent_item.clone().upgrade() {
1444                parent.map_to_native_window(
1445                    parent.geometry().origin + (popup.position_access)().to_euclid().to_vector(),
1446                )
1447            } else {
1448                LogicalPoint::zero()
1449            }
1450        };
1451        let mut active_popups = self.active_popups.borrow_mut();
1452        let Some(popup) = active_popups.iter_mut().find(|p| p.popup_id == popup_id) else { return };
1453        match &mut popup.location {
1454            PopupWindowLocation::ChildWindow(old_location) => {
1455                let (old_popup_region, new_popup_region) =
1456                    popup.properties_tracker.as_ref().evaluate_as_dependency_root(|| {
1457                        let component = ItemTreeRc::borrow_pin(&popup.component);
1458                        let root_item = component.as_ref().get_item_ref(0);
1459                        let window_item =
1460                            ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1461                                .expect("Popup component is a Window item");
1462                        // Access the properties to set them as dependencies
1463                        let old_popup_region = LogicalRect::new(
1464                            *old_location,
1465                            crate::lengths::LogicalSize::new(
1466                                window_item.width().0,
1467                                window_item.height().0,
1468                            ),
1469                        );
1470
1471                        let width = {
1472                            let layout_info_h = component
1473                                .as_ref()
1474                                .layout_info(crate::layout::Orientation::Horizontal);
1475                            let w = layout_info_h.min.min(layout_info_h.max);
1476                            window_item.width.set(LogicalLength::new(w));
1477                            w
1478                        };
1479
1480                        let height = {
1481                            let layout_info_v = component
1482                                .as_ref()
1483                                .layout_info(crate::layout::Orientation::Vertical);
1484                            let h = layout_info_v.min.min(layout_info_v.max);
1485                            window_item.height.set(LogicalLength::new(h));
1486                            h
1487                        };
1488
1489                        let clip_region = Some(LogicalRect::new(
1490                            LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1491                            self.window_adapter()
1492                                .size()
1493                                .to_logical(self.scale_factor())
1494                                .to_euclid(),
1495                        ));
1496
1497                        let new_region_clipped = popup::place_popup(
1498                            popup::Placement::Fixed(LogicalRect::new(
1499                                offset,
1500                                crate::lengths::LogicalSize::new(width, height),
1501                            )),
1502                            &clip_region,
1503                        );
1504
1505                        (old_popup_region, new_region_clipped)
1506                    });
1507
1508                self.window_adapter().request_redraw();
1509
1510                // Set new location
1511                *old_location = new_popup_region.origin;
1512
1513                if let Some(adapter) = self.window_adapter_weak.upgrade() {
1514                    if !old_popup_region.is_empty() {
1515                        adapter.renderer().mark_dirty_region(old_popup_region.into());
1516                    }
1517
1518                    if !new_popup_region.is_empty() {
1519                        adapter.renderer().mark_dirty_region(new_popup_region.into());
1520                    }
1521                    adapter.request_redraw();
1522                }
1523            }
1524            PopupWindowLocation::TopLevel(adapter) => {
1525                // The size is already tracked in the windowadapter
1526                let mut new_position: Option<LogicalPosition> = None;
1527                popup.properties_tracker.as_ref().evaluate_as_dependency_root(|| {
1528                    (popup.position_access)(); // Dummy access to track position changes
1529                    new_position = Some(LogicalPosition::from_euclid(offset));
1530                });
1531                if let Some(pos) = new_position {
1532                    adapter.window().set_position(pos);
1533                }
1534            }
1535        }
1536    }
1537
1538    /// Calls the render_components to render the main component and any sub-window components, tracked by a
1539    /// property dependency tracker.
1540    ///
1541    /// The closure also receives a `post_render` callback. The renderer must invoke it
1542    /// once with its `ItemRenderer` after walking the components but before flushing,
1543    /// so the runtime can draw overlays that sit on top of the scene without being part
1544    /// of any item tree.
1545    ///
1546    /// Returns None if no component is set yet.
1547    pub fn draw_contents<T>(
1548        &self,
1549        render_components: impl FnOnce(
1550            &[(ItemTreeWeak, LogicalPoint)],
1551            &dyn Fn(&mut dyn crate::item_rendering::ItemRenderer),
1552        ) -> T,
1553    ) -> Option<T> {
1554        crate::properties::evaluate_no_tracking(|| self.ensure_tree_instantiated());
1555        let component_weak = ItemTreeRc::downgrade(&self.try_component()?);
1556        let post_render = |renderer: &mut dyn crate::item_rendering::ItemRenderer| {
1557            self.render_drag_image_overlay(renderer);
1558        };
1559        Some(self.pinned_fields.as_ref().project_ref().redraw_tracker.evaluate_as_dependency_root(
1560            || {
1561                if !self
1562                    .active_popups
1563                    .borrow()
1564                    .iter()
1565                    .any(|p| matches!(p.location, PopupWindowLocation::ChildWindow(..)))
1566                {
1567                    render_components(&[(component_weak, LogicalPoint::default())], &post_render)
1568                } else {
1569                    let borrow = self.active_popups.borrow();
1570                    let mut item_trees = Vec::with_capacity(borrow.len() + 1);
1571                    item_trees.push((component_weak, LogicalPoint::default()));
1572                    for popup in borrow.iter() {
1573                        // If the popup is not a real window and does not have its own coordinate system.
1574                        // We have to draw the popup and consider the location for subelements because everything must
1575                        // be rendered relative to the main window position
1576                        if let PopupWindowLocation::ChildWindow(location) = &popup.location {
1577                            item_trees.push((ItemTreeRc::downgrade(&popup.component), *location));
1578                        }
1579                    }
1580                    drop(borrow);
1581                    render_components(&item_trees, &post_render)
1582                }
1583            },
1584        ))
1585    }
1586
1587    /// Draws the source `DragArea`'s `drag-image` under the cursor when a drag is in flight.
1588    /// No-op when no drag is active or the source has no image set.
1589    ///
1590    /// Marks the painted rect dirty for partial renderers, so the next frame clears the area
1591    /// before redrawing — same trick the linuxkms cursor injection uses, no per-frame state needed.
1592    fn render_drag_image_overlay(
1593        &self,
1594        item_renderer: &mut dyn crate::item_rendering::ItemRenderer,
1595    ) {
1596        let state = self.mouse_input_state.take();
1597        let cursor = state.drag_data.as_ref().map(|d| d.event.position);
1598        let source = state.drag_source.as_ref().and_then(|w| w.upgrade());
1599        self.mouse_input_state.set(state);
1600
1601        let (Some(cursor), Some(source)) = (cursor, source) else { return };
1602        let Some(drag_area) = source.downcast::<crate::items::DragArea>() else { return };
1603        let drag_area = drag_area.as_pin_ref();
1604        let image = drag_area.drag_image();
1605        let size = crate::lengths::LogicalSize::from_untyped(image.size().cast());
1606        if size.is_empty() {
1607            return;
1608        }
1609        let cursor = crate::lengths::logical_point_from_api(cursor);
1610        let offset = LogicalVector::new(
1611            drag_area.drag_image_offset_x() as Coord,
1612            drag_area.drag_image_offset_y() as Coord,
1613        );
1614        let top_left = cursor - offset;
1615
1616        item_renderer.save_state();
1617        item_renderer.translate(top_left.to_vector());
1618        item_renderer.draw_image_direct(image);
1619        item_renderer.restore_state();
1620
1621        self.window_adapter().renderer().mark_dirty_region(LogicalRect::new(top_left, size).into());
1622    }
1623
1624    /// Registers the window with the windowing system, in order to render the component's items and react
1625    /// to input events once the event loop spins.
1626    pub fn show(&self) -> Result<(), PlatformError> {
1627        if let Some(component) = self.try_component() {
1628            let was_visible = self.strong_component_ref.replace(Some(component)).is_some();
1629            if !was_visible {
1630                self.context().acquire_keepalive();
1631            }
1632        }
1633
1634        self.ensure_tree_instantiated();
1635        self.update_window_properties();
1636        self.window_adapter().set_visible(true)?;
1637        // Make sure that the window's inner size is in sync with the root window item's
1638        // width/height.
1639        let size = self.window_adapter().size();
1640        let scale_factor = self.scale_factor();
1641        self.set_window_item_geometry(size.to_logical(scale_factor).to_euclid());
1642        let inset = self
1643            .window_adapter()
1644            .internal(crate::InternalToken)
1645            .map(|internal| internal.safe_area_inset())
1646            .unwrap_or_default();
1647        self.set_window_item_safe_area(inset.to_logical(scale_factor));
1648        self.window_adapter().renderer().resize(size).unwrap();
1649        if let Some(hook) = self.context().0.window_shown_hook.borrow_mut().as_mut() {
1650            hook(&self.window_adapter());
1651        }
1652        Ok(())
1653    }
1654
1655    /// De-registers the window with the windowing system.
1656    pub fn hide(&self) -> Result<(), PlatformError> {
1657        let result = self.window_adapter().set_visible(false);
1658        let was_visible = self.strong_component_ref.borrow_mut().take().is_some();
1659        if was_visible {
1660            self.context().release_keepalive();
1661        }
1662        result
1663    }
1664
1665    /// Return whether the platform supports native menu bars
1666    pub fn supports_native_menu_bar(&self) -> bool {
1667        self.window_adapter()
1668            .internal(crate::InternalToken)
1669            .is_some_and(|x| x.supports_native_menu_bar())
1670    }
1671
1672    /// Setup the native menu bar
1673    pub fn setup_menubar(&self, menubar: vtable::VRc<MenuVTable>) {
1674        if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1675            x.setup_menubar(menubar);
1676        }
1677    }
1678
1679    /// Setup the shortcuts for the menubar
1680    /// Note: We still register the same shortcuts if the native menubar is active.
1681    /// Generally, the native menubar should capture the shortcuts first,
1682    /// but in case it doesn't, the window can still match them manually.
1683    pub fn setup_menubar_shortcuts(&self, menubar: VRc<MenuVTable>) {
1684        *self.menubar.borrow_mut() = Some(VRc::downgrade(&menubar));
1685        let weak = VRc::downgrade(&menubar);
1686        self.pinned_fields.menubar_shortcuts.set_binding(move || {
1687            fn flatten_menu(
1688                root: vtable::VRef<'_, MenuVTable>,
1689                parent: Option<&MenuEntry>,
1690            ) -> SharedVector<MenuEntry> {
1691                let mut menu_entries = Default::default();
1692                root.sub_menu(parent, &mut menu_entries);
1693
1694                let mut result = menu_entries.clone();
1695
1696                for entry in menu_entries {
1697                    result.extend(flatten_menu(root, Some(&entry)));
1698                }
1699                result
1700            }
1701
1702            let Some(menubar) = weak.upgrade() else {
1703                return SharedVector::default();
1704            };
1705            flatten_menu(VRc::borrow(&menubar), None)
1706                .into_iter()
1707                .filter(|entry| entry.enabled && entry.shortcut != Keys::default())
1708                .collect()
1709        });
1710    }
1711    /// Create a new popup window adapter
1712    /// This window adapter can be used on a popup component and shown with show_popup()
1713    pub fn create_child_window_adapter(&self, kind: WindowKind) -> Option<Rc<dyn WindowAdapter>> {
1714        self.window_adapter()
1715            .internal(crate::InternalToken)
1716            .and_then(|s| s.create_child_window_adapter(kind))
1717    }
1718
1719    /// Show a popup at the given position relative to the `parent_item` and returns its ID.
1720    /// The returned ID will always be non-zero.
1721    ///
1722    /// `is_open_setter` keeps the parent component's `PopupWindow::is-open` property in sync with this
1723    /// popup: it is invoked immediately with `true`, and again with `false` when the popup is closed
1724    /// through any path (the `Drop` impl of [`PopupWindow`] handles the `false`). Pass a no-op closure
1725    /// for popups (such as menus) that do not expose `is-open`.
1726    pub fn show_popup(
1727        &self,
1728        popup_componentrc: &ItemTreeRc,
1729        popup_access_position: Box<dyn Fn() -> LogicalPosition>,
1730        close_policy: PopupClosePolicy,
1731        parent_item: &ItemRc,
1732        window_kind: WindowKind,
1733        is_open_setter: Box<dyn Fn(bool)>,
1734    ) -> NonZeroU32 {
1735        // Popups live in their own ItemTree, which was invisible to any
1736        // earlier instantiation pass; materialize it before the layout queries below.
1737        crate::item_tree::ensure_item_tree_instantiated(popup_componentrc);
1738        let position = parent_item.map_to_native_window(
1739            parent_item.geometry().origin + popup_access_position().to_euclid().to_vector(),
1740        );
1741        let popup_component = ItemTreeRc::borrow_pin(popup_componentrc);
1742        let popup_root = popup_component.as_ref().get_item_ref(0);
1743
1744        let (mut w, mut h) = if let Some(window_item) =
1745            ItemRef::downcast_pin::<crate::items::WindowItem>(popup_root)
1746        {
1747            (window_item.width(), window_item.height())
1748        } else {
1749            (LogicalLength::zero(), LogicalLength::zero())
1750        };
1751
1752        let layout_info_h =
1753            popup_component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
1754        let layout_info_v =
1755            popup_component.as_ref().layout_info(crate::layout::Orientation::Vertical);
1756
1757        if w <= LogicalLength::zero() {
1758            w = LogicalLength::new(layout_info_h.preferred);
1759        }
1760        if h <= LogicalLength::zero() {
1761            h = LogicalLength::new(layout_info_v.preferred);
1762        }
1763        w = w.max(LogicalLength::new(layout_info_h.min)).min(LogicalLength::new(layout_info_h.max));
1764        h = h.max(LogicalLength::new(layout_info_v.min)).min(LogicalLength::new(layout_info_v.max));
1765
1766        let size = crate::lengths::LogicalSize::from_lengths(w, h);
1767
1768        if let Some(window_item) = ItemRef::downcast_pin(popup_root) {
1769            let width_property =
1770                crate::items::WindowItem::FIELD_OFFSETS.width().apply_pin(window_item);
1771            let height_property =
1772                crate::items::WindowItem::FIELD_OFFSETS.height().apply_pin(window_item);
1773            width_property.set(size.width_length());
1774            height_property.set(size.height_length());
1775        };
1776
1777        let popup_id = self.next_popup_id.get();
1778        self.next_popup_id.set(popup_id.checked_add(1).unwrap());
1779        let parent_window_adapter_weak = Rc::downgrade(&self.window_adapter());
1780
1781        // Close active popups before creating a new one.
1782        let siblings: Vec<_> = self
1783            .active_popups
1784            .borrow()
1785            .iter()
1786            .filter(|p| p.parent_item == parent_item.downgrade())
1787            .map(|p| p.popup_id)
1788            .collect();
1789
1790        for sibling in siblings {
1791            self.close_popup(sibling);
1792        }
1793
1794        let root_of = |mut item_tree: ItemTreeRc| loop {
1795            if ItemRc::new_root(item_tree.clone()).downcast::<crate::items::WindowItem>().is_some()
1796            {
1797                return item_tree;
1798            }
1799            let mut r = crate::item_tree::ItemWeak::default();
1800            ItemTreeRc::borrow_pin(&item_tree).as_ref().parent_node(&mut r);
1801            match r.upgrade() {
1802                None => return item_tree,
1803                Some(x) => item_tree = x.item_tree().clone(),
1804            }
1805        };
1806
1807        let parent_root_item_tree = root_of(parent_item.item_tree().clone());
1808        let parent_window_adapter = if let Some(parent_popup) = self
1809            .active_popups
1810            .borrow()
1811            .iter()
1812            .find(|p| ItemTreeRc::ptr_eq(&p.component, &parent_root_item_tree))
1813        {
1814            // Popup in a popup
1815            match &parent_popup.location {
1816                PopupWindowLocation::TopLevel(wa) => wa.clone(),
1817                PopupWindowLocation::ChildWindow(_) => self.window_adapter(),
1818            }
1819        } else {
1820            self.window_adapter()
1821        };
1822
1823        let popup_window_adapter = {
1824            let mut popup_window_adapter = None;
1825            ItemTreeRc::borrow_pin(popup_componentrc)
1826                .as_ref()
1827                .window_adapter(false, &mut popup_window_adapter);
1828            popup_window_adapter.expect("It must be there because we set the global")
1829        };
1830
1831        // If the window adapter of the popup window and the parent window are equal, create a ChildWindow
1832        // because we weren't able to create a dedicated popup adapter (for example if the backend does not support it).
1833        let (location, properties_tracker) =
1834            if Rc::ptr_eq(&parent_window_adapter, &popup_window_adapter) {
1835                // Tooltips may extend past the window (e.g. above/left of the anchor); do not clamp.
1836                let clip_region = Some(LogicalRect::new(
1837                    LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1838                    self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
1839                ));
1840                let rect = popup::place_popup(
1841                    popup::Placement::Fixed(LogicalRect::new(position, size)),
1842                    &clip_region,
1843                );
1844                self.window_adapter().request_redraw();
1845                (
1846                    PopupWindowLocation::ChildWindow(rect.origin),
1847                    Box::pin(PropertyTracker::new_with_dirty_handler(
1848                        PopupWindowPropertiesTracker {
1849                            parent_window_adapter_weak: parent_window_adapter_weak.clone(),
1850                            popup_id,
1851                        },
1852                    )),
1853                )
1854            } else {
1855                let popup_window = popup_window_adapter.window();
1856                WindowInner::from_pub(popup_window).set_component(popup_componentrc);
1857                popup_window.set_position(LogicalPosition::from_euclid(position));
1858                popup_window.set_size(WindowSize::Logical(LogicalSize::from_euclid(size)));
1859
1860                popup_window_adapter.set_visible(true).expect("unable to show popup window");
1861                (
1862                    PopupWindowLocation::TopLevel(popup_window_adapter),
1863                    Box::pin(PropertyTracker::new_with_dirty_handler(
1864                        PopupWindowPropertiesTracker {
1865                            parent_window_adapter_weak: parent_window_adapter_weak.clone(),
1866                            popup_id,
1867                        },
1868                    )),
1869                )
1870            };
1871
1872        let focus_item = if matches!(window_kind, WindowKind::ToolTip) {
1873            Default::default()
1874        } else {
1875            self.take_focus_item(&FocusEvent::FocusOut(FocusReason::PopupActivation))
1876                .map(|item| item.downgrade())
1877                .unwrap_or_default()
1878        };
1879
1880        // Reflect the freshly shown popup in the parent's `is-open` property; the matching `false` is
1881        // emitted when the stored `PopupWindow` is dropped (see its `Drop` impl), which every close
1882        // path funnels through. Called before the popup is stored so we do not hold a borrow on
1883        // `active_popups` while running user-provided code.
1884        is_open_setter(true);
1885
1886        self.active_popups.borrow_mut().push(PopupWindow {
1887            popup_id,
1888            location,
1889            component: popup_componentrc.clone(),
1890            close_policy,
1891            focus_item_in_parent: focus_item,
1892            parent_item: parent_item.downgrade(),
1893            window_kind,
1894            position_access: popup_access_position,
1895            is_open_setter,
1896            properties_tracker,
1897        });
1898
1899        self.update_popup_properties(popup_id);
1900
1901        popup_id
1902    }
1903
1904    /// Attempt to show a native popup menu
1905    ///
1906    /// context_menu_item is an instance of a ContextMenu
1907    ///
1908    /// Returns false if the native platform doesn't support it
1909    pub fn show_native_popup_menu(
1910        &self,
1911        context_menu_item: vtable::VRc<MenuVTable>,
1912        position: LogicalPosition,
1913        parent_item: &ItemRc,
1914    ) -> bool {
1915        if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1916            let position = parent_item.map_to_native_window(
1917                parent_item.geometry().origin + position.to_euclid().to_vector(),
1918            );
1919            let position = crate::lengths::logical_position_to_api(position);
1920            x.show_native_popup_menu(context_menu_item, position)
1921        } else {
1922            false
1923        }
1924    }
1925
1926    // Close the popup associated with the given popup window.
1927    // The parent's `is-open` property is reset to false when `current_popup` is dropped (see the
1928    // `Drop` impl for `PopupWindow`), which every close path eventually does.
1929    fn close_popup_impl(&self, current_popup: &PopupWindow) {
1930        match &current_popup.location {
1931            PopupWindowLocation::ChildWindow(offset) => {
1932                // Refresh the area that was previously covered by the popup.
1933                let popup_region = crate::properties::evaluate_no_tracking(|| {
1934                    let popup_component = ItemTreeRc::borrow_pin(&current_popup.component);
1935                    popup_component.as_ref().item_geometry(0)
1936                })
1937                .translate(offset.to_vector());
1938
1939                if !popup_region.is_empty() {
1940                    let window_adapter = self.window_adapter();
1941                    window_adapter.renderer().mark_dirty_region(popup_region.into());
1942                    window_adapter.request_redraw();
1943                }
1944            }
1945            PopupWindowLocation::TopLevel(adapter) => {
1946                let _ = adapter.set_visible(false);
1947            }
1948        }
1949        if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
1950            self.set_focus_item(&focus, true, FocusReason::PopupActivation);
1951        }
1952    }
1953
1954    /// Removes the popup matching the given ID.
1955    pub fn close_popup(&self, popup_id: NonZeroU32) {
1956        let mut active_popups = self.active_popups.borrow_mut();
1957        let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
1958
1959        if let Some(popup_index) = maybe_index {
1960            let p = active_popups.remove(popup_index);
1961            drop(active_popups);
1962            self.close_popup_impl(&p);
1963            if matches!(p.window_kind, WindowKind::Menu) {
1964                // close all sub-menus
1965                while self
1966                    .active_popups
1967                    .borrow()
1968                    .get(popup_index)
1969                    .is_some_and(|p| matches!(p.window_kind, WindowKind::Menu))
1970                {
1971                    let p = self.active_popups.borrow_mut().remove(popup_index);
1972                    self.close_popup_impl(&p);
1973                }
1974            }
1975        }
1976    }
1977
1978    /// Close all active popups.
1979    pub fn close_all_popups(&self) {
1980        for popup in self.active_popups.take() {
1981            self.close_popup_impl(&popup);
1982        }
1983    }
1984
1985    /// Close the top-most popup.
1986    pub fn close_top_popup(&self) {
1987        let popup = self.active_popups.borrow_mut().pop();
1988        if let Some(popup) = popup {
1989            self.close_popup_impl(&popup);
1990        }
1991    }
1992
1993    /// Returns the scale factor set on the window, as provided by the windowing system.
1994    pub fn scale_factor(&self) -> f32 {
1995        self.pinned_fields.as_ref().project_ref().scale_factor.get()
1996    }
1997
1998    /// Sets the scale factor for the window. This is set by the backend or for testing.
1999    pub(crate) fn set_scale_factor(&self, factor: f32) {
2000        if !self.pinned_fields.scale_factor.is_constant() {
2001            self.pinned_fields.scale_factor.set(factor)
2002        }
2003    }
2004
2005    /// Sets the scale factor for the window.
2006    /// From that point on, the scale factor is constant and cannot be changed anymore.
2007    pub fn set_const_scale_factor(&self, factor: f32) {
2008        if !self.pinned_fields.scale_factor.is_constant() {
2009            self.pinned_fields.scale_factor.set(factor);
2010            self.pinned_fields.scale_factor.set_constant();
2011        }
2012    }
2013
2014    /// Reads the global property `TextInputInterface.text-input-focused`
2015    pub fn text_input_focused(&self) -> bool {
2016        self.pinned_fields.as_ref().project_ref().text_input_focused.get()
2017    }
2018
2019    /// Sets the global property `TextInputInterface.text-input-focused`
2020    pub fn set_text_input_focused(&self, value: bool) {
2021        if !value && let Some(window_adapter) = self.window_adapter().internal(crate::InternalToken)
2022        {
2023            window_adapter.input_method_request(InputMethodRequest::Disable);
2024        }
2025        self.pinned_fields.text_input_focused.set(value)
2026    }
2027
2028    /// Returns true if the window is visible
2029    pub fn is_visible(&self) -> bool {
2030        self.strong_component_ref.borrow().is_some()
2031    }
2032
2033    /// Returns the window item that is the first item in the component. When Some()
2034    /// is returned, it's guaranteed to be safe to downcast to `WindowItem`.
2035    pub fn window_item_rc(&self) -> Option<ItemRc> {
2036        self.try_component().and_then(|component_rc| {
2037            let item_rc = ItemRc::new_root(component_rc);
2038            if item_rc.downcast::<crate::items::WindowItem>().is_some() {
2039                Some(item_rc)
2040            } else {
2041                None
2042            }
2043        })
2044    }
2045
2046    /// Returns the window item that is the first item in the component.
2047    pub fn window_item(&self) -> Option<VRcMapped<ItemTreeVTable, crate::items::WindowItem>> {
2048        self.try_component().and_then(|component_rc| {
2049            ItemRc::new_root(component_rc).downcast::<crate::items::WindowItem>()
2050        })
2051    }
2052
2053    /// Sets the size of the window item. This method is typically called in response to receiving a
2054    /// window resize event from the windowing system.
2055    pub(crate) fn set_window_item_geometry(&self, size: crate::lengths::LogicalSize) {
2056        if let Some(component_rc) = self.try_component() {
2057            let component = ItemTreeRc::borrow_pin(&component_rc);
2058            let root_item = component.as_ref().get_item_ref(0);
2059            if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
2060            {
2061                window_item.width.set(size.width_length());
2062                window_item.height.set(size.height_length());
2063            }
2064        }
2065    }
2066
2067    /// The safe area of the window has changed.
2068    pub fn set_window_item_safe_area(&self, inset: crate::lengths::LogicalEdges) {
2069        if let Some(component_rc) = self.try_component() {
2070            let component = ItemTreeRc::borrow_pin(&component_rc);
2071            let root_item = component.as_ref().get_item_ref(0);
2072            if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
2073            {
2074                window_item.safe_area_insets.set(inset);
2075            }
2076        }
2077    }
2078
2079    pub(crate) fn set_window_item_virtual_keyboard(
2080        &self,
2081        origin: crate::lengths::LogicalPoint,
2082        size: crate::lengths::LogicalSize,
2083    ) {
2084        let Some(component_rc) = self.try_component() else {
2085            return;
2086        };
2087        let component = ItemTreeRc::borrow_pin(&component_rc);
2088        let root_item = component.as_ref().get_item_ref(0);
2089        let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item) else {
2090            return;
2091        };
2092        window_item.virtual_keyboard_position.set(origin);
2093        window_item.virtual_keyboard_size.set(size);
2094        if let Some(focus_item) = self.focus_item.borrow().upgrade() {
2095            focus_item.try_scroll_into_visible();
2096        }
2097    }
2098
2099    // Get geometry of the virtual keyboard if available
2100    pub(crate) fn window_item_virtual_keyboard(
2101        &self,
2102    ) -> Option<(crate::lengths::LogicalPoint, crate::lengths::LogicalSize)> {
2103        let component_rc = self.try_component()?;
2104        let component = ItemTreeRc::borrow_pin(&component_rc);
2105        let root_item = component.as_ref().get_item_ref(0);
2106        let window_item = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)?;
2107        let keyboard_size = window_item.virtual_keyboard_size();
2108        if keyboard_size.width == 0. as Coord || keyboard_size.height == 0. as Coord {
2109            None
2110        } else {
2111            Some((window_item.virtual_keyboard_position(), keyboard_size))
2112        }
2113    }
2114
2115    /// Sets the close_requested callback. The callback will be run when the user tries to close a window.
2116    pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
2117        self.close_requested.set_handler(move |()| callback());
2118    }
2119
2120    /// Runs the close_requested callback.
2121    /// If the callback returns KeepWindowShown, this function returns false. That should prevent the Window from closing.
2122    /// Otherwise it returns true, which allows the Window to hide.
2123    pub fn request_close(&self) -> bool {
2124        match self.close_requested.call(&()) {
2125            CloseRequestResponse::HideWindow => true,
2126            CloseRequestResponse::KeepWindowShown => false,
2127        }
2128    }
2129
2130    /// Returns if the window is currently in fullscreen mode
2131    pub fn is_fullscreen(&self) -> bool {
2132        if let Some(window_item) = self.window_item() {
2133            window_item.as_pin_ref().full_screen()
2134        } else {
2135            false
2136        }
2137    }
2138
2139    /// Set or unset the window to display fullscreen.
2140    pub fn set_fullscreen(&self, enabled: bool) {
2141        if let Some(window_item) = self.window_item() {
2142            window_item.as_pin_ref().full_screen.set(enabled);
2143            self.update_window_properties()
2144        }
2145    }
2146
2147    /// Returns if the window is currently maximized
2148    pub fn is_maximized(&self) -> bool {
2149        self.window_item().is_some_and(|window_item| window_item.as_pin_ref().maximized())
2150    }
2151
2152    /// Set the window as maximized or unmaximized
2153    pub fn set_maximized(&self, maximized: bool) {
2154        if let Some(window_item) = self.window_item() {
2155            window_item.as_pin_ref().maximized.set(maximized);
2156            self.update_window_properties()
2157        }
2158    }
2159
2160    /// Returns if the window is currently minimized
2161    pub fn is_minimized(&self) -> bool {
2162        self.window_item().is_some_and(|window_item| window_item.as_pin_ref().minimized())
2163    }
2164
2165    /// Set the window as minimized or unminimized
2166    pub fn set_minimized(&self, minimized: bool) {
2167        if let Some(window_item) = self.window_item() {
2168            window_item.as_pin_ref().minimized.set(minimized);
2169            self.update_window_properties()
2170        }
2171    }
2172
2173    /// Returns the (context global) xdg app id for use with wayland and x11.
2174    pub fn xdg_app_id(&self) -> Option<SharedString> {
2175        self.context().xdg_app_id()
2176    }
2177
2178    /// Returns the upgraded window adapter
2179    pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
2180        self.window_adapter_weak.upgrade().unwrap()
2181    }
2182
2183    /// Private access to the WindowInner for a given window.
2184    pub fn from_pub(window: &crate::api::Window) -> &Self {
2185        &window.0
2186    }
2187
2188    /// Provides access to the Windows' Slint context.
2189    pub fn context(&self) -> &crate::SlintContext {
2190        self.ctx
2191            .get_or_init(|| crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().unwrap().clone()))
2192    }
2193
2194    /// Like [`Self::context`], but returns `None` instead of panicking when no context is
2195    /// available yet.
2196    pub fn try_context(&self) -> Option<&crate::SlintContext> {
2197        if self.ctx.get().is_none()
2198            && let Some(ctx) = crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().cloned())
2199        {
2200            let _ = self.ctx.set(ctx);
2201        }
2202        self.ctx.get()
2203    }
2204
2205    /// Set the SlintContext.
2206    /// This needs to be called once before any other functions that would use the context.
2207    pub fn set_context(&self, ctx: crate::SlintContext) {
2208        self.ctx.set(ctx).map_err(|_| ()).expect("context shouldn't have been set before")
2209    }
2210}
2211
2212/// Internal alias for `Rc<dyn WindowAdapter>`.
2213pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
2214
2215/// Resolve the [`crate::SlintContext`] associated with a component root by
2216/// asking it for (or creating) its window adapter and reading the context off
2217/// the resulting window. Returns `None` only when no adapter can be produced.
2218pub fn context_for_root(root: &ItemTreeRc) -> Option<crate::SlintContext> {
2219    let comp_ref_pin = vtable::VRc::borrow_pin(root);
2220    let mut adapter = None;
2221    comp_ref_pin.as_ref().window_adapter(true, &mut adapter);
2222    adapter.map(|a| WindowInner::from_pub(a.window()).context().clone())
2223}
2224
2225/// Runtime entry point for `BuiltinFunction::AccentColor`. Returns the accent color
2226/// from the component's [`crate::SlintContext`] reached via its window adapter, or
2227/// transparent if none is associated.
2228pub fn accent_color(root: &crate::item_tree::ItemTreeRc) -> crate::graphics::Color {
2229    let comp_ref_pin = vtable::VRc::borrow_pin(root);
2230    let mut adapter = None;
2231    comp_ref_pin.as_ref().window_adapter(true, &mut adapter);
2232    adapter.map_or(crate::graphics::Color::default(), |a| {
2233        WindowInner::from_pub(a.window()).context().accent_color()
2234    })
2235}
2236
2237/// This module contains the functions needed to interface with the event loop and window traits
2238/// from outside the Rust language.
2239#[cfg(feature = "ffi")]
2240pub mod ffi {
2241    #![allow(unsafe_code)]
2242    #![allow(clippy::missing_safety_doc)]
2243    #![allow(missing_docs)]
2244
2245    use super::*;
2246    use crate::SharedVector;
2247    use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
2248    use crate::graphics::Size;
2249    use crate::graphics::{IntSize, Rgba8Pixel};
2250    use crate::items::WindowItem;
2251    use core::ffi::c_void;
2252
2253    /// This enum describes a low-level access to specific graphics APIs used
2254    /// by the renderer.
2255    #[repr(u8)]
2256    pub enum GraphicsAPI {
2257        /// The rendering is done using OpenGL.
2258        NativeOpenGL,
2259        /// The rendering is done using APIs inaccessible from C++, such as WGPU.
2260        Inaccessible,
2261    }
2262
2263    struct WithUserData<T> {
2264        callback: T,
2265        drop_user_data: extern "C" fn(*mut c_void),
2266        user_data: *mut c_void,
2267    }
2268
2269    impl<T> Drop for WithUserData<T> {
2270        fn drop(&mut self) {
2271            (self.drop_user_data)(self.user_data)
2272        }
2273    }
2274
2275    impl WithUserData<extern "C" fn(user_data: *mut c_void, pos: &mut LogicalPosition)> {
2276        fn call(&self) -> LogicalPosition {
2277            let mut logical_position = LogicalPosition::default();
2278            (self.callback)(self.user_data, &mut logical_position);
2279            logical_position
2280        }
2281    }
2282
2283    impl WithUserData<extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse> {
2284        fn call(&self) -> CloseRequestResponse {
2285            (self.callback)(self.user_data)
2286        }
2287    }
2288
2289    impl WithUserData<extern "C" fn(user_data: *mut c_void, is_open: bool)> {
2290        fn call(&self, is_open: bool) {
2291            (self.callback)(self.user_data, is_open)
2292        }
2293    }
2294
2295    /// Same layout as WindowAdapterRc
2296    #[repr(C)]
2297    pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
2298
2299    /// Releases the reference to the windowrc held by handle.
2300    #[unsafe(no_mangle)]
2301    pub unsafe extern "C" fn slint_windowrc_drop(handle: *mut WindowAdapterRcOpaque) {
2302        unsafe {
2303            assert_eq!(
2304                core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2305                core::mem::size_of::<WindowAdapterRcOpaque>()
2306            );
2307            assert_eq!(
2308                core::mem::size_of::<Option<Rc<dyn WindowAdapter>>>(),
2309                core::mem::size_of::<WindowAdapterRcOpaque>()
2310            );
2311            drop(core::ptr::read(handle as *mut Option<Rc<dyn WindowAdapter>>));
2312        }
2313    }
2314
2315    /// Releases the reference to the component window held by handle.
2316    #[unsafe(no_mangle)]
2317    pub unsafe extern "C" fn slint_windowrc_clone(
2318        source: *const WindowAdapterRcOpaque,
2319        target: *mut WindowAdapterRcOpaque,
2320    ) {
2321        unsafe {
2322            assert_eq!(
2323                core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2324                core::mem::size_of::<WindowAdapterRcOpaque>()
2325            );
2326            let window = &*(source as *const Rc<dyn WindowAdapter>);
2327            core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window.clone());
2328        }
2329    }
2330
2331    /// Ensure repeaters, conditionals and component containers are instantiated.
2332    #[unsafe(no_mangle)]
2333    pub unsafe extern "C" fn slint_windowrc_ensure_tree_instantiated(
2334        handle: *const WindowAdapterRcOpaque,
2335    ) {
2336        unsafe {
2337            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2338            WindowInner::from_pub(window_adapter.window()).ensure_tree_instantiated();
2339        }
2340    }
2341
2342    /// Spins an event loop and renders the items of the provided component in this window.
2343    #[unsafe(no_mangle)]
2344    pub unsafe extern "C" fn slint_windowrc_show(handle: *const WindowAdapterRcOpaque) {
2345        unsafe {
2346            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2347
2348            window_adapter.window().show().unwrap();
2349        }
2350    }
2351
2352    /// Spins an event loop and renders the items of the provided component in this window.
2353    #[unsafe(no_mangle)]
2354    pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) {
2355        unsafe {
2356            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2357            window_adapter.window().hide().unwrap();
2358        }
2359    }
2360
2361    /// Returns the visibility state of the window. This function can return false even if you previously called show()
2362    /// on it, for example if the user minimized the window.
2363    #[unsafe(no_mangle)]
2364    pub unsafe extern "C" fn slint_windowrc_is_visible(
2365        handle: *const WindowAdapterRcOpaque,
2366    ) -> bool {
2367        unsafe {
2368            let window = &*(handle as *const Rc<dyn WindowAdapter>);
2369            window.window().is_visible()
2370        }
2371    }
2372
2373    /// Returns the window scale factor.
2374    #[unsafe(no_mangle)]
2375    pub unsafe extern "C" fn slint_windowrc_get_scale_factor(
2376        handle: *const WindowAdapterRcOpaque,
2377    ) -> f32 {
2378        unsafe {
2379            assert_eq!(
2380                core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2381                core::mem::size_of::<WindowAdapterRcOpaque>()
2382            );
2383            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2384            WindowInner::from_pub(window_adapter.window()).scale_factor()
2385        }
2386    }
2387
2388    /// Sets the window scale factor, merely for testing purposes.
2389    #[unsafe(no_mangle)]
2390    pub unsafe extern "C" fn slint_windowrc_set_const_scale_factor(
2391        handle: *const WindowAdapterRcOpaque,
2392        value: f32,
2393    ) {
2394        unsafe {
2395            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2396            WindowInner::from_pub(window_adapter.window()).set_const_scale_factor(value)
2397        }
2398    }
2399
2400    /// Returns the text-input-focused property value.
2401    #[unsafe(no_mangle)]
2402    pub unsafe extern "C" fn slint_windowrc_get_text_input_focused(
2403        handle: *const WindowAdapterRcOpaque,
2404    ) -> bool {
2405        unsafe {
2406            assert_eq!(
2407                core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2408                core::mem::size_of::<WindowAdapterRcOpaque>()
2409            );
2410            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2411            WindowInner::from_pub(window_adapter.window()).text_input_focused()
2412        }
2413    }
2414
2415    /// Set the text-input-focused property.
2416    #[unsafe(no_mangle)]
2417    pub unsafe extern "C" fn slint_windowrc_set_text_input_focused(
2418        handle: *const WindowAdapterRcOpaque,
2419        value: bool,
2420    ) {
2421        unsafe {
2422            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2423            WindowInner::from_pub(window_adapter.window()).set_text_input_focused(value)
2424        }
2425    }
2426
2427    /// Sets the focus item.
2428    #[unsafe(no_mangle)]
2429    pub unsafe extern "C" fn slint_windowrc_set_focus_item(
2430        handle: *const WindowAdapterRcOpaque,
2431        focus_item: &ItemRc,
2432        set_focus: bool,
2433        reason: FocusReason,
2434    ) {
2435        unsafe {
2436            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2437            WindowInner::from_pub(window_adapter.window())
2438                .set_focus_item(focus_item, set_focus, reason)
2439        }
2440    }
2441
2442    /// Associates the window with the given component.
2443    #[unsafe(no_mangle)]
2444    pub unsafe extern "C" fn slint_windowrc_set_component(
2445        handle: *const WindowAdapterRcOpaque,
2446        component: &ItemTreeRc,
2447    ) {
2448        unsafe {
2449            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2450            WindowInner::from_pub(window_adapter.window()).set_component(component)
2451        }
2452    }
2453
2454    /// Show a popup and return its ID. The returned ID will always be non-zero.
2455    #[unsafe(no_mangle)]
2456    pub unsafe extern "C" fn slint_windowrc_show_popup(
2457        handle: *const WindowAdapterRcOpaque,
2458        popup: &ItemTreeRc,
2459        position: extern "C" fn(user_data: *mut c_void, pos: &mut LogicalPosition),
2460        drop_user_data: extern "C" fn(user_data: *mut c_void),
2461        user_data: *mut c_void,
2462        close_policy: PopupClosePolicy,
2463        parent_item: &ItemRc,
2464        window_kind: WindowKind,
2465        is_open_setter: extern "C" fn(user_data: *mut c_void, is_open: bool),
2466        is_open_setter_drop_user_data: extern "C" fn(user_data: *mut c_void),
2467        is_open_setter_user_data: *mut c_void,
2468    ) -> NonZeroU32 {
2469        unsafe {
2470            let with_user_data = WithUserData { callback: position, drop_user_data, user_data };
2471            let is_open_with_user_data = WithUserData {
2472                callback: is_open_setter,
2473                drop_user_data: is_open_setter_drop_user_data,
2474                user_data: is_open_setter_user_data,
2475            };
2476            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2477            WindowInner::from_pub(window_adapter.window()).show_popup(
2478                popup,
2479                Box::new(move || with_user_data.call()),
2480                close_policy,
2481                parent_item,
2482                window_kind,
2483                Box::new(move |is_open| is_open_with_user_data.call(is_open)),
2484            )
2485        }
2486    }
2487
2488    /// Create a popup window adapter. Returns true if a new adapter was created and written to result.
2489    /// Returns false if the backend does not support top-level popups.
2490    /// This can be used to set the correct window adapter on a popup component before showing it.
2491    #[unsafe(no_mangle)]
2492    pub unsafe extern "C" fn slint_windowrc_create_child_window_adapter(
2493        handle: *const WindowAdapterRcOpaque,
2494        window_kind: WindowKind,
2495        result: *mut WindowAdapterRcOpaque,
2496    ) -> bool {
2497        unsafe {
2498            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2499            match WindowInner::from_pub(window_adapter.window())
2500                .create_child_window_adapter(window_kind)
2501            {
2502                Some(wa) => {
2503                    core::ptr::write(result as *mut Rc<dyn WindowAdapter>, wa);
2504                    true
2505                }
2506                None => false,
2507            }
2508        }
2509    }
2510
2511    /// Close the popup by the given ID.
2512    #[unsafe(no_mangle)]
2513    pub unsafe extern "C" fn slint_windowrc_close_popup(
2514        handle: *const WindowAdapterRcOpaque,
2515        popup_id: NonZeroU32,
2516    ) {
2517        unsafe {
2518            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2519            WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
2520        }
2521    }
2522
2523    /// C binding to the set_rendering_notifier() API of Window
2524    #[unsafe(no_mangle)]
2525    pub unsafe extern "C" fn slint_windowrc_set_rendering_notifier(
2526        handle: *const WindowAdapterRcOpaque,
2527        callback: extern "C" fn(
2528            rendering_state: RenderingState,
2529            graphics_api: GraphicsAPI,
2530            user_data: *mut c_void,
2531        ),
2532        drop_user_data: extern "C" fn(user_data: *mut c_void),
2533        user_data: *mut c_void,
2534        error: *mut SetRenderingNotifierError,
2535    ) -> bool {
2536        unsafe {
2537            struct CNotifier {
2538                callback: extern "C" fn(
2539                    rendering_state: RenderingState,
2540                    graphics_api: GraphicsAPI,
2541                    user_data: *mut c_void,
2542                ),
2543                drop_user_data: extern "C" fn(*mut c_void),
2544                user_data: *mut c_void,
2545            }
2546
2547            impl Drop for CNotifier {
2548                fn drop(&mut self) {
2549                    (self.drop_user_data)(self.user_data)
2550                }
2551            }
2552
2553            impl RenderingNotifier for CNotifier {
2554                fn notify(
2555                    &mut self,
2556                    state: RenderingState,
2557                    graphics_api: &crate::api::GraphicsAPI,
2558                ) {
2559                    let cpp_graphics_api = match graphics_api {
2560                        crate::api::GraphicsAPI::NativeOpenGL { .. } => GraphicsAPI::NativeOpenGL,
2561                        crate::api::GraphicsAPI::WebGL { .. } => unreachable!(), // We don't support wasm with C++
2562                        #[cfg(feature = "unstable-wgpu-28")]
2563                        crate::api::GraphicsAPI::WGPU28 { .. } => GraphicsAPI::Inaccessible, // There is no C++ API for wgpu (maybe wgpu c in the future?)
2564                        #[cfg(feature = "unstable-wgpu-29")]
2565                        crate::api::GraphicsAPI::WGPU29 { .. } => GraphicsAPI::Inaccessible, // There is no C++ API for wgpu (maybe wgpu c in the future?)
2566                    };
2567                    (self.callback)(state, cpp_graphics_api, self.user_data)
2568                }
2569            }
2570
2571            let window = &*(handle as *const Rc<dyn WindowAdapter>);
2572            match window.renderer().set_rendering_notifier(Box::new(CNotifier {
2573                callback,
2574                drop_user_data,
2575                user_data,
2576            })) {
2577                Ok(()) => true,
2578                Err(err) => {
2579                    *error = err;
2580                    false
2581                }
2582            }
2583        }
2584    }
2585
2586    /// C binding to the on_close_requested() API of Window
2587    #[unsafe(no_mangle)]
2588    pub unsafe extern "C" fn slint_windowrc_on_close_requested(
2589        handle: *const WindowAdapterRcOpaque,
2590        callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
2591        drop_user_data: extern "C" fn(user_data: *mut c_void),
2592        user_data: *mut c_void,
2593    ) {
2594        unsafe {
2595            let with_user_data = WithUserData { callback, drop_user_data, user_data };
2596            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2597            window_adapter.window().on_close_requested(move || with_user_data.call());
2598        }
2599    }
2600
2601    /// This function issues a request to the windowing system to redraw the contents of the window.
2602    #[unsafe(no_mangle)]
2603    pub unsafe extern "C" fn slint_windowrc_request_redraw(handle: *const WindowAdapterRcOpaque) {
2604        unsafe {
2605            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2606            window_adapter.request_redraw();
2607        }
2608    }
2609
2610    /// Returns the position of the window on the screen, in physical screen coordinates and including
2611    /// a window frame (if present).
2612    #[unsafe(no_mangle)]
2613    pub unsafe extern "C" fn slint_windowrc_position(
2614        handle: *const WindowAdapterRcOpaque,
2615        pos: &mut euclid::default::Point2D<i32>,
2616    ) {
2617        unsafe {
2618            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2619            *pos = window_adapter.position().unwrap_or_default().to_euclid()
2620        }
2621    }
2622
2623    /// Sets the position of the window on the screen, in physical screen coordinates and including
2624    /// a window frame (if present).
2625    /// Note that on some windowing systems, such as Wayland, this functionality is not available.
2626    #[unsafe(no_mangle)]
2627    pub unsafe extern "C" fn slint_windowrc_set_physical_position(
2628        handle: *const WindowAdapterRcOpaque,
2629        pos: &euclid::default::Point2D<i32>,
2630    ) {
2631        unsafe {
2632            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2633            window_adapter.set_position(crate::api::PhysicalPosition::new(pos.x, pos.y).into());
2634        }
2635    }
2636
2637    /// Sets the position of the window on the screen, in physical screen coordinates and including
2638    /// a window frame (if present).
2639    /// Note that on some windowing systems, such as Wayland, this functionality is not available.
2640    #[unsafe(no_mangle)]
2641    pub unsafe extern "C" fn slint_windowrc_set_logical_position(
2642        handle: *const WindowAdapterRcOpaque,
2643        pos: &euclid::default::Point2D<f32>,
2644    ) {
2645        unsafe {
2646            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2647            window_adapter.set_position(LogicalPosition::new(pos.x, pos.y).into());
2648        }
2649    }
2650
2651    /// Returns the size of the window on the screen, in physical screen coordinates and excluding
2652    /// a window frame (if present).
2653    #[unsafe(no_mangle)]
2654    pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
2655        unsafe {
2656            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2657            window_adapter.size().to_euclid().cast()
2658        }
2659    }
2660
2661    /// Resizes the window to the specified size on the screen, in physical pixels and excluding
2662    /// a window frame (if present).
2663    #[unsafe(no_mangle)]
2664    pub unsafe extern "C" fn slint_windowrc_set_physical_size(
2665        handle: *const WindowAdapterRcOpaque,
2666        size: &IntSize,
2667    ) {
2668        unsafe {
2669            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2670            window_adapter
2671                .window()
2672                .set_size(crate::api::PhysicalSize::new(size.width, size.height));
2673        }
2674    }
2675
2676    /// Resizes the window to the specified size on the screen, in physical pixels and excluding
2677    /// a window frame (if present).
2678    #[unsafe(no_mangle)]
2679    pub unsafe extern "C" fn slint_windowrc_set_logical_size(
2680        handle: *const WindowAdapterRcOpaque,
2681        size: &Size,
2682    ) {
2683        unsafe {
2684            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2685            window_adapter.window().set_size(crate::api::LogicalSize::new(size.width, size.height));
2686        }
2687    }
2688
2689    /// Return whether the platform supports native menu bars
2690    #[unsafe(no_mangle)]
2691    pub unsafe extern "C" fn slint_windowrc_supports_native_menu_bar(
2692        handle: *const WindowAdapterRcOpaque,
2693    ) -> bool {
2694        unsafe {
2695            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2696            window_adapter
2697                .internal(crate::InternalToken)
2698                .is_some_and(|x| x.supports_native_menu_bar())
2699        }
2700    }
2701
2702    /// Setup the native menu bar
2703    #[unsafe(no_mangle)]
2704    pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
2705        handle: *const WindowAdapterRcOpaque,
2706        menu_instance: &vtable::VRc<MenuVTable>,
2707    ) {
2708        let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2709        let window = window_adapter.window();
2710        window.0.setup_menubar(vtable::VRc::clone(menu_instance));
2711    }
2712
2713    #[unsafe(no_mangle)]
2714    pub unsafe extern "C" fn slint_windowrc_setup_menu_bar_shortcuts(
2715        handle: *const WindowAdapterRcOpaque,
2716        menu_instance: &vtable::VRc<MenuVTable>,
2717    ) {
2718        let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2719        let window = window_adapter.window();
2720        window.0.setup_menubar_shortcuts(vtable::VRc::clone(menu_instance));
2721    }
2722
2723    /// Show a native context menu
2724    #[unsafe(no_mangle)]
2725    pub unsafe extern "C" fn slint_windowrc_show_native_popup_menu(
2726        handle: *const WindowAdapterRcOpaque,
2727        context_menu: &vtable::VRc<MenuVTable>,
2728        position: LogicalPosition,
2729        parent_item: &ItemRc,
2730    ) -> bool {
2731        unsafe {
2732            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2733            WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(
2734                context_menu.clone(),
2735                position,
2736                parent_item,
2737            )
2738        }
2739    }
2740
2741    /// Return the default-font-size property of the WindowItem
2742    #[unsafe(no_mangle)]
2743    pub unsafe extern "C" fn slint_windowrc_resolved_default_font_size(
2744        item_tree: &ItemTreeRc,
2745    ) -> f32 {
2746        WindowItem::resolved_default_font_size(item_tree.clone()).get()
2747    }
2748
2749    /// Dispatch a key pressed or release event
2750    #[unsafe(no_mangle)]
2751    pub unsafe extern "C" fn slint_windowrc_dispatch_key_event(
2752        handle: *const WindowAdapterRcOpaque,
2753        event_type: crate::input::KeyEventType,
2754        text: &SharedString,
2755        repeat: bool,
2756    ) {
2757        unsafe {
2758            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2759            window_adapter.window().0.process_key_input(InternalKeyEvent {
2760                event_type,
2761                key_event: crate::items::KeyEvent {
2762                    text: text.clone(),
2763                    repeat,
2764                    ..Default::default()
2765                },
2766                ..Default::default()
2767            });
2768        }
2769    }
2770
2771    /// Dispatch a mouse event
2772    #[unsafe(no_mangle)]
2773    pub unsafe extern "C" fn slint_windowrc_dispatch_pointer_event(
2774        handle: *const WindowAdapterRcOpaque,
2775        event: &crate::input::MouseEvent,
2776    ) {
2777        unsafe {
2778            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2779            window_adapter.window().0.process_mouse_input(event.clone());
2780        }
2781    }
2782
2783    /// Dispatch a window event
2784    #[unsafe(no_mangle)]
2785    pub unsafe extern "C" fn slint_windowrc_dispatch_event(
2786        handle: *const WindowAdapterRcOpaque,
2787        event: &crate::platform::WindowEvent,
2788    ) {
2789        unsafe {
2790            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2791            window_adapter.window().dispatch_event(event.clone());
2792        }
2793    }
2794
2795    #[unsafe(no_mangle)]
2796    pub unsafe extern "C" fn slint_windowrc_is_fullscreen(
2797        handle: *const WindowAdapterRcOpaque,
2798    ) -> bool {
2799        unsafe {
2800            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2801            window_adapter.window().is_fullscreen()
2802        }
2803    }
2804
2805    #[unsafe(no_mangle)]
2806    pub unsafe extern "C" fn slint_windowrc_is_minimized(
2807        handle: *const WindowAdapterRcOpaque,
2808    ) -> bool {
2809        unsafe {
2810            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2811            window_adapter.window().is_minimized()
2812        }
2813    }
2814
2815    #[unsafe(no_mangle)]
2816    pub unsafe extern "C" fn slint_windowrc_is_maximized(
2817        handle: *const WindowAdapterRcOpaque,
2818    ) -> bool {
2819        unsafe {
2820            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2821            window_adapter.window().is_maximized()
2822        }
2823    }
2824
2825    #[unsafe(no_mangle)]
2826    pub unsafe extern "C" fn slint_windowrc_set_fullscreen(
2827        handle: *const WindowAdapterRcOpaque,
2828        value: bool,
2829    ) {
2830        unsafe {
2831            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2832            window_adapter.window().set_fullscreen(value)
2833        }
2834    }
2835
2836    #[unsafe(no_mangle)]
2837    pub unsafe extern "C" fn slint_windowrc_set_minimized(
2838        handle: *const WindowAdapterRcOpaque,
2839        value: bool,
2840    ) {
2841        unsafe {
2842            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2843            window_adapter.window().set_minimized(value)
2844        }
2845    }
2846
2847    #[unsafe(no_mangle)]
2848    pub unsafe extern "C" fn slint_windowrc_set_maximized(
2849        handle: *const WindowAdapterRcOpaque,
2850        value: bool,
2851    ) {
2852        unsafe {
2853            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2854            window_adapter.window().set_maximized(value)
2855        }
2856    }
2857
2858    /// Takes a snapshot of the window contents and returns it as RGBA8 encoded pixel buffer.
2859    #[unsafe(no_mangle)]
2860    pub unsafe extern "C" fn slint_windowrc_take_snapshot(
2861        handle: *const WindowAdapterRcOpaque,
2862        data: &mut SharedVector<Rgba8Pixel>,
2863        width: &mut u32,
2864        height: &mut u32,
2865    ) -> bool {
2866        unsafe {
2867            let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2868            if let Ok(snapshot) = window_adapter.window().take_snapshot() {
2869                *data = snapshot.data.clone();
2870                *width = snapshot.width();
2871                *height = snapshot.height();
2872                true
2873            } else {
2874                false
2875            }
2876        }
2877    }
2878}
2879
2880/// This module contains the functions needed to interface with window handles from outside the Rust language.
2881#[cfg(all(feature = "ffi", feature = "raw-window-handle-06"))]
2882pub mod ffi_window {
2883    #![allow(unsafe_code)]
2884    #![allow(clippy::missing_safety_doc)]
2885
2886    use super::ffi::WindowAdapterRcOpaque;
2887    use super::*;
2888    use std::ffi::c_void;
2889    use std::ptr::null_mut;
2890    use std::sync::Arc;
2891
2892    /// Helper to grab the `HasWindowHandle` for the `WindowAdapter` behind `handle`.
2893    fn has_window_handle(
2894        handle: *const WindowAdapterRcOpaque,
2895    ) -> Option<Arc<dyn raw_window_handle_06::HasWindowHandle>> {
2896        let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2897        let window_adapter = window_adapter.internal(crate::InternalToken)?;
2898        window_adapter.window_handle_06_rc().ok()
2899    }
2900
2901    /// Helper to grab the `HasDisplayHandle` for the `WindowAdapter` behind `handle`.
2902    fn has_display_handle(
2903        handle: *const WindowAdapterRcOpaque,
2904    ) -> Option<Arc<dyn raw_window_handle_06::HasDisplayHandle>> {
2905        let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2906        let window_adapter = window_adapter.internal(crate::InternalToken)?;
2907        window_adapter.display_handle_06_rc().ok()
2908    }
2909
2910    /// Returns the `HWND` associated with this window, or null if it doesn't exist or isn't created yet.
2911    #[unsafe(no_mangle)]
2912    pub unsafe extern "C" fn slint_windowrc_hwnd_win32(
2913        handle: *const WindowAdapterRcOpaque,
2914    ) -> *mut c_void {
2915        use raw_window_handle_06::HasWindowHandle;
2916
2917        if let Some(has_window_handle) = has_window_handle(handle)
2918            && let Ok(window_handle) = has_window_handle.window_handle()
2919            && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
2920        {
2921            isize::from(win32.hwnd) as *mut c_void
2922        } else {
2923            null_mut()
2924        }
2925    }
2926
2927    /// Returns the `HINSTANCE` associated with this window, or null if it doesn't exist or isn't created yet.
2928    #[unsafe(no_mangle)]
2929    pub unsafe extern "C" fn slint_windowrc_hinstance_win32(
2930        handle: *const WindowAdapterRcOpaque,
2931    ) -> *mut c_void {
2932        use raw_window_handle_06::HasWindowHandle;
2933
2934        if let Some(has_window_handle) = has_window_handle(handle)
2935            && let Ok(window_handle) = has_window_handle.window_handle()
2936            && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
2937        {
2938            win32
2939                .hinstance
2940                .map(|hinstance| isize::from(hinstance) as *mut c_void)
2941                .unwrap_or_default()
2942        } else {
2943            null_mut()
2944        }
2945    }
2946
2947    /// Returns the `wl_surface` associated with this window, or null if it doesn't exist or isn't created yet.
2948    #[unsafe(no_mangle)]
2949    pub unsafe extern "C" fn slint_windowrc_wlsurface_wayland(
2950        handle: *const WindowAdapterRcOpaque,
2951    ) -> *mut c_void {
2952        use raw_window_handle_06::HasWindowHandle;
2953
2954        if let Some(has_window_handle) = has_window_handle(handle)
2955            && let Ok(window_handle) = has_window_handle.window_handle()
2956            && let raw_window_handle_06::RawWindowHandle::Wayland(wayland) = window_handle.as_raw()
2957        {
2958            wayland.surface.as_ptr()
2959        } else {
2960            null_mut()
2961        }
2962    }
2963
2964    /// Returns the `wl_display` associated with this window, or null if it doesn't exist or isn't created yet.
2965    #[unsafe(no_mangle)]
2966    pub unsafe extern "C" fn slint_windowrc_wldisplay_wayland(
2967        handle: *const WindowAdapterRcOpaque,
2968    ) -> *mut c_void {
2969        use raw_window_handle_06::HasDisplayHandle;
2970
2971        if let Some(has_display_handle) = has_display_handle(handle)
2972            && let Ok(display_handle) = has_display_handle.display_handle()
2973            && let raw_window_handle_06::RawDisplayHandle::Wayland(wayland) =
2974                display_handle.as_raw()
2975        {
2976            wayland.display.as_ptr()
2977        } else {
2978            null_mut()
2979        }
2980    }
2981
2982    /// Returns the `NSView` associated with this window, or null if it doesn't exist or isn't created yet.
2983    #[unsafe(no_mangle)]
2984    pub unsafe extern "C" fn slint_windowrc_nsview_appkit(
2985        handle: *const WindowAdapterRcOpaque,
2986    ) -> *mut c_void {
2987        use raw_window_handle_06::HasWindowHandle;
2988
2989        if let Some(has_window_handle) = has_window_handle(handle)
2990            && let Ok(window_handle) = has_window_handle.window_handle()
2991            && let raw_window_handle_06::RawWindowHandle::AppKit(appkit) = window_handle.as_raw()
2992        {
2993            appkit.ns_view.as_ptr()
2994        } else {
2995            null_mut()
2996        }
2997    }
2998}