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 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 *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 let mut new_position: Option<LogicalPosition> = None;
1527 popup.properties_tracker.as_ref().evaluate_as_dependency_root(|| {
1528 (popup.position_access)(); 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let (location, properties_tracker) =
1834 if Rc::ptr_eq(&parent_window_adapter, &popup_window_adapter) {
1835 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 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 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 fn close_popup_impl(&self, current_popup: &PopupWindow) {
1930 match ¤t_popup.location {
1931 PopupWindowLocation::ChildWindow(offset) => {
1932 let popup_region = crate::properties::evaluate_no_tracking(|| {
1934 let popup_component = ItemTreeRc::borrow_pin(¤t_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 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 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 pub fn close_all_popups(&self) {
1980 for popup in self.active_popups.take() {
1981 self.close_popup_impl(&popup);
1982 }
1983 }
1984
1985 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 pub fn scale_factor(&self) -> f32 {
1995 self.pinned_fields.as_ref().project_ref().scale_factor.get()
1996 }
1997
1998 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 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 pub fn text_input_focused(&self) -> bool {
2016 self.pinned_fields.as_ref().project_ref().text_input_focused.get()
2017 }
2018
2019 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 pub fn is_visible(&self) -> bool {
2030 self.strong_component_ref.borrow().is_some()
2031 }
2032
2033 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 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 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 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 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 pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
2117 self.close_requested.set_handler(move |()| callback());
2118 }
2119
2120 pub fn request_close(&self) -> bool {
2124 match self.close_requested.call(&()) {
2125 CloseRequestResponse::HideWindow => true,
2126 CloseRequestResponse::KeepWindowShown => false,
2127 }
2128 }
2129
2130 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 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 pub fn is_maximized(&self) -> bool {
2149 self.window_item().is_some_and(|window_item| window_item.as_pin_ref().maximized())
2150 }
2151
2152 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 pub fn is_minimized(&self) -> bool {
2162 self.window_item().is_some_and(|window_item| window_item.as_pin_ref().minimized())
2163 }
2164
2165 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 pub fn xdg_app_id(&self) -> Option<SharedString> {
2175 self.context().xdg_app_id()
2176 }
2177
2178 pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
2180 self.window_adapter_weak.upgrade().unwrap()
2181 }
2182
2183 pub fn from_pub(window: &crate::api::Window) -> &Self {
2185 &window.0
2186 }
2187
2188 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 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 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
2212pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
2214
2215pub 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
2225pub 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#[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 #[repr(u8)]
2256 pub enum GraphicsAPI {
2257 NativeOpenGL,
2259 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 #[repr(C)]
2297 pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
2298
2299 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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!(), #[cfg(feature = "unstable-wgpu-28")]
2563 crate::api::GraphicsAPI::WGPU28 { .. } => GraphicsAPI::Inaccessible, #[cfg(feature = "unstable-wgpu-29")]
2565 crate::api::GraphicsAPI::WGPU29 { .. } => GraphicsAPI::Inaccessible, };
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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[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 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 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 #[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 #[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 #[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 #[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 #[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}