1#![warn(missing_docs)]
7use crate::api::{
9 CloseRequestResponse, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize,
10 PlatformError, Window, WindowPosition, WindowSize,
11};
12use crate::cursor::MouseCursorInner;
13use crate::input::{
14 BackendDragEvent, ClickState, DragData, FocusEvent, FocusReason, InternalKeyEvent,
15 KeyEventResult, KeyEventType, Keys, MouseEvent, MouseInputState, PointerEventButton,
16 TextCursorBlinker, TouchPhase, TouchState, key_codes,
17};
18use crate::item_tree::{
19 ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeRefPin, ItemTreeVTable, ItemTreeWeak, ItemWeak,
20 ParentItemTraversalMode,
21};
22use crate::items::{
23 BuiltInMouseCursor, InputMethodHints, InputType, ItemRef, MenuEntry, PopupClosePolicy,
24};
25use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalVector, SizeLengths};
26use crate::menus::MenuVTable;
27use crate::properties::{ChangeTracker, Property, PropertyTracker};
28use crate::renderer::Renderer;
29use crate::{Callback, Coord, SharedString, SharedVector};
30use alloc::boxed::Box;
31use alloc::rc::{Rc, Weak};
32use alloc::vec::Vec;
33use core::cell::{Cell, RefCell};
34use core::num::NonZeroU32;
35use core::pin::Pin;
36use euclid::num::Zero;
37use vtable::{VRc, VRcMapped};
38
39pub mod popup;
40
41fn next_focus_item(item: ItemRc) -> ItemRc {
42 item.next_focus_item()
43}
44
45fn previous_focus_item(item: ItemRc) -> ItemRc {
46 item.previous_focus_item()
47}
48
49#[repr(C)]
51pub enum WindowKind {
52 ToolTip,
54 Popup,
56 Menu,
58}
59
60pub trait WindowAdapter {
87 fn window(&self) -> &Window;
89
90 fn set_visible(&self, _visible: bool) -> Result<(), PlatformError> {
92 Ok(())
93 }
94
95 fn position(&self) -> Option<PhysicalPosition> {
102 None
103 }
104 fn set_position(&self, _position: WindowPosition) {}
111
112 fn set_size(&self, _size: WindowSize) {}
123
124 fn size(&self) -> PhysicalSize;
126
127 fn request_redraw(&self) {}
139
140 fn renderer(&self) -> &dyn Renderer;
145
146 fn update_window_properties(&self, _properties: WindowProperties<'_>) {}
152
153 #[doc(hidden)]
154 fn internal(&self, _: crate::InternalToken) -> Option<&dyn WindowAdapterInternal> {
155 None
156 }
157
158 #[cfg(feature = "raw-window-handle-06")]
160 fn window_handle_06(
161 &self,
162 ) -> Result<raw_window_handle_06::WindowHandle<'_>, raw_window_handle_06::HandleError> {
163 Err(raw_window_handle_06::HandleError::NotSupported)
164 }
165
166 #[cfg(feature = "raw-window-handle-06")]
168 fn display_handle_06(
169 &self,
170 ) -> Result<raw_window_handle_06::DisplayHandle<'_>, raw_window_handle_06::HandleError> {
171 Err(raw_window_handle_06::HandleError::NotSupported)
172 }
173}
174
175#[derive(Clone)]
181#[doc(hidden)]
182pub struct DragRequest {
183 pub(crate) data: crate::data_transfer::DataTransfer,
184 pub(crate) allowed: crate::items::AllowedDragActions,
185 pub(crate) drag_image: crate::graphics::Image,
186 pub(crate) drag_image_offset: euclid::default::Vector2D<i32>,
187}
188
189impl DragRequest {
190 pub fn data(&self) -> &crate::data_transfer::DataTransfer {
192 &self.data
193 }
194 pub fn allowed_actions(&self) -> crate::items::AllowedDragActions {
196 self.allowed
197 }
198 pub fn drag_image(&self) -> &crate::graphics::Image {
200 &self.drag_image
201 }
202 pub fn drag_image_offset(&self) -> euclid::default::Vector2D<i32> {
204 self.drag_image_offset
205 }
206}
207
208#[derive(Clone)]
212pub(crate) struct NativePendingDrag {
213 pub(crate) request: DragRequest,
214 pub(crate) source: ItemWeak,
216 pub(crate) seed_position: LogicalPosition,
218}
219
220#[doc(hidden)]
225pub trait WindowAdapterInternal: core::any::Any {
226 fn register_item_tree(&self, _: ItemTreeRefPin) {}
228
229 fn unregister_item_tree(
232 &self,
233 _component: ItemTreeRef,
234 _items: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
235 ) {
236 }
237
238 fn get_parent(&self) -> Option<Rc<dyn WindowAdapter>> {
240 None
241 }
242
243 fn create_child_window_adapter(
250 &self,
251 _window_kind: WindowKind,
252 ) -> Option<Rc<dyn WindowAdapter>> {
253 None
254 }
255
256 fn set_mouse_cursor(&self, _cursor: MouseCursorInner) {}
259
260 fn input_method_request(&self, _: InputMethodRequest) {}
262
263 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {}
266
267 fn supports_native_menu_bar(&self) -> bool {
269 false
270 }
271
272 fn setup_menubar(&self, _menubar: vtable::VRc<MenuVTable>) {}
273
274 fn show_native_popup_menu(
275 &self,
276 _context_menu_item: vtable::VRc<MenuVTable>,
277 _position: LogicalPosition,
278 ) -> bool {
279 false
280 }
281
282 #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
284 fn window_handle_06_rc(
285 &self,
286 ) -> Result<
287 std::sync::Arc<dyn raw_window_handle_06::HasWindowHandle>,
288 raw_window_handle_06::HandleError,
289 > {
290 Err(raw_window_handle_06::HandleError::NotSupported)
291 }
292
293 #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
295 fn display_handle_06_rc(
296 &self,
297 ) -> Result<
298 std::sync::Arc<dyn raw_window_handle_06::HasDisplayHandle>,
299 raw_window_handle_06::HandleError,
300 > {
301 Err(raw_window_handle_06::HandleError::NotSupported)
302 }
303
304 fn bring_to_front(&self) -> Result<(), PlatformError> {
306 Ok(())
307 }
308
309 fn safe_area_inset(&self) -> crate::lengths::PhysicalEdges {
312 Default::default()
313 }
314
315 fn start_drag(&self, _request: &DragRequest) -> bool {
325 false
326 }
327
328 fn start_window_move(&self) {}
334}
335
336#[non_exhaustive]
339#[derive(Debug, Clone)]
340pub enum InputMethodRequest {
341 Enable(InputMethodProperties),
343 Update(InputMethodProperties),
345 Disable,
347}
348
349#[non_exhaustive]
351#[derive(Clone, Default, Debug)]
352pub struct InputMethodProperties {
353 pub text: SharedString,
357 pub cursor_position: usize,
359 pub anchor_position: Option<usize>,
362 pub preedit_text: SharedString,
366 pub preedit_offset: usize,
368 pub cursor_rect_origin: LogicalPosition,
370 pub cursor_rect_size: crate::api::LogicalSize,
372 pub anchor_point: LogicalPosition,
374 pub input_type: InputType,
376 pub input_method_hints: InputMethodHints,
378 pub clip_rect: Option<LogicalRect>,
380}
381
382#[non_exhaustive]
384#[derive(Copy, Clone, Debug, PartialEq, Default)]
385pub struct LayoutConstraints {
386 pub min: Option<crate::api::LogicalSize>,
388 pub max: Option<crate::api::LogicalSize>,
390 pub preferred: crate::api::LogicalSize,
392}
393
394pub struct WindowProperties<'a>(&'a WindowInner);
397
398impl WindowProperties<'_> {
399 pub fn title(&self) -> SharedString {
401 self.0.window_item().map(|w| w.as_pin_ref().title()).unwrap_or_default()
402 }
403
404 pub fn background(&self) -> crate::Brush {
406 self.0
407 .window_item()
408 .map(|w: VRcMapped<ItemTreeVTable, crate::items::WindowItem>| {
409 w.as_pin_ref().background()
410 })
411 .unwrap_or_default()
412 }
413
414 pub fn layout_constraints(&self) -> LayoutConstraints {
416 let component = self.0.component();
417 let component = ItemTreeRc::borrow_pin(&component);
418 let h = component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
419 let v = component.as_ref().layout_info(crate::layout::Orientation::Vertical);
420 let (min, max) = crate::layout::min_max_size_for_layout_constraints(h, v);
421 LayoutConstraints {
422 min,
423 max,
424 preferred: crate::api::LogicalSize::new(
425 h.preferred_bounded() as f32,
426 v.preferred_bounded() as f32,
427 ),
428 }
429 }
430
431 #[deprecated(note = "Please use `is_fullscreen` instead")]
433 pub fn fullscreen(&self) -> bool {
434 self.is_fullscreen()
435 }
436
437 pub fn is_fullscreen(&self) -> bool {
439 self.0.is_fullscreen()
440 }
441
442 pub fn is_maximized(&self) -> bool {
444 self.0.is_maximized()
445 }
446
447 pub fn is_minimized(&self) -> bool {
449 self.0.is_minimized()
450 }
451}
452
453struct WindowPropertiesTracker {
454 window_adapter_weak: Weak<dyn WindowAdapter>,
455}
456
457impl crate::properties::PropertyDirtyHandler for WindowPropertiesTracker {
458 fn notify(self: Pin<&Self>) {
459 let win = self.window_adapter_weak.clone();
460 let Some(adapter) = win.upgrade() else { return };
461 WindowInner::from_pub(adapter.window()).context().single_shot(
462 Default::default(),
463 move || {
464 if let Some(window_adapter) = win.upgrade() {
465 WindowInner::from_pub(window_adapter.window()).update_window_properties();
466 };
467 },
468 )
469 }
470}
471
472pub(crate) struct PopupWindowPropertiesTracker {
473 parent_window_adapter_weak: Weak<dyn WindowAdapter>,
475 popup_id: NonZeroU32,
477}
478
479impl crate::properties::PropertyDirtyHandler for PopupWindowPropertiesTracker {
480 fn notify(self: Pin<&Self>) {
481 let parent = self.parent_window_adapter_weak.clone();
482 let popup_id = self.popup_id;
483 let Some(parent_adapter) = parent.upgrade() else { return };
484 WindowInner::from_pub(parent_adapter.window()).context().single_shot(
487 Default::default(),
488 move || {
489 if let Some(parent_adapter) = parent.upgrade() {
490 WindowInner::from_pub(parent_adapter.window())
491 .update_popup_properties(popup_id);
492 }
493 },
494 );
495 }
496}
497
498struct WindowRedrawTracker {
499 window_adapter_weak: Weak<dyn WindowAdapter>,
500}
501
502impl crate::properties::PropertyDirtyHandler for WindowRedrawTracker {
503 fn notify(self: Pin<&Self>) {
504 if let Some(window_adapter) = self.window_adapter_weak.upgrade() {
505 window_adapter.request_redraw();
506 };
507 }
508}
509
510pub enum PopupWindowLocation {
512 TopLevel(Rc<dyn WindowAdapter>),
514 ChildWindow(LogicalPoint),
516}
517
518pub struct PopupWindow {
521 pub popup_id: NonZeroU32,
523 pub location: PopupWindowLocation,
525 pub component: ItemTreeRc,
527 pub close_policy: PopupClosePolicy,
529 focus_item_in_parent: ItemWeak,
531 pub parent_item: ItemWeak,
533 pub window_kind: WindowKind,
536 position_access: Box<dyn Fn() -> LogicalPosition>,
540 is_open_setter: Box<dyn Fn(bool)>,
545 properties_tracker: Pin<Box<PropertyTracker<true, PopupWindowPropertiesTracker>>>,
547}
548
549impl Drop for PopupWindow {
550 fn drop(&mut self) {
551 (self.is_open_setter)(false);
556 }
557}
558
559#[pin_project::pin_project]
560struct WindowPinnedFields {
561 #[pin]
562 redraw_tracker: PropertyTracker<false, WindowRedrawTracker>,
563 #[pin]
565 window_properties_tracker: PropertyTracker<true, WindowPropertiesTracker>,
566 #[pin]
567 scale_factor: Property<f32>,
568 #[pin]
569 active: Property<bool>,
570 #[pin]
571 text_input_focused: Property<bool>,
572 #[pin]
573 menubar_shortcuts: Property<SharedVector<MenuEntry>>,
574}
575
576#[derive(Copy, Clone, Debug)]
578pub(crate) struct MouseDispatchResult {
579 pub drag_action: Option<crate::items::DragAction>,
583 pub accepted: bool,
586}
587
588#[cfg(feature = "std")]
591fn program_name(path: &std::path::Path) -> Option<SharedString> {
592 let is_exe = path.extension().is_some_and(|ext| ext.eq_ignore_ascii_case("exe"));
594 let name = if is_exe { path.file_stem()? } else { path.file_name()? };
595 let name = name.to_string_lossy();
596 (!name.is_empty()).then(|| name.as_ref().into())
597}
598
599pub fn application_name() -> SharedString {
605 #[cfg(feature = "std")]
606 {
607 static NAME: std::sync::LazyLock<SharedString> = std::sync::LazyLock::new(|| {
608 std::env::args_os()
609 .next()
610 .and_then(|arg| program_name(std::path::Path::new(&arg)))
611 .or_else(|| std::env::current_exe().ok().as_deref().and_then(program_name))
612 .unwrap_or_default()
613 });
614 NAME.clone()
615 }
616 #[cfg(not(feature = "std"))]
617 SharedString::default()
618}
619
620crate::thread_local! {
621 static DEFAULT_WINDOW_TITLE: core::cell::RefCell<Option<SharedString>> = Default::default();
622}
623
624pub fn set_default_window_title(title: SharedString) {
631 DEFAULT_WINDOW_TITLE.with(|slot| slot.replace(Some(title)));
632}
633
634pub fn default_window_title() -> SharedString {
640 DEFAULT_WINDOW_TITLE.with(|slot| slot.borrow().clone()).unwrap_or_else(application_name)
641}
642
643pub struct WindowInner {
645 window_adapter_weak: Weak<dyn WindowAdapter>,
646 component: RefCell<ItemTreeWeak>,
647 strong_component_ref: RefCell<Option<ItemTreeRc>>,
649 mouse_input_state: Cell<MouseInputState>,
650 touch_state: RefCell<TouchState>,
651
652 pub focus_item: RefCell<crate::item_tree::ItemWeak>,
654 focus_item_visibility_tracker: ChangeTracker,
655 focus_item_position_tracker: ChangeTracker,
656 pub(crate) last_ime_text: RefCell<SharedString>,
658 pub(crate) prevent_focus_change: Cell<bool>,
663 cursor_blinker: RefCell<pin_weak::rc::PinWeak<crate::input::TextCursorBlinker>>,
664
665 pinned_fields: Pin<Box<WindowPinnedFields>>,
666
667 menubar: RefCell<Option<vtable::VWeak<MenuVTable>>>,
668
669 pub active_popups: RefCell<Vec<PopupWindow>>,
671 next_popup_id: Cell<NonZeroU32>,
672 had_popup_on_press: Cell<bool>,
673 close_requested: Callback<(), CloseRequestResponse>,
674 click_state: ClickState,
675 ctx: core::cell::OnceCell<crate::SlintContext>,
676 native_drag: RefCell<Option<NativePendingDrag>>,
681}
682
683impl Drop for WindowInner {
684 fn drop(&mut self) {
685 if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() {
686 existing_blinker.stop();
687 }
688 }
689}
690
691impl WindowInner {
692 pub fn new(window_adapter_weak: Weak<dyn WindowAdapter>) -> Self {
694 #![allow(unused_mut)]
695
696 let mut window_properties_tracker =
697 PropertyTracker::new_with_dirty_handler(WindowPropertiesTracker {
698 window_adapter_weak: window_adapter_weak.clone(),
699 });
700
701 let mut redraw_tracker = PropertyTracker::new_with_dirty_handler(WindowRedrawTracker {
702 window_adapter_weak: window_adapter_weak.clone(),
703 });
704
705 #[cfg(slint_debug_property)]
706 {
707 window_properties_tracker
708 .set_debug_name("i_slint_core::Window::window_properties_tracker".into());
709 redraw_tracker.set_debug_name("i_slint_core::Window::redraw_tracker".into());
710 }
711
712 Self {
713 window_adapter_weak,
714 component: Default::default(),
715 strong_component_ref: Default::default(),
716 mouse_input_state: Default::default(),
717 touch_state: Default::default(),
718 pinned_fields: Box::pin(WindowPinnedFields {
719 redraw_tracker,
720 window_properties_tracker,
721 scale_factor: Property::new_named(1., "i_slint_core::Window::scale_factor"),
722 active: Property::new_named(false, "i_slint_core::Window::active"),
723 text_input_focused: Property::new_named(
724 false,
725 "i_slint_core::Window::text_input_focused",
726 ),
727 menubar_shortcuts: Property::new_named(
728 SharedVector::default(),
729 "i_slint_core::Window::menubar_shortcuts",
730 ),
731 }),
732 focus_item: Default::default(),
733 focus_item_visibility_tracker: Default::default(),
734 focus_item_position_tracker: Default::default(),
735 last_ime_text: Default::default(),
736 cursor_blinker: Default::default(),
737 active_popups: Default::default(),
738 next_popup_id: Cell::new(NonZeroU32::MIN),
739 had_popup_on_press: Default::default(),
740 close_requested: Default::default(),
741 click_state: ClickState::default(),
742 prevent_focus_change: Default::default(),
743 ctx: Default::default(),
744 menubar: Default::default(),
745 native_drag: Default::default(),
746 }
747 }
748
749 pub fn set_component(&self, component: &ItemTreeRc) {
752 self.close_all_popups();
753 self.focus_item_visibility_tracker.clear();
754 self.focus_item_position_tracker.clear();
755 self.focus_item.replace(Default::default());
756 self.mouse_input_state.replace(Default::default());
757 self.touch_state.replace(Default::default());
758 self.component.replace(ItemTreeRc::downgrade(component));
759 self.pinned_fields.window_properties_tracker.set_dirty(); let window_adapter = self.window_adapter();
761 window_adapter.renderer().set_window_adapter(&window_adapter);
762 let scale_factor = self.scale_factor();
763 self.set_window_item_geometry(window_adapter.size().to_logical(scale_factor).to_euclid());
764 let inset = window_adapter
765 .internal(crate::InternalToken)
766 .map(|internal| internal.safe_area_inset())
767 .unwrap_or_default();
768 self.set_window_item_safe_area(inset.to_logical(scale_factor));
769 window_adapter.request_redraw();
770 let weak = Rc::downgrade(&window_adapter);
771 self.context().single_shot(Default::default(), move || {
772 if let Some(window_adapter) = weak.upgrade() {
773 WindowInner::from_pub(window_adapter.window()).update_window_properties();
774 }
775 })
776 }
777
778 pub fn component(&self) -> ItemTreeRc {
781 self.component.borrow().upgrade().unwrap()
782 }
783
784 pub fn try_component(&self) -> Option<ItemTreeRc> {
786 self.component.borrow().upgrade()
787 }
788
789 pub fn ensure_tree_instantiated(&self) {
795 for _ in 0..10 {
798 let mut changed = false;
799 if let Some(component) = self.try_component() {
800 changed |= crate::item_tree::ensure_item_tree_instantiated(&component);
801 }
802 for popup in self.active_popups.borrow().iter() {
803 changed |= crate::item_tree::ensure_item_tree_instantiated(&popup.component);
804 }
805 changed |= crate::properties::ChangeTracker::run_change_handlers_once();
806 if !changed {
807 return;
808 }
809 }
810 crate::debug_log!("Slint: long callback/instantiation chain detected");
811 }
812
813 pub fn active_popups(&self) -> core::cell::Ref<'_, [PopupWindow]> {
815 core::cell::Ref::map(self.active_popups.borrow(), |v| v.as_slice())
816 }
817
818 pub(crate) fn process_mouse_input(&self, mut event: MouseEvent) -> Option<MouseDispatchResult> {
837 crate::animations::update_animations(crate::animations::Instant::now(self.context()));
838
839 let item_tree = self.try_component()?;
840 self.ensure_tree_instantiated();
841
842 if self.focus_item.borrow().upgrade().is_some_and(|i| !i.is_visible()) {
847 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation));
848 }
849
850 event = self.click_state.check_repeat(event, self.context());
852
853 let window_adapter = self.window_adapter();
854 let mut mouse_input_state = self.mouse_input_state.take();
855
856 let was_dragging = mouse_input_state.drag_data.is_some();
857 let old_cursor = core::mem::replace(
858 &mut mouse_input_state.cursor,
859 MouseCursorInner::BuiltIn(BuiltInMouseCursor::Default),
860 );
861
862 let mut pending_drag_finished: Option<(
866 crate::item_tree::ItemWeak,
867 Option<crate::item_tree::ItemWeak>,
868 )> = None;
869
870 if let Some(DragData { event: mut drop_event, allowed }) =
871 mouse_input_state.drag_data.clone()
872 {
873 match &event {
874 MouseEvent::Released { position, button: PointerEventButton::Left, .. } => {
875 mouse_input_state.drag_data = None;
876 let source = mouse_input_state.drag_source.take();
877 if let Some(target_weak) = mouse_input_state.drop_target.take() {
878 let hovered = target_weak
882 .upgrade()
883 .and_then(|t| t.downcast::<crate::items::DropArea>())
884 .map(|d| d.as_pin_ref().current_action())
885 .unwrap_or(crate::items::DragAction::None);
886 drop_event.proposed_action = hovered;
887 drop_event.position = crate::lengths::logical_position_to_api(*position);
888 event = MouseEvent::Drop { event: drop_event, allowed };
889 if let Some(s) = source {
890 pending_drag_finished = Some((s, Some(target_weak)));
891 }
892 } else {
893 event = MouseEvent::Exit;
899 if let Some(s) = source {
900 pending_drag_finished = Some((s, None));
901 }
902 }
903 }
904 MouseEvent::Moved { position, .. } => {
905 drop_event.position = crate::lengths::logical_position_to_api(*position);
906 drop_event.proposed_action = crate::items::compute_proposed_action(
909 self.context().0.modifiers.get().into(),
910 allowed,
911 );
912 if let Some(d) = mouse_input_state.drag_data.as_mut() {
917 d.event.position = drop_event.position;
918 d.event.proposed_action = drop_event.proposed_action;
919 }
920 mouse_input_state.cursor =
921 MouseCursorInner::BuiltIn(BuiltInMouseCursor::NoDrop);
922 event = MouseEvent::DragMove { event: drop_event, allowed };
923 }
924 MouseEvent::Exit => {
925 mouse_input_state.drag_data = None;
926 mouse_input_state.drop_target = None;
927 if let Some(s) = mouse_input_state.drag_source.take() {
928 pending_drag_finished = Some((s, None));
929 }
930 }
931 _ => {}
932 }
933 } else if let MouseEvent::DragMove { event, .. } | MouseEvent::Drop { event, .. } =
934 &mut event
935 {
936 if let Some(pending) = self.native_drag.borrow().as_ref() {
940 event.data = pending.request.data.clone();
941 }
942 }
943
944 let pressed_event = matches!(event, MouseEvent::Pressed { .. });
945 let released_event = matches!(event, MouseEvent::Released { .. });
946 let had_delay = mouse_input_state.has_delayed_event();
947
948 let last_top_item = mouse_input_state.top_item_including_delayed();
949 if released_event {
950 mouse_input_state =
951 crate::input::process_delayed_event(&window_adapter, mouse_input_state);
952 }
953
954 let parent_adapter = window_adapter
955 .internal(crate::InternalToken)
956 .and_then(|internal| internal.get_parent())
957 .unwrap_or_else(|| window_adapter.clone());
958 let active_popups = &WindowInner::from_pub(parent_adapter.window()).active_popups;
959 let native_popup_index = active_popups.borrow().iter().position(|p| {
960 if let PopupWindowLocation::TopLevel(wa) = &p.location {
961 Rc::ptr_eq(wa, &window_adapter)
962 } else {
963 false
964 }
965 });
966
967 if pressed_event {
968 self.had_popup_on_press.set(!active_popups.borrow().is_empty());
969 }
970
971 let mut popup_to_close = active_popups.borrow().last().and_then(|popup| {
972 let mouse_inside_popup = || {
973 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
974 event.position().is_none_or(|pos| {
975 ItemTreeRc::borrow_pin(&popup.component)
976 .as_ref()
977 .item_geometry(0)
978 .contains(pos - coordinates.to_vector())
979 })
980 } else {
981 native_popup_index.is_some_and(|idx| idx == active_popups.borrow().len() - 1)
982 && event.position().is_none_or(|pos| {
983 ItemTreeRc::borrow_pin(&item_tree)
984 .as_ref()
985 .item_geometry(0)
986 .contains(pos)
987 })
988 }
989 };
990 match popup.close_policy {
991 PopupClosePolicy::CloseOnClick => {
992 let mouse_inside_popup = mouse_inside_popup();
993 (mouse_inside_popup && released_event && self.had_popup_on_press.get())
994 || (!mouse_inside_popup && pressed_event)
995 }
996 PopupClosePolicy::CloseOnClickOutside => !mouse_inside_popup() && pressed_event,
997 PopupClosePolicy::NoAutoClose => false,
998 }
999 .then_some(popup.popup_id)
1000 });
1001
1002 let grab_result =
1003 crate::input::handle_mouse_grab(&event, &window_adapter, &mut mouse_input_state);
1004 let grab_accepted = grab_result.accepted;
1005
1006 let mut dispatch_accepted = false;
1007 mouse_input_state = if let Some(mut event) = grab_result.event {
1008 self.ensure_tree_instantiated();
1012 let mut item_tree = self.component.borrow().upgrade();
1013 let mut offset = LogicalPoint::default();
1014 let mut menubar_item = None;
1015 for (idx, popup) in active_popups.borrow().iter().enumerate().rev() {
1016 if matches!(popup.window_kind, WindowKind::ToolTip) {
1017 continue;
1018 }
1019 item_tree = None;
1020 menubar_item = None;
1021 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
1022 let geom = ItemTreeRc::borrow_pin(&popup.component).as_ref().item_geometry(0);
1023 let mouse_inside_popup = event
1024 .position()
1025 .is_none_or(|pos| geom.contains(pos - coordinates.to_vector()));
1026 if mouse_inside_popup {
1027 item_tree = Some(popup.component.clone());
1028 offset = *coordinates;
1029 break;
1030 }
1031 } else if native_popup_index.is_some_and(|i| i == idx) {
1032 item_tree = self.component.borrow().upgrade();
1033 break;
1034 }
1035
1036 if !matches!(popup.window_kind, WindowKind::Menu) {
1037 break;
1038 } else if popup_to_close.is_some() {
1039 popup_to_close = Some(popup.popup_id);
1041 }
1042
1043 menubar_item = popup.parent_item.upgrade();
1044 }
1045
1046 let root = match menubar_item {
1047 None => item_tree.map(|item_tree| ItemRc::new_root(item_tree.clone())),
1048 Some(menubar_item) => {
1049 event.translate(
1050 menubar_item
1051 .map_to_item_tree(Default::default(), &self.component())
1052 .to_vector(),
1053 );
1054 menubar_item.parent_item(ParentItemTraversalMode::StopAtPopups)
1055 }
1056 };
1057
1058 if let Some(root) = root {
1059 event.translate(-offset.to_vector());
1060 let crate::input::MouseInputResult { mut state, accepted } =
1061 crate::input::process_mouse_input(
1062 root,
1063 &event,
1064 &window_adapter,
1065 mouse_input_state,
1066 );
1067 state.offset = offset;
1068 dispatch_accepted = accepted;
1069 state
1070 } else {
1071 let mut new_input_state = MouseInputState::default();
1073 crate::input::send_exit_events(
1074 &mouse_input_state,
1075 &mut new_input_state,
1076 event.position(),
1077 &window_adapter,
1078 );
1079 new_input_state
1080 }
1081 } else {
1082 mouse_input_state
1083 };
1084
1085 let accepted = dispatch_accepted | grab_accepted;
1086
1087 if last_top_item != mouse_input_state.top_item_including_delayed() {
1088 self.click_state.reset();
1089 self.click_state.check_repeat(event, self.context());
1090 }
1091
1092 if !had_delay && mouse_input_state.has_delayed_event() {
1093 mouse_input_state.cursor = old_cursor;
1095 } else if old_cursor != mouse_input_state.cursor
1096 && let Some(window_adapter) = window_adapter.internal(crate::InternalToken)
1097 {
1098 window_adapter.set_mouse_cursor(mouse_input_state.cursor.clone());
1099 }
1100
1101 let is_dragging = mouse_input_state.drag_data.is_some();
1102 let drag_action = mouse_input_state.drop_target_action();
1103 self.mouse_input_state.set(mouse_input_state);
1104
1105 if was_dragging || is_dragging {
1110 window_adapter.request_redraw();
1111 }
1112
1113 if pending_drag_finished.is_some() {
1114 self.native_drag.borrow_mut().take();
1117 }
1118 if let Some((source_weak, target_weak)) = pending_drag_finished
1119 && let Some(source) = source_weak.upgrade()
1120 && let Some(drag_area) = source.downcast::<crate::items::DragArea>()
1121 {
1122 let target = target_weak
1125 .and_then(|w| w.upgrade())
1126 .and_then(|i| i.downcast::<crate::items::DropArea>());
1127 let action = target
1128 .as_ref()
1129 .map(|d| d.as_pin_ref().current_action())
1130 .unwrap_or(crate::items::DragAction::None);
1131 drag_area.as_pin_ref().finish_drag(action);
1132 if let Some(target) = target {
1135 target.as_pin_ref().current_action.set(crate::items::DragAction::None);
1136 }
1137 }
1138
1139 if let Some(popup_id) = popup_to_close {
1140 WindowInner::from_pub(parent_adapter.window()).close_popup(popup_id);
1141 }
1142
1143 self.ensure_tree_instantiated();
1144
1145 Some(MouseDispatchResult { drag_action, accepted })
1146 }
1147
1148 pub fn process_drag_event(&self, event: BackendDragEvent) -> Option<crate::items::DragAction> {
1158 self.process_mouse_input(event.into()).and_then(|result| result.drag_action)
1159 }
1160
1161 pub(crate) fn set_native_drag(&self, drag: Option<NativePendingDrag>) {
1164 *self.native_drag.borrow_mut() = drag;
1165 }
1166
1167 pub fn report_drag_finished(&self, action: crate::items::DragAction) {
1172 let Some(pending) = self.native_drag.borrow_mut().take() else {
1173 return;
1174 };
1175 if let Some(drag_area) =
1176 pending.source.upgrade().and_then(|i| i.downcast::<crate::items::DragArea>())
1177 {
1178 drag_area.as_pin_ref().finish_drag(action);
1179 }
1180 }
1181
1182 pub fn start_in_window_drag(&self) {
1187 let (source, seed_position) = {
1188 let native_drag = self.native_drag.borrow();
1189 let Some(drag) = native_drag.as_ref() else {
1190 return;
1191 };
1192 (drag.source.clone(), drag.seed_position)
1193 };
1194 let Some(drag_area) = source.upgrade().and_then(|i| i.downcast::<crate::items::DragArea>())
1195 else {
1196 return;
1197 };
1198 let mut state = self.mouse_input_state.take();
1199 state.arm_in_window_drag(drag_area.as_pin_ref(), source, seed_position);
1200 self.mouse_input_state.set(state);
1201 self.window_adapter().request_redraw();
1202 }
1203
1204 pub(crate) fn process_touch_input(
1217 &self,
1218 id: i32,
1219 position: LogicalPoint,
1220 phase: TouchPhase,
1221 ) -> Option<MouseDispatchResult> {
1222 let events = self.touch_state.borrow_mut().process(id, position, phase);
1223 let mut aggregate: Option<MouseDispatchResult> = None;
1224 for event in events.into_iter() {
1225 if let Some(r) = self.process_mouse_input(event) {
1226 let agg = aggregate
1227 .get_or_insert(MouseDispatchResult { drag_action: None, accepted: false });
1228 agg.accepted |= r.accepted;
1229 agg.drag_action = r.drag_action;
1230 }
1231 }
1232 aggregate
1233 }
1234
1235 pub(crate) fn process_delayed_event(&self) {
1237 self.mouse_input_state.set(crate::input::process_delayed_event(
1238 &self.window_adapter(),
1239 self.mouse_input_state.take(),
1240 ));
1241 }
1242
1243 pub(crate) fn process_key_input(
1249 &self,
1250 mut internal_key_event: InternalKeyEvent,
1251 ) -> crate::input::KeyEventResult {
1252 self.ensure_tree_instantiated();
1253 #[cfg(feature = "shared-parley")]
1258 {
1259 let normalizer = icu_normalizer::ComposingNormalizer::new_nfc();
1260 let normalized = normalizer.normalize(&internal_key_event.key_event.text);
1261 if let alloc::borrow::Cow::Owned(normalized) = normalized {
1264 internal_key_event.key_event.text = normalized.into();
1265 }
1266 }
1267
1268 if let Some(updated_modifier) = self.context().0.modifiers.get().state_update(
1269 internal_key_event.event_type == KeyEventType::KeyPressed,
1270 &internal_key_event.key_event.text,
1271 ) {
1272 self.context().0.modifiers.set(updated_modifier);
1274
1275 let drag_pos = {
1280 let state = self.mouse_input_state.take();
1281 let pos = state.drag_data.as_ref().map(|d| d.event.position);
1282 self.mouse_input_state.replace(state);
1283 pos
1284 };
1285 if let Some(pos) = drag_pos {
1286 self.process_mouse_input(MouseEvent::Moved {
1287 position: crate::lengths::logical_point_from_api(pos),
1288 touch_finger_id: 0,
1289 });
1290 }
1291 }
1292
1293 internal_key_event.key_event.modifiers =
1294 self.context().0.modifiers.get().modifiers_for(&internal_key_event);
1295
1296 if self.process_menubar_shortcuts(&internal_key_event) == KeyEventResult::EventAccepted {
1300 self.ensure_tree_instantiated();
1301 return crate::input::KeyEventResult::EventAccepted;
1302 }
1303
1304 let mut item = self.focus_item.borrow().clone().upgrade();
1305
1306 if item.as_ref().is_some_and(|i| !i.is_visible()) {
1307 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation));
1309 item = None;
1310 }
1311
1312 let item_list = {
1313 let mut tmp = Vec::new();
1314 let mut item = item.clone();
1315
1316 while let Some(i) = item {
1317 tmp.push(i.clone());
1318 item = i.parent_item(ParentItemTraversalMode::StopAtPopups);
1319 }
1320
1321 tmp
1322 };
1323
1324 for i in item_list.iter().rev() {
1326 if i.borrow().as_ref().capture_key_event(&internal_key_event, &self.window_adapter(), i)
1327 == crate::input::KeyEventResult::EventAccepted
1328 {
1329 self.ensure_tree_instantiated();
1330 return crate::input::KeyEventResult::EventAccepted;
1331 }
1332 }
1333
1334 drop(item_list);
1335
1336 while let Some(focus_item) = item {
1338 if focus_item.borrow().as_ref().key_event(
1339 &internal_key_event,
1340 &self.window_adapter(),
1341 &focus_item,
1342 ) == crate::input::KeyEventResult::EventAccepted
1343 {
1344 self.ensure_tree_instantiated();
1345 return crate::input::KeyEventResult::EventAccepted;
1346 }
1347 item = focus_item.parent_item(ParentItemTraversalMode::StopAtPopups);
1348 }
1349
1350 let extra_mod = internal_key_event.key_event.modifiers.control
1352 || internal_key_event.key_event.modifiers.meta
1353 || internal_key_event.key_event.modifiers.alt;
1354 if internal_key_event.key_event.text.starts_with(key_codes::Tab)
1355 && !internal_key_event.key_event.modifiers.shift
1356 && !extra_mod
1357 && internal_key_event.event_type == KeyEventType::KeyPressed
1358 {
1359 self.focus_next_item();
1360 self.ensure_tree_instantiated();
1361 return crate::input::KeyEventResult::EventAccepted;
1362 } else if (internal_key_event.key_event.text.starts_with(key_codes::Backtab)
1363 || (internal_key_event.key_event.text.starts_with(key_codes::Tab)
1364 && internal_key_event.key_event.modifiers.shift))
1365 && internal_key_event.event_type == KeyEventType::KeyPressed
1366 && !extra_mod
1367 {
1368 self.focus_previous_item();
1369 self.ensure_tree_instantiated();
1370 return crate::input::KeyEventResult::EventAccepted;
1371 } else if internal_key_event.event_type == KeyEventType::KeyPressed
1372 && internal_key_event.key_event.text.starts_with(key_codes::Escape)
1373 {
1374 let mut adapter = self.window_adapter();
1378 let item_tree = self.component();
1379 let mut a = None;
1380 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut a);
1381 if let Some(a) = a {
1382 adapter = a;
1383 }
1384 let window = WindowInner::from_pub(adapter.window());
1385
1386 let close_on_escape = if let Some(popup) = window.active_popups.borrow().last() {
1387 popup.close_policy == PopupClosePolicy::CloseOnClick
1388 || popup.close_policy == PopupClosePolicy::CloseOnClickOutside
1389 } else {
1390 false
1391 };
1392
1393 if close_on_escape {
1394 window.close_top_popup();
1395 }
1396 self.ensure_tree_instantiated();
1397 return crate::input::KeyEventResult::EventAccepted;
1398 }
1399
1400 self.ensure_tree_instantiated();
1401 crate::input::KeyEventResult::EventIgnored
1402 }
1403
1404 fn process_menubar_shortcuts(
1405 &self,
1406 internal_key_event: &InternalKeyEvent,
1407 ) -> crate::input::KeyEventResult {
1408 let event_type = internal_key_event.event_type;
1409 let menubar = self.menubar.borrow().as_ref().and_then(vtable::VWeak::upgrade);
1410
1411 if (event_type == KeyEventType::KeyReleased || event_type == KeyEventType::KeyPressed)
1412 && let Some(menubar) = menubar
1413 {
1414 let shortcuts = self.pinned_fields.as_ref().project_ref().menubar_shortcuts.get();
1415 let mut matches = shortcuts
1416 .into_iter()
1417 .filter(|entry| entry.shortcut.matches(&internal_key_event.key_event));
1418 if let Some(entry) = matches.next() {
1419 if internal_key_event.event_type == KeyEventType::KeyPressed {
1420 VRc::borrow(&menubar).activate(&entry);
1421 if matches.next().is_some() {
1422 crate::debug_log!(
1423 "Warning: Ambiguous menubar shortcut: {}",
1424 entry.shortcut
1425 );
1426 }
1427 }
1428 return crate::input::KeyEventResult::EventAccepted;
1429 }
1430 }
1431 crate::input::KeyEventResult::EventIgnored
1432 }
1433
1434 pub fn set_cursor_blink_binding(&self, prop: &crate::Property<bool>) {
1436 let existing_blinker = self.cursor_blinker.borrow().clone();
1437
1438 let blinker = existing_blinker.upgrade().unwrap_or_else(|| {
1439 let new_blinker = TextCursorBlinker::new();
1440 *self.cursor_blinker.borrow_mut() =
1441 pin_weak::rc::PinWeak::downgrade(new_blinker.clone());
1442 new_blinker
1443 });
1444
1445 let ctx = self.context();
1446 TextCursorBlinker::set_binding(blinker, prop, ctx, ctx.platform().cursor_flash_cycle());
1447 }
1448
1449 pub fn set_focus_item(&self, new_focus_item: &ItemRc, set_focus: bool, reason: FocusReason) {
1452 if self.prevent_focus_change.get() {
1453 return;
1454 }
1455
1456 let popup_wa = self.active_popups.borrow().last().and_then(|p| match &p.location {
1457 PopupWindowLocation::TopLevel(wa) => Some(wa.clone()),
1458 PopupWindowLocation::ChildWindow(..) => None,
1459 });
1460 if let Some(popup_wa) = popup_wa {
1461 popup_wa.window().0.set_focus_item(new_focus_item, set_focus, reason);
1463 return;
1464 }
1465
1466 let current_focus_item = self.focus_item.borrow().clone();
1467 if let Some(current_focus_item_rc) = current_focus_item.upgrade() {
1468 if set_focus {
1469 if current_focus_item_rc == *new_focus_item {
1470 return;
1472 }
1473 } else if current_focus_item_rc != *new_focus_item {
1474 return;
1476 }
1477 }
1478
1479 let old = self.take_focus_item(&FocusEvent::FocusOut(reason));
1480 let new = if set_focus {
1481 self.move_focus(new_focus_item.clone(), next_focus_item, reason)
1482 } else {
1483 None
1484 };
1485 let window_adapter = self.window_adapter();
1486 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1487 window_adapter.handle_focus_change(old, new);
1488 }
1489 }
1490
1491 fn take_focus_item(&self, event: &FocusEvent) -> Option<ItemRc> {
1495 self.focus_item_visibility_tracker.clear();
1496 self.focus_item_position_tracker.clear();
1497 let focus_item = self.focus_item.take();
1498 assert!(matches!(event, FocusEvent::FocusOut(_)));
1499
1500 if let Some(focus_item_rc) = focus_item.upgrade() {
1501 focus_item_rc.borrow().as_ref().focus_event(
1502 event,
1503 &self.window_adapter(),
1504 &focus_item_rc,
1505 );
1506 Some(focus_item_rc)
1507 } else {
1508 None
1509 }
1510 }
1511
1512 fn publish_focus_item(
1516 &self,
1517 item: &Option<ItemRc>,
1518 reason: FocusReason,
1519 ) -> crate::input::FocusEventResult {
1520 match item {
1521 Some(item) => {
1522 *self.focus_item.borrow_mut() = item.downgrade();
1523 let result = item.borrow().as_ref().focus_event(
1524 &FocusEvent::FocusIn(reason),
1525 &self.window_adapter(),
1526 item,
1527 );
1528 if result == crate::input::FocusEventResult::FocusAccepted {
1530 self.track_focus_item(item);
1531 item.try_scroll_into_visible();
1532 }
1533
1534 result
1535 }
1536 None => {
1537 self.focus_item_visibility_tracker.clear();
1538 self.focus_item_position_tracker.clear();
1539 *self.focus_item.borrow_mut() = Default::default();
1540 crate::input::FocusEventResult::FocusAccepted }
1542 }
1543 }
1544
1545 fn track_focus_item(&self, item: &ItemRc) {
1546 let visibility_clips = item.visibility_clips();
1548 self.focus_item_visibility_tracker.init(
1549 (item.downgrade(), self.window_adapter_weak.clone(), visibility_clips),
1550 |(_, _, visibility_clips)| {
1551 visibility_clips
1552 .iter()
1553 .all(|clip| clip.upgrade().is_some_and(|clip| !clip.as_pin_ref().clip()))
1554 },
1555 |(item, window_adapter, _), visible| {
1556 if *visible {
1557 return;
1558 }
1559 let Some(item) = item.upgrade() else { return };
1560 let Some(window_adapter) = window_adapter.upgrade() else { return };
1561 WindowInner::from_pub(window_adapter.window()).set_focus_item(
1562 &item,
1563 false,
1564 FocusReason::Programmatic,
1565 );
1566 },
1567 );
1568
1569 if item.downcast::<crate::items::TextInput>().is_some() {
1571 self.focus_item_position_tracker.init(
1572 (item.downgrade(), self.window_adapter_weak.clone()),
1573 |(item, _)| {
1574 let Some(item) = item.upgrade() else { return Default::default() };
1575 Some(item.map_to_native_window(item.geometry().origin))
1576 },
1577 |(item, window_adapter), _| {
1578 let (Some(item), Some(window_adapter)) =
1579 (item.upgrade(), window_adapter.upgrade())
1580 else {
1581 return;
1582 };
1583 if let Some(text_input) = item.downcast::<crate::items::TextInput>() {
1584 text_input.as_pin_ref().update_ime(&window_adapter, &item);
1585 }
1586 },
1587 );
1588 }
1589 }
1590
1591 fn move_focus(
1592 &self,
1593 start_item: ItemRc,
1594 forward: impl Fn(ItemRc) -> ItemRc,
1595 reason: FocusReason,
1596 ) -> Option<ItemRc> {
1597 let mut current_item = start_item.clone();
1598 let mut checkpoint = start_item.clone();
1602 let mut steps = 0usize;
1603 let mut next_checkpoint = 1usize;
1604
1605 loop {
1606 let can_receive_focus = match reason {
1607 FocusReason::Programmatic => true,
1608 FocusReason::TabNavigation => current_item.is_visible_or_clipped_by_flickable(),
1609 _ => current_item.is_visible(),
1610 };
1611 if can_receive_focus
1612 && self.publish_focus_item(&Some(current_item.clone()), reason)
1613 == crate::input::FocusEventResult::FocusAccepted
1614 {
1615 return Some(current_item); }
1617 current_item = forward(current_item);
1618
1619 if current_item == start_item || current_item == checkpoint {
1620 return None; }
1622 steps += 1;
1623 if steps == next_checkpoint {
1624 checkpoint = current_item.clone();
1625 steps = 0;
1626 next_checkpoint *= 2;
1627 }
1628 }
1629 }
1630
1631 pub fn focus_next_item(&self) {
1633 let start_item = self
1634 .take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation))
1635 .map(next_focus_item)
1636 .unwrap_or_else(|| {
1637 ItemRc::new(
1638 self.active_popups
1639 .borrow()
1640 .last()
1641 .map_or_else(|| self.component(), |p| p.component.clone()),
1642 0,
1643 )
1644 });
1645 let end_item =
1646 self.move_focus(start_item.clone(), next_focus_item, FocusReason::TabNavigation);
1647 let window_adapter = self.window_adapter();
1648 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1649 window_adapter.handle_focus_change(Some(start_item), end_item);
1650 }
1651 }
1652
1653 pub fn focus_previous_item(&self) {
1655 let start_item = previous_focus_item(
1656 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation)).unwrap_or_else(
1657 || {
1658 ItemRc::new(
1659 self.active_popups
1660 .borrow()
1661 .last()
1662 .map_or_else(|| self.component(), |p| p.component.clone()),
1663 0,
1664 )
1665 },
1666 ),
1667 );
1668 let end_item =
1669 self.move_focus(start_item.clone(), previous_focus_item, FocusReason::TabNavigation);
1670 let window_adapter = self.window_adapter();
1671 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1672 window_adapter.handle_focus_change(Some(start_item), end_item);
1673 }
1674 }
1675
1676 pub fn set_active(&self, have_focus: bool) {
1682 self.pinned_fields.as_ref().project_ref().active.set(have_focus);
1683
1684 let event = if have_focus {
1685 FocusEvent::FocusIn(FocusReason::WindowActivation)
1686 } else {
1687 FocusEvent::FocusOut(FocusReason::WindowActivation)
1688 };
1689
1690 if let Some(focus_item) = self.focus_item.borrow().upgrade() {
1691 focus_item.borrow().as_ref().focus_event(&event, &self.window_adapter(), &focus_item);
1692 }
1693
1694 if !have_focus {
1697 self.context().0.modifiers.take();
1698 }
1699 }
1700
1701 pub fn active(&self) -> bool {
1704 self.pinned_fields.as_ref().project_ref().active.get()
1705 }
1706
1707 pub fn update_window_properties(&self) {
1710 let window_adapter = self.window_adapter();
1711
1712 self.pinned_fields
1715 .as_ref()
1716 .project_ref()
1717 .window_properties_tracker
1718 .evaluate_as_dependency_root(|| {
1719 window_adapter.update_window_properties(WindowProperties(self));
1720 });
1721 }
1722
1723 fn update_popup_properties(&self, popup_id: NonZeroU32) {
1726 let offset = {
1727 let active_popups = self.active_popups.borrow();
1728 let Some(popup) = active_popups.iter().find(|p| p.popup_id == popup_id) else { return };
1729 if let Some(parent) = popup.parent_item.clone().upgrade() {
1730 parent.map_to_native_window(
1731 parent.geometry().origin + (popup.position_access)().to_euclid().to_vector(),
1732 )
1733 } else {
1734 LogicalPoint::zero()
1735 }
1736 };
1737 let mut active_popups = self.active_popups.borrow_mut();
1738 let Some(popup) = active_popups.iter_mut().find(|p| p.popup_id == popup_id) else { return };
1739 match &mut popup.location {
1740 PopupWindowLocation::ChildWindow(old_location) => {
1741 let (old_popup_region, new_popup_region) =
1742 popup.properties_tracker.as_ref().evaluate_as_dependency_root(|| {
1743 let component = ItemTreeRc::borrow_pin(&popup.component);
1744 let root_item = component.as_ref().get_item_ref(0);
1745 let window_item =
1746 ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1747 .expect("Popup component is a Window item");
1748 let old_popup_region = LogicalRect::new(
1750 *old_location,
1751 crate::lengths::LogicalSize::new(
1752 window_item.width().0,
1753 window_item.height().0,
1754 ),
1755 );
1756
1757 let width = {
1758 let layout_info_h = component
1759 .as_ref()
1760 .layout_info(crate::layout::Orientation::Horizontal);
1761 let w = layout_info_h
1762 .preferred
1763 .max(layout_info_h.min)
1764 .min(layout_info_h.max);
1765 window_item.width.set(LogicalLength::new(w));
1766 w
1767 };
1768
1769 let height = {
1770 let layout_info_v = component
1771 .as_ref()
1772 .layout_info(crate::layout::Orientation::Vertical);
1773 let h = layout_info_v
1774 .preferred
1775 .max(layout_info_v.min)
1776 .min(layout_info_v.max);
1777 window_item.height.set(LogicalLength::new(h));
1778 h
1779 };
1780
1781 let clip_region = Some(LogicalRect::new(
1782 LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1783 self.window_adapter()
1784 .size()
1785 .to_logical(self.scale_factor())
1786 .to_euclid(),
1787 ));
1788
1789 let new_region_clipped = popup::place_popup(
1790 popup::Placement::Fixed(LogicalRect::new(
1791 offset,
1792 crate::lengths::LogicalSize::new(width, height),
1793 )),
1794 &clip_region,
1795 );
1796
1797 (old_popup_region, new_region_clipped)
1798 });
1799
1800 self.window_adapter().request_redraw();
1801
1802 *old_location = new_popup_region.origin;
1804
1805 if let Some(adapter) = self.window_adapter_weak.upgrade() {
1806 if !old_popup_region.is_empty() {
1807 adapter.renderer().mark_dirty_region(old_popup_region.into());
1808 }
1809
1810 if !new_popup_region.is_empty() {
1811 adapter.renderer().mark_dirty_region(new_popup_region.into());
1812 }
1813 adapter.request_redraw();
1814 }
1815 }
1816 PopupWindowLocation::TopLevel(adapter) => {
1817 let mut new_position: Option<LogicalPosition> = None;
1819 popup.properties_tracker.as_ref().evaluate_as_dependency_root(|| {
1820 (popup.position_access)(); new_position = Some(LogicalPosition::from_euclid(offset));
1822 });
1823 if let Some(pos) = new_position {
1824 adapter.window().set_position(pos);
1825 }
1826 }
1827 }
1828 }
1829
1830 pub fn draw_contents<T>(
1840 &self,
1841 render_components: impl FnOnce(
1842 &[(ItemTreeWeak, LogicalPoint)],
1843 &dyn Fn(&mut dyn crate::item_rendering::ItemRenderer),
1844 ) -> T,
1845 ) -> Option<T> {
1846 crate::properties::evaluate_no_tracking(|| self.ensure_tree_instantiated());
1847 #[cfg(feature = "shared-parley")]
1848 if let Some(cache) = self.window_adapter().renderer().text_layout_cache() {
1849 cache.begin_frame();
1850 }
1851 let component_weak = ItemTreeRc::downgrade(&self.try_component()?);
1852 let post_render = |renderer: &mut dyn crate::item_rendering::ItemRenderer| {
1853 self.render_drag_image_overlay(renderer);
1854 };
1855 Some(self.pinned_fields.as_ref().project_ref().redraw_tracker.evaluate_as_dependency_root(
1856 || {
1857 if !self
1858 .active_popups
1859 .borrow()
1860 .iter()
1861 .any(|p| matches!(p.location, PopupWindowLocation::ChildWindow(..)))
1862 {
1863 render_components(&[(component_weak, LogicalPoint::default())], &post_render)
1864 } else {
1865 let borrow = self.active_popups.borrow();
1866 let mut item_trees = Vec::with_capacity(borrow.len() + 1);
1867 item_trees.push((component_weak, LogicalPoint::default()));
1868 for popup in borrow.iter() {
1869 if let PopupWindowLocation::ChildWindow(location) = &popup.location {
1873 item_trees.push((ItemTreeRc::downgrade(&popup.component), *location));
1874 }
1875 }
1876 drop(borrow);
1877 render_components(&item_trees, &post_render)
1878 }
1879 },
1880 ))
1881 }
1882
1883 fn render_drag_image_overlay(
1889 &self,
1890 item_renderer: &mut dyn crate::item_rendering::ItemRenderer,
1891 ) {
1892 let state = self.mouse_input_state.take();
1893 let cursor = state.drag_data.as_ref().map(|d| d.event.position);
1894 let source = state.drag_source.as_ref().and_then(|w| w.upgrade());
1895 self.mouse_input_state.set(state);
1896
1897 let (Some(cursor), Some(source)) = (cursor, source) else { return };
1898 let Some(drag_area) = source.downcast::<crate::items::DragArea>() else { return };
1899 let drag_area = drag_area.as_pin_ref();
1900 let image = drag_area.drag_image();
1901 let size = crate::lengths::LogicalSize::from_untyped(image.size().cast());
1902 if size.is_empty() {
1903 return;
1904 }
1905 let cursor = crate::lengths::logical_point_from_api(cursor);
1906 let offset = LogicalVector::new(
1907 drag_area.drag_image_offset_x() as Coord,
1908 drag_area.drag_image_offset_y() as Coord,
1909 );
1910 let top_left = cursor - offset;
1911
1912 item_renderer.save_state();
1913 item_renderer.translate(top_left.to_vector());
1914 item_renderer.draw_image_direct(image);
1915 item_renderer.restore_state();
1916
1917 self.window_adapter().renderer().mark_dirty_region(LogicalRect::new(top_left, size).into());
1918 }
1919
1920 pub fn show(&self) -> Result<(), PlatformError> {
1923 if let Some(component) = self.try_component() {
1924 let was_visible = self.strong_component_ref.replace(Some(component)).is_some();
1925 if !was_visible {
1926 self.context().acquire_keepalive();
1927 }
1928 }
1929
1930 self.ensure_tree_instantiated();
1931 self.update_window_properties();
1932 self.window_adapter().set_visible(true)?;
1933 let size = self.window_adapter().size();
1936 let scale_factor = self.scale_factor();
1937 self.set_window_item_geometry(size.to_logical(scale_factor).to_euclid());
1938 let inset = self
1939 .window_adapter()
1940 .internal(crate::InternalToken)
1941 .map(|internal| internal.safe_area_inset())
1942 .unwrap_or_default();
1943 self.set_window_item_safe_area(inset.to_logical(scale_factor));
1944 self.window_adapter().renderer().resize(size).unwrap();
1945 if let Some(hook) = self.context().0.window_shown_hook.borrow_mut().as_mut() {
1946 hook(&self.window_adapter());
1947 }
1948 Ok(())
1949 }
1950
1951 pub fn hide(&self) -> Result<(), PlatformError> {
1953 let result = self.window_adapter().set_visible(false);
1954 let was_visible = self.strong_component_ref.borrow_mut().take().is_some();
1955 if was_visible {
1956 self.context().release_keepalive();
1957 }
1958 result
1959 }
1960
1961 pub fn supports_native_menu_bar(&self) -> bool {
1963 self.window_adapter()
1964 .internal(crate::InternalToken)
1965 .is_some_and(|x| x.supports_native_menu_bar())
1966 }
1967
1968 pub fn setup_menubar(&self, menubar: vtable::VRc<MenuVTable>) {
1970 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1971 x.setup_menubar(menubar);
1972 }
1973 }
1974
1975 pub fn setup_menubar_shortcuts(&self, menubar: VRc<MenuVTable>) {
1980 *self.menubar.borrow_mut() = Some(VRc::downgrade(&menubar));
1981 let weak = VRc::downgrade(&menubar);
1982 self.pinned_fields.menubar_shortcuts.set_binding(move || {
1983 fn flatten_menu(
1984 root: vtable::VRef<'_, MenuVTable>,
1985 parent: Option<&MenuEntry>,
1986 ) -> SharedVector<MenuEntry> {
1987 let mut menu_entries = Default::default();
1988 root.sub_menu(parent, &mut menu_entries);
1989
1990 let mut result = menu_entries.clone();
1991
1992 for entry in menu_entries {
1993 result.extend(flatten_menu(root, Some(&entry)));
1994 }
1995 result
1996 }
1997
1998 let Some(menubar) = weak.upgrade() else {
1999 return SharedVector::default();
2000 };
2001 flatten_menu(VRc::borrow(&menubar), None)
2002 .into_iter()
2003 .filter(|entry| entry.enabled && entry.shortcut != Keys::default())
2004 .collect()
2005 });
2006 }
2007 pub fn create_child_window_adapter(&self, kind: WindowKind) -> Option<Rc<dyn WindowAdapter>> {
2010 self.window_adapter()
2011 .internal(crate::InternalToken)
2012 .and_then(|s| s.create_child_window_adapter(kind))
2013 }
2014
2015 pub fn show_popup(
2023 &self,
2024 popup_componentrc: &ItemTreeRc,
2025 popup_access_position: Box<dyn Fn() -> LogicalPosition>,
2026 close_policy: PopupClosePolicy,
2027 parent_item: &ItemRc,
2028 window_kind: WindowKind,
2029 is_open_setter: Box<dyn Fn(bool)>,
2030 ) -> NonZeroU32 {
2031 crate::item_tree::ensure_item_tree_instantiated(popup_componentrc);
2034 let position = parent_item.map_to_native_window(
2035 parent_item.geometry().origin + popup_access_position().to_euclid().to_vector(),
2036 );
2037 let popup_component = ItemTreeRc::borrow_pin(popup_componentrc);
2038 let popup_root = popup_component.as_ref().get_item_ref(0);
2039
2040 let (mut w, mut h) = if let Some(window_item) =
2041 ItemRef::downcast_pin::<crate::items::WindowItem>(popup_root)
2042 {
2043 (window_item.width(), window_item.height())
2044 } else {
2045 (LogicalLength::zero(), LogicalLength::zero())
2046 };
2047
2048 let layout_info_h =
2049 popup_component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
2050 let layout_info_v =
2051 popup_component.as_ref().layout_info(crate::layout::Orientation::Vertical);
2052
2053 if w <= LogicalLength::zero() {
2054 w = LogicalLength::new(layout_info_h.preferred);
2055 }
2056 if h <= LogicalLength::zero() {
2057 h = LogicalLength::new(layout_info_v.preferred);
2058 }
2059 w = w.max(LogicalLength::new(layout_info_h.min)).min(LogicalLength::new(layout_info_h.max));
2060 h = h.max(LogicalLength::new(layout_info_v.min)).min(LogicalLength::new(layout_info_v.max));
2061
2062 let size = crate::lengths::LogicalSize::from_lengths(w, h);
2063
2064 if let Some(window_item) = ItemRef::downcast_pin(popup_root) {
2065 let width_property =
2066 crate::items::WindowItem::FIELD_OFFSETS.width().apply_pin(window_item);
2067 let height_property =
2068 crate::items::WindowItem::FIELD_OFFSETS.height().apply_pin(window_item);
2069 width_property.set(size.width_length());
2070 height_property.set(size.height_length());
2071 };
2072
2073 let popup_id = self.next_popup_id.get();
2074 self.next_popup_id.set(popup_id.checked_add(1).unwrap());
2075 let parent_window_adapter_weak = Rc::downgrade(&self.window_adapter());
2076
2077 let siblings: Vec<_> = self
2079 .active_popups
2080 .borrow()
2081 .iter()
2082 .filter(|p| p.parent_item == parent_item.downgrade())
2083 .map(|p| p.popup_id)
2084 .collect();
2085
2086 for sibling in siblings {
2087 self.close_popup(sibling);
2088 }
2089
2090 let root_of = |mut item_tree: ItemTreeRc| loop {
2091 if ItemRc::new_root(item_tree.clone()).downcast::<crate::items::WindowItem>().is_some()
2092 {
2093 return item_tree;
2094 }
2095 let mut r = crate::item_tree::ItemWeak::default();
2096 ItemTreeRc::borrow_pin(&item_tree).as_ref().parent_node(&mut r);
2097 match r.upgrade() {
2098 None => return item_tree,
2099 Some(x) => item_tree = x.item_tree().clone(),
2100 }
2101 };
2102
2103 let parent_root_item_tree = root_of(parent_item.item_tree().clone());
2104 let parent_window_adapter = if let Some(parent_popup) = self
2105 .active_popups
2106 .borrow()
2107 .iter()
2108 .find(|p| ItemTreeRc::ptr_eq(&p.component, &parent_root_item_tree))
2109 {
2110 match &parent_popup.location {
2112 PopupWindowLocation::TopLevel(wa) => wa.clone(),
2113 PopupWindowLocation::ChildWindow(_) => self.window_adapter(),
2114 }
2115 } else {
2116 self.window_adapter()
2117 };
2118
2119 let popup_window_adapter = {
2120 let mut popup_window_adapter = None;
2121 ItemTreeRc::borrow_pin(popup_componentrc)
2122 .as_ref()
2123 .window_adapter(false, &mut popup_window_adapter);
2124 popup_window_adapter.expect("It must be there because we set the global")
2125 };
2126
2127 let (location, properties_tracker) =
2130 if Rc::ptr_eq(&parent_window_adapter, &popup_window_adapter) {
2131 let clip_region = Some(LogicalRect::new(
2133 LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
2134 self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
2135 ));
2136 let rect = popup::place_popup(
2137 popup::Placement::Fixed(LogicalRect::new(position, size)),
2138 &clip_region,
2139 );
2140 self.window_adapter().request_redraw();
2141 (
2142 PopupWindowLocation::ChildWindow(rect.origin),
2143 Box::pin(PropertyTracker::new_with_dirty_handler(
2144 PopupWindowPropertiesTracker {
2145 parent_window_adapter_weak: parent_window_adapter_weak.clone(),
2146 popup_id,
2147 },
2148 )),
2149 )
2150 } else {
2151 let popup_window = popup_window_adapter.window();
2152 WindowInner::from_pub(popup_window).set_component(popup_componentrc);
2153 popup_window.set_position(LogicalPosition::from_euclid(position));
2154 popup_window.set_size(WindowSize::Logical(LogicalSize::from_euclid(size)));
2155
2156 popup_window_adapter.set_visible(true).expect("unable to show popup window");
2157 (
2158 PopupWindowLocation::TopLevel(popup_window_adapter),
2159 Box::pin(PropertyTracker::new_with_dirty_handler(
2160 PopupWindowPropertiesTracker {
2161 parent_window_adapter_weak: parent_window_adapter_weak.clone(),
2162 popup_id,
2163 },
2164 )),
2165 )
2166 };
2167
2168 let focus_item = if matches!(window_kind, WindowKind::ToolTip) {
2169 Default::default()
2170 } else {
2171 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::PopupActivation))
2172 .map(|item| item.downgrade())
2173 .unwrap_or_default()
2174 };
2175
2176 is_open_setter(true);
2181
2182 self.active_popups.borrow_mut().push(PopupWindow {
2183 popup_id,
2184 location,
2185 component: popup_componentrc.clone(),
2186 close_policy,
2187 focus_item_in_parent: focus_item,
2188 parent_item: parent_item.downgrade(),
2189 window_kind,
2190 position_access: popup_access_position,
2191 is_open_setter,
2192 properties_tracker,
2193 });
2194
2195 self.update_popup_properties(popup_id);
2196
2197 popup_id
2198 }
2199
2200 pub fn show_native_popup_menu(
2206 &self,
2207 context_menu_item: vtable::VRc<MenuVTable>,
2208 position: LogicalPosition,
2209 parent_item: &ItemRc,
2210 ) -> bool {
2211 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
2212 let position = parent_item.map_to_native_window(
2213 parent_item.geometry().origin + position.to_euclid().to_vector(),
2214 );
2215 let position = crate::lengths::logical_position_to_api(position);
2216 x.show_native_popup_menu(context_menu_item, position)
2217 } else {
2218 false
2219 }
2220 }
2221
2222 fn close_popup_impl(&self, current_popup: &PopupWindow) {
2226 match ¤t_popup.location {
2227 PopupWindowLocation::ChildWindow(offset) => {
2228 let popup_region = crate::properties::evaluate_no_tracking(|| {
2230 let popup_component = ItemTreeRc::borrow_pin(¤t_popup.component);
2231 popup_component.as_ref().item_geometry(0)
2232 })
2233 .translate(offset.to_vector());
2234
2235 if !popup_region.is_empty() {
2236 let window_adapter = self.window_adapter();
2237 window_adapter.renderer().mark_dirty_region(popup_region.into());
2238 window_adapter.request_redraw();
2239 }
2240 }
2241 PopupWindowLocation::TopLevel(adapter) => {
2242 let _ = adapter.set_visible(false);
2243 }
2244 }
2245 if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
2246 self.set_focus_item(&focus, true, FocusReason::PopupActivation);
2247 }
2248 }
2249
2250 pub fn close_popup(&self, popup_id: NonZeroU32) {
2252 let mut active_popups = self.active_popups.borrow_mut();
2253 let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
2254
2255 if let Some(popup_index) = maybe_index {
2256 let p = active_popups.remove(popup_index);
2257 drop(active_popups);
2258 self.close_popup_impl(&p);
2259 if matches!(p.window_kind, WindowKind::Menu) {
2260 while self
2262 .active_popups
2263 .borrow()
2264 .get(popup_index)
2265 .is_some_and(|p| matches!(p.window_kind, WindowKind::Menu))
2266 {
2267 let p = self.active_popups.borrow_mut().remove(popup_index);
2268 self.close_popup_impl(&p);
2269 }
2270 }
2271 }
2272 }
2273
2274 pub fn close_all_popups(&self) {
2276 for popup in self.active_popups.take() {
2277 self.close_popup_impl(&popup);
2278 }
2279 }
2280
2281 pub fn close_top_popup(&self) {
2283 let popup = self.active_popups.borrow_mut().pop();
2284 if let Some(popup) = popup {
2285 self.close_popup_impl(&popup);
2286 }
2287 }
2288
2289 pub fn scale_factor(&self) -> f32 {
2291 self.pinned_fields.as_ref().project_ref().scale_factor.get()
2292 }
2293
2294 pub(crate) fn set_scale_factor(&self, factor: f32) {
2296 if !self.pinned_fields.scale_factor.is_constant() {
2297 self.pinned_fields.scale_factor.set(factor)
2298 }
2299 }
2300
2301 pub fn set_const_scale_factor(&self, factor: f32) {
2304 if !self.pinned_fields.scale_factor.is_constant() {
2305 self.pinned_fields.scale_factor.set(factor);
2306 self.pinned_fields.scale_factor.set_constant();
2307 }
2308 }
2309
2310 pub fn text_input_focused(&self) -> bool {
2312 self.pinned_fields.as_ref().project_ref().text_input_focused.get()
2313 }
2314
2315 pub fn set_text_input_focused(&self, value: bool) {
2317 if !value && let Some(window_adapter) = self.window_adapter().internal(crate::InternalToken)
2318 {
2319 window_adapter.input_method_request(InputMethodRequest::Disable);
2320 }
2321 self.pinned_fields.text_input_focused.set(value)
2322 }
2323
2324 pub fn is_visible(&self) -> bool {
2326 self.strong_component_ref.borrow().is_some()
2327 }
2328
2329 pub fn window_item_rc(&self) -> Option<ItemRc> {
2332 self.try_component().and_then(|component_rc| {
2333 let item_rc = ItemRc::new_root(component_rc);
2334 if item_rc.downcast::<crate::items::WindowItem>().is_some() {
2335 Some(item_rc)
2336 } else {
2337 None
2338 }
2339 })
2340 }
2341
2342 pub fn window_item(&self) -> Option<VRcMapped<ItemTreeVTable, crate::items::WindowItem>> {
2344 self.try_component().and_then(|component_rc| {
2345 ItemRc::new_root(component_rc).downcast::<crate::items::WindowItem>()
2346 })
2347 }
2348
2349 pub(crate) fn set_window_item_geometry(&self, size: crate::lengths::LogicalSize) {
2352 if let Some(component_rc) = self.try_component() {
2353 let component = ItemTreeRc::borrow_pin(&component_rc);
2354 let root_item = component.as_ref().get_item_ref(0);
2355 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
2356 {
2357 window_item.width.set(size.width_length());
2358 window_item.height.set(size.height_length());
2359 }
2360 }
2361 }
2362
2363 pub fn set_window_item_safe_area(&self, inset: crate::lengths::LogicalEdges) {
2365 if let Some(component_rc) = self.try_component() {
2366 let component = ItemTreeRc::borrow_pin(&component_rc);
2367 let root_item = component.as_ref().get_item_ref(0);
2368 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
2369 {
2370 window_item.safe_area_insets.set(inset);
2371 }
2372 }
2373 }
2374
2375 pub(crate) fn set_window_item_virtual_keyboard(
2376 &self,
2377 origin: crate::lengths::LogicalPoint,
2378 size: crate::lengths::LogicalSize,
2379 ) {
2380 let Some(component_rc) = self.try_component() else {
2381 return;
2382 };
2383 let component = ItemTreeRc::borrow_pin(&component_rc);
2384 let root_item = component.as_ref().get_item_ref(0);
2385 let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item) else {
2386 return;
2387 };
2388 window_item.virtual_keyboard_position.set(origin);
2389 window_item.virtual_keyboard_size.set(size);
2390 if let Some(focus_item) = self.focus_item.borrow().upgrade() {
2391 focus_item.try_scroll_into_visible();
2392 }
2393 }
2394
2395 pub(crate) fn window_item_virtual_keyboard(
2397 &self,
2398 ) -> Option<(crate::lengths::LogicalPoint, crate::lengths::LogicalSize)> {
2399 let component_rc = self.try_component()?;
2400 let component = ItemTreeRc::borrow_pin(&component_rc);
2401 let root_item = component.as_ref().get_item_ref(0);
2402 let window_item = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)?;
2403 let keyboard_size = window_item.virtual_keyboard_size();
2404 if keyboard_size.width == 0. as Coord || keyboard_size.height == 0. as Coord {
2405 None
2406 } else {
2407 Some((window_item.virtual_keyboard_position(), keyboard_size))
2408 }
2409 }
2410
2411 pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
2413 self.close_requested.set_handler(move |()| callback());
2414 }
2415
2416 pub fn request_close(&self) -> bool {
2420 match self.close_requested.call(&()) {
2421 CloseRequestResponse::HideWindow => true,
2422 CloseRequestResponse::KeepWindowShown => false,
2423 }
2424 }
2425
2426 pub fn is_fullscreen(&self) -> bool {
2428 if let Some(window_item) = self.window_item() {
2429 window_item.as_pin_ref().full_screen()
2430 } else {
2431 false
2432 }
2433 }
2434
2435 pub fn set_fullscreen(&self, enabled: bool) {
2437 if let Some(window_item) = self.window_item() {
2438 window_item.as_pin_ref().full_screen.set(enabled);
2439 self.update_window_properties()
2440 }
2441 }
2442
2443 pub fn is_maximized(&self) -> bool {
2445 self.window_item().is_some_and(|window_item| window_item.as_pin_ref().maximized())
2446 }
2447
2448 pub fn set_maximized(&self, maximized: bool) {
2450 if let Some(window_item) = self.window_item() {
2451 window_item.as_pin_ref().maximized.set(maximized);
2452 self.update_window_properties()
2453 }
2454 }
2455
2456 pub fn is_minimized(&self) -> bool {
2458 self.window_item().is_some_and(|window_item| window_item.as_pin_ref().minimized())
2459 }
2460
2461 pub fn set_minimized(&self, minimized: bool) {
2463 if let Some(window_item) = self.window_item() {
2464 window_item.as_pin_ref().minimized.set(minimized);
2465 self.update_window_properties()
2466 }
2467 }
2468
2469 pub fn xdg_app_id(&self) -> Option<SharedString> {
2471 self.context().xdg_app_id()
2472 }
2473
2474 pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
2476 self.window_adapter_weak.upgrade().unwrap()
2477 }
2478
2479 pub fn from_pub(window: &crate::api::Window) -> &Self {
2481 &window.0
2482 }
2483
2484 pub fn context(&self) -> &crate::SlintContext {
2486 self.ctx
2487 .get_or_init(|| crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().unwrap().clone()))
2488 }
2489
2490 pub fn try_context(&self) -> Option<&crate::SlintContext> {
2493 if self.ctx.get().is_none()
2494 && let Some(ctx) = crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().cloned())
2495 {
2496 let _ = self.ctx.set(ctx);
2497 }
2498 self.ctx.get()
2499 }
2500
2501 pub fn set_context(&self, ctx: crate::SlintContext) {
2504 self.ctx.set(ctx).map_err(|_| ()).expect("context shouldn't have been set before")
2505 }
2506}
2507
2508pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
2510
2511pub fn context_for_root(root: &ItemTreeRc) -> Option<crate::SlintContext> {
2515 let comp_ref_pin = vtable::VRc::borrow_pin(root);
2516 let mut adapter = None;
2517 comp_ref_pin.as_ref().window_adapter(true, &mut adapter);
2518 adapter.map(|a| WindowInner::from_pub(a.window()).context().clone())
2519}
2520
2521pub fn accent_color(root: &crate::item_tree::ItemTreeRc) -> crate::graphics::Color {
2525 let comp_ref_pin = vtable::VRc::borrow_pin(root);
2526 let mut adapter = None;
2527 comp_ref_pin.as_ref().window_adapter(true, &mut adapter);
2528 adapter.map_or(crate::graphics::Color::default(), |a| {
2529 WindowInner::from_pub(a.window()).context().accent_color()
2530 })
2531}
2532
2533#[cfg(feature = "ffi")]
2536pub mod ffi {
2537 #![allow(unsafe_code)]
2538 #![allow(clippy::missing_safety_doc)]
2539 #![allow(missing_docs)]
2540
2541 use super::*;
2542 #[cfg(feature = "std")]
2543 use crate::SharedVector;
2544 use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
2545 use crate::graphics::IntSize;
2546 #[cfg(feature = "std")]
2547 use crate::graphics::Rgba8Pixel;
2548 use crate::graphics::Size;
2549 use crate::items::WindowItem;
2550 use core::ffi::c_void;
2551
2552 #[repr(u8)]
2555 pub enum GraphicsAPI {
2556 NativeOpenGL,
2558 Inaccessible,
2560 }
2561
2562 struct WithUserData<T> {
2563 callback: T,
2564 drop_user_data: extern "C" fn(*mut c_void),
2565 user_data: *mut c_void,
2566 }
2567
2568 impl<T> Drop for WithUserData<T> {
2569 fn drop(&mut self) {
2570 (self.drop_user_data)(self.user_data)
2571 }
2572 }
2573
2574 impl WithUserData<extern "C" fn(user_data: *mut c_void, pos: &mut LogicalPosition)> {
2575 fn call(&self) -> LogicalPosition {
2576 let mut logical_position = LogicalPosition::default();
2577 (self.callback)(self.user_data, &mut logical_position);
2578 logical_position
2579 }
2580 }
2581
2582 impl WithUserData<extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse> {
2583 fn call(&self) -> CloseRequestResponse {
2584 (self.callback)(self.user_data)
2585 }
2586 }
2587
2588 impl WithUserData<extern "C" fn(user_data: *mut c_void, is_open: bool)> {
2589 fn call(&self, is_open: bool) {
2590 (self.callback)(self.user_data, is_open)
2591 }
2592 }
2593
2594 #[repr(C)]
2596 pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
2597
2598 #[unsafe(no_mangle)]
2600 pub extern "C" fn slint_default_window_title(out: &mut SharedString) {
2601 *out = super::default_window_title();
2602 }
2603
2604 #[unsafe(no_mangle)]
2606 pub unsafe extern "C" fn slint_windowrc_drop(handle: *mut WindowAdapterRcOpaque) {
2607 unsafe {
2608 assert_eq!(
2609 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2610 core::mem::size_of::<WindowAdapterRcOpaque>()
2611 );
2612 assert_eq!(
2613 core::mem::size_of::<Option<Rc<dyn WindowAdapter>>>(),
2614 core::mem::size_of::<WindowAdapterRcOpaque>()
2615 );
2616 drop(core::ptr::read(handle as *mut Option<Rc<dyn WindowAdapter>>));
2617 }
2618 }
2619
2620 #[unsafe(no_mangle)]
2622 pub unsafe extern "C" fn slint_windowrc_clone(
2623 source: *const WindowAdapterRcOpaque,
2624 target: *mut WindowAdapterRcOpaque,
2625 ) {
2626 unsafe {
2627 assert_eq!(
2628 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2629 core::mem::size_of::<WindowAdapterRcOpaque>()
2630 );
2631 let window = &*(source as *const Rc<dyn WindowAdapter>);
2632 core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window.clone());
2633 }
2634 }
2635
2636 #[unsafe(no_mangle)]
2638 pub unsafe extern "C" fn slint_windowrc_ensure_tree_instantiated(
2639 handle: *const WindowAdapterRcOpaque,
2640 ) {
2641 unsafe {
2642 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2643 WindowInner::from_pub(window_adapter.window()).ensure_tree_instantiated();
2644 }
2645 }
2646
2647 #[unsafe(no_mangle)]
2649 pub unsafe extern "C" fn slint_windowrc_show(handle: *const WindowAdapterRcOpaque) {
2650 unsafe {
2651 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2652
2653 window_adapter.window().show().unwrap();
2654 }
2655 }
2656
2657 #[unsafe(no_mangle)]
2659 pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) {
2660 unsafe {
2661 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2662 window_adapter.window().hide().unwrap();
2663 }
2664 }
2665
2666 #[unsafe(no_mangle)]
2669 pub unsafe extern "C" fn slint_windowrc_is_visible(
2670 handle: *const WindowAdapterRcOpaque,
2671 ) -> bool {
2672 unsafe {
2673 let window = &*(handle as *const Rc<dyn WindowAdapter>);
2674 window.window().is_visible()
2675 }
2676 }
2677
2678 #[unsafe(no_mangle)]
2680 pub unsafe extern "C" fn slint_windowrc_get_scale_factor(
2681 handle: *const WindowAdapterRcOpaque,
2682 ) -> f32 {
2683 unsafe {
2684 assert_eq!(
2685 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2686 core::mem::size_of::<WindowAdapterRcOpaque>()
2687 );
2688 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2689 WindowInner::from_pub(window_adapter.window()).scale_factor()
2690 }
2691 }
2692
2693 #[unsafe(no_mangle)]
2695 pub unsafe extern "C" fn slint_windowrc_set_const_scale_factor(
2696 handle: *const WindowAdapterRcOpaque,
2697 value: f32,
2698 ) {
2699 unsafe {
2700 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2701 WindowInner::from_pub(window_adapter.window()).set_const_scale_factor(value)
2702 }
2703 }
2704
2705 #[unsafe(no_mangle)]
2707 pub unsafe extern "C" fn slint_windowrc_get_text_input_focused(
2708 handle: *const WindowAdapterRcOpaque,
2709 ) -> bool {
2710 unsafe {
2711 assert_eq!(
2712 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
2713 core::mem::size_of::<WindowAdapterRcOpaque>()
2714 );
2715 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2716 WindowInner::from_pub(window_adapter.window()).text_input_focused()
2717 }
2718 }
2719
2720 #[unsafe(no_mangle)]
2722 pub unsafe extern "C" fn slint_windowrc_set_text_input_focused(
2723 handle: *const WindowAdapterRcOpaque,
2724 value: bool,
2725 ) {
2726 unsafe {
2727 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2728 WindowInner::from_pub(window_adapter.window()).set_text_input_focused(value)
2729 }
2730 }
2731
2732 #[unsafe(no_mangle)]
2734 pub unsafe extern "C" fn slint_windowrc_set_focus_item(
2735 handle: *const WindowAdapterRcOpaque,
2736 focus_item: &ItemRc,
2737 set_focus: bool,
2738 reason: FocusReason,
2739 ) {
2740 unsafe {
2741 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2742 WindowInner::from_pub(window_adapter.window())
2743 .set_focus_item(focus_item, set_focus, reason)
2744 }
2745 }
2746
2747 #[unsafe(no_mangle)]
2749 pub unsafe extern "C" fn slint_windowrc_set_component(
2750 handle: *const WindowAdapterRcOpaque,
2751 component: &ItemTreeRc,
2752 ) {
2753 unsafe {
2754 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2755 WindowInner::from_pub(window_adapter.window()).set_component(component)
2756 }
2757 }
2758
2759 #[unsafe(no_mangle)]
2761 pub unsafe extern "C" fn slint_windowrc_show_popup(
2762 handle: *const WindowAdapterRcOpaque,
2763 popup: &ItemTreeRc,
2764 position: extern "C" fn(user_data: *mut c_void, pos: &mut LogicalPosition),
2765 drop_user_data: extern "C" fn(user_data: *mut c_void),
2766 user_data: *mut c_void,
2767 close_policy: PopupClosePolicy,
2768 parent_item: &ItemRc,
2769 window_kind: WindowKind,
2770 is_open_setter: extern "C" fn(user_data: *mut c_void, is_open: bool),
2771 is_open_setter_drop_user_data: extern "C" fn(user_data: *mut c_void),
2772 is_open_setter_user_data: *mut c_void,
2773 ) -> NonZeroU32 {
2774 unsafe {
2775 let with_user_data = WithUserData { callback: position, drop_user_data, user_data };
2776 let is_open_with_user_data = WithUserData {
2777 callback: is_open_setter,
2778 drop_user_data: is_open_setter_drop_user_data,
2779 user_data: is_open_setter_user_data,
2780 };
2781 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2782 WindowInner::from_pub(window_adapter.window()).show_popup(
2783 popup,
2784 Box::new(move || with_user_data.call()),
2785 close_policy,
2786 parent_item,
2787 window_kind,
2788 Box::new(move |is_open| is_open_with_user_data.call(is_open)),
2789 )
2790 }
2791 }
2792
2793 #[unsafe(no_mangle)]
2797 pub unsafe extern "C" fn slint_windowrc_create_child_window_adapter(
2798 handle: *const WindowAdapterRcOpaque,
2799 window_kind: WindowKind,
2800 result: *mut WindowAdapterRcOpaque,
2801 ) -> bool {
2802 unsafe {
2803 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2804 match WindowInner::from_pub(window_adapter.window())
2805 .create_child_window_adapter(window_kind)
2806 {
2807 Some(wa) => {
2808 core::ptr::write(result as *mut Rc<dyn WindowAdapter>, wa);
2809 true
2810 }
2811 None => false,
2812 }
2813 }
2814 }
2815
2816 #[unsafe(no_mangle)]
2818 pub unsafe extern "C" fn slint_windowrc_close_popup(
2819 handle: *const WindowAdapterRcOpaque,
2820 popup_id: NonZeroU32,
2821 ) {
2822 unsafe {
2823 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2824 WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
2825 }
2826 }
2827
2828 #[unsafe(no_mangle)]
2830 pub unsafe extern "C" fn slint_windowrc_set_rendering_notifier(
2831 handle: *const WindowAdapterRcOpaque,
2832 callback: extern "C" fn(
2833 rendering_state: RenderingState,
2834 graphics_api: GraphicsAPI,
2835 user_data: *mut c_void,
2836 ),
2837 drop_user_data: extern "C" fn(user_data: *mut c_void),
2838 user_data: *mut c_void,
2839 error: *mut SetRenderingNotifierError,
2840 ) -> bool {
2841 unsafe {
2842 struct CNotifier {
2843 callback: extern "C" fn(
2844 rendering_state: RenderingState,
2845 graphics_api: GraphicsAPI,
2846 user_data: *mut c_void,
2847 ),
2848 drop_user_data: extern "C" fn(*mut c_void),
2849 user_data: *mut c_void,
2850 }
2851
2852 impl Drop for CNotifier {
2853 fn drop(&mut self) {
2854 (self.drop_user_data)(self.user_data)
2855 }
2856 }
2857
2858 impl RenderingNotifier for CNotifier {
2859 fn notify(
2860 &mut self,
2861 state: RenderingState,
2862 graphics_api: &crate::api::GraphicsAPI,
2863 ) {
2864 let cpp_graphics_api = match graphics_api {
2865 crate::api::GraphicsAPI::NativeOpenGL { .. } => GraphicsAPI::NativeOpenGL,
2866 crate::api::GraphicsAPI::WebGL { .. } => unreachable!(), #[cfg(feature = "unstable-wgpu-29")]
2868 crate::api::GraphicsAPI::WGPU29 { .. } => GraphicsAPI::Inaccessible, #[cfg(feature = "unstable-wgpu-30")]
2870 crate::api::GraphicsAPI::WGPU30 { .. } => GraphicsAPI::Inaccessible, };
2872 (self.callback)(state, cpp_graphics_api, self.user_data)
2873 }
2874 }
2875
2876 let window = &*(handle as *const Rc<dyn WindowAdapter>);
2877 match window.renderer().set_rendering_notifier(Box::new(CNotifier {
2878 callback,
2879 drop_user_data,
2880 user_data,
2881 })) {
2882 Ok(()) => true,
2883 Err(err) => {
2884 *error = err;
2885 false
2886 }
2887 }
2888 }
2889 }
2890
2891 #[unsafe(no_mangle)]
2893 pub unsafe extern "C" fn slint_windowrc_on_close_requested(
2894 handle: *const WindowAdapterRcOpaque,
2895 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
2896 drop_user_data: extern "C" fn(user_data: *mut c_void),
2897 user_data: *mut c_void,
2898 ) {
2899 unsafe {
2900 let with_user_data = WithUserData { callback, drop_user_data, user_data };
2901 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2902 window_adapter.window().on_close_requested(move || with_user_data.call());
2903 }
2904 }
2905
2906 #[unsafe(no_mangle)]
2908 pub unsafe extern "C" fn slint_windowrc_request_redraw(handle: *const WindowAdapterRcOpaque) {
2909 unsafe {
2910 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2911 window_adapter.request_redraw();
2912 }
2913 }
2914
2915 #[unsafe(no_mangle)]
2918 pub unsafe extern "C" fn slint_windowrc_position(
2919 handle: *const WindowAdapterRcOpaque,
2920 pos: &mut euclid::default::Point2D<i32>,
2921 ) {
2922 unsafe {
2923 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2924 *pos = window_adapter.position().unwrap_or_default().to_euclid()
2925 }
2926 }
2927
2928 #[unsafe(no_mangle)]
2932 pub unsafe extern "C" fn slint_windowrc_set_physical_position(
2933 handle: *const WindowAdapterRcOpaque,
2934 pos: &euclid::default::Point2D<i32>,
2935 ) {
2936 unsafe {
2937 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2938 window_adapter.set_position(crate::api::PhysicalPosition::new(pos.x, pos.y).into());
2939 }
2940 }
2941
2942 #[unsafe(no_mangle)]
2946 pub unsafe extern "C" fn slint_windowrc_set_logical_position(
2947 handle: *const WindowAdapterRcOpaque,
2948 pos: &euclid::default::Point2D<f32>,
2949 ) {
2950 unsafe {
2951 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2952 window_adapter.set_position(LogicalPosition::new(pos.x, pos.y).into());
2953 }
2954 }
2955
2956 #[unsafe(no_mangle)]
2959 pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
2960 unsafe {
2961 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2962 window_adapter.size().to_euclid().cast()
2963 }
2964 }
2965
2966 #[unsafe(no_mangle)]
2969 pub unsafe extern "C" fn slint_windowrc_set_physical_size(
2970 handle: *const WindowAdapterRcOpaque,
2971 size: &IntSize,
2972 ) {
2973 unsafe {
2974 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2975 window_adapter
2976 .window()
2977 .set_size(crate::api::PhysicalSize::new(size.width, size.height));
2978 }
2979 }
2980
2981 #[unsafe(no_mangle)]
2984 pub unsafe extern "C" fn slint_windowrc_set_logical_size(
2985 handle: *const WindowAdapterRcOpaque,
2986 size: &Size,
2987 ) {
2988 unsafe {
2989 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2990 window_adapter.window().set_size(crate::api::LogicalSize::new(size.width, size.height));
2991 }
2992 }
2993
2994 #[unsafe(no_mangle)]
2996 pub unsafe extern "C" fn slint_windowrc_supports_native_menu_bar(
2997 handle: *const WindowAdapterRcOpaque,
2998 ) -> bool {
2999 unsafe {
3000 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3001 window_adapter
3002 .internal(crate::InternalToken)
3003 .is_some_and(|x| x.supports_native_menu_bar())
3004 }
3005 }
3006
3007 #[unsafe(no_mangle)]
3009 pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
3010 handle: *const WindowAdapterRcOpaque,
3011 menu_instance: &vtable::VRc<MenuVTable>,
3012 ) {
3013 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
3014 let window = window_adapter.window();
3015 window.0.setup_menubar(vtable::VRc::clone(menu_instance));
3016 }
3017
3018 #[unsafe(no_mangle)]
3019 pub unsafe extern "C" fn slint_windowrc_setup_menu_bar_shortcuts(
3020 handle: *const WindowAdapterRcOpaque,
3021 menu_instance: &vtable::VRc<MenuVTable>,
3022 ) {
3023 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
3024 let window = window_adapter.window();
3025 window.0.setup_menubar_shortcuts(vtable::VRc::clone(menu_instance));
3026 }
3027
3028 #[unsafe(no_mangle)]
3030 pub unsafe extern "C" fn slint_windowrc_show_native_popup_menu(
3031 handle: *const WindowAdapterRcOpaque,
3032 context_menu: &vtable::VRc<MenuVTable>,
3033 position: LogicalPosition,
3034 parent_item: &ItemRc,
3035 ) -> bool {
3036 unsafe {
3037 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3038 WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(
3039 context_menu.clone(),
3040 position,
3041 parent_item,
3042 )
3043 }
3044 }
3045
3046 #[unsafe(no_mangle)]
3048 pub unsafe extern "C" fn slint_windowrc_resolved_default_font_size(
3049 item_tree: &ItemTreeRc,
3050 ) -> f32 {
3051 WindowItem::resolved_default_font_size(item_tree.clone()).get()
3052 }
3053
3054 #[unsafe(no_mangle)]
3056 pub unsafe extern "C" fn slint_windowrc_dispatch_key_event(
3057 handle: *const WindowAdapterRcOpaque,
3058 event_type: crate::input::KeyEventType,
3059 text: &SharedString,
3060 repeat: bool,
3061 ) {
3062 unsafe {
3063 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3064 window_adapter.window().dispatch_event(crate::platform::WindowEvent::internal(
3065 InternalKeyEvent {
3066 event_type,
3067 key_event: crate::items::KeyEvent {
3068 text: text.clone(),
3069 repeat,
3070 ..Default::default()
3071 },
3072 ..Default::default()
3073 },
3074 ));
3075 }
3076 }
3077
3078 #[unsafe(no_mangle)]
3080 pub unsafe extern "C" fn slint_windowrc_dispatch_pointer_event(
3081 handle: *const WindowAdapterRcOpaque,
3082 event: &crate::input::BackendMouseEvent,
3083 ) {
3084 unsafe {
3085 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3086 window_adapter.window().dispatch_event(crate::platform::WindowEvent::internal(*event));
3087 }
3088 }
3089
3090 #[unsafe(no_mangle)]
3092 pub unsafe extern "C" fn slint_windowrc_dispatch_event(
3093 handle: *const WindowAdapterRcOpaque,
3094 event: &crate::platform::WindowEvent,
3095 ) {
3096 unsafe {
3097 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3098 window_adapter.window().dispatch_event(event.clone());
3099 }
3100 }
3101
3102 #[unsafe(no_mangle)]
3103 pub unsafe extern "C" fn slint_windowrc_is_fullscreen(
3104 handle: *const WindowAdapterRcOpaque,
3105 ) -> bool {
3106 unsafe {
3107 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3108 window_adapter.window().is_fullscreen()
3109 }
3110 }
3111
3112 #[unsafe(no_mangle)]
3113 pub unsafe extern "C" fn slint_windowrc_is_minimized(
3114 handle: *const WindowAdapterRcOpaque,
3115 ) -> bool {
3116 unsafe {
3117 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3118 window_adapter.window().is_minimized()
3119 }
3120 }
3121
3122 #[unsafe(no_mangle)]
3123 pub unsafe extern "C" fn slint_windowrc_is_maximized(
3124 handle: *const WindowAdapterRcOpaque,
3125 ) -> bool {
3126 unsafe {
3127 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3128 window_adapter.window().is_maximized()
3129 }
3130 }
3131
3132 #[unsafe(no_mangle)]
3133 pub unsafe extern "C" fn slint_windowrc_set_fullscreen(
3134 handle: *const WindowAdapterRcOpaque,
3135 value: bool,
3136 ) {
3137 unsafe {
3138 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3139 window_adapter.window().set_fullscreen(value)
3140 }
3141 }
3142
3143 #[unsafe(no_mangle)]
3144 pub unsafe extern "C" fn slint_windowrc_set_minimized(
3145 handle: *const WindowAdapterRcOpaque,
3146 value: bool,
3147 ) {
3148 unsafe {
3149 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3150 window_adapter.window().set_minimized(value)
3151 }
3152 }
3153
3154 #[unsafe(no_mangle)]
3155 pub unsafe extern "C" fn slint_windowrc_set_maximized(
3156 handle: *const WindowAdapterRcOpaque,
3157 value: bool,
3158 ) {
3159 unsafe {
3160 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3161 window_adapter.window().set_maximized(value)
3162 }
3163 }
3164
3165 #[cfg(feature = "std")]
3167 #[unsafe(no_mangle)]
3168 pub unsafe extern "C" fn slint_windowrc_take_snapshot(
3169 handle: *const WindowAdapterRcOpaque,
3170 data: &mut SharedVector<Rgba8Pixel>,
3171 width: &mut u32,
3172 height: &mut u32,
3173 ) -> bool {
3174 unsafe {
3175 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
3176 if let Ok(snapshot) = window_adapter.window().take_snapshot() {
3177 *data = snapshot.data.clone();
3178 *width = snapshot.width();
3179 *height = snapshot.height();
3180 true
3181 } else {
3182 false
3183 }
3184 }
3185 }
3186}
3187
3188#[cfg(all(feature = "ffi", feature = "raw-window-handle-06"))]
3190pub mod ffi_window {
3191 #![allow(unsafe_code)]
3192 #![allow(clippy::missing_safety_doc)]
3193
3194 use super::ffi::WindowAdapterRcOpaque;
3195 use super::*;
3196 use std::ffi::c_void;
3197 use std::ptr::null_mut;
3198 use std::sync::Arc;
3199
3200 fn has_window_handle(
3202 handle: *const WindowAdapterRcOpaque,
3203 ) -> Option<Arc<dyn raw_window_handle_06::HasWindowHandle>> {
3204 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
3205 let window_adapter = window_adapter.internal(crate::InternalToken)?;
3206 window_adapter.window_handle_06_rc().ok()
3207 }
3208
3209 fn has_display_handle(
3211 handle: *const WindowAdapterRcOpaque,
3212 ) -> Option<Arc<dyn raw_window_handle_06::HasDisplayHandle>> {
3213 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
3214 let window_adapter = window_adapter.internal(crate::InternalToken)?;
3215 window_adapter.display_handle_06_rc().ok()
3216 }
3217
3218 #[unsafe(no_mangle)]
3220 pub unsafe extern "C" fn slint_windowrc_hwnd_win32(
3221 handle: *const WindowAdapterRcOpaque,
3222 ) -> *mut c_void {
3223 use raw_window_handle_06::HasWindowHandle;
3224
3225 if let Some(has_window_handle) = has_window_handle(handle)
3226 && let Ok(window_handle) = has_window_handle.window_handle()
3227 && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
3228 {
3229 isize::from(win32.hwnd) as *mut c_void
3230 } else {
3231 null_mut()
3232 }
3233 }
3234
3235 #[unsafe(no_mangle)]
3237 pub unsafe extern "C" fn slint_windowrc_hinstance_win32(
3238 handle: *const WindowAdapterRcOpaque,
3239 ) -> *mut c_void {
3240 use raw_window_handle_06::HasWindowHandle;
3241
3242 if let Some(has_window_handle) = has_window_handle(handle)
3243 && let Ok(window_handle) = has_window_handle.window_handle()
3244 && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
3245 {
3246 win32
3247 .hinstance
3248 .map(|hinstance| isize::from(hinstance) as *mut c_void)
3249 .unwrap_or_default()
3250 } else {
3251 null_mut()
3252 }
3253 }
3254
3255 #[unsafe(no_mangle)]
3257 pub unsafe extern "C" fn slint_windowrc_wlsurface_wayland(
3258 handle: *const WindowAdapterRcOpaque,
3259 ) -> *mut c_void {
3260 use raw_window_handle_06::HasWindowHandle;
3261
3262 if let Some(has_window_handle) = has_window_handle(handle)
3263 && let Ok(window_handle) = has_window_handle.window_handle()
3264 && let raw_window_handle_06::RawWindowHandle::Wayland(wayland) = window_handle.as_raw()
3265 {
3266 wayland.surface.as_ptr()
3267 } else {
3268 null_mut()
3269 }
3270 }
3271
3272 #[unsafe(no_mangle)]
3274 pub unsafe extern "C" fn slint_windowrc_wldisplay_wayland(
3275 handle: *const WindowAdapterRcOpaque,
3276 ) -> *mut c_void {
3277 use raw_window_handle_06::HasDisplayHandle;
3278
3279 if let Some(has_display_handle) = has_display_handle(handle)
3280 && let Ok(display_handle) = has_display_handle.display_handle()
3281 && let raw_window_handle_06::RawDisplayHandle::Wayland(wayland) =
3282 display_handle.as_raw()
3283 {
3284 wayland.display.as_ptr()
3285 } else {
3286 null_mut()
3287 }
3288 }
3289
3290 #[unsafe(no_mangle)]
3292 pub unsafe extern "C" fn slint_windowrc_nsview_appkit(
3293 handle: *const WindowAdapterRcOpaque,
3294 ) -> *mut c_void {
3295 use raw_window_handle_06::HasWindowHandle;
3296
3297 if let Some(has_window_handle) = has_window_handle(handle)
3298 && let Ok(window_handle) = has_window_handle.window_handle()
3299 && let raw_window_handle_06::RawWindowHandle::AppKit(appkit) = window_handle.as_raw()
3300 {
3301 appkit.ns_view.as_ptr()
3302 } else {
3303 null_mut()
3304 }
3305 }
3306}
3307
3308#[cfg(all(test, feature = "std"))]
3309mod tests {
3310 use super::*;
3311
3312 fn name_of(path: &str) -> Option<SharedString> {
3313 program_name(std::path::Path::new(path))
3314 }
3315
3316 #[test]
3317 fn a_program_name_is_the_bare_file_name() {
3318 assert_eq!(name_of("/usr/bin/gallery").as_deref(), Some("gallery"));
3319 assert_eq!(name_of("gallery").as_deref(), Some("gallery"));
3320 assert_eq!(name_of("./gallery").as_deref(), Some("gallery"));
3321 assert_eq!(name_of("/opt/my-tool.v2").as_deref(), Some("my-tool.v2"));
3322 assert_eq!(name_of("gallery.exe").as_deref(), Some("gallery"));
3324 assert_eq!(name_of("GALLERY.EXE").as_deref(), Some("GALLERY"));
3325 assert_eq!(name_of(".exe").as_deref(), Some(".exe"));
3327 assert_eq!(name_of(""), None);
3328 assert_eq!(name_of("/"), None);
3329 assert_eq!(name_of("some/dir/").as_deref(), Some("dir"));
3330 }
3331
3332 #[test]
3333 fn a_host_overrides_the_default_title() {
3334 assert!(!default_window_title().is_empty());
3336 set_default_window_title("Some Viewer".into());
3337 assert_eq!(default_window_title(), "Some Viewer");
3338 DEFAULT_WINDOW_TITLE.with(|slot| slot.replace(None));
3339 assert_eq!(default_window_title(), application_name());
3340 }
3341}