1#![warn(missing_docs)]
7use crate::api::{
10 CloseRequestResponse, LogicalPosition, PhysicalPosition, PhysicalSize, PlatformError, Window,
11 WindowPosition, WindowSize,
12};
13use crate::input::{
14 key_codes, ClickState, FocusEvent, FocusReason, InternalKeyboardModifierState, KeyEvent,
15 KeyEventType, MouseEvent, MouseInputState, TextCursorBlinker,
16};
17use crate::item_tree::{
18 ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeVTable, ItemTreeWeak, ItemWeak,
19 ParentItemTraversalMode,
20};
21use crate::items::{ColorScheme, InputType, ItemRef, MouseCursor, PopupClosePolicy};
22use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, SizeLengths};
23use crate::menus::MenuVTable;
24use crate::properties::{Property, PropertyTracker};
25use crate::renderer::Renderer;
26use crate::{Callback, Coord, SharedString};
27use alloc::boxed::Box;
28use alloc::rc::{Rc, Weak};
29use alloc::vec::Vec;
30use core::cell::{Cell, RefCell};
31use core::num::NonZeroU32;
32use core::pin::Pin;
33use euclid::num::Zero;
34use vtable::VRcMapped;
35
36pub mod popup;
37
38fn next_focus_item(item: ItemRc) -> ItemRc {
39 item.next_focus_item()
40}
41
42fn previous_focus_item(item: ItemRc) -> ItemRc {
43 item.previous_focus_item()
44}
45
46pub trait WindowAdapter {
73 fn window(&self) -> &Window;
75
76 fn set_visible(&self, _visible: bool) -> Result<(), PlatformError> {
78 Ok(())
79 }
80
81 fn position(&self) -> Option<PhysicalPosition> {
88 None
89 }
90 fn set_position(&self, _position: WindowPosition) {}
97
98 fn set_size(&self, _size: WindowSize) {}
109
110 fn size(&self) -> PhysicalSize;
112
113 fn request_redraw(&self) {}
125
126 fn renderer(&self) -> &dyn Renderer;
133
134 fn update_window_properties(&self, _properties: WindowProperties<'_>) {}
140
141 #[doc(hidden)]
142 fn internal(&self, _: crate::InternalToken) -> Option<&dyn WindowAdapterInternal> {
143 None
144 }
145
146 #[cfg(feature = "raw-window-handle-06")]
148 fn window_handle_06(
149 &self,
150 ) -> Result<raw_window_handle_06::WindowHandle<'_>, raw_window_handle_06::HandleError> {
151 Err(raw_window_handle_06::HandleError::NotSupported)
152 }
153
154 #[cfg(feature = "raw-window-handle-06")]
156 fn display_handle_06(
157 &self,
158 ) -> Result<raw_window_handle_06::DisplayHandle<'_>, raw_window_handle_06::HandleError> {
159 Err(raw_window_handle_06::HandleError::NotSupported)
160 }
161}
162
163#[doc(hidden)]
168pub trait WindowAdapterInternal {
169 fn register_item_tree(&self) {}
171
172 fn unregister_item_tree(
175 &self,
176 _component: ItemTreeRef,
177 _items: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
178 ) {
179 }
180
181 fn create_popup(&self, _geometry: LogicalRect) -> Option<Rc<dyn WindowAdapter>> {
188 None
189 }
190
191 fn set_mouse_cursor(&self, _cursor: MouseCursor) {}
194
195 fn input_method_request(&self, _: InputMethodRequest) {}
197
198 fn as_any(&self) -> &dyn core::any::Any {
201 &()
202 }
203
204 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {}
207
208 fn color_scheme(&self) -> ColorScheme {
210 ColorScheme::Unknown
211 }
212
213 fn supports_native_menu_bar(&self) -> bool {
215 false
216 }
217
218 fn setup_menubar(&self, _menubar: vtable::VBox<MenuVTable>) {}
219
220 #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
222 fn window_handle_06_rc(
223 &self,
224 ) -> Result<
225 std::sync::Arc<dyn raw_window_handle_06::HasWindowHandle>,
226 raw_window_handle_06::HandleError,
227 > {
228 Err(raw_window_handle_06::HandleError::NotSupported)
229 }
230
231 #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
233 fn display_handle_06_rc(
234 &self,
235 ) -> Result<
236 std::sync::Arc<dyn raw_window_handle_06::HasDisplayHandle>,
237 raw_window_handle_06::HandleError,
238 > {
239 Err(raw_window_handle_06::HandleError::NotSupported)
240 }
241
242 fn bring_to_front(&self) -> Result<(), PlatformError> {
244 Ok(())
245 }
246}
247
248#[non_exhaustive]
251#[derive(Debug, Clone)]
252pub enum InputMethodRequest {
253 Enable(InputMethodProperties),
255 Update(InputMethodProperties),
257 Disable,
259}
260
261#[non_exhaustive]
263#[derive(Clone, Default, Debug)]
264pub struct InputMethodProperties {
265 pub text: SharedString,
269 pub cursor_position: usize,
271 pub anchor_position: Option<usize>,
274 pub preedit_text: SharedString,
278 pub preedit_offset: usize,
280 pub cursor_rect_origin: LogicalPosition,
282 pub cursor_rect_size: crate::api::LogicalSize,
284 pub anchor_point: LogicalPosition,
286 pub input_type: InputType,
288}
289
290#[non_exhaustive]
292#[derive(Copy, Clone, Debug, PartialEq, Default)]
293pub struct LayoutConstraints {
294 pub min: Option<crate::api::LogicalSize>,
296 pub max: Option<crate::api::LogicalSize>,
298 pub preferred: crate::api::LogicalSize,
300}
301
302pub struct WindowProperties<'a>(&'a WindowInner);
305
306impl WindowProperties<'_> {
307 pub fn title(&self) -> SharedString {
309 self.0.window_item().map(|w| w.as_pin_ref().title()).unwrap_or_default()
310 }
311
312 pub fn background(&self) -> crate::Brush {
314 self.0
315 .window_item()
316 .map(|w: VRcMapped<ItemTreeVTable, crate::items::WindowItem>| {
317 w.as_pin_ref().background()
318 })
319 .unwrap_or_default()
320 }
321
322 pub fn layout_constraints(&self) -> LayoutConstraints {
324 let component = self.0.component();
325 let component = ItemTreeRc::borrow_pin(&component);
326 let h = component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
327 let v = component.as_ref().layout_info(crate::layout::Orientation::Vertical);
328 let (min, max) = crate::layout::min_max_size_for_layout_constraints(h, v);
329 LayoutConstraints {
330 min,
331 max,
332 preferred: crate::api::LogicalSize::new(
333 h.preferred_bounded() as f32,
334 v.preferred_bounded() as f32,
335 ),
336 }
337 }
338
339 #[deprecated(note = "Please use `is_fullscreen` instead")]
341 pub fn fullscreen(&self) -> bool {
342 self.is_fullscreen()
343 }
344
345 pub fn is_fullscreen(&self) -> bool {
347 self.0.is_fullscreen()
348 }
349
350 pub fn is_maximized(&self) -> bool {
352 self.0.maximized.get()
353 }
354
355 pub fn is_minimized(&self) -> bool {
357 self.0.minimized.get()
358 }
359}
360
361struct WindowPropertiesTracker {
362 window_adapter_weak: Weak<dyn WindowAdapter>,
363}
364
365impl crate::properties::PropertyDirtyHandler for WindowPropertiesTracker {
366 fn notify(self: Pin<&Self>) {
367 let win = self.window_adapter_weak.clone();
368 crate::timers::Timer::single_shot(Default::default(), move || {
369 if let Some(window_adapter) = win.upgrade() {
370 WindowInner::from_pub(window_adapter.window()).update_window_properties();
371 };
372 })
373 }
374}
375
376struct WindowRedrawTracker {
377 window_adapter_weak: Weak<dyn WindowAdapter>,
378}
379
380impl crate::properties::PropertyDirtyHandler for WindowRedrawTracker {
381 fn notify(self: Pin<&Self>) {
382 if let Some(window_adapter) = self.window_adapter_weak.upgrade() {
383 window_adapter.request_redraw();
384 };
385 }
386}
387
388#[derive(Clone)]
390pub enum PopupWindowLocation {
391 TopLevel(Rc<dyn WindowAdapter>),
393 ChildWindow(LogicalPoint),
395}
396
397#[derive(Clone)]
400pub struct PopupWindow {
401 pub popup_id: NonZeroU32,
403 pub location: PopupWindowLocation,
405 pub component: ItemTreeRc,
407 pub close_policy: PopupClosePolicy,
409 focus_item_in_parent: ItemWeak,
411 pub parent_item: ItemWeak,
413 is_menu: bool,
416}
417
418#[pin_project::pin_project]
419struct WindowPinnedFields {
420 #[pin]
421 redraw_tracker: PropertyTracker<WindowRedrawTracker>,
422 #[pin]
424 window_properties_tracker: PropertyTracker<WindowPropertiesTracker>,
425 #[pin]
426 scale_factor: Property<f32>,
427 #[pin]
428 active: Property<bool>,
429 #[pin]
430 text_input_focused: Property<bool>,
431}
432
433pub struct WindowInner {
435 window_adapter_weak: Weak<dyn WindowAdapter>,
436 component: RefCell<ItemTreeWeak>,
437 strong_component_ref: RefCell<Option<ItemTreeRc>>,
439 mouse_input_state: Cell<MouseInputState>,
440 pub(crate) modifiers: Cell<InternalKeyboardModifierState>,
441
442 pub focus_item: RefCell<crate::item_tree::ItemWeak>,
444 pub(crate) last_ime_text: RefCell<SharedString>,
446 pub(crate) prevent_focus_change: Cell<bool>,
451 cursor_blinker: RefCell<pin_weak::rc::PinWeak<crate::input::TextCursorBlinker>>,
452
453 pinned_fields: Pin<Box<WindowPinnedFields>>,
454 maximized: Cell<bool>,
455 minimized: Cell<bool>,
456
457 active_popups: RefCell<Vec<PopupWindow>>,
459 next_popup_id: Cell<NonZeroU32>,
460 had_popup_on_press: Cell<bool>,
461 close_requested: Callback<(), CloseRequestResponse>,
462 click_state: ClickState,
463 pub(crate) ctx: once_cell::unsync::Lazy<crate::SlintContext>,
464}
465
466impl Drop for WindowInner {
467 fn drop(&mut self) {
468 if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() {
469 existing_blinker.stop();
470 }
471 }
472}
473
474impl WindowInner {
475 pub fn new(window_adapter_weak: Weak<dyn WindowAdapter>) -> Self {
477 #![allow(unused_mut)]
478
479 let mut window_properties_tracker =
480 PropertyTracker::new_with_dirty_handler(WindowPropertiesTracker {
481 window_adapter_weak: window_adapter_weak.clone(),
482 });
483
484 let mut redraw_tracker = PropertyTracker::new_with_dirty_handler(WindowRedrawTracker {
485 window_adapter_weak: window_adapter_weak.clone(),
486 });
487
488 #[cfg(slint_debug_property)]
489 {
490 window_properties_tracker
491 .set_debug_name("i_slint_core::Window::window_properties_tracker".into());
492 redraw_tracker.set_debug_name("i_slint_core::Window::redraw_tracker".into());
493 }
494
495 Self {
496 window_adapter_weak,
497 component: Default::default(),
498 strong_component_ref: Default::default(),
499 mouse_input_state: Default::default(),
500 modifiers: Default::default(),
501 pinned_fields: Box::pin(WindowPinnedFields {
502 redraw_tracker,
503 window_properties_tracker,
504 scale_factor: Property::new_named(1., "i_slint_core::Window::scale_factor"),
505 active: Property::new_named(false, "i_slint_core::Window::active"),
506 text_input_focused: Property::new_named(
507 false,
508 "i_slint_core::Window::text_input_focused",
509 ),
510 }),
511 maximized: Cell::new(false),
512 minimized: Cell::new(false),
513 focus_item: Default::default(),
514 last_ime_text: Default::default(),
515 cursor_blinker: Default::default(),
516 active_popups: Default::default(),
517 next_popup_id: Cell::new(NonZeroU32::MIN),
518 had_popup_on_press: Default::default(),
519 close_requested: Default::default(),
520 click_state: ClickState::default(),
521 prevent_focus_change: Default::default(),
522 ctx: once_cell::unsync::Lazy::new(|| {
525 crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().unwrap().clone())
526 }),
527 }
528 }
529
530 pub fn set_component(&self, component: &ItemTreeRc) {
533 self.close_all_popups();
534 self.focus_item.replace(Default::default());
535 self.mouse_input_state.replace(Default::default());
536 self.modifiers.replace(Default::default());
537 self.component.replace(ItemTreeRc::downgrade(component));
538 self.pinned_fields.window_properties_tracker.set_dirty(); let window_adapter = self.window_adapter();
540 window_adapter.renderer().set_window_adapter(&window_adapter);
541 {
542 let component = ItemTreeRc::borrow_pin(component);
543 let root_item = component.as_ref().get_item_ref(0);
544 let window_item = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item).unwrap();
545
546 let default_font_size_prop =
547 crate::items::WindowItem::FIELD_OFFSETS.default_font_size.apply_pin(window_item);
548 if default_font_size_prop.get().get() <= 0 as Coord {
549 default_font_size_prop.set(window_adapter.renderer().default_font_size());
550 }
551 }
552 self.set_window_item_geometry(
553 window_adapter.size().to_logical(self.scale_factor()).to_euclid(),
554 );
555 window_adapter.request_redraw();
556 let weak = Rc::downgrade(&window_adapter);
557 crate::timers::Timer::single_shot(Default::default(), move || {
558 if let Some(window_adapter) = weak.upgrade() {
559 WindowInner::from_pub(window_adapter.window()).update_window_properties();
560 }
561 })
562 }
563
564 pub fn component(&self) -> ItemTreeRc {
567 self.component.borrow().upgrade().unwrap()
568 }
569
570 pub fn try_component(&self) -> Option<ItemTreeRc> {
572 self.component.borrow().upgrade()
573 }
574
575 pub fn active_popups(&self) -> core::cell::Ref<'_, [PopupWindow]> {
577 core::cell::Ref::map(self.active_popups.borrow(), |v| v.as_slice())
578 }
579
580 pub fn process_mouse_input(&self, mut event: MouseEvent) {
583 crate::animations::update_animations();
584
585 event = self.click_state.check_repeat(event, self.ctx.platform().click_interval());
587
588 let pressed_event = matches!(event, MouseEvent::Pressed { .. });
589 let released_event = matches!(event, MouseEvent::Released { .. });
590
591 let window_adapter = self.window_adapter();
592 let mut mouse_input_state = self.mouse_input_state.take();
593 let last_top_item = mouse_input_state.top_item_including_delayed();
594 if released_event {
595 mouse_input_state =
596 crate::input::process_delayed_event(&window_adapter, mouse_input_state);
597 }
598
599 let Some(item_tree) = self.try_component() else { return };
600
601 let mut root_adapter = None;
603 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut root_adapter);
604 let root_adapter = root_adapter.unwrap_or_else(|| window_adapter.clone());
605 let active_popups = &WindowInner::from_pub(root_adapter.window()).active_popups;
606 let native_popup_index = active_popups.borrow().iter().position(|p| {
607 if let PopupWindowLocation::TopLevel(wa) = &p.location {
608 Rc::ptr_eq(wa, &window_adapter)
609 } else {
610 false
611 }
612 });
613
614 if pressed_event {
615 self.had_popup_on_press.set(!active_popups.borrow().is_empty());
616 }
617
618 let mut popup_to_close = active_popups.borrow().last().and_then(|popup| {
619 let mouse_inside_popup = || {
620 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
621 event.position().is_none_or(|pos| {
622 ItemTreeRc::borrow_pin(&popup.component)
623 .as_ref()
624 .item_geometry(0)
625 .contains(pos - coordinates.to_vector())
626 })
627 } else {
628 native_popup_index.is_some_and(|idx| idx == active_popups.borrow().len() - 1)
629 && event.position().is_none_or(|pos| {
630 ItemTreeRc::borrow_pin(&item_tree)
631 .as_ref()
632 .item_geometry(0)
633 .contains(pos)
634 })
635 }
636 };
637 match popup.close_policy {
638 PopupClosePolicy::CloseOnClick => {
639 let mouse_inside_popup = mouse_inside_popup();
640 (mouse_inside_popup && released_event && self.had_popup_on_press.get())
641 || (!mouse_inside_popup && pressed_event)
642 }
643 PopupClosePolicy::CloseOnClickOutside => !mouse_inside_popup() && pressed_event,
644 PopupClosePolicy::NoAutoClose => false,
645 }
646 .then_some(popup.popup_id)
647 });
648
649 mouse_input_state = if let Some(mut event) =
650 crate::input::handle_mouse_grab(event, &window_adapter, &mut mouse_input_state)
651 {
652 let mut item_tree = self.component.borrow().upgrade();
653 let mut offset = LogicalPoint::default();
654 let mut menubar_item = None;
655 for (idx, popup) in active_popups.borrow().iter().enumerate().rev() {
656 item_tree = None;
657 menubar_item = None;
658 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
659 let geom = ItemTreeRc::borrow_pin(&popup.component).as_ref().item_geometry(0);
660 let mouse_inside_popup = event
661 .position()
662 .is_none_or(|pos| geom.contains(pos - coordinates.to_vector()));
663 if mouse_inside_popup {
664 item_tree = Some(popup.component.clone());
665 offset = *coordinates;
666 break;
667 }
668 } else if native_popup_index.is_some_and(|i| i == idx) {
669 item_tree = self.component.borrow().upgrade();
670 break;
671 }
672
673 if !popup.is_menu {
674 break;
675 } else if popup_to_close.is_some() {
676 popup_to_close = Some(popup.popup_id);
678 }
679
680 menubar_item = popup.parent_item.upgrade();
681 }
682
683 let root = match menubar_item {
684 None => item_tree.map(|item_tree| ItemRc::new(item_tree.clone(), 0)),
685 Some(menubar_item) => {
686 assert_ne!(menubar_item.index(), 0, "ContextMenuInternal cannot be root");
687 event.translate(
688 menubar_item
689 .map_to_item_tree(Default::default(), &self.component())
690 .to_vector(),
691 );
692 menubar_item.parent_item(ParentItemTraversalMode::StopAtPopups)
693 }
694 };
695
696 if let Some(root) = root {
697 event.translate(-offset.to_vector());
698 let mut new_input_state = crate::input::process_mouse_input(
699 root,
700 event,
701 &window_adapter,
702 mouse_input_state,
703 );
704 new_input_state.offset = offset;
705 new_input_state
706 } else {
707 let mut new_input_state = MouseInputState::default();
709 crate::input::send_exit_events(
710 &mouse_input_state,
711 &mut new_input_state,
712 event.position(),
713 &window_adapter,
714 );
715 new_input_state
716 }
717 } else {
718 mouse_input_state
719 };
720
721 if last_top_item != mouse_input_state.top_item_including_delayed() {
722 self.click_state.reset();
723 self.click_state.check_repeat(event, self.ctx.platform().click_interval());
724 }
725
726 self.mouse_input_state.set(mouse_input_state);
727
728 if let Some(popup_id) = popup_to_close {
729 WindowInner::from_pub(root_adapter.window()).close_popup(popup_id);
730 }
731
732 crate::properties::ChangeTracker::run_change_handlers();
733 }
734
735 pub(crate) fn process_delayed_event(&self) {
737 self.mouse_input_state.set(crate::input::process_delayed_event(
738 &self.window_adapter(),
739 self.mouse_input_state.take(),
740 ));
741 }
742
743 pub fn process_key_input(&self, mut event: KeyEvent) {
750 if let Some(updated_modifier) = self
751 .modifiers
752 .get()
753 .state_update(event.event_type == KeyEventType::KeyPressed, &event.text)
754 {
755 self.modifiers.set(updated_modifier);
757 }
758
759 event.modifiers = self.modifiers.get().into();
760
761 let mut item = self.focus_item.borrow().clone().upgrade();
762
763 if item.as_ref().is_some_and(|i| !i.is_visible()) {
764 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation));
766 item = None;
767 }
768
769 while let Some(focus_item) = item {
770 if focus_item.borrow().as_ref().key_event(&event, &self.window_adapter(), &focus_item)
771 == crate::input::KeyEventResult::EventAccepted
772 {
773 crate::properties::ChangeTracker::run_change_handlers();
774 return;
775 }
776 item = focus_item.parent_item(ParentItemTraversalMode::StopAtPopups);
777 }
778
779 let extra_mod = event.modifiers.control || event.modifiers.meta || event.modifiers.alt;
781 if event.text.starts_with(key_codes::Tab)
782 && !event.modifiers.shift
783 && !extra_mod
784 && event.event_type == KeyEventType::KeyPressed
785 {
786 self.focus_next_item();
787 } else if (event.text.starts_with(key_codes::Backtab)
788 || (event.text.starts_with(key_codes::Tab) && event.modifiers.shift))
789 && event.event_type == KeyEventType::KeyPressed
790 && !extra_mod
791 {
792 self.focus_previous_item();
793 } else if event.event_type == KeyEventType::KeyPressed
794 && event.text.starts_with(key_codes::Escape)
795 {
796 let mut adapter = self.window_adapter();
800 let item_tree = self.component();
801 let mut a = None;
802 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut a);
803 if let Some(a) = a {
804 adapter = a;
805 }
806 let window = WindowInner::from_pub(adapter.window());
807
808 let close_on_escape = if let Some(popup) = window.active_popups.borrow().last() {
809 popup.close_policy == PopupClosePolicy::CloseOnClick
810 || popup.close_policy == PopupClosePolicy::CloseOnClickOutside
811 } else {
812 false
813 };
814
815 if close_on_escape {
816 window.close_top_popup();
817 }
818 }
819 crate::properties::ChangeTracker::run_change_handlers();
820 }
821
822 pub fn set_cursor_blink_binding(&self, prop: &crate::Property<bool>) {
824 let existing_blinker = self.cursor_blinker.borrow().clone();
825
826 let blinker = existing_blinker.upgrade().unwrap_or_else(|| {
827 let new_blinker = TextCursorBlinker::new();
828 *self.cursor_blinker.borrow_mut() =
829 pin_weak::rc::PinWeak::downgrade(new_blinker.clone());
830 new_blinker
831 });
832
833 TextCursorBlinker::set_binding(blinker, prop);
834 }
835
836 pub fn set_focus_item(&self, new_focus_item: &ItemRc, set_focus: bool, reason: FocusReason) {
839 if self.prevent_focus_change.get() {
840 return;
841 }
842
843 let popup_wa = self.active_popups.borrow().last().and_then(|p| match &p.location {
844 PopupWindowLocation::TopLevel(wa) => Some(wa.clone()),
845 PopupWindowLocation::ChildWindow(..) => None,
846 });
847 if let Some(popup_wa) = popup_wa {
848 popup_wa.window().0.set_focus_item(new_focus_item, set_focus, reason);
850 return;
851 }
852
853 let current_focus_item = self.focus_item.borrow().clone();
854 if let Some(current_focus_item_rc) = current_focus_item.upgrade() {
855 if set_focus {
856 if current_focus_item_rc == *new_focus_item {
857 return;
859 }
860 } else if current_focus_item_rc != *new_focus_item {
861 return;
863 }
864 }
865
866 let old = self.take_focus_item(&FocusEvent::FocusOut(reason));
867 let new = if set_focus {
868 self.move_focus(new_focus_item.clone(), next_focus_item, reason)
869 } else {
870 None
871 };
872 let window_adapter = self.window_adapter();
873 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
874 window_adapter.handle_focus_change(old, new);
875 }
876 }
877
878 fn take_focus_item(&self, event: &FocusEvent) -> Option<ItemRc> {
882 let focus_item = self.focus_item.take();
883 assert!(matches!(event, FocusEvent::FocusOut(_)));
884
885 if let Some(focus_item_rc) = focus_item.upgrade() {
886 focus_item_rc.borrow().as_ref().focus_event(
887 event,
888 &self.window_adapter(),
889 &focus_item_rc,
890 );
891 Some(focus_item_rc)
892 } else {
893 None
894 }
895 }
896
897 fn publish_focus_item(
901 &self,
902 item: &Option<ItemRc>,
903 reason: FocusReason,
904 ) -> crate::input::FocusEventResult {
905 match item {
906 Some(item) => {
907 *self.focus_item.borrow_mut() = item.downgrade();
908 item.borrow().as_ref().focus_event(
909 &FocusEvent::FocusIn(reason),
910 &self.window_adapter(),
911 item,
912 )
913 }
914 None => {
915 *self.focus_item.borrow_mut() = Default::default();
916 crate::input::FocusEventResult::FocusAccepted }
918 }
919 }
920
921 fn move_focus(
922 &self,
923 start_item: ItemRc,
924 forward: impl Fn(ItemRc) -> ItemRc,
925 reason: FocusReason,
926 ) -> Option<ItemRc> {
927 let mut current_item = start_item;
928 let mut visited = Vec::new();
929
930 loop {
931 if current_item.is_visible()
932 && self.publish_focus_item(&Some(current_item.clone()), reason)
933 == crate::input::FocusEventResult::FocusAccepted
934 {
935 return Some(current_item); }
937 visited.push(current_item.clone());
938 current_item = forward(current_item);
939
940 if visited.iter().any(|i| *i == current_item) {
941 return None; }
943 }
944 }
945
946 pub fn focus_next_item(&self) {
948 let start_item = self
949 .take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation))
950 .map(next_focus_item)
951 .unwrap_or_else(|| {
952 ItemRc::new(
953 self.active_popups
954 .borrow()
955 .last()
956 .map_or_else(|| self.component(), |p| p.component.clone()),
957 0,
958 )
959 });
960 let end_item =
961 self.move_focus(start_item.clone(), next_focus_item, FocusReason::TabNavigation);
962 let window_adapter = self.window_adapter();
963 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
964 window_adapter.handle_focus_change(Some(start_item), end_item);
965 }
966 }
967
968 pub fn focus_previous_item(&self) {
970 let start_item = previous_focus_item(
971 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation)).unwrap_or_else(
972 || {
973 ItemRc::new(
974 self.active_popups
975 .borrow()
976 .last()
977 .map_or_else(|| self.component(), |p| p.component.clone()),
978 0,
979 )
980 },
981 ),
982 );
983 let end_item =
984 self.move_focus(start_item.clone(), previous_focus_item, FocusReason::TabNavigation);
985 let window_adapter = self.window_adapter();
986 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
987 window_adapter.handle_focus_change(Some(start_item), end_item);
988 }
989 }
990
991 pub fn set_active(&self, have_focus: bool) {
997 self.pinned_fields.as_ref().project_ref().active.set(have_focus);
998
999 let event = if have_focus {
1000 FocusEvent::FocusIn(FocusReason::WindowActivation)
1001 } else {
1002 FocusEvent::FocusOut(FocusReason::WindowActivation)
1003 };
1004
1005 if let Some(focus_item) = self.focus_item.borrow().upgrade() {
1006 focus_item.borrow().as_ref().focus_event(&event, &self.window_adapter(), &focus_item);
1007 }
1008
1009 if !have_focus {
1012 self.modifiers.take();
1013 }
1014 }
1015
1016 pub fn active(&self) -> bool {
1019 self.pinned_fields.as_ref().project_ref().active.get()
1020 }
1021
1022 pub fn update_window_properties(&self) {
1025 let window_adapter = self.window_adapter();
1026
1027 self.pinned_fields
1030 .as_ref()
1031 .project_ref()
1032 .window_properties_tracker
1033 .evaluate_as_dependency_root(|| {
1034 window_adapter.update_window_properties(WindowProperties(self));
1035 });
1036 }
1037
1038 pub fn draw_contents<T>(
1042 &self,
1043 render_components: impl FnOnce(&[(&ItemTreeRc, LogicalPoint)]) -> T,
1044 ) -> Option<T> {
1045 let component_rc = self.try_component()?;
1046 Some(self.pinned_fields.as_ref().project_ref().redraw_tracker.evaluate_as_dependency_root(
1047 || {
1048 if !self
1049 .active_popups
1050 .borrow()
1051 .iter()
1052 .any(|p| matches!(p.location, PopupWindowLocation::ChildWindow(..)))
1053 {
1054 render_components(&[(&component_rc, LogicalPoint::default())])
1055 } else {
1056 let borrow = self.active_popups.borrow();
1057 let mut cmps = Vec::with_capacity(borrow.len() + 1);
1058 cmps.push((&component_rc, LogicalPoint::default()));
1059 for popup in borrow.iter() {
1060 if let PopupWindowLocation::ChildWindow(location) = &popup.location {
1061 cmps.push((&popup.component, *location));
1062 }
1063 }
1064 render_components(&cmps)
1065 }
1066 },
1067 ))
1068 }
1069
1070 pub fn show(&self) -> Result<(), PlatformError> {
1073 if let Some(component) = self.try_component() {
1074 let was_visible = self.strong_component_ref.replace(Some(component)).is_some();
1075 if !was_visible {
1076 *(self.ctx.0.window_count.borrow_mut()) += 1;
1077 }
1078 }
1079
1080 self.update_window_properties();
1081 self.window_adapter().set_visible(true)?;
1082 let size = self.window_adapter().size();
1085 self.set_window_item_geometry(size.to_logical(self.scale_factor()).to_euclid());
1086 self.window_adapter().renderer().resize(size).unwrap();
1087 if let Some(hook) = self.ctx.0.window_shown_hook.borrow_mut().as_mut() {
1088 hook(&self.window_adapter());
1089 }
1090 Ok(())
1091 }
1092
1093 pub fn hide(&self) -> Result<(), PlatformError> {
1095 let result = self.window_adapter().set_visible(false);
1096 let was_visible = self.strong_component_ref.borrow_mut().take().is_some();
1097 if was_visible {
1098 let mut count = self.ctx.0.window_count.borrow_mut();
1099 *count -= 1;
1100 if *count <= 0 {
1101 drop(count);
1102 let _ = self.ctx.event_loop_proxy().and_then(|p| p.quit_event_loop().ok());
1103 }
1104 }
1105 result
1106 }
1107
1108 pub fn color_scheme(&self) -> ColorScheme {
1110 self.window_adapter()
1111 .internal(crate::InternalToken)
1112 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1113 }
1114
1115 pub fn supports_native_menu_bar(&self) -> bool {
1117 self.window_adapter()
1118 .internal(crate::InternalToken)
1119 .is_some_and(|x| x.supports_native_menu_bar())
1120 }
1121
1122 pub fn setup_menubar(&self, menubar: vtable::VBox<MenuVTable>) {
1124 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1125 x.setup_menubar(menubar);
1126 }
1127 }
1128
1129 pub fn show_popup(
1133 &self,
1134 popup_componentrc: &ItemTreeRc,
1135 position: LogicalPosition,
1136 close_policy: PopupClosePolicy,
1137 parent_item: &ItemRc,
1138 is_menu: bool,
1139 ) -> NonZeroU32 {
1140 let position = parent_item
1141 .map_to_window(parent_item.geometry().origin + position.to_euclid().to_vector());
1142 let root_of = |mut item_tree: ItemTreeRc| loop {
1143 if ItemRc::new(item_tree.clone(), 0).downcast::<crate::items::WindowItem>().is_some() {
1144 return item_tree;
1145 }
1146 let mut r = crate::item_tree::ItemWeak::default();
1147 ItemTreeRc::borrow_pin(&item_tree).as_ref().parent_node(&mut r);
1148 match r.upgrade() {
1149 None => return item_tree,
1150 Some(x) => item_tree = x.item_tree().clone(),
1151 }
1152 };
1153 let parent_root_item_tree = root_of(parent_item.item_tree().clone());
1154 let (parent_window_adapter, position) = if let Some(parent_popup) = self
1155 .active_popups
1156 .borrow()
1157 .iter()
1158 .find(|p| ItemTreeRc::ptr_eq(&p.component, &parent_root_item_tree))
1159 {
1160 match &parent_popup.location {
1161 PopupWindowLocation::TopLevel(wa) => (wa.clone(), position),
1162 PopupWindowLocation::ChildWindow(offset) => {
1163 (self.window_adapter(), position + offset.to_vector())
1164 }
1165 }
1166 } else {
1167 (self.window_adapter(), position)
1168 };
1169
1170 let popup_component = ItemTreeRc::borrow_pin(popup_componentrc);
1171 let popup_root = popup_component.as_ref().get_item_ref(0);
1172
1173 let (mut w, mut h) = if let Some(window_item) =
1174 ItemRef::downcast_pin::<crate::items::WindowItem>(popup_root)
1175 {
1176 (window_item.width(), window_item.height())
1177 } else {
1178 (LogicalLength::zero(), LogicalLength::zero())
1179 };
1180
1181 let layout_info_h =
1182 popup_component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
1183 let layout_info_v =
1184 popup_component.as_ref().layout_info(crate::layout::Orientation::Vertical);
1185
1186 if w <= LogicalLength::zero() {
1187 w = LogicalLength::new(layout_info_h.preferred);
1188 }
1189 if h <= LogicalLength::zero() {
1190 h = LogicalLength::new(layout_info_v.preferred);
1191 }
1192 w = w.max(LogicalLength::new(layout_info_h.min)).min(LogicalLength::new(layout_info_h.max));
1193 h = h.max(LogicalLength::new(layout_info_v.min)).min(LogicalLength::new(layout_info_v.max));
1194
1195 let size = crate::lengths::LogicalSize::from_lengths(w, h);
1196
1197 if let Some(window_item) = ItemRef::downcast_pin(popup_root) {
1198 let width_property =
1199 crate::items::WindowItem::FIELD_OFFSETS.width.apply_pin(window_item);
1200 let height_property =
1201 crate::items::WindowItem::FIELD_OFFSETS.height.apply_pin(window_item);
1202 width_property.set(size.width_length());
1203 height_property.set(size.height_length());
1204 };
1205
1206 let popup_id = self.next_popup_id.get();
1207 self.next_popup_id.set(self.next_popup_id.get().checked_add(1).unwrap());
1208
1209 let location = match parent_window_adapter
1210 .internal(crate::InternalToken)
1211 .and_then(|x| x.create_popup(LogicalRect::new(position, size)))
1212 {
1213 None => {
1214 let clip = LogicalRect::new(
1215 LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1216 self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
1217 );
1218 let rect = popup::place_popup(
1219 popup::Placement::Fixed(LogicalRect::new(position, size)),
1220 &Some(clip),
1221 );
1222 self.window_adapter().request_redraw();
1223 PopupWindowLocation::ChildWindow(rect.origin)
1224 }
1225 Some(window_adapter) => {
1226 WindowInner::from_pub(window_adapter.window()).set_component(popup_componentrc);
1227 PopupWindowLocation::TopLevel(window_adapter)
1228 }
1229 };
1230
1231 let focus_item = self
1232 .take_focus_item(&FocusEvent::FocusOut(FocusReason::PopupActivation))
1233 .map(|item| item.downgrade())
1234 .unwrap_or_default();
1235
1236 self.active_popups.borrow_mut().push(PopupWindow {
1237 popup_id,
1238 location,
1239 component: popup_componentrc.clone(),
1240 close_policy,
1241 focus_item_in_parent: focus_item,
1242 parent_item: parent_item.downgrade(),
1243 is_menu,
1244 });
1245
1246 popup_id
1247 }
1248
1249 pub fn show_native_popup_menu(
1255 &self,
1256 _context_menu_item: &ItemRc,
1257 _position: LogicalPosition,
1258 ) -> bool {
1259 false
1261 }
1262
1263 fn close_popup_impl(&self, current_popup: &PopupWindow) {
1265 match ¤t_popup.location {
1266 PopupWindowLocation::ChildWindow(offset) => {
1267 let popup_region = crate::properties::evaluate_no_tracking(|| {
1269 let popup_component = ItemTreeRc::borrow_pin(¤t_popup.component);
1270 popup_component.as_ref().item_geometry(0)
1271 })
1272 .translate(offset.to_vector());
1273
1274 if !popup_region.is_empty() {
1275 let window_adapter = self.window_adapter();
1276 window_adapter.renderer().mark_dirty_region(popup_region.into());
1277 window_adapter.request_redraw();
1278 }
1279 }
1280 PopupWindowLocation::TopLevel(adapter) => {
1281 let _ = adapter.set_visible(false);
1282 }
1283 }
1284 if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
1285 self.set_focus_item(&focus, true, FocusReason::PopupActivation);
1286 }
1287 }
1288
1289 pub fn close_popup(&self, popup_id: NonZeroU32) {
1291 let mut active_popups = self.active_popups.borrow_mut();
1292 let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
1293
1294 if let Some(popup_index) = maybe_index {
1295 let p = active_popups.remove(popup_index);
1296 drop(active_popups);
1297 self.close_popup_impl(&p);
1298 if p.is_menu {
1299 while self.active_popups.borrow().get(popup_index).is_some_and(|p| p.is_menu) {
1301 let p = self.active_popups.borrow_mut().remove(popup_index);
1302 self.close_popup_impl(&p);
1303 }
1304 }
1305 }
1306 }
1307
1308 pub fn close_all_popups(&self) {
1310 for popup in self.active_popups.take() {
1311 self.close_popup_impl(&popup);
1312 }
1313 }
1314
1315 pub fn close_top_popup(&self) {
1317 let popup = self.active_popups.borrow_mut().pop();
1318 if let Some(popup) = popup {
1319 self.close_popup_impl(&popup);
1320 }
1321 }
1322
1323 pub fn scale_factor(&self) -> f32 {
1325 self.pinned_fields.as_ref().project_ref().scale_factor.get()
1326 }
1327
1328 pub(crate) fn set_scale_factor(&self, factor: f32) {
1330 self.pinned_fields.scale_factor.set(factor)
1331 }
1332
1333 pub fn text_input_focused(&self) -> bool {
1335 self.pinned_fields.as_ref().project_ref().text_input_focused.get()
1336 }
1337
1338 pub fn set_text_input_focused(&self, value: bool) {
1340 self.pinned_fields.text_input_focused.set(value)
1341 }
1342
1343 pub fn is_visible(&self) -> bool {
1345 self.strong_component_ref.borrow().is_some()
1346 }
1347
1348 pub fn window_item_rc(&self) -> Option<ItemRc> {
1351 self.try_component().and_then(|component_rc| {
1352 let item_rc = ItemRc::new(component_rc, 0);
1353 if item_rc.downcast::<crate::items::WindowItem>().is_some() {
1354 Some(item_rc)
1355 } else {
1356 None
1357 }
1358 })
1359 }
1360
1361 pub fn window_item(&self) -> Option<VRcMapped<ItemTreeVTable, crate::items::WindowItem>> {
1363 self.try_component().and_then(|component_rc| {
1364 ItemRc::new(component_rc, 0).downcast::<crate::items::WindowItem>()
1365 })
1366 }
1367
1368 pub(crate) fn set_window_item_geometry(&self, size: crate::lengths::LogicalSize) {
1371 if let Some(component_rc) = self.try_component() {
1372 let component = ItemTreeRc::borrow_pin(&component_rc);
1373 let root_item = component.as_ref().get_item_ref(0);
1374 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1375 {
1376 window_item.width.set(size.width_length());
1377 window_item.height.set(size.height_length());
1378 }
1379 }
1380 }
1381
1382 pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
1384 self.close_requested.set_handler(move |()| callback());
1385 }
1386
1387 pub fn request_close(&self) -> bool {
1391 match self.close_requested.call(&()) {
1392 CloseRequestResponse::HideWindow => true,
1393 CloseRequestResponse::KeepWindowShown => false,
1394 }
1395 }
1396
1397 pub fn is_fullscreen(&self) -> bool {
1399 if let Some(window_item) = self.window_item() {
1400 window_item.as_pin_ref().full_screen()
1401 } else {
1402 false
1403 }
1404 }
1405
1406 pub fn set_fullscreen(&self, enabled: bool) {
1408 if let Some(window_item) = self.window_item() {
1409 window_item.as_pin_ref().full_screen.set(enabled);
1410 self.update_window_properties()
1411 }
1412 }
1413
1414 pub fn is_maximized(&self) -> bool {
1416 self.maximized.get()
1417 }
1418
1419 pub fn set_maximized(&self, maximized: bool) {
1421 self.maximized.set(maximized);
1422 self.update_window_properties()
1423 }
1424
1425 pub fn is_minimized(&self) -> bool {
1427 self.minimized.get()
1428 }
1429
1430 pub fn set_minimized(&self, minimized: bool) {
1432 self.minimized.set(minimized);
1433 self.update_window_properties()
1434 }
1435
1436 pub fn xdg_app_id(&self) -> Option<SharedString> {
1438 self.ctx.xdg_app_id()
1439 }
1440
1441 pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
1443 self.window_adapter_weak.upgrade().unwrap()
1444 }
1445
1446 pub fn from_pub(window: &crate::api::Window) -> &Self {
1448 &window.0
1449 }
1450
1451 pub fn context(&self) -> &crate::SlintContext {
1453 &self.ctx
1454 }
1455}
1456
1457pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
1459
1460#[cfg(feature = "ffi")]
1463pub mod ffi {
1464 #![allow(unsafe_code)]
1465 #![allow(clippy::missing_safety_doc)]
1466 #![allow(missing_docs)]
1467
1468 use super::*;
1469 use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
1470 use crate::graphics::Size;
1471 use crate::graphics::{IntSize, Rgba8Pixel};
1472 use crate::SharedVector;
1473 use core::ptr::NonNull;
1474
1475 #[repr(u8)]
1478 pub enum GraphicsAPI {
1479 NativeOpenGL,
1481 Inaccessible,
1483 }
1484
1485 #[allow(non_camel_case_types)]
1486 type c_void = ();
1487
1488 #[repr(C)]
1490 pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
1491
1492 #[unsafe(no_mangle)]
1494 pub unsafe extern "C" fn slint_windowrc_drop(handle: *mut WindowAdapterRcOpaque) {
1495 assert_eq!(
1496 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1497 core::mem::size_of::<WindowAdapterRcOpaque>()
1498 );
1499 assert_eq!(
1500 core::mem::size_of::<Option<Rc<dyn WindowAdapter>>>(),
1501 core::mem::size_of::<WindowAdapterRcOpaque>()
1502 );
1503 drop(core::ptr::read(handle as *mut Option<Rc<dyn WindowAdapter>>));
1504 }
1505
1506 #[unsafe(no_mangle)]
1508 pub unsafe extern "C" fn slint_windowrc_clone(
1509 source: *const WindowAdapterRcOpaque,
1510 target: *mut WindowAdapterRcOpaque,
1511 ) {
1512 assert_eq!(
1513 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1514 core::mem::size_of::<WindowAdapterRcOpaque>()
1515 );
1516 let window = &*(source as *const Rc<dyn WindowAdapter>);
1517 core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window.clone());
1518 }
1519
1520 #[unsafe(no_mangle)]
1522 pub unsafe extern "C" fn slint_windowrc_show(handle: *const WindowAdapterRcOpaque) {
1523 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1524
1525 window_adapter.window().show().unwrap();
1526 }
1527
1528 #[unsafe(no_mangle)]
1530 pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) {
1531 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1532 window_adapter.window().hide().unwrap();
1533 }
1534
1535 #[unsafe(no_mangle)]
1538 pub unsafe extern "C" fn slint_windowrc_is_visible(
1539 handle: *const WindowAdapterRcOpaque,
1540 ) -> bool {
1541 let window = &*(handle as *const Rc<dyn WindowAdapter>);
1542 window.window().is_visible()
1543 }
1544
1545 #[unsafe(no_mangle)]
1547 pub unsafe extern "C" fn slint_windowrc_get_scale_factor(
1548 handle: *const WindowAdapterRcOpaque,
1549 ) -> f32 {
1550 assert_eq!(
1551 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1552 core::mem::size_of::<WindowAdapterRcOpaque>()
1553 );
1554 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1555 WindowInner::from_pub(window_adapter.window()).scale_factor()
1556 }
1557
1558 #[unsafe(no_mangle)]
1560 pub unsafe extern "C" fn slint_windowrc_set_scale_factor(
1561 handle: *const WindowAdapterRcOpaque,
1562 value: f32,
1563 ) {
1564 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1565 WindowInner::from_pub(window_adapter.window()).set_scale_factor(value)
1566 }
1567
1568 #[unsafe(no_mangle)]
1570 pub unsafe extern "C" fn slint_windowrc_get_text_input_focused(
1571 handle: *const WindowAdapterRcOpaque,
1572 ) -> bool {
1573 assert_eq!(
1574 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1575 core::mem::size_of::<WindowAdapterRcOpaque>()
1576 );
1577 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1578 WindowInner::from_pub(window_adapter.window()).text_input_focused()
1579 }
1580
1581 #[unsafe(no_mangle)]
1583 pub unsafe extern "C" fn slint_windowrc_set_text_input_focused(
1584 handle: *const WindowAdapterRcOpaque,
1585 value: bool,
1586 ) {
1587 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1588 WindowInner::from_pub(window_adapter.window()).set_text_input_focused(value)
1589 }
1590
1591 #[unsafe(no_mangle)]
1593 pub unsafe extern "C" fn slint_windowrc_set_focus_item(
1594 handle: *const WindowAdapterRcOpaque,
1595 focus_item: &ItemRc,
1596 set_focus: bool,
1597 reason: FocusReason,
1598 ) {
1599 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1600 WindowInner::from_pub(window_adapter.window()).set_focus_item(focus_item, set_focus, reason)
1601 }
1602
1603 #[unsafe(no_mangle)]
1605 pub unsafe extern "C" fn slint_windowrc_set_component(
1606 handle: *const WindowAdapterRcOpaque,
1607 component: &ItemTreeRc,
1608 ) {
1609 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1610 WindowInner::from_pub(window_adapter.window()).set_component(component)
1611 }
1612
1613 #[unsafe(no_mangle)]
1615 pub unsafe extern "C" fn slint_windowrc_show_popup(
1616 handle: *const WindowAdapterRcOpaque,
1617 popup: &ItemTreeRc,
1618 position: LogicalPosition,
1619 close_policy: PopupClosePolicy,
1620 parent_item: &ItemRc,
1621 is_menu: bool,
1622 ) -> NonZeroU32 {
1623 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1624 WindowInner::from_pub(window_adapter.window()).show_popup(
1625 popup,
1626 position,
1627 close_policy,
1628 parent_item,
1629 is_menu,
1630 )
1631 }
1632
1633 #[unsafe(no_mangle)]
1635 pub unsafe extern "C" fn slint_windowrc_close_popup(
1636 handle: *const WindowAdapterRcOpaque,
1637 popup_id: NonZeroU32,
1638 ) {
1639 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1640 WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
1641 }
1642
1643 #[unsafe(no_mangle)]
1645 pub unsafe extern "C" fn slint_windowrc_set_rendering_notifier(
1646 handle: *const WindowAdapterRcOpaque,
1647 callback: extern "C" fn(
1648 rendering_state: RenderingState,
1649 graphics_api: GraphicsAPI,
1650 user_data: *mut c_void,
1651 ),
1652 drop_user_data: extern "C" fn(user_data: *mut c_void),
1653 user_data: *mut c_void,
1654 error: *mut SetRenderingNotifierError,
1655 ) -> bool {
1656 struct CNotifier {
1657 callback: extern "C" fn(
1658 rendering_state: RenderingState,
1659 graphics_api: GraphicsAPI,
1660 user_data: *mut c_void,
1661 ),
1662 drop_user_data: extern "C" fn(*mut c_void),
1663 user_data: *mut c_void,
1664 }
1665
1666 impl Drop for CNotifier {
1667 fn drop(&mut self) {
1668 (self.drop_user_data)(self.user_data)
1669 }
1670 }
1671
1672 impl RenderingNotifier for CNotifier {
1673 fn notify(&mut self, state: RenderingState, graphics_api: &crate::api::GraphicsAPI) {
1674 let cpp_graphics_api = match graphics_api {
1675 crate::api::GraphicsAPI::NativeOpenGL { .. } => GraphicsAPI::NativeOpenGL,
1676 crate::api::GraphicsAPI::WebGL { .. } => unreachable!(), #[cfg(feature = "unstable-wgpu-24")]
1678 crate::api::GraphicsAPI::WGPU24 { .. } => GraphicsAPI::Inaccessible, };
1680 (self.callback)(state, cpp_graphics_api, self.user_data)
1681 }
1682 }
1683
1684 let window = &*(handle as *const Rc<dyn WindowAdapter>);
1685 match window.renderer().set_rendering_notifier(Box::new(CNotifier {
1686 callback,
1687 drop_user_data,
1688 user_data,
1689 })) {
1690 Ok(()) => true,
1691 Err(err) => {
1692 *error = err;
1693 false
1694 }
1695 }
1696 }
1697
1698 #[unsafe(no_mangle)]
1700 pub unsafe extern "C" fn slint_windowrc_on_close_requested(
1701 handle: *const WindowAdapterRcOpaque,
1702 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
1703 drop_user_data: extern "C" fn(user_data: *mut c_void),
1704 user_data: *mut c_void,
1705 ) {
1706 struct WithUserData {
1707 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
1708 drop_user_data: extern "C" fn(*mut c_void),
1709 user_data: *mut c_void,
1710 }
1711
1712 impl Drop for WithUserData {
1713 fn drop(&mut self) {
1714 (self.drop_user_data)(self.user_data)
1715 }
1716 }
1717
1718 impl WithUserData {
1719 fn call(&self) -> CloseRequestResponse {
1720 (self.callback)(self.user_data)
1721 }
1722 }
1723
1724 let with_user_data = WithUserData { callback, drop_user_data, user_data };
1725
1726 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1727 window_adapter.window().on_close_requested(move || with_user_data.call());
1728 }
1729
1730 #[unsafe(no_mangle)]
1732 pub unsafe extern "C" fn slint_windowrc_request_redraw(handle: *const WindowAdapterRcOpaque) {
1733 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1734 window_adapter.request_redraw();
1735 }
1736
1737 #[unsafe(no_mangle)]
1740 pub unsafe extern "C" fn slint_windowrc_position(
1741 handle: *const WindowAdapterRcOpaque,
1742 pos: &mut euclid::default::Point2D<i32>,
1743 ) {
1744 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1745 *pos = window_adapter.position().unwrap_or_default().to_euclid()
1746 }
1747
1748 #[unsafe(no_mangle)]
1752 pub unsafe extern "C" fn slint_windowrc_set_physical_position(
1753 handle: *const WindowAdapterRcOpaque,
1754 pos: &euclid::default::Point2D<i32>,
1755 ) {
1756 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1757 window_adapter.set_position(crate::api::PhysicalPosition::new(pos.x, pos.y).into());
1758 }
1759
1760 #[unsafe(no_mangle)]
1764 pub unsafe extern "C" fn slint_windowrc_set_logical_position(
1765 handle: *const WindowAdapterRcOpaque,
1766 pos: &euclid::default::Point2D<f32>,
1767 ) {
1768 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1769 window_adapter.set_position(LogicalPosition::new(pos.x, pos.y).into());
1770 }
1771
1772 #[unsafe(no_mangle)]
1775 pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
1776 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1777 window_adapter.size().to_euclid().cast()
1778 }
1779
1780 #[unsafe(no_mangle)]
1783 pub unsafe extern "C" fn slint_windowrc_set_physical_size(
1784 handle: *const WindowAdapterRcOpaque,
1785 size: &IntSize,
1786 ) {
1787 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1788 window_adapter.window().set_size(crate::api::PhysicalSize::new(size.width, size.height));
1789 }
1790
1791 #[unsafe(no_mangle)]
1794 pub unsafe extern "C" fn slint_windowrc_set_logical_size(
1795 handle: *const WindowAdapterRcOpaque,
1796 size: &Size,
1797 ) {
1798 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1799 window_adapter.window().set_size(crate::api::LogicalSize::new(size.width, size.height));
1800 }
1801
1802 #[unsafe(no_mangle)]
1804 pub unsafe extern "C" fn slint_windowrc_color_scheme(
1805 handle: *const WindowAdapterRcOpaque,
1806 ) -> ColorScheme {
1807 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1808 window_adapter
1809 .internal(crate::InternalToken)
1810 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1811 }
1812
1813 #[unsafe(no_mangle)]
1815 pub unsafe extern "C" fn slint_windowrc_supports_native_menu_bar(
1816 handle: *const WindowAdapterRcOpaque,
1817 ) -> bool {
1818 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1819 window_adapter.internal(crate::InternalToken).is_some_and(|x| x.supports_native_menu_bar())
1820 }
1821
1822 #[unsafe(no_mangle)]
1824 pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
1825 handle: *const WindowAdapterRcOpaque,
1826 vtable: NonNull<MenuVTable>,
1827 menu_instance: NonNull<c_void>,
1828 ) {
1829 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1830 window_adapter
1831 .internal(crate::InternalToken)
1832 .map(|x| x.setup_menubar(vtable::VBox::from_raw(vtable, menu_instance.cast())));
1833 }
1834
1835 #[unsafe(no_mangle)]
1837 pub unsafe extern "C" fn slint_windowrc_default_font_size(
1838 handle: *const WindowAdapterRcOpaque,
1839 ) -> f32 {
1840 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1841 window_adapter.window().0.window_item().unwrap().as_pin_ref().default_font_size().get()
1842 }
1843
1844 #[unsafe(no_mangle)]
1846 pub unsafe extern "C" fn slint_windowrc_dispatch_key_event(
1847 handle: *const WindowAdapterRcOpaque,
1848 event_type: crate::input::KeyEventType,
1849 text: &SharedString,
1850 repeat: bool,
1851 ) {
1852 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1853 window_adapter.window().0.process_key_input(crate::items::KeyEvent {
1854 text: text.clone(),
1855 repeat,
1856 event_type,
1857 ..Default::default()
1858 });
1859 }
1860
1861 #[unsafe(no_mangle)]
1863 pub unsafe extern "C" fn slint_windowrc_dispatch_pointer_event(
1864 handle: *const WindowAdapterRcOpaque,
1865 event: crate::input::MouseEvent,
1866 ) {
1867 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1868 window_adapter.window().0.process_mouse_input(event);
1869 }
1870
1871 #[unsafe(no_mangle)]
1873 pub unsafe extern "C" fn slint_windowrc_dispatch_event(
1874 handle: *const WindowAdapterRcOpaque,
1875 event: &crate::platform::WindowEvent,
1876 ) {
1877 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1878 window_adapter.window().dispatch_event(event.clone());
1879 }
1880
1881 #[unsafe(no_mangle)]
1882 pub unsafe extern "C" fn slint_windowrc_is_fullscreen(
1883 handle: *const WindowAdapterRcOpaque,
1884 ) -> bool {
1885 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1886 window_adapter.window().is_fullscreen()
1887 }
1888
1889 #[unsafe(no_mangle)]
1890 pub unsafe extern "C" fn slint_windowrc_is_minimized(
1891 handle: *const WindowAdapterRcOpaque,
1892 ) -> bool {
1893 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1894 window_adapter.window().is_minimized()
1895 }
1896
1897 #[unsafe(no_mangle)]
1898 pub unsafe extern "C" fn slint_windowrc_is_maximized(
1899 handle: *const WindowAdapterRcOpaque,
1900 ) -> bool {
1901 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1902 window_adapter.window().is_maximized()
1903 }
1904
1905 #[unsafe(no_mangle)]
1906 pub unsafe extern "C" fn slint_windowrc_set_fullscreen(
1907 handle: *const WindowAdapterRcOpaque,
1908 value: bool,
1909 ) {
1910 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1911 window_adapter.window().set_fullscreen(value)
1912 }
1913
1914 #[unsafe(no_mangle)]
1915 pub unsafe extern "C" fn slint_windowrc_set_minimized(
1916 handle: *const WindowAdapterRcOpaque,
1917 value: bool,
1918 ) {
1919 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1920 window_adapter.window().set_minimized(value)
1921 }
1922
1923 #[unsafe(no_mangle)]
1924 pub unsafe extern "C" fn slint_windowrc_set_maximized(
1925 handle: *const WindowAdapterRcOpaque,
1926 value: bool,
1927 ) {
1928 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1929 window_adapter.window().set_maximized(value)
1930 }
1931
1932 #[unsafe(no_mangle)]
1934 pub unsafe extern "C" fn slint_windowrc_take_snapshot(
1935 handle: *const WindowAdapterRcOpaque,
1936 data: &mut SharedVector<Rgba8Pixel>,
1937 width: &mut u32,
1938 height: &mut u32,
1939 ) -> bool {
1940 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1941 if let Ok(snapshot) = window_adapter.window().take_snapshot() {
1942 *data = snapshot.data.clone();
1943 *width = snapshot.width();
1944 *height = snapshot.height();
1945 true
1946 } else {
1947 false
1948 }
1949 }
1950}
1951
1952#[cfg(feature = "software-renderer")]
1953#[test]
1954fn test_empty_window() {
1955 let msw = crate::software_renderer::MinimalSoftwareWindow::new(
1962 crate::software_renderer::RepaintBufferType::NewBuffer,
1963 );
1964 msw.window().request_redraw();
1965 let mut region = None;
1966 let render_called = msw.draw_if_needed(|renderer| {
1967 let mut buffer =
1968 crate::graphics::SharedPixelBuffer::<crate::graphics::Rgb8Pixel>::new(100, 100);
1969 let stride = buffer.width() as usize;
1970 region = Some(renderer.render(buffer.make_mut_slice(), stride));
1971 });
1972 assert!(render_called);
1973 let region = region.unwrap();
1974 assert_eq!(region.bounding_box_size(), PhysicalSize::default());
1975 assert_eq!(region.bounding_box_origin(), PhysicalPosition::default());
1976}