1#![warn(missing_docs)]
7use 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#[repr(C)]
48pub enum WindowKind {
49 ToolTip,
51 Popup,
53 Menu,
55}
56
57pub trait WindowAdapter {
84 fn window(&self) -> &Window;
86
87 fn set_visible(&self, _visible: bool) -> Result<(), PlatformError> {
89 Ok(())
90 }
91
92 fn position(&self) -> Option<PhysicalPosition> {
99 None
100 }
101 fn set_position(&self, _position: WindowPosition) {}
108
109 fn set_size(&self, _size: WindowSize) {}
120
121 fn size(&self) -> PhysicalSize;
123
124 fn request_redraw(&self) {}
136
137 fn renderer(&self) -> &dyn Renderer;
142
143 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 #[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 #[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#[doc(hidden)]
177pub trait WindowAdapterInternal: core::any::Any {
178 fn register_item_tree(&self, _: ItemTreeRefPin) {}
180
181 fn unregister_item_tree(
184 &self,
185 _component: ItemTreeRef,
186 _items: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
187 ) {
188 }
189
190 fn get_parent(&self) -> Option<Rc<dyn WindowAdapter>> {
192 None
193 }
194
195 fn create_child_window_adapter(
202 &self,
203 _window_kind: WindowKind,
204 ) -> Option<Rc<dyn WindowAdapter>> {
205 None
206 }
207
208 fn set_mouse_cursor(&self, _cursor: MouseCursor) {}
211
212 fn input_method_request(&self, _: InputMethodRequest) {}
214
215 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {}
218
219 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 #[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 #[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 fn bring_to_front(&self) -> Result<(), PlatformError> {
258 Ok(())
259 }
260
261 fn safe_area_inset(&self) -> crate::lengths::PhysicalEdges {
264 Default::default()
265 }
266}
267
268#[non_exhaustive]
271#[derive(Debug, Clone)]
272pub enum InputMethodRequest {
273 Enable(InputMethodProperties),
275 Update(InputMethodProperties),
277 Disable,
279}
280
281#[non_exhaustive]
283#[derive(Clone, Default, Debug)]
284pub struct InputMethodProperties {
285 pub text: SharedString,
289 pub cursor_position: usize,
291 pub anchor_position: Option<usize>,
294 pub preedit_text: SharedString,
298 pub preedit_offset: usize,
300 pub cursor_rect_origin: LogicalPosition,
302 pub cursor_rect_size: crate::api::LogicalSize,
304 pub anchor_point: LogicalPosition,
306 pub input_type: InputType,
308 pub clip_rect: Option<LogicalRect>,
310}
311
312#[non_exhaustive]
314#[derive(Copy, Clone, Debug, PartialEq, Default)]
315pub struct LayoutConstraints {
316 pub min: Option<crate::api::LogicalSize>,
318 pub max: Option<crate::api::LogicalSize>,
320 pub preferred: crate::api::LogicalSize,
322}
323
324pub struct WindowProperties<'a>(&'a WindowInner);
327
328impl WindowProperties<'_> {
329 pub fn title(&self) -> SharedString {
331 self.0.window_item().map(|w| w.as_pin_ref().title()).unwrap_or_default()
332 }
333
334 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 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 #[deprecated(note = "Please use `is_fullscreen` instead")]
363 pub fn fullscreen(&self) -> bool {
364 self.is_fullscreen()
365 }
366
367 pub fn is_fullscreen(&self) -> bool {
369 self.0.is_fullscreen()
370 }
371
372 pub fn is_maximized(&self) -> bool {
374 self.0.is_maximized()
375 }
376
377 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 parent_window_adapter_weak: Weak<dyn WindowAdapter>,
401 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 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
431pub enum PopupWindowLocation {
433 TopLevel(Rc<dyn WindowAdapter>),
435 ChildWindow(LogicalPoint),
437}
438
439pub struct PopupWindow {
442 pub popup_id: NonZeroU32,
444 pub location: PopupWindowLocation,
446 pub component: ItemTreeRc,
448 pub close_policy: PopupClosePolicy,
450 focus_item_in_parent: ItemWeak,
452 pub parent_item: ItemWeak,
454 pub window_kind: WindowKind,
457 position_access: Box<dyn Fn() -> LogicalPosition>,
461 is_open_setter: Box<dyn Fn(bool)>,
466 properties_tracker: Pin<Box<PropertyTracker<true, PopupWindowPropertiesTracker>>>,
468}
469
470impl Drop for PopupWindow {
471 fn drop(&mut self) {
472 (self.is_open_setter)(false);
477 }
478}
479
480#[pin_project::pin_project]
481struct WindowPinnedFields {
482 #[pin]
483 redraw_tracker: PropertyTracker<false, WindowRedrawTracker>,
484 #[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#[derive(Copy, Clone, Debug)]
499pub struct MouseDispatchResult {
500 pub drag_action: Option<crate::items::DragAction>,
504 pub accepted: bool,
507}
508
509pub struct WindowInner {
511 window_adapter_weak: Weak<dyn WindowAdapter>,
512 component: RefCell<ItemTreeWeak>,
513 strong_component_ref: RefCell<Option<ItemTreeRc>>,
515 mouse_input_state: Cell<MouseInputState>,
516 touch_state: RefCell<TouchState>,
517
518 pub focus_item: RefCell<crate::item_tree::ItemWeak>,
520 pub(crate) last_ime_text: RefCell<SharedString>,
522 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 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 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 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(); 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 pub fn component(&self) -> ItemTreeRc {
635 self.component.borrow().upgrade().unwrap()
636 }
637
638 pub fn try_component(&self) -> Option<ItemTreeRc> {
640 self.component.borrow().upgrade()
641 }
642
643 pub fn ensure_tree_instantiated(&self) {
649 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 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 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 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 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 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 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 drop_event.proposed_action = crate::items::compute_proposed_action(
747 self.context().0.modifiers.get().into(),
748 allowed,
749 );
750 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 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 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 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 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 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 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 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 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 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 pub fn process_key_input(
1021 &self,
1022 mut internal_key_event: InternalKeyEvent,
1023 ) -> crate::input::KeyEventResult {
1024 self.ensure_tree_instantiated();
1025 #[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 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 self.context().0.modifiers.set(updated_modifier);
1046
1047 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 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 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 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 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 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 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 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 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 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 return;
1247 }
1248 } else if current_focus_item_rc != *new_focus_item {
1249 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 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 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 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 }
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); }
1336 visited.push(current_item.clone());
1337 current_item = forward(current_item);
1338
1339 if visited.contains(¤t_item) {
1340 return None; }
1342 }
1343 }
1344
1345 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 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 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 !have_focus {
1411 self.context().0.modifiers.take();
1412 }
1413 }
1414
1415 pub fn active(&self) -> bool {
1418 self.pinned_fields.as_ref().project_ref().active.get()
1419 }
1420
1421 pub fn update_window_properties(&self) {
1424 let window_adapter = self.window_adapter();
1425
1426 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 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 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 (
1490 old_popup_region,
1491 LogicalRect::new(
1492 (popup.position_access)().to_euclid(),
1493 crate::lengths::LogicalSize::new(width, height),
1494 ),
1495 )
1496 });
1497
1498 *old_location = offset;
1500
1501 if let Some(adapter) = self.window_adapter_weak.upgrade() {
1502 if !old_popup_region.is_empty() {
1503 adapter.renderer().mark_dirty_region(old_popup_region.into());
1504 }
1505
1506 if !new_popup_region.is_empty() {
1507 adapter.renderer().mark_dirty_region(new_popup_region.into());
1508 }
1509 adapter.request_redraw();
1510 }
1511 }
1512 PopupWindowLocation::TopLevel(adapter) => {
1513 let mut new_position: Option<LogicalPosition> = None;
1515 popup.properties_tracker.as_ref().evaluate_as_dependency_root(|| {
1516 (popup.position_access)(); new_position = Some(LogicalPosition::from_euclid(offset));
1518 });
1519 if let Some(pos) = new_position {
1520 adapter.window().set_position(pos);
1521 }
1522 }
1523 }
1524 }
1525
1526 pub fn draw_contents<T>(
1536 &self,
1537 render_components: impl FnOnce(
1538 &[(ItemTreeWeak, LogicalPoint)],
1539 &dyn Fn(&mut dyn crate::item_rendering::ItemRenderer),
1540 ) -> T,
1541 ) -> Option<T> {
1542 crate::properties::evaluate_no_tracking(|| self.ensure_tree_instantiated());
1543 let component_weak = ItemTreeRc::downgrade(&self.try_component()?);
1544 let post_render = |renderer: &mut dyn crate::item_rendering::ItemRenderer| {
1545 self.render_drag_image_overlay(renderer);
1546 };
1547 Some(self.pinned_fields.as_ref().project_ref().redraw_tracker.evaluate_as_dependency_root(
1548 || {
1549 if !self
1550 .active_popups
1551 .borrow()
1552 .iter()
1553 .any(|p| matches!(p.location, PopupWindowLocation::ChildWindow(..)))
1554 {
1555 render_components(&[(component_weak, LogicalPoint::default())], &post_render)
1556 } else {
1557 let borrow = self.active_popups.borrow();
1558 let mut item_trees = Vec::with_capacity(borrow.len() + 1);
1559 item_trees.push((component_weak, LogicalPoint::default()));
1560 for popup in borrow.iter() {
1561 if let PopupWindowLocation::ChildWindow(location) = &popup.location {
1565 item_trees.push((ItemTreeRc::downgrade(&popup.component), *location));
1566 }
1567 }
1568 drop(borrow);
1569 render_components(&item_trees, &post_render)
1570 }
1571 },
1572 ))
1573 }
1574
1575 fn render_drag_image_overlay(
1581 &self,
1582 item_renderer: &mut dyn crate::item_rendering::ItemRenderer,
1583 ) {
1584 let state = self.mouse_input_state.take();
1585 let cursor = state.drag_data.as_ref().map(|d| d.event.position);
1586 let source = state.drag_source.as_ref().and_then(|w| w.upgrade());
1587 self.mouse_input_state.set(state);
1588
1589 let (Some(cursor), Some(source)) = (cursor, source) else { return };
1590 let Some(drag_area) = source.downcast::<crate::items::DragArea>() else { return };
1591 let drag_area = drag_area.as_pin_ref();
1592 let image = drag_area.drag_image();
1593 let size = crate::lengths::LogicalSize::from_untyped(image.size().cast());
1594 if size.is_empty() {
1595 return;
1596 }
1597 let cursor = crate::lengths::logical_point_from_api(cursor);
1598 let offset = LogicalVector::new(
1599 drag_area.drag_image_offset_x() as Coord,
1600 drag_area.drag_image_offset_y() as Coord,
1601 );
1602 let top_left = cursor - offset;
1603
1604 item_renderer.save_state();
1605 item_renderer.translate(top_left.to_vector());
1606 item_renderer.draw_image_direct(image);
1607 item_renderer.restore_state();
1608
1609 self.window_adapter().renderer().mark_dirty_region(LogicalRect::new(top_left, size).into());
1610 }
1611
1612 pub fn show(&self) -> Result<(), PlatformError> {
1615 if let Some(component) = self.try_component() {
1616 let was_visible = self.strong_component_ref.replace(Some(component)).is_some();
1617 if !was_visible {
1618 self.context().acquire_keepalive();
1619 }
1620 }
1621
1622 self.ensure_tree_instantiated();
1623 self.update_window_properties();
1624 self.window_adapter().set_visible(true)?;
1625 let size = self.window_adapter().size();
1628 let scale_factor = self.scale_factor();
1629 self.set_window_item_geometry(size.to_logical(scale_factor).to_euclid());
1630 let inset = self
1631 .window_adapter()
1632 .internal(crate::InternalToken)
1633 .map(|internal| internal.safe_area_inset())
1634 .unwrap_or_default();
1635 self.set_window_item_safe_area(inset.to_logical(scale_factor));
1636 self.window_adapter().renderer().resize(size).unwrap();
1637 if let Some(hook) = self.context().0.window_shown_hook.borrow_mut().as_mut() {
1638 hook(&self.window_adapter());
1639 }
1640 Ok(())
1641 }
1642
1643 pub fn hide(&self) -> Result<(), PlatformError> {
1645 let result = self.window_adapter().set_visible(false);
1646 let was_visible = self.strong_component_ref.borrow_mut().take().is_some();
1647 if was_visible {
1648 self.context().release_keepalive();
1649 }
1650 result
1651 }
1652
1653 pub fn supports_native_menu_bar(&self) -> bool {
1655 self.window_adapter()
1656 .internal(crate::InternalToken)
1657 .is_some_and(|x| x.supports_native_menu_bar())
1658 }
1659
1660 pub fn setup_menubar(&self, menubar: vtable::VRc<MenuVTable>) {
1662 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1663 x.setup_menubar(menubar);
1664 }
1665 }
1666
1667 pub fn setup_menubar_shortcuts(&self, menubar: VRc<MenuVTable>) {
1672 *self.menubar.borrow_mut() = Some(VRc::downgrade(&menubar));
1673 let weak = VRc::downgrade(&menubar);
1674 self.pinned_fields.menubar_shortcuts.set_binding(move || {
1675 fn flatten_menu(
1676 root: vtable::VRef<'_, MenuVTable>,
1677 parent: Option<&MenuEntry>,
1678 ) -> SharedVector<MenuEntry> {
1679 let mut menu_entries = Default::default();
1680 root.sub_menu(parent, &mut menu_entries);
1681
1682 let mut result = menu_entries.clone();
1683
1684 for entry in menu_entries {
1685 result.extend(flatten_menu(root, Some(&entry)));
1686 }
1687 result
1688 }
1689
1690 let Some(menubar) = weak.upgrade() else {
1691 return SharedVector::default();
1692 };
1693 flatten_menu(VRc::borrow(&menubar), None)
1694 .into_iter()
1695 .filter(|entry| entry.enabled && entry.shortcut != Keys::default())
1696 .collect()
1697 });
1698 }
1699 pub fn create_child_window_adapter(&self, kind: WindowKind) -> Option<Rc<dyn WindowAdapter>> {
1702 self.window_adapter()
1703 .internal(crate::InternalToken)
1704 .and_then(|s| s.create_child_window_adapter(kind))
1705 }
1706
1707 pub fn show_popup(
1715 &self,
1716 popup_componentrc: &ItemTreeRc,
1717 popup_access_position: Box<dyn Fn() -> LogicalPosition>,
1718 close_policy: PopupClosePolicy,
1719 parent_item: &ItemRc,
1720 window_kind: WindowKind,
1721 is_open_setter: Box<dyn Fn(bool)>,
1722 ) -> NonZeroU32 {
1723 crate::item_tree::ensure_item_tree_instantiated(popup_componentrc);
1726 let position = parent_item.map_to_native_window(
1727 parent_item.geometry().origin + popup_access_position().to_euclid().to_vector(),
1728 );
1729 let popup_component = ItemTreeRc::borrow_pin(popup_componentrc);
1730 let popup_root = popup_component.as_ref().get_item_ref(0);
1731
1732 let (mut w, mut h) = if let Some(window_item) =
1733 ItemRef::downcast_pin::<crate::items::WindowItem>(popup_root)
1734 {
1735 (window_item.width(), window_item.height())
1736 } else {
1737 (LogicalLength::zero(), LogicalLength::zero())
1738 };
1739
1740 let layout_info_h =
1741 popup_component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
1742 let layout_info_v =
1743 popup_component.as_ref().layout_info(crate::layout::Orientation::Vertical);
1744
1745 if w <= LogicalLength::zero() {
1746 w = LogicalLength::new(layout_info_h.preferred);
1747 }
1748 if h <= LogicalLength::zero() {
1749 h = LogicalLength::new(layout_info_v.preferred);
1750 }
1751 w = w.max(LogicalLength::new(layout_info_h.min)).min(LogicalLength::new(layout_info_h.max));
1752 h = h.max(LogicalLength::new(layout_info_v.min)).min(LogicalLength::new(layout_info_v.max));
1753
1754 let size = crate::lengths::LogicalSize::from_lengths(w, h);
1755
1756 if let Some(window_item) = ItemRef::downcast_pin(popup_root) {
1757 let width_property =
1758 crate::items::WindowItem::FIELD_OFFSETS.width().apply_pin(window_item);
1759 let height_property =
1760 crate::items::WindowItem::FIELD_OFFSETS.height().apply_pin(window_item);
1761 width_property.set(size.width_length());
1762 height_property.set(size.height_length());
1763 };
1764
1765 let popup_id = self.next_popup_id.get();
1766 self.next_popup_id.set(popup_id.checked_add(1).unwrap());
1767 let parent_window_adapter_weak = Rc::downgrade(&self.window_adapter());
1768
1769 let siblings: Vec<_> = self
1771 .active_popups
1772 .borrow()
1773 .iter()
1774 .filter(|p| p.parent_item == parent_item.downgrade())
1775 .map(|p| p.popup_id)
1776 .collect();
1777
1778 for sibling in siblings {
1779 self.close_popup(sibling);
1780 }
1781
1782 let root_of = |mut item_tree: ItemTreeRc| loop {
1783 if ItemRc::new_root(item_tree.clone()).downcast::<crate::items::WindowItem>().is_some()
1784 {
1785 return item_tree;
1786 }
1787 let mut r = crate::item_tree::ItemWeak::default();
1788 ItemTreeRc::borrow_pin(&item_tree).as_ref().parent_node(&mut r);
1789 match r.upgrade() {
1790 None => return item_tree,
1791 Some(x) => item_tree = x.item_tree().clone(),
1792 }
1793 };
1794
1795 let parent_root_item_tree = root_of(parent_item.item_tree().clone());
1796 let parent_window_adapter = if let Some(parent_popup) = self
1797 .active_popups
1798 .borrow()
1799 .iter()
1800 .find(|p| ItemTreeRc::ptr_eq(&p.component, &parent_root_item_tree))
1801 {
1802 match &parent_popup.location {
1804 PopupWindowLocation::TopLevel(wa) => wa.clone(),
1805 PopupWindowLocation::ChildWindow(_) => self.window_adapter(),
1806 }
1807 } else {
1808 self.window_adapter()
1809 };
1810
1811 let popup_window_adapter = {
1812 let mut popup_window_adapter = None;
1813 ItemTreeRc::borrow_pin(popup_componentrc)
1814 .as_ref()
1815 .window_adapter(false, &mut popup_window_adapter);
1816 popup_window_adapter.expect("It must be there because we set the global")
1817 };
1818
1819 let (location, properties_tracker) =
1822 if Rc::ptr_eq(&parent_window_adapter, &popup_window_adapter) {
1823 let clip_region = Some(LogicalRect::new(
1825 LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1826 self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
1827 ));
1828 let rect = popup::place_popup(
1829 popup::Placement::Fixed(LogicalRect::new(position, size)),
1830 &clip_region,
1831 );
1832 self.window_adapter().request_redraw();
1833 (
1834 PopupWindowLocation::ChildWindow(rect.origin),
1835 Box::pin(PropertyTracker::new_with_dirty_handler(
1836 PopupWindowPropertiesTracker {
1837 parent_window_adapter_weak: parent_window_adapter_weak.clone(),
1838 popup_id,
1839 },
1840 )),
1841 )
1842 } else {
1843 let popup_window = popup_window_adapter.window();
1844 WindowInner::from_pub(popup_window).set_component(popup_componentrc);
1845 popup_window.set_position(LogicalPosition::from_euclid(position));
1846 popup_window.set_size(WindowSize::Logical(LogicalSize::from_euclid(size)));
1847
1848 popup_window_adapter.set_visible(true).expect("unable to show popup window");
1849 (
1850 PopupWindowLocation::TopLevel(popup_window_adapter),
1851 Box::pin(PropertyTracker::new_with_dirty_handler(
1852 PopupWindowPropertiesTracker {
1853 parent_window_adapter_weak: parent_window_adapter_weak.clone(),
1854 popup_id,
1855 },
1856 )),
1857 )
1858 };
1859
1860 let focus_item = if matches!(window_kind, WindowKind::ToolTip) {
1861 Default::default()
1862 } else {
1863 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::PopupActivation))
1864 .map(|item| item.downgrade())
1865 .unwrap_or_default()
1866 };
1867
1868 is_open_setter(true);
1873
1874 self.active_popups.borrow_mut().push(PopupWindow {
1875 popup_id,
1876 location,
1877 component: popup_componentrc.clone(),
1878 close_policy,
1879 focus_item_in_parent: focus_item,
1880 parent_item: parent_item.downgrade(),
1881 window_kind,
1882 position_access: popup_access_position,
1883 is_open_setter,
1884 properties_tracker,
1885 });
1886
1887 self.update_popup_properties(popup_id);
1888
1889 popup_id
1890 }
1891
1892 pub fn show_native_popup_menu(
1898 &self,
1899 context_menu_item: vtable::VRc<MenuVTable>,
1900 position: LogicalPosition,
1901 parent_item: &ItemRc,
1902 ) -> bool {
1903 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1904 let position = parent_item.map_to_native_window(
1905 parent_item.geometry().origin + position.to_euclid().to_vector(),
1906 );
1907 let position = crate::lengths::logical_position_to_api(position);
1908 x.show_native_popup_menu(context_menu_item, position)
1909 } else {
1910 false
1911 }
1912 }
1913
1914 fn close_popup_impl(&self, current_popup: &PopupWindow) {
1918 match ¤t_popup.location {
1919 PopupWindowLocation::ChildWindow(offset) => {
1920 let popup_region = crate::properties::evaluate_no_tracking(|| {
1922 let popup_component = ItemTreeRc::borrow_pin(¤t_popup.component);
1923 popup_component.as_ref().item_geometry(0)
1924 })
1925 .translate(offset.to_vector());
1926
1927 if !popup_region.is_empty() {
1928 let window_adapter = self.window_adapter();
1929 window_adapter.renderer().mark_dirty_region(popup_region.into());
1930 window_adapter.request_redraw();
1931 }
1932 }
1933 PopupWindowLocation::TopLevel(adapter) => {
1934 let _ = adapter.set_visible(false);
1935 }
1936 }
1937 if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
1938 self.set_focus_item(&focus, true, FocusReason::PopupActivation);
1939 }
1940 }
1941
1942 pub fn close_popup(&self, popup_id: NonZeroU32) {
1944 let mut active_popups = self.active_popups.borrow_mut();
1945 let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
1946
1947 if let Some(popup_index) = maybe_index {
1948 let p = active_popups.remove(popup_index);
1949 drop(active_popups);
1950 self.close_popup_impl(&p);
1951 if matches!(p.window_kind, WindowKind::Menu) {
1952 while self
1954 .active_popups
1955 .borrow()
1956 .get(popup_index)
1957 .is_some_and(|p| matches!(p.window_kind, WindowKind::Menu))
1958 {
1959 let p = self.active_popups.borrow_mut().remove(popup_index);
1960 self.close_popup_impl(&p);
1961 }
1962 }
1963 }
1964 }
1965
1966 pub fn close_all_popups(&self) {
1968 for popup in self.active_popups.take() {
1969 self.close_popup_impl(&popup);
1970 }
1971 }
1972
1973 pub fn close_top_popup(&self) {
1975 let popup = self.active_popups.borrow_mut().pop();
1976 if let Some(popup) = popup {
1977 self.close_popup_impl(&popup);
1978 }
1979 }
1980
1981 pub fn scale_factor(&self) -> f32 {
1983 self.pinned_fields.as_ref().project_ref().scale_factor.get()
1984 }
1985
1986 pub(crate) fn set_scale_factor(&self, factor: f32) {
1988 if !self.pinned_fields.scale_factor.is_constant() {
1989 self.pinned_fields.scale_factor.set(factor)
1990 }
1991 }
1992
1993 pub fn set_const_scale_factor(&self, factor: f32) {
1996 if !self.pinned_fields.scale_factor.is_constant() {
1997 self.pinned_fields.scale_factor.set(factor);
1998 self.pinned_fields.scale_factor.set_constant();
1999 }
2000 }
2001
2002 pub fn text_input_focused(&self) -> bool {
2004 self.pinned_fields.as_ref().project_ref().text_input_focused.get()
2005 }
2006
2007 pub fn set_text_input_focused(&self, value: bool) {
2009 if !value && let Some(window_adapter) = self.window_adapter().internal(crate::InternalToken)
2010 {
2011 window_adapter.input_method_request(InputMethodRequest::Disable);
2012 }
2013 self.pinned_fields.text_input_focused.set(value)
2014 }
2015
2016 pub fn is_visible(&self) -> bool {
2018 self.strong_component_ref.borrow().is_some()
2019 }
2020
2021 pub fn window_item_rc(&self) -> Option<ItemRc> {
2024 self.try_component().and_then(|component_rc| {
2025 let item_rc = ItemRc::new_root(component_rc);
2026 if item_rc.downcast::<crate::items::WindowItem>().is_some() {
2027 Some(item_rc)
2028 } else {
2029 None
2030 }
2031 })
2032 }
2033
2034 pub fn window_item(&self) -> Option<VRcMapped<ItemTreeVTable, crate::items::WindowItem>> {
2036 self.try_component().and_then(|component_rc| {
2037 ItemRc::new_root(component_rc).downcast::<crate::items::WindowItem>()
2038 })
2039 }
2040
2041 pub(crate) fn set_window_item_geometry(&self, size: crate::lengths::LogicalSize) {
2044 if let Some(component_rc) = self.try_component() {
2045 let component = ItemTreeRc::borrow_pin(&component_rc);
2046 let root_item = component.as_ref().get_item_ref(0);
2047 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
2048 {
2049 window_item.width.set(size.width_length());
2050 window_item.height.set(size.height_length());
2051 }
2052 }
2053 }
2054
2055 pub fn set_window_item_safe_area(&self, inset: crate::lengths::LogicalEdges) {
2057 if let Some(component_rc) = self.try_component() {
2058 let component = ItemTreeRc::borrow_pin(&component_rc);
2059 let root_item = component.as_ref().get_item_ref(0);
2060 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
2061 {
2062 window_item.safe_area_insets.set(inset);
2063 }
2064 }
2065 }
2066
2067 pub(crate) fn set_window_item_virtual_keyboard(
2068 &self,
2069 origin: crate::lengths::LogicalPoint,
2070 size: crate::lengths::LogicalSize,
2071 ) {
2072 let Some(component_rc) = self.try_component() else {
2073 return;
2074 };
2075 let component = ItemTreeRc::borrow_pin(&component_rc);
2076 let root_item = component.as_ref().get_item_ref(0);
2077 let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item) else {
2078 return;
2079 };
2080 window_item.virtual_keyboard_position.set(origin);
2081 window_item.virtual_keyboard_size.set(size);
2082 if let Some(focus_item) = self.focus_item.borrow().upgrade() {
2083 focus_item.try_scroll_into_visible();
2084 }
2085 }
2086
2087 pub(crate) fn window_item_virtual_keyboard(
2089 &self,
2090 ) -> Option<(crate::lengths::LogicalPoint, crate::lengths::LogicalSize)> {
2091 let component_rc = self.try_component()?;
2092 let component = ItemTreeRc::borrow_pin(&component_rc);
2093 let root_item = component.as_ref().get_item_ref(0);
2094 let window_item = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)?;
2095 let keyboard_size = window_item.virtual_keyboard_size();
2096 if keyboard_size.width == 0. as Coord || keyboard_size.height == 0. as Coord {
2097 None
2098 } else {
2099 Some((window_item.virtual_keyboard_position(), keyboard_size))
2100 }
2101 }
2102
2103 pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
2105 self.close_requested.set_handler(move |()| callback());
2106 }
2107
2108 pub fn request_close(&self) -> bool {
2112 match self.close_requested.call(&()) {
2113 CloseRequestResponse::HideWindow => true,
2114 CloseRequestResponse::KeepWindowShown => false,
2115 }
2116 }
2117
2118 pub fn is_fullscreen(&self) -> bool {
2120 if let Some(window_item) = self.window_item() {
2121 window_item.as_pin_ref().full_screen()
2122 } else {
2123 false
2124 }
2125 }
2126
2127 pub fn set_fullscreen(&self, enabled: bool) {
2129 if let Some(window_item) = self.window_item() {
2130 window_item.as_pin_ref().full_screen.set(enabled);
2131 self.update_window_properties()
2132 }
2133 }
2134
2135 pub fn is_maximized(&self) -> bool {
2137 self.window_item().is_some_and(|window_item| window_item.as_pin_ref().maximized())
2138 }
2139
2140 pub fn set_maximized(&self, maximized: bool) {
2142 if let Some(window_item) = self.window_item() {
2143 window_item.as_pin_ref().maximized.set(maximized);
2144 self.update_window_properties()
2145 }
2146 }
2147
2148 pub fn is_minimized(&self) -> bool {
2150 self.window_item().is_some_and(|window_item| window_item.as_pin_ref().minimized())
2151 }
2152
2153 pub fn set_minimized(&self, minimized: bool) {
2155 if let Some(window_item) = self.window_item() {
2156 window_item.as_pin_ref().minimized.set(minimized);
2157 self.update_window_properties()
2158 }
2159 }
2160
2161 pub fn xdg_app_id(&self) -> Option<SharedString> {
2163 self.context().xdg_app_id()
2164 }
2165
2166 pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
2168 self.window_adapter_weak.upgrade().unwrap()
2169 }
2170
2171 pub fn from_pub(window: &crate::api::Window) -> &Self {
2173 &window.0
2174 }
2175
2176 pub fn context(&self) -> &crate::SlintContext {
2178 self.ctx
2179 .get_or_init(|| crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().unwrap().clone()))
2180 }
2181
2182 pub fn try_context(&self) -> Option<&crate::SlintContext> {
2185 if self.ctx.get().is_none()
2186 && let Some(ctx) = crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().cloned())
2187 {
2188 let _ = self.ctx.set(ctx);
2189 }
2190 self.ctx.get()
2191 }
2192
2193 pub fn set_context(&self, ctx: crate::SlintContext) {
2196 self.ctx.set(ctx).map_err(|_| ()).expect("context shouldn't have been set before")
2197 }
2198}
2199
2200pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
2202
2203pub fn context_for_root(root: &ItemTreeRc) -> Option<crate::SlintContext> {
2207 let comp_ref_pin = vtable::VRc::borrow_pin(root);
2208 let mut adapter = None;
2209 comp_ref_pin.as_ref().window_adapter(true, &mut adapter);
2210 adapter.map(|a| WindowInner::from_pub(a.window()).context().clone())
2211}
2212
2213pub fn accent_color(root: &crate::item_tree::ItemTreeRc) -> crate::graphics::Color {
2217 let comp_ref_pin = vtable::VRc::borrow_pin(root);
2218 let mut adapter = None;
2219 comp_ref_pin.as_ref().window_adapter(true, &mut adapter);
2220 adapter.map_or(crate::graphics::Color::default(), |a| {
2221 WindowInner::from_pub(a.window()).context().accent_color()
2222 })
2223}
2224
2225#[cfg(feature = "ffi")]
2228pub mod ffi {
2229 #![allow(unsafe_code)]
2230 #![allow(clippy::missing_safety_doc)]
2231 #![allow(missing_docs)]
2232
2233 use super::*;
2234 use crate::SharedVector;
2235 use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
2236 use crate::graphics::Size;
2237 use crate::graphics::{IntSize, Rgba8Pixel};
2238 use crate::items::WindowItem;
2239 use core::ffi::c_void;
2240
2241 #[repr(u8)]
2244 pub enum GraphicsAPI {
2245 NativeOpenGL,
2247 Inaccessible,
2249 }
2250
2251 struct WithUserData<T> {
2252 callback: T,
2253 drop_user_data: extern "C" fn(*mut c_void),
2254 user_data: *mut c_void,
2255 }
2256
2257 impl<T> Drop for WithUserData<T> {
2258 fn drop(&mut self) {
2259 (self.drop_user_data)(self.user_data)
2260 }
2261 }
2262
2263 impl WithUserData<extern "C" fn(user_data: *mut c_void, pos: &mut LogicalPosition)> {
2264 fn call(&self) -> LogicalPosition {
2265 let mut logical_position = LogicalPosition::default();
2266 (self.callback)(self.user_data, &mut logical_position);
2267 logical_position
2268 }
2269 }
2270
2271 impl WithUserData<extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse> {
2272 fn call(&self) -> CloseRequestResponse {
2273 (self.callback)(self.user_data)
2274 }
2275 }
2276
2277 impl WithUserData<extern "C" fn(user_data: *mut c_void, is_open: bool)> {
2278 fn call(&self, is_open: bool) {
2279 (self.callback)(self.user_data, is_open)
2280 }
2281 }
2282
2283 #[repr(C)]
2285 pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
2286
2287 #[unsafe(no_mangle)]
2289 pub unsafe extern "C" fn slint_windowrc_drop(handle: *mut WindowAdapterRcOpaque) {
2290 unsafe {
2291 assert_eq!(
2292 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2293 core::mem::size_of::<WindowAdapterRcOpaque>()
2294 );
2295 assert_eq!(
2296 core::mem::size_of::<Option<Rc<dyn WindowAdapter>>>(),
2297 core::mem::size_of::<WindowAdapterRcOpaque>()
2298 );
2299 drop(core::ptr::read(handle as *mut Option<Rc<dyn WindowAdapter>>));
2300 }
2301 }
2302
2303 #[unsafe(no_mangle)]
2305 pub unsafe extern "C" fn slint_windowrc_clone(
2306 source: *const WindowAdapterRcOpaque,
2307 target: *mut WindowAdapterRcOpaque,
2308 ) {
2309 unsafe {
2310 assert_eq!(
2311 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2312 core::mem::size_of::<WindowAdapterRcOpaque>()
2313 );
2314 let window = &*(source as *const Rc<dyn WindowAdapter>);
2315 core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window.clone());
2316 }
2317 }
2318
2319 #[unsafe(no_mangle)]
2321 pub unsafe extern "C" fn slint_windowrc_ensure_tree_instantiated(
2322 handle: *const WindowAdapterRcOpaque,
2323 ) {
2324 unsafe {
2325 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2326 WindowInner::from_pub(window_adapter.window()).ensure_tree_instantiated();
2327 }
2328 }
2329
2330 #[unsafe(no_mangle)]
2332 pub unsafe extern "C" fn slint_windowrc_show(handle: *const WindowAdapterRcOpaque) {
2333 unsafe {
2334 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2335
2336 window_adapter.window().show().unwrap();
2337 }
2338 }
2339
2340 #[unsafe(no_mangle)]
2342 pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) {
2343 unsafe {
2344 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2345 window_adapter.window().hide().unwrap();
2346 }
2347 }
2348
2349 #[unsafe(no_mangle)]
2352 pub unsafe extern "C" fn slint_windowrc_is_visible(
2353 handle: *const WindowAdapterRcOpaque,
2354 ) -> bool {
2355 unsafe {
2356 let window = &*(handle as *const Rc<dyn WindowAdapter>);
2357 window.window().is_visible()
2358 }
2359 }
2360
2361 #[unsafe(no_mangle)]
2363 pub unsafe extern "C" fn slint_windowrc_get_scale_factor(
2364 handle: *const WindowAdapterRcOpaque,
2365 ) -> f32 {
2366 unsafe {
2367 assert_eq!(
2368 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2369 core::mem::size_of::<WindowAdapterRcOpaque>()
2370 );
2371 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2372 WindowInner::from_pub(window_adapter.window()).scale_factor()
2373 }
2374 }
2375
2376 #[unsafe(no_mangle)]
2378 pub unsafe extern "C" fn slint_windowrc_set_const_scale_factor(
2379 handle: *const WindowAdapterRcOpaque,
2380 value: f32,
2381 ) {
2382 unsafe {
2383 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2384 WindowInner::from_pub(window_adapter.window()).set_const_scale_factor(value)
2385 }
2386 }
2387
2388 #[unsafe(no_mangle)]
2390 pub unsafe extern "C" fn slint_windowrc_get_text_input_focused(
2391 handle: *const WindowAdapterRcOpaque,
2392 ) -> bool {
2393 unsafe {
2394 assert_eq!(
2395 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2396 core::mem::size_of::<WindowAdapterRcOpaque>()
2397 );
2398 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2399 WindowInner::from_pub(window_adapter.window()).text_input_focused()
2400 }
2401 }
2402
2403 #[unsafe(no_mangle)]
2405 pub unsafe extern "C" fn slint_windowrc_set_text_input_focused(
2406 handle: *const WindowAdapterRcOpaque,
2407 value: bool,
2408 ) {
2409 unsafe {
2410 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2411 WindowInner::from_pub(window_adapter.window()).set_text_input_focused(value)
2412 }
2413 }
2414
2415 #[unsafe(no_mangle)]
2417 pub unsafe extern "C" fn slint_windowrc_set_focus_item(
2418 handle: *const WindowAdapterRcOpaque,
2419 focus_item: &ItemRc,
2420 set_focus: bool,
2421 reason: FocusReason,
2422 ) {
2423 unsafe {
2424 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2425 WindowInner::from_pub(window_adapter.window())
2426 .set_focus_item(focus_item, set_focus, reason)
2427 }
2428 }
2429
2430 #[unsafe(no_mangle)]
2432 pub unsafe extern "C" fn slint_windowrc_set_component(
2433 handle: *const WindowAdapterRcOpaque,
2434 component: &ItemTreeRc,
2435 ) {
2436 unsafe {
2437 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2438 WindowInner::from_pub(window_adapter.window()).set_component(component)
2439 }
2440 }
2441
2442 #[unsafe(no_mangle)]
2444 pub unsafe extern "C" fn slint_windowrc_show_popup(
2445 handle: *const WindowAdapterRcOpaque,
2446 popup: &ItemTreeRc,
2447 position: extern "C" fn(user_data: *mut c_void, pos: &mut LogicalPosition),
2448 drop_user_data: extern "C" fn(user_data: *mut c_void),
2449 user_data: *mut c_void,
2450 close_policy: PopupClosePolicy,
2451 parent_item: &ItemRc,
2452 window_kind: WindowKind,
2453 is_open_setter: extern "C" fn(user_data: *mut c_void, is_open: bool),
2454 is_open_setter_drop_user_data: extern "C" fn(user_data: *mut c_void),
2455 is_open_setter_user_data: *mut c_void,
2456 ) -> NonZeroU32 {
2457 unsafe {
2458 let with_user_data = WithUserData { callback: position, drop_user_data, user_data };
2459 let is_open_with_user_data = WithUserData {
2460 callback: is_open_setter,
2461 drop_user_data: is_open_setter_drop_user_data,
2462 user_data: is_open_setter_user_data,
2463 };
2464 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2465 WindowInner::from_pub(window_adapter.window()).show_popup(
2466 popup,
2467 Box::new(move || with_user_data.call()),
2468 close_policy,
2469 parent_item,
2470 window_kind,
2471 Box::new(move |is_open| is_open_with_user_data.call(is_open)),
2472 )
2473 }
2474 }
2475
2476 #[unsafe(no_mangle)]
2480 pub unsafe extern "C" fn slint_windowrc_create_child_window_adapter(
2481 handle: *const WindowAdapterRcOpaque,
2482 window_kind: WindowKind,
2483 result: *mut WindowAdapterRcOpaque,
2484 ) -> bool {
2485 unsafe {
2486 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2487 match WindowInner::from_pub(window_adapter.window())
2488 .create_child_window_adapter(window_kind)
2489 {
2490 Some(wa) => {
2491 core::ptr::write(result as *mut Rc<dyn WindowAdapter>, wa);
2492 true
2493 }
2494 None => false,
2495 }
2496 }
2497 }
2498
2499 #[unsafe(no_mangle)]
2501 pub unsafe extern "C" fn slint_windowrc_close_popup(
2502 handle: *const WindowAdapterRcOpaque,
2503 popup_id: NonZeroU32,
2504 ) {
2505 unsafe {
2506 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2507 WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
2508 }
2509 }
2510
2511 #[unsafe(no_mangle)]
2513 pub unsafe extern "C" fn slint_windowrc_set_rendering_notifier(
2514 handle: *const WindowAdapterRcOpaque,
2515 callback: extern "C" fn(
2516 rendering_state: RenderingState,
2517 graphics_api: GraphicsAPI,
2518 user_data: *mut c_void,
2519 ),
2520 drop_user_data: extern "C" fn(user_data: *mut c_void),
2521 user_data: *mut c_void,
2522 error: *mut SetRenderingNotifierError,
2523 ) -> bool {
2524 unsafe {
2525 struct CNotifier {
2526 callback: extern "C" fn(
2527 rendering_state: RenderingState,
2528 graphics_api: GraphicsAPI,
2529 user_data: *mut c_void,
2530 ),
2531 drop_user_data: extern "C" fn(*mut c_void),
2532 user_data: *mut c_void,
2533 }
2534
2535 impl Drop for CNotifier {
2536 fn drop(&mut self) {
2537 (self.drop_user_data)(self.user_data)
2538 }
2539 }
2540
2541 impl RenderingNotifier for CNotifier {
2542 fn notify(
2543 &mut self,
2544 state: RenderingState,
2545 graphics_api: &crate::api::GraphicsAPI,
2546 ) {
2547 let cpp_graphics_api = match graphics_api {
2548 crate::api::GraphicsAPI::NativeOpenGL { .. } => GraphicsAPI::NativeOpenGL,
2549 crate::api::GraphicsAPI::WebGL { .. } => unreachable!(), #[cfg(feature = "unstable-wgpu-28")]
2551 crate::api::GraphicsAPI::WGPU28 { .. } => GraphicsAPI::Inaccessible, #[cfg(feature = "unstable-wgpu-29")]
2553 crate::api::GraphicsAPI::WGPU29 { .. } => GraphicsAPI::Inaccessible, };
2555 (self.callback)(state, cpp_graphics_api, self.user_data)
2556 }
2557 }
2558
2559 let window = &*(handle as *const Rc<dyn WindowAdapter>);
2560 match window.renderer().set_rendering_notifier(Box::new(CNotifier {
2561 callback,
2562 drop_user_data,
2563 user_data,
2564 })) {
2565 Ok(()) => true,
2566 Err(err) => {
2567 *error = err;
2568 false
2569 }
2570 }
2571 }
2572 }
2573
2574 #[unsafe(no_mangle)]
2576 pub unsafe extern "C" fn slint_windowrc_on_close_requested(
2577 handle: *const WindowAdapterRcOpaque,
2578 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
2579 drop_user_data: extern "C" fn(user_data: *mut c_void),
2580 user_data: *mut c_void,
2581 ) {
2582 unsafe {
2583 let with_user_data = WithUserData { callback, drop_user_data, user_data };
2584 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2585 window_adapter.window().on_close_requested(move || with_user_data.call());
2586 }
2587 }
2588
2589 #[unsafe(no_mangle)]
2591 pub unsafe extern "C" fn slint_windowrc_request_redraw(handle: *const WindowAdapterRcOpaque) {
2592 unsafe {
2593 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2594 window_adapter.request_redraw();
2595 }
2596 }
2597
2598 #[unsafe(no_mangle)]
2601 pub unsafe extern "C" fn slint_windowrc_position(
2602 handle: *const WindowAdapterRcOpaque,
2603 pos: &mut euclid::default::Point2D<i32>,
2604 ) {
2605 unsafe {
2606 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2607 *pos = window_adapter.position().unwrap_or_default().to_euclid()
2608 }
2609 }
2610
2611 #[unsafe(no_mangle)]
2615 pub unsafe extern "C" fn slint_windowrc_set_physical_position(
2616 handle: *const WindowAdapterRcOpaque,
2617 pos: &euclid::default::Point2D<i32>,
2618 ) {
2619 unsafe {
2620 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2621 window_adapter.set_position(crate::api::PhysicalPosition::new(pos.x, pos.y).into());
2622 }
2623 }
2624
2625 #[unsafe(no_mangle)]
2629 pub unsafe extern "C" fn slint_windowrc_set_logical_position(
2630 handle: *const WindowAdapterRcOpaque,
2631 pos: &euclid::default::Point2D<f32>,
2632 ) {
2633 unsafe {
2634 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2635 window_adapter.set_position(LogicalPosition::new(pos.x, pos.y).into());
2636 }
2637 }
2638
2639 #[unsafe(no_mangle)]
2642 pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
2643 unsafe {
2644 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2645 window_adapter.size().to_euclid().cast()
2646 }
2647 }
2648
2649 #[unsafe(no_mangle)]
2652 pub unsafe extern "C" fn slint_windowrc_set_physical_size(
2653 handle: *const WindowAdapterRcOpaque,
2654 size: &IntSize,
2655 ) {
2656 unsafe {
2657 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2658 window_adapter
2659 .window()
2660 .set_size(crate::api::PhysicalSize::new(size.width, size.height));
2661 }
2662 }
2663
2664 #[unsafe(no_mangle)]
2667 pub unsafe extern "C" fn slint_windowrc_set_logical_size(
2668 handle: *const WindowAdapterRcOpaque,
2669 size: &Size,
2670 ) {
2671 unsafe {
2672 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2673 window_adapter.window().set_size(crate::api::LogicalSize::new(size.width, size.height));
2674 }
2675 }
2676
2677 #[unsafe(no_mangle)]
2679 pub unsafe extern "C" fn slint_windowrc_supports_native_menu_bar(
2680 handle: *const WindowAdapterRcOpaque,
2681 ) -> bool {
2682 unsafe {
2683 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2684 window_adapter
2685 .internal(crate::InternalToken)
2686 .is_some_and(|x| x.supports_native_menu_bar())
2687 }
2688 }
2689
2690 #[unsafe(no_mangle)]
2692 pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
2693 handle: *const WindowAdapterRcOpaque,
2694 menu_instance: &vtable::VRc<MenuVTable>,
2695 ) {
2696 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2697 let window = window_adapter.window();
2698 window.0.setup_menubar(vtable::VRc::clone(menu_instance));
2699 }
2700
2701 #[unsafe(no_mangle)]
2702 pub unsafe extern "C" fn slint_windowrc_setup_menu_bar_shortcuts(
2703 handle: *const WindowAdapterRcOpaque,
2704 menu_instance: &vtable::VRc<MenuVTable>,
2705 ) {
2706 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2707 let window = window_adapter.window();
2708 window.0.setup_menubar_shortcuts(vtable::VRc::clone(menu_instance));
2709 }
2710
2711 #[unsafe(no_mangle)]
2713 pub unsafe extern "C" fn slint_windowrc_show_native_popup_menu(
2714 handle: *const WindowAdapterRcOpaque,
2715 context_menu: &vtable::VRc<MenuVTable>,
2716 position: LogicalPosition,
2717 parent_item: &ItemRc,
2718 ) -> bool {
2719 unsafe {
2720 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2721 WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(
2722 context_menu.clone(),
2723 position,
2724 parent_item,
2725 )
2726 }
2727 }
2728
2729 #[unsafe(no_mangle)]
2731 pub unsafe extern "C" fn slint_windowrc_resolved_default_font_size(
2732 item_tree: &ItemTreeRc,
2733 ) -> f32 {
2734 WindowItem::resolved_default_font_size(item_tree.clone()).get()
2735 }
2736
2737 #[unsafe(no_mangle)]
2739 pub unsafe extern "C" fn slint_windowrc_dispatch_key_event(
2740 handle: *const WindowAdapterRcOpaque,
2741 event_type: crate::input::KeyEventType,
2742 text: &SharedString,
2743 repeat: bool,
2744 ) {
2745 unsafe {
2746 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2747 window_adapter.window().0.process_key_input(InternalKeyEvent {
2748 event_type,
2749 key_event: crate::items::KeyEvent {
2750 text: text.clone(),
2751 repeat,
2752 ..Default::default()
2753 },
2754 ..Default::default()
2755 });
2756 }
2757 }
2758
2759 #[unsafe(no_mangle)]
2761 pub unsafe extern "C" fn slint_windowrc_dispatch_pointer_event(
2762 handle: *const WindowAdapterRcOpaque,
2763 event: &crate::input::MouseEvent,
2764 ) {
2765 unsafe {
2766 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2767 window_adapter.window().0.process_mouse_input(event.clone());
2768 }
2769 }
2770
2771 #[unsafe(no_mangle)]
2773 pub unsafe extern "C" fn slint_windowrc_dispatch_event(
2774 handle: *const WindowAdapterRcOpaque,
2775 event: &crate::platform::WindowEvent,
2776 ) {
2777 unsafe {
2778 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2779 window_adapter.window().dispatch_event(event.clone());
2780 }
2781 }
2782
2783 #[unsafe(no_mangle)]
2784 pub unsafe extern "C" fn slint_windowrc_is_fullscreen(
2785 handle: *const WindowAdapterRcOpaque,
2786 ) -> bool {
2787 unsafe {
2788 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2789 window_adapter.window().is_fullscreen()
2790 }
2791 }
2792
2793 #[unsafe(no_mangle)]
2794 pub unsafe extern "C" fn slint_windowrc_is_minimized(
2795 handle: *const WindowAdapterRcOpaque,
2796 ) -> bool {
2797 unsafe {
2798 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2799 window_adapter.window().is_minimized()
2800 }
2801 }
2802
2803 #[unsafe(no_mangle)]
2804 pub unsafe extern "C" fn slint_windowrc_is_maximized(
2805 handle: *const WindowAdapterRcOpaque,
2806 ) -> bool {
2807 unsafe {
2808 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2809 window_adapter.window().is_maximized()
2810 }
2811 }
2812
2813 #[unsafe(no_mangle)]
2814 pub unsafe extern "C" fn slint_windowrc_set_fullscreen(
2815 handle: *const WindowAdapterRcOpaque,
2816 value: bool,
2817 ) {
2818 unsafe {
2819 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2820 window_adapter.window().set_fullscreen(value)
2821 }
2822 }
2823
2824 #[unsafe(no_mangle)]
2825 pub unsafe extern "C" fn slint_windowrc_set_minimized(
2826 handle: *const WindowAdapterRcOpaque,
2827 value: bool,
2828 ) {
2829 unsafe {
2830 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2831 window_adapter.window().set_minimized(value)
2832 }
2833 }
2834
2835 #[unsafe(no_mangle)]
2836 pub unsafe extern "C" fn slint_windowrc_set_maximized(
2837 handle: *const WindowAdapterRcOpaque,
2838 value: bool,
2839 ) {
2840 unsafe {
2841 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2842 window_adapter.window().set_maximized(value)
2843 }
2844 }
2845
2846 #[unsafe(no_mangle)]
2848 pub unsafe extern "C" fn slint_windowrc_take_snapshot(
2849 handle: *const WindowAdapterRcOpaque,
2850 data: &mut SharedVector<Rgba8Pixel>,
2851 width: &mut u32,
2852 height: &mut u32,
2853 ) -> bool {
2854 unsafe {
2855 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2856 if let Ok(snapshot) = window_adapter.window().take_snapshot() {
2857 *data = snapshot.data.clone();
2858 *width = snapshot.width();
2859 *height = snapshot.height();
2860 true
2861 } else {
2862 false
2863 }
2864 }
2865 }
2866}
2867
2868#[cfg(all(feature = "ffi", feature = "raw-window-handle-06"))]
2870pub mod ffi_window {
2871 #![allow(unsafe_code)]
2872 #![allow(clippy::missing_safety_doc)]
2873
2874 use super::ffi::WindowAdapterRcOpaque;
2875 use super::*;
2876 use std::ffi::c_void;
2877 use std::ptr::null_mut;
2878 use std::sync::Arc;
2879
2880 fn has_window_handle(
2882 handle: *const WindowAdapterRcOpaque,
2883 ) -> Option<Arc<dyn raw_window_handle_06::HasWindowHandle>> {
2884 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2885 let window_adapter = window_adapter.internal(crate::InternalToken)?;
2886 window_adapter.window_handle_06_rc().ok()
2887 }
2888
2889 fn has_display_handle(
2891 handle: *const WindowAdapterRcOpaque,
2892 ) -> Option<Arc<dyn raw_window_handle_06::HasDisplayHandle>> {
2893 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2894 let window_adapter = window_adapter.internal(crate::InternalToken)?;
2895 window_adapter.display_handle_06_rc().ok()
2896 }
2897
2898 #[unsafe(no_mangle)]
2900 pub unsafe extern "C" fn slint_windowrc_hwnd_win32(
2901 handle: *const WindowAdapterRcOpaque,
2902 ) -> *mut c_void {
2903 use raw_window_handle_06::HasWindowHandle;
2904
2905 if let Some(has_window_handle) = has_window_handle(handle)
2906 && let Ok(window_handle) = has_window_handle.window_handle()
2907 && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
2908 {
2909 isize::from(win32.hwnd) as *mut c_void
2910 } else {
2911 null_mut()
2912 }
2913 }
2914
2915 #[unsafe(no_mangle)]
2917 pub unsafe extern "C" fn slint_windowrc_hinstance_win32(
2918 handle: *const WindowAdapterRcOpaque,
2919 ) -> *mut c_void {
2920 use raw_window_handle_06::HasWindowHandle;
2921
2922 if let Some(has_window_handle) = has_window_handle(handle)
2923 && let Ok(window_handle) = has_window_handle.window_handle()
2924 && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
2925 {
2926 win32
2927 .hinstance
2928 .map(|hinstance| isize::from(hinstance) as *mut c_void)
2929 .unwrap_or_default()
2930 } else {
2931 null_mut()
2932 }
2933 }
2934
2935 #[unsafe(no_mangle)]
2937 pub unsafe extern "C" fn slint_windowrc_wlsurface_wayland(
2938 handle: *const WindowAdapterRcOpaque,
2939 ) -> *mut c_void {
2940 use raw_window_handle_06::HasWindowHandle;
2941
2942 if let Some(has_window_handle) = has_window_handle(handle)
2943 && let Ok(window_handle) = has_window_handle.window_handle()
2944 && let raw_window_handle_06::RawWindowHandle::Wayland(wayland) = window_handle.as_raw()
2945 {
2946 wayland.surface.as_ptr()
2947 } else {
2948 null_mut()
2949 }
2950 }
2951
2952 #[unsafe(no_mangle)]
2954 pub unsafe extern "C" fn slint_windowrc_wldisplay_wayland(
2955 handle: *const WindowAdapterRcOpaque,
2956 ) -> *mut c_void {
2957 use raw_window_handle_06::HasDisplayHandle;
2958
2959 if let Some(has_display_handle) = has_display_handle(handle)
2960 && let Ok(display_handle) = has_display_handle.display_handle()
2961 && let raw_window_handle_06::RawDisplayHandle::Wayland(wayland) =
2962 display_handle.as_raw()
2963 {
2964 wayland.display.as_ptr()
2965 } else {
2966 null_mut()
2967 }
2968 }
2969
2970 #[unsafe(no_mangle)]
2972 pub unsafe extern "C" fn slint_windowrc_nsview_appkit(
2973 handle: *const WindowAdapterRcOpaque,
2974 ) -> *mut c_void {
2975 use raw_window_handle_06::HasWindowHandle;
2976
2977 if let Some(has_window_handle) = has_window_handle(handle)
2978 && let Ok(window_handle) = has_window_handle.window_handle()
2979 && let raw_window_handle_06::RawWindowHandle::AppKit(appkit) = window_handle.as_raw()
2980 {
2981 appkit.ns_view.as_ptr()
2982 } else {
2983 null_mut()
2984 }
2985 }
2986}