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, PointerEventButton, 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, 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::VRc<MenuVTable>) {}
219
220 fn show_native_popup_menu(
221 &self,
222 _context_menu_item: vtable::VRc<MenuVTable>,
223 _position: LogicalPosition,
224 ) -> bool {
225 false
226 }
227
228 #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
230 fn window_handle_06_rc(
231 &self,
232 ) -> Result<
233 std::sync::Arc<dyn raw_window_handle_06::HasWindowHandle>,
234 raw_window_handle_06::HandleError,
235 > {
236 Err(raw_window_handle_06::HandleError::NotSupported)
237 }
238
239 #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
241 fn display_handle_06_rc(
242 &self,
243 ) -> Result<
244 std::sync::Arc<dyn raw_window_handle_06::HasDisplayHandle>,
245 raw_window_handle_06::HandleError,
246 > {
247 Err(raw_window_handle_06::HandleError::NotSupported)
248 }
249
250 fn bring_to_front(&self) -> Result<(), PlatformError> {
252 Ok(())
253 }
254}
255
256#[non_exhaustive]
259#[derive(Debug, Clone)]
260pub enum InputMethodRequest {
261 Enable(InputMethodProperties),
263 Update(InputMethodProperties),
265 Disable,
267}
268
269#[non_exhaustive]
271#[derive(Clone, Default, Debug)]
272pub struct InputMethodProperties {
273 pub text: SharedString,
277 pub cursor_position: usize,
279 pub anchor_position: Option<usize>,
282 pub preedit_text: SharedString,
286 pub preedit_offset: usize,
288 pub cursor_rect_origin: LogicalPosition,
290 pub cursor_rect_size: crate::api::LogicalSize,
292 pub anchor_point: LogicalPosition,
294 pub input_type: InputType,
296}
297
298#[non_exhaustive]
300#[derive(Copy, Clone, Debug, PartialEq, Default)]
301pub struct LayoutConstraints {
302 pub min: Option<crate::api::LogicalSize>,
304 pub max: Option<crate::api::LogicalSize>,
306 pub preferred: crate::api::LogicalSize,
308}
309
310pub struct WindowProperties<'a>(&'a WindowInner);
313
314impl WindowProperties<'_> {
315 pub fn title(&self) -> SharedString {
317 self.0.window_item().map(|w| w.as_pin_ref().title()).unwrap_or_default()
318 }
319
320 pub fn background(&self) -> crate::Brush {
322 self.0
323 .window_item()
324 .map(|w: VRcMapped<ItemTreeVTable, crate::items::WindowItem>| {
325 w.as_pin_ref().background()
326 })
327 .unwrap_or_default()
328 }
329
330 pub fn layout_constraints(&self) -> LayoutConstraints {
332 let component = self.0.component();
333 let component = ItemTreeRc::borrow_pin(&component);
334 let h = component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
335 let v = component.as_ref().layout_info(crate::layout::Orientation::Vertical);
336 let (min, max) = crate::layout::min_max_size_for_layout_constraints(h, v);
337 LayoutConstraints {
338 min,
339 max,
340 preferred: crate::api::LogicalSize::new(
341 h.preferred_bounded() as f32,
342 v.preferred_bounded() as f32,
343 ),
344 }
345 }
346
347 #[deprecated(note = "Please use `is_fullscreen` instead")]
349 pub fn fullscreen(&self) -> bool {
350 self.is_fullscreen()
351 }
352
353 pub fn is_fullscreen(&self) -> bool {
355 self.0.is_fullscreen()
356 }
357
358 pub fn is_maximized(&self) -> bool {
360 self.0.maximized.get()
361 }
362
363 pub fn is_minimized(&self) -> bool {
365 self.0.minimized.get()
366 }
367}
368
369struct WindowPropertiesTracker {
370 window_adapter_weak: Weak<dyn WindowAdapter>,
371}
372
373impl crate::properties::PropertyDirtyHandler for WindowPropertiesTracker {
374 fn notify(self: Pin<&Self>) {
375 let win = self.window_adapter_weak.clone();
376 crate::timers::Timer::single_shot(Default::default(), move || {
377 if let Some(window_adapter) = win.upgrade() {
378 WindowInner::from_pub(window_adapter.window()).update_window_properties();
379 };
380 })
381 }
382}
383
384struct WindowRedrawTracker {
385 window_adapter_weak: Weak<dyn WindowAdapter>,
386}
387
388impl crate::properties::PropertyDirtyHandler for WindowRedrawTracker {
389 fn notify(self: Pin<&Self>) {
390 if let Some(window_adapter) = self.window_adapter_weak.upgrade() {
391 window_adapter.request_redraw();
392 };
393 }
394}
395
396#[derive(Clone)]
398pub enum PopupWindowLocation {
399 TopLevel(Rc<dyn WindowAdapter>),
401 ChildWindow(LogicalPoint),
403}
404
405#[derive(Clone)]
408pub struct PopupWindow {
409 pub popup_id: NonZeroU32,
411 pub location: PopupWindowLocation,
413 pub component: ItemTreeRc,
415 pub close_policy: PopupClosePolicy,
417 focus_item_in_parent: ItemWeak,
419 pub parent_item: ItemWeak,
421 is_menu: bool,
424}
425
426#[pin_project::pin_project]
427struct WindowPinnedFields {
428 #[pin]
429 redraw_tracker: PropertyTracker<WindowRedrawTracker>,
430 #[pin]
432 window_properties_tracker: PropertyTracker<WindowPropertiesTracker>,
433 #[pin]
434 scale_factor: Property<f32>,
435 #[pin]
436 active: Property<bool>,
437 #[pin]
438 text_input_focused: Property<bool>,
439}
440
441pub struct WindowInner {
443 window_adapter_weak: Weak<dyn WindowAdapter>,
444 component: RefCell<ItemTreeWeak>,
445 strong_component_ref: RefCell<Option<ItemTreeRc>>,
447 mouse_input_state: Cell<MouseInputState>,
448 pub(crate) modifiers: Cell<InternalKeyboardModifierState>,
449
450 pub focus_item: RefCell<crate::item_tree::ItemWeak>,
452 pub(crate) last_ime_text: RefCell<SharedString>,
454 pub(crate) prevent_focus_change: Cell<bool>,
459 cursor_blinker: RefCell<pin_weak::rc::PinWeak<crate::input::TextCursorBlinker>>,
460
461 pinned_fields: Pin<Box<WindowPinnedFields>>,
462 maximized: Cell<bool>,
463 minimized: Cell<bool>,
464
465 active_popups: RefCell<Vec<PopupWindow>>,
467 next_popup_id: Cell<NonZeroU32>,
468 had_popup_on_press: Cell<bool>,
469 close_requested: Callback<(), CloseRequestResponse>,
470 click_state: ClickState,
471 pub(crate) ctx: once_cell::unsync::Lazy<crate::SlintContext>,
472}
473
474impl Drop for WindowInner {
475 fn drop(&mut self) {
476 if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() {
477 existing_blinker.stop();
478 }
479 }
480}
481
482impl WindowInner {
483 pub fn new(window_adapter_weak: Weak<dyn WindowAdapter>) -> Self {
485 #![allow(unused_mut)]
486
487 let mut window_properties_tracker =
488 PropertyTracker::new_with_dirty_handler(WindowPropertiesTracker {
489 window_adapter_weak: window_adapter_weak.clone(),
490 });
491
492 let mut redraw_tracker = PropertyTracker::new_with_dirty_handler(WindowRedrawTracker {
493 window_adapter_weak: window_adapter_weak.clone(),
494 });
495
496 #[cfg(slint_debug_property)]
497 {
498 window_properties_tracker
499 .set_debug_name("i_slint_core::Window::window_properties_tracker".into());
500 redraw_tracker.set_debug_name("i_slint_core::Window::redraw_tracker".into());
501 }
502
503 Self {
504 window_adapter_weak,
505 component: Default::default(),
506 strong_component_ref: Default::default(),
507 mouse_input_state: Default::default(),
508 modifiers: Default::default(),
509 pinned_fields: Box::pin(WindowPinnedFields {
510 redraw_tracker,
511 window_properties_tracker,
512 scale_factor: Property::new_named(1., "i_slint_core::Window::scale_factor"),
513 active: Property::new_named(false, "i_slint_core::Window::active"),
514 text_input_focused: Property::new_named(
515 false,
516 "i_slint_core::Window::text_input_focused",
517 ),
518 }),
519 maximized: Cell::new(false),
520 minimized: Cell::new(false),
521 focus_item: Default::default(),
522 last_ime_text: Default::default(),
523 cursor_blinker: Default::default(),
524 active_popups: Default::default(),
525 next_popup_id: Cell::new(NonZeroU32::MIN),
526 had_popup_on_press: Default::default(),
527 close_requested: Default::default(),
528 click_state: ClickState::default(),
529 prevent_focus_change: Default::default(),
530 ctx: once_cell::unsync::Lazy::new(|| {
533 crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().unwrap().clone())
534 }),
535 }
536 }
537
538 pub fn set_component(&self, component: &ItemTreeRc) {
541 self.close_all_popups();
542 self.focus_item.replace(Default::default());
543 self.mouse_input_state.replace(Default::default());
544 self.modifiers.replace(Default::default());
545 self.component.replace(ItemTreeRc::downgrade(component));
546 self.pinned_fields.window_properties_tracker.set_dirty(); let window_adapter = self.window_adapter();
548 window_adapter.renderer().set_window_adapter(&window_adapter);
549 self.set_window_item_geometry(
550 window_adapter.size().to_logical(self.scale_factor()).to_euclid(),
551 );
552 window_adapter.request_redraw();
553 let weak = Rc::downgrade(&window_adapter);
554 crate::timers::Timer::single_shot(Default::default(), move || {
555 if let Some(window_adapter) = weak.upgrade() {
556 WindowInner::from_pub(window_adapter.window()).update_window_properties();
557 }
558 })
559 }
560
561 pub fn component(&self) -> ItemTreeRc {
564 self.component.borrow().upgrade().unwrap()
565 }
566
567 pub fn try_component(&self) -> Option<ItemTreeRc> {
569 self.component.borrow().upgrade()
570 }
571
572 pub fn active_popups(&self) -> core::cell::Ref<'_, [PopupWindow]> {
574 core::cell::Ref::map(self.active_popups.borrow(), |v| v.as_slice())
575 }
576
577 pub fn process_mouse_input(&self, mut event: MouseEvent) {
580 crate::animations::update_animations();
581
582 event = self.click_state.check_repeat(event, self.ctx.platform().click_interval());
584
585 let window_adapter = self.window_adapter();
586 let mut mouse_input_state = self.mouse_input_state.take();
587 if let Some(mut drop_event) = mouse_input_state.drag_data.clone() {
588 match &event {
589 MouseEvent::Released { position, button: PointerEventButton::Left, .. } => {
590 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
591 window_adapter.set_mouse_cursor(MouseCursor::Default);
592 }
593 drop_event.position = crate::lengths::logical_position_to_api(*position);
594 event = MouseEvent::Drop(drop_event);
595 mouse_input_state.drag_data = None;
596 }
597 MouseEvent::Moved { position } => {
598 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
599 window_adapter.set_mouse_cursor(MouseCursor::NoDrop);
600 }
601 drop_event.position = crate::lengths::logical_position_to_api(*position);
602 event = MouseEvent::DragMove(drop_event);
603 }
604 MouseEvent::Exit => {
605 mouse_input_state.drag_data = None;
606 }
607 _ => {}
608 }
609 }
610
611 let pressed_event = matches!(event, MouseEvent::Pressed { .. });
612 let released_event = matches!(event, MouseEvent::Released { .. });
613
614 let last_top_item = mouse_input_state.top_item_including_delayed();
615 if released_event {
616 mouse_input_state =
617 crate::input::process_delayed_event(&window_adapter, mouse_input_state);
618 }
619
620 let Some(item_tree) = self.try_component() else { return };
621
622 let mut root_adapter = None;
624 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut root_adapter);
625 let root_adapter = root_adapter.unwrap_or_else(|| window_adapter.clone());
626 let active_popups = &WindowInner::from_pub(root_adapter.window()).active_popups;
627 let native_popup_index = active_popups.borrow().iter().position(|p| {
628 if let PopupWindowLocation::TopLevel(wa) = &p.location {
629 Rc::ptr_eq(wa, &window_adapter)
630 } else {
631 false
632 }
633 });
634
635 if pressed_event {
636 self.had_popup_on_press.set(!active_popups.borrow().is_empty());
637 }
638
639 let mut popup_to_close = active_popups.borrow().last().and_then(|popup| {
640 let mouse_inside_popup = || {
641 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
642 event.position().is_none_or(|pos| {
643 ItemTreeRc::borrow_pin(&popup.component)
644 .as_ref()
645 .item_geometry(0)
646 .contains(pos - coordinates.to_vector())
647 })
648 } else {
649 native_popup_index.is_some_and(|idx| idx == active_popups.borrow().len() - 1)
650 && event.position().is_none_or(|pos| {
651 ItemTreeRc::borrow_pin(&item_tree)
652 .as_ref()
653 .item_geometry(0)
654 .contains(pos)
655 })
656 }
657 };
658 match popup.close_policy {
659 PopupClosePolicy::CloseOnClick => {
660 let mouse_inside_popup = mouse_inside_popup();
661 (mouse_inside_popup && released_event && self.had_popup_on_press.get())
662 || (!mouse_inside_popup && pressed_event)
663 }
664 PopupClosePolicy::CloseOnClickOutside => !mouse_inside_popup() && pressed_event,
665 PopupClosePolicy::NoAutoClose => false,
666 }
667 .then_some(popup.popup_id)
668 });
669
670 mouse_input_state = if let Some(mut event) =
671 crate::input::handle_mouse_grab(&event, &window_adapter, &mut mouse_input_state)
672 {
673 let mut item_tree = self.component.borrow().upgrade();
674 let mut offset = LogicalPoint::default();
675 let mut menubar_item = None;
676 for (idx, popup) in active_popups.borrow().iter().enumerate().rev() {
677 item_tree = None;
678 menubar_item = None;
679 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
680 let geom = ItemTreeRc::borrow_pin(&popup.component).as_ref().item_geometry(0);
681 let mouse_inside_popup = event
682 .position()
683 .is_none_or(|pos| geom.contains(pos - coordinates.to_vector()));
684 if mouse_inside_popup {
685 item_tree = Some(popup.component.clone());
686 offset = *coordinates;
687 break;
688 }
689 } else if native_popup_index.is_some_and(|i| i == idx) {
690 item_tree = self.component.borrow().upgrade();
691 break;
692 }
693
694 if !popup.is_menu {
695 break;
696 } else if popup_to_close.is_some() {
697 popup_to_close = Some(popup.popup_id);
699 }
700
701 menubar_item = popup.parent_item.upgrade();
702 }
703
704 let root = match menubar_item {
705 None => item_tree.map(|item_tree| ItemRc::new(item_tree.clone(), 0)),
706 Some(menubar_item) => {
707 assert_ne!(menubar_item.index(), 0, "ContextMenuInternal cannot be root");
708 event.translate(
709 menubar_item
710 .map_to_item_tree(Default::default(), &self.component())
711 .to_vector(),
712 );
713 menubar_item.parent_item(ParentItemTraversalMode::StopAtPopups)
714 }
715 };
716
717 if let Some(root) = root {
718 event.translate(-offset.to_vector());
719 let mut new_input_state = crate::input::process_mouse_input(
720 root,
721 &event,
722 &window_adapter,
723 mouse_input_state,
724 );
725 new_input_state.offset = offset;
726 new_input_state
727 } else {
728 let mut new_input_state = MouseInputState::default();
730 crate::input::send_exit_events(
731 &mouse_input_state,
732 &mut new_input_state,
733 event.position(),
734 &window_adapter,
735 );
736 new_input_state
737 }
738 } else {
739 mouse_input_state
740 };
741
742 if last_top_item != mouse_input_state.top_item_including_delayed() {
743 self.click_state.reset();
744 self.click_state.check_repeat(event, self.ctx.platform().click_interval());
745 }
746
747 self.mouse_input_state.set(mouse_input_state);
748
749 if let Some(popup_id) = popup_to_close {
750 WindowInner::from_pub(root_adapter.window()).close_popup(popup_id);
751 }
752
753 crate::properties::ChangeTracker::run_change_handlers();
754 }
755
756 pub(crate) fn process_delayed_event(&self) {
758 self.mouse_input_state.set(crate::input::process_delayed_event(
759 &self.window_adapter(),
760 self.mouse_input_state.take(),
761 ));
762 }
763
764 pub fn process_key_input(&self, mut event: KeyEvent) -> crate::input::KeyEventResult {
770 if let Some(updated_modifier) = self
771 .modifiers
772 .get()
773 .state_update(event.event_type == KeyEventType::KeyPressed, &event.text)
774 {
775 self.modifiers.set(updated_modifier);
777 }
778
779 event.modifiers = self.modifiers.get().into();
780
781 let mut item = self.focus_item.borrow().clone().upgrade();
782
783 if item.as_ref().is_some_and(|i| !i.is_visible()) {
784 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation));
786 item = None;
787 }
788
789 let item_list = {
790 let mut tmp = Vec::new();
791 let mut item = item.clone();
792
793 while let Some(i) = item {
794 tmp.push(i.clone());
795 item = i.parent_item(ParentItemTraversalMode::StopAtPopups);
796 }
797
798 tmp
799 };
800
801 for i in item_list.iter().rev() {
803 if i.borrow().as_ref().capture_key_event(&event, &self.window_adapter(), &i)
804 == crate::input::KeyEventResult::EventAccepted
805 {
806 crate::properties::ChangeTracker::run_change_handlers();
807 return crate::input::KeyEventResult::EventAccepted;
808 }
809 }
810
811 drop(item_list);
812
813 while let Some(focus_item) = item {
815 if focus_item.borrow().as_ref().key_event(&event, &self.window_adapter(), &focus_item)
816 == crate::input::KeyEventResult::EventAccepted
817 {
818 crate::properties::ChangeTracker::run_change_handlers();
819 return crate::input::KeyEventResult::EventAccepted;
820 }
821 item = focus_item.parent_item(ParentItemTraversalMode::StopAtPopups);
822 }
823
824 let extra_mod = event.modifiers.control || event.modifiers.meta || event.modifiers.alt;
826 if event.text.starts_with(key_codes::Tab)
827 && !event.modifiers.shift
828 && !extra_mod
829 && event.event_type == KeyEventType::KeyPressed
830 {
831 self.focus_next_item();
832 crate::properties::ChangeTracker::run_change_handlers();
833 return crate::input::KeyEventResult::EventAccepted;
834 } else if (event.text.starts_with(key_codes::Backtab)
835 || (event.text.starts_with(key_codes::Tab) && event.modifiers.shift))
836 && event.event_type == KeyEventType::KeyPressed
837 && !extra_mod
838 {
839 self.focus_previous_item();
840 crate::properties::ChangeTracker::run_change_handlers();
841 return crate::input::KeyEventResult::EventAccepted;
842 } else if event.event_type == KeyEventType::KeyPressed
843 && event.text.starts_with(key_codes::Escape)
844 {
845 let mut adapter = self.window_adapter();
849 let item_tree = self.component();
850 let mut a = None;
851 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut a);
852 if let Some(a) = a {
853 adapter = a;
854 }
855 let window = WindowInner::from_pub(adapter.window());
856
857 let close_on_escape = if let Some(popup) = window.active_popups.borrow().last() {
858 popup.close_policy == PopupClosePolicy::CloseOnClick
859 || popup.close_policy == PopupClosePolicy::CloseOnClickOutside
860 } else {
861 false
862 };
863
864 if close_on_escape {
865 window.close_top_popup();
866 }
867 crate::properties::ChangeTracker::run_change_handlers();
868 return crate::input::KeyEventResult::EventAccepted;
869 }
870 crate::properties::ChangeTracker::run_change_handlers();
871 crate::input::KeyEventResult::EventIgnored
872 }
873
874 pub fn set_cursor_blink_binding(&self, prop: &crate::Property<bool>) {
876 let existing_blinker = self.cursor_blinker.borrow().clone();
877
878 let blinker = existing_blinker.upgrade().unwrap_or_else(|| {
879 let new_blinker = TextCursorBlinker::new();
880 *self.cursor_blinker.borrow_mut() =
881 pin_weak::rc::PinWeak::downgrade(new_blinker.clone());
882 new_blinker
883 });
884
885 TextCursorBlinker::set_binding(blinker, prop, self.ctx.platform().cursor_flash_cycle());
886 }
887
888 pub fn set_focus_item(&self, new_focus_item: &ItemRc, set_focus: bool, reason: FocusReason) {
891 if self.prevent_focus_change.get() {
892 return;
893 }
894
895 let popup_wa = self.active_popups.borrow().last().and_then(|p| match &p.location {
896 PopupWindowLocation::TopLevel(wa) => Some(wa.clone()),
897 PopupWindowLocation::ChildWindow(..) => None,
898 });
899 if let Some(popup_wa) = popup_wa {
900 popup_wa.window().0.set_focus_item(new_focus_item, set_focus, reason);
902 return;
903 }
904
905 let current_focus_item = self.focus_item.borrow().clone();
906 if let Some(current_focus_item_rc) = current_focus_item.upgrade() {
907 if set_focus {
908 if current_focus_item_rc == *new_focus_item {
909 return;
911 }
912 } else if current_focus_item_rc != *new_focus_item {
913 return;
915 }
916 }
917
918 let old = self.take_focus_item(&FocusEvent::FocusOut(reason));
919 let new = if set_focus {
920 self.move_focus(new_focus_item.clone(), next_focus_item, reason)
921 } else {
922 None
923 };
924 let window_adapter = self.window_adapter();
925 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
926 window_adapter.handle_focus_change(old, new);
927 }
928 }
929
930 fn take_focus_item(&self, event: &FocusEvent) -> Option<ItemRc> {
934 let focus_item = self.focus_item.take();
935 assert!(matches!(event, FocusEvent::FocusOut(_)));
936
937 if let Some(focus_item_rc) = focus_item.upgrade() {
938 focus_item_rc.borrow().as_ref().focus_event(
939 event,
940 &self.window_adapter(),
941 &focus_item_rc,
942 );
943 Some(focus_item_rc)
944 } else {
945 None
946 }
947 }
948
949 fn publish_focus_item(
953 &self,
954 item: &Option<ItemRc>,
955 reason: FocusReason,
956 ) -> crate::input::FocusEventResult {
957 match item {
958 Some(item) => {
959 *self.focus_item.borrow_mut() = item.downgrade();
960 item.borrow().as_ref().focus_event(
961 &FocusEvent::FocusIn(reason),
962 &self.window_adapter(),
963 item,
964 )
965 }
966 None => {
967 *self.focus_item.borrow_mut() = Default::default();
968 crate::input::FocusEventResult::FocusAccepted }
970 }
971 }
972
973 fn move_focus(
974 &self,
975 start_item: ItemRc,
976 forward: impl Fn(ItemRc) -> ItemRc,
977 reason: FocusReason,
978 ) -> Option<ItemRc> {
979 let mut current_item = start_item;
980 let mut visited = Vec::new();
981
982 loop {
983 if (current_item.is_visible() || reason == FocusReason::Programmatic)
984 && self.publish_focus_item(&Some(current_item.clone()), reason)
985 == crate::input::FocusEventResult::FocusAccepted
986 {
987 return Some(current_item); }
989 visited.push(current_item.clone());
990 current_item = forward(current_item);
991
992 if visited.iter().any(|i| *i == current_item) {
993 return None; }
995 }
996 }
997
998 pub fn focus_next_item(&self) {
1000 let start_item = self
1001 .take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation))
1002 .map(next_focus_item)
1003 .unwrap_or_else(|| {
1004 ItemRc::new(
1005 self.active_popups
1006 .borrow()
1007 .last()
1008 .map_or_else(|| self.component(), |p| p.component.clone()),
1009 0,
1010 )
1011 });
1012 let end_item =
1013 self.move_focus(start_item.clone(), next_focus_item, FocusReason::TabNavigation);
1014 let window_adapter = self.window_adapter();
1015 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1016 window_adapter.handle_focus_change(Some(start_item), end_item);
1017 }
1018 }
1019
1020 pub fn focus_previous_item(&self) {
1022 let start_item = previous_focus_item(
1023 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation)).unwrap_or_else(
1024 || {
1025 ItemRc::new(
1026 self.active_popups
1027 .borrow()
1028 .last()
1029 .map_or_else(|| self.component(), |p| p.component.clone()),
1030 0,
1031 )
1032 },
1033 ),
1034 );
1035 let end_item =
1036 self.move_focus(start_item.clone(), previous_focus_item, FocusReason::TabNavigation);
1037 let window_adapter = self.window_adapter();
1038 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1039 window_adapter.handle_focus_change(Some(start_item), end_item);
1040 }
1041 }
1042
1043 pub fn set_active(&self, have_focus: bool) {
1049 self.pinned_fields.as_ref().project_ref().active.set(have_focus);
1050
1051 let event = if have_focus {
1052 FocusEvent::FocusIn(FocusReason::WindowActivation)
1053 } else {
1054 FocusEvent::FocusOut(FocusReason::WindowActivation)
1055 };
1056
1057 if let Some(focus_item) = self.focus_item.borrow().upgrade() {
1058 focus_item.borrow().as_ref().focus_event(&event, &self.window_adapter(), &focus_item);
1059 }
1060
1061 if !have_focus {
1064 self.modifiers.take();
1065 }
1066 }
1067
1068 pub fn active(&self) -> bool {
1071 self.pinned_fields.as_ref().project_ref().active.get()
1072 }
1073
1074 pub fn update_window_properties(&self) {
1077 let window_adapter = self.window_adapter();
1078
1079 self.pinned_fields
1082 .as_ref()
1083 .project_ref()
1084 .window_properties_tracker
1085 .evaluate_as_dependency_root(|| {
1086 window_adapter.update_window_properties(WindowProperties(self));
1087 });
1088 }
1089
1090 pub fn draw_contents<T>(
1094 &self,
1095 render_components: impl FnOnce(&[(&ItemTreeRc, LogicalPoint)]) -> T,
1096 ) -> Option<T> {
1097 let component_rc = self.try_component()?;
1098 Some(self.pinned_fields.as_ref().project_ref().redraw_tracker.evaluate_as_dependency_root(
1099 || {
1100 if !self
1101 .active_popups
1102 .borrow()
1103 .iter()
1104 .any(|p| matches!(p.location, PopupWindowLocation::ChildWindow(..)))
1105 {
1106 render_components(&[(&component_rc, LogicalPoint::default())])
1107 } else {
1108 let borrow = self.active_popups.borrow();
1109 let mut cmps = Vec::with_capacity(borrow.len() + 1);
1110 cmps.push((&component_rc, LogicalPoint::default()));
1111 for popup in borrow.iter() {
1112 if let PopupWindowLocation::ChildWindow(location) = &popup.location {
1113 cmps.push((&popup.component, *location));
1114 }
1115 }
1116 render_components(&cmps)
1117 }
1118 },
1119 ))
1120 }
1121
1122 pub fn show(&self) -> Result<(), PlatformError> {
1125 if let Some(component) = self.try_component() {
1126 let was_visible = self.strong_component_ref.replace(Some(component)).is_some();
1127 if !was_visible {
1128 *(self.ctx.0.window_count.borrow_mut()) += 1;
1129 }
1130 }
1131
1132 self.update_window_properties();
1133 self.window_adapter().set_visible(true)?;
1134 let size = self.window_adapter().size();
1137 self.set_window_item_geometry(size.to_logical(self.scale_factor()).to_euclid());
1138 self.window_adapter().renderer().resize(size).unwrap();
1139 if let Some(hook) = self.ctx.0.window_shown_hook.borrow_mut().as_mut() {
1140 hook(&self.window_adapter());
1141 }
1142 Ok(())
1143 }
1144
1145 pub fn hide(&self) -> Result<(), PlatformError> {
1147 let result = self.window_adapter().set_visible(false);
1148 let was_visible = self.strong_component_ref.borrow_mut().take().is_some();
1149 if was_visible {
1150 let mut count = self.ctx.0.window_count.borrow_mut();
1151 *count -= 1;
1152 if *count <= 0 {
1153 drop(count);
1154 let _ = self.ctx.event_loop_proxy().and_then(|p| p.quit_event_loop().ok());
1155 }
1156 }
1157 result
1158 }
1159
1160 pub fn color_scheme(&self) -> ColorScheme {
1162 self.window_adapter()
1163 .internal(crate::InternalToken)
1164 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1165 }
1166
1167 pub fn supports_native_menu_bar(&self) -> bool {
1169 self.window_adapter()
1170 .internal(crate::InternalToken)
1171 .is_some_and(|x| x.supports_native_menu_bar())
1172 }
1173
1174 pub fn setup_menubar(&self, menubar: vtable::VRc<MenuVTable>) {
1176 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1177 x.setup_menubar(menubar);
1178 }
1179 }
1180
1181 pub fn show_popup(
1185 &self,
1186 popup_componentrc: &ItemTreeRc,
1187 position: LogicalPosition,
1188 close_policy: PopupClosePolicy,
1189 parent_item: &ItemRc,
1190 is_menu: bool,
1191 ) -> NonZeroU32 {
1192 let position = parent_item
1193 .map_to_window(parent_item.geometry().origin + position.to_euclid().to_vector());
1194 let root_of = |mut item_tree: ItemTreeRc| loop {
1195 if ItemRc::new(item_tree.clone(), 0).downcast::<crate::items::WindowItem>().is_some() {
1196 return item_tree;
1197 }
1198 let mut r = crate::item_tree::ItemWeak::default();
1199 ItemTreeRc::borrow_pin(&item_tree).as_ref().parent_node(&mut r);
1200 match r.upgrade() {
1201 None => return item_tree,
1202 Some(x) => item_tree = x.item_tree().clone(),
1203 }
1204 };
1205 let parent_root_item_tree = root_of(parent_item.item_tree().clone());
1206 let (parent_window_adapter, position) = if let Some(parent_popup) = self
1207 .active_popups
1208 .borrow()
1209 .iter()
1210 .find(|p| ItemTreeRc::ptr_eq(&p.component, &parent_root_item_tree))
1211 {
1212 match &parent_popup.location {
1213 PopupWindowLocation::TopLevel(wa) => (wa.clone(), position),
1214 PopupWindowLocation::ChildWindow(offset) => {
1215 (self.window_adapter(), position + offset.to_vector())
1216 }
1217 }
1218 } else {
1219 (self.window_adapter(), position)
1220 };
1221
1222 let popup_component = ItemTreeRc::borrow_pin(popup_componentrc);
1223 let popup_root = popup_component.as_ref().get_item_ref(0);
1224
1225 let (mut w, mut h) = if let Some(window_item) =
1226 ItemRef::downcast_pin::<crate::items::WindowItem>(popup_root)
1227 {
1228 (window_item.width(), window_item.height())
1229 } else {
1230 (LogicalLength::zero(), LogicalLength::zero())
1231 };
1232
1233 let layout_info_h =
1234 popup_component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
1235 let layout_info_v =
1236 popup_component.as_ref().layout_info(crate::layout::Orientation::Vertical);
1237
1238 if w <= LogicalLength::zero() {
1239 w = LogicalLength::new(layout_info_h.preferred);
1240 }
1241 if h <= LogicalLength::zero() {
1242 h = LogicalLength::new(layout_info_v.preferred);
1243 }
1244 w = w.max(LogicalLength::new(layout_info_h.min)).min(LogicalLength::new(layout_info_h.max));
1245 h = h.max(LogicalLength::new(layout_info_v.min)).min(LogicalLength::new(layout_info_v.max));
1246
1247 let size = crate::lengths::LogicalSize::from_lengths(w, h);
1248
1249 if let Some(window_item) = ItemRef::downcast_pin(popup_root) {
1250 let width_property =
1251 crate::items::WindowItem::FIELD_OFFSETS.width.apply_pin(window_item);
1252 let height_property =
1253 crate::items::WindowItem::FIELD_OFFSETS.height.apply_pin(window_item);
1254 width_property.set(size.width_length());
1255 height_property.set(size.height_length());
1256 };
1257
1258 let popup_id = self.next_popup_id.get();
1259 self.next_popup_id.set(self.next_popup_id.get().checked_add(1).unwrap());
1260
1261 let location = match parent_window_adapter
1262 .internal(crate::InternalToken)
1263 .and_then(|x| x.create_popup(LogicalRect::new(position, size)))
1264 {
1265 None => {
1266 let clip = LogicalRect::new(
1267 LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1268 self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
1269 );
1270 let rect = popup::place_popup(
1271 popup::Placement::Fixed(LogicalRect::new(position, size)),
1272 &Some(clip),
1273 );
1274 self.window_adapter().request_redraw();
1275 PopupWindowLocation::ChildWindow(rect.origin)
1276 }
1277 Some(window_adapter) => {
1278 WindowInner::from_pub(window_adapter.window()).set_component(popup_componentrc);
1279 PopupWindowLocation::TopLevel(window_adapter)
1280 }
1281 };
1282
1283 let focus_item = self
1284 .take_focus_item(&FocusEvent::FocusOut(FocusReason::PopupActivation))
1285 .map(|item| item.downgrade())
1286 .unwrap_or_default();
1287
1288 self.active_popups.borrow_mut().push(PopupWindow {
1289 popup_id,
1290 location,
1291 component: popup_componentrc.clone(),
1292 close_policy,
1293 focus_item_in_parent: focus_item,
1294 parent_item: parent_item.downgrade(),
1295 is_menu,
1296 });
1297
1298 popup_id
1299 }
1300
1301 pub fn show_native_popup_menu(
1307 &self,
1308 context_menu_item: vtable::VRc<MenuVTable>,
1309 position: LogicalPosition,
1310 parent_item: &ItemRc,
1311 ) -> bool {
1312 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1313 let position = parent_item
1314 .map_to_window(parent_item.geometry().origin + position.to_euclid().to_vector());
1315 let position = crate::lengths::logical_position_to_api(position);
1316 x.show_native_popup_menu(context_menu_item, position)
1317 } else {
1318 false
1319 }
1320 }
1321
1322 fn close_popup_impl(&self, current_popup: &PopupWindow) {
1324 match ¤t_popup.location {
1325 PopupWindowLocation::ChildWindow(offset) => {
1326 let popup_region = crate::properties::evaluate_no_tracking(|| {
1328 let popup_component = ItemTreeRc::borrow_pin(¤t_popup.component);
1329 popup_component.as_ref().item_geometry(0)
1330 })
1331 .translate(offset.to_vector());
1332
1333 if !popup_region.is_empty() {
1334 let window_adapter = self.window_adapter();
1335 window_adapter.renderer().mark_dirty_region(popup_region.into());
1336 window_adapter.request_redraw();
1337 }
1338 }
1339 PopupWindowLocation::TopLevel(adapter) => {
1340 let _ = adapter.set_visible(false);
1341 }
1342 }
1343 if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
1344 self.set_focus_item(&focus, true, FocusReason::PopupActivation);
1345 }
1346 }
1347
1348 pub fn close_popup(&self, popup_id: NonZeroU32) {
1350 let mut active_popups = self.active_popups.borrow_mut();
1351 let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
1352
1353 if let Some(popup_index) = maybe_index {
1354 let p = active_popups.remove(popup_index);
1355 drop(active_popups);
1356 self.close_popup_impl(&p);
1357 if p.is_menu {
1358 while self.active_popups.borrow().get(popup_index).is_some_and(|p| p.is_menu) {
1360 let p = self.active_popups.borrow_mut().remove(popup_index);
1361 self.close_popup_impl(&p);
1362 }
1363 }
1364 }
1365 }
1366
1367 pub fn close_all_popups(&self) {
1369 for popup in self.active_popups.take() {
1370 self.close_popup_impl(&popup);
1371 }
1372 }
1373
1374 pub fn close_top_popup(&self) {
1376 let popup = self.active_popups.borrow_mut().pop();
1377 if let Some(popup) = popup {
1378 self.close_popup_impl(&popup);
1379 }
1380 }
1381
1382 pub fn scale_factor(&self) -> f32 {
1384 self.pinned_fields.as_ref().project_ref().scale_factor.get()
1385 }
1386
1387 pub(crate) fn set_scale_factor(&self, factor: f32) {
1389 self.pinned_fields.scale_factor.set(factor)
1390 }
1391
1392 pub fn text_input_focused(&self) -> bool {
1394 self.pinned_fields.as_ref().project_ref().text_input_focused.get()
1395 }
1396
1397 pub fn set_text_input_focused(&self, value: bool) {
1399 self.pinned_fields.text_input_focused.set(value)
1400 }
1401
1402 pub fn is_visible(&self) -> bool {
1404 self.strong_component_ref.borrow().is_some()
1405 }
1406
1407 pub fn window_item_rc(&self) -> Option<ItemRc> {
1410 self.try_component().and_then(|component_rc| {
1411 let item_rc = ItemRc::new(component_rc, 0);
1412 if item_rc.downcast::<crate::items::WindowItem>().is_some() {
1413 Some(item_rc)
1414 } else {
1415 None
1416 }
1417 })
1418 }
1419
1420 pub fn window_item(&self) -> Option<VRcMapped<ItemTreeVTable, crate::items::WindowItem>> {
1422 self.try_component().and_then(|component_rc| {
1423 ItemRc::new(component_rc, 0).downcast::<crate::items::WindowItem>()
1424 })
1425 }
1426
1427 pub(crate) fn set_window_item_geometry(&self, size: crate::lengths::LogicalSize) {
1430 if let Some(component_rc) = self.try_component() {
1431 let component = ItemTreeRc::borrow_pin(&component_rc);
1432 let root_item = component.as_ref().get_item_ref(0);
1433 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1434 {
1435 window_item.width.set(size.width_length());
1436 window_item.height.set(size.height_length());
1437 }
1438 }
1439 }
1440
1441 pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
1443 self.close_requested.set_handler(move |()| callback());
1444 }
1445
1446 pub fn request_close(&self) -> bool {
1450 match self.close_requested.call(&()) {
1451 CloseRequestResponse::HideWindow => true,
1452 CloseRequestResponse::KeepWindowShown => false,
1453 }
1454 }
1455
1456 pub fn is_fullscreen(&self) -> bool {
1458 if let Some(window_item) = self.window_item() {
1459 window_item.as_pin_ref().full_screen()
1460 } else {
1461 false
1462 }
1463 }
1464
1465 pub fn set_fullscreen(&self, enabled: bool) {
1467 if let Some(window_item) = self.window_item() {
1468 window_item.as_pin_ref().full_screen.set(enabled);
1469 self.update_window_properties()
1470 }
1471 }
1472
1473 pub fn is_maximized(&self) -> bool {
1475 self.maximized.get()
1476 }
1477
1478 pub fn set_maximized(&self, maximized: bool) {
1480 self.maximized.set(maximized);
1481 self.update_window_properties()
1482 }
1483
1484 pub fn is_minimized(&self) -> bool {
1486 self.minimized.get()
1487 }
1488
1489 pub fn set_minimized(&self, minimized: bool) {
1491 self.minimized.set(minimized);
1492 self.update_window_properties()
1493 }
1494
1495 pub fn xdg_app_id(&self) -> Option<SharedString> {
1497 self.ctx.xdg_app_id()
1498 }
1499
1500 pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
1502 self.window_adapter_weak.upgrade().unwrap()
1503 }
1504
1505 pub fn from_pub(window: &crate::api::Window) -> &Self {
1507 &window.0
1508 }
1509
1510 pub fn context(&self) -> &crate::SlintContext {
1512 &self.ctx
1513 }
1514}
1515
1516pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
1518
1519#[cfg(feature = "ffi")]
1522pub mod ffi {
1523 #![allow(unsafe_code)]
1524 #![allow(clippy::missing_safety_doc)]
1525 #![allow(missing_docs)]
1526
1527 use super::*;
1528 use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
1529 use crate::graphics::Size;
1530 use crate::graphics::{IntSize, Rgba8Pixel};
1531 use crate::items::WindowItem;
1532 use crate::SharedVector;
1533
1534 #[repr(u8)]
1537 pub enum GraphicsAPI {
1538 NativeOpenGL,
1540 Inaccessible,
1542 }
1543
1544 #[allow(non_camel_case_types)]
1545 type c_void = ();
1546
1547 #[repr(C)]
1549 pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
1550
1551 #[unsafe(no_mangle)]
1553 pub unsafe extern "C" fn slint_windowrc_drop(handle: *mut WindowAdapterRcOpaque) {
1554 assert_eq!(
1555 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1556 core::mem::size_of::<WindowAdapterRcOpaque>()
1557 );
1558 assert_eq!(
1559 core::mem::size_of::<Option<Rc<dyn WindowAdapter>>>(),
1560 core::mem::size_of::<WindowAdapterRcOpaque>()
1561 );
1562 drop(core::ptr::read(handle as *mut Option<Rc<dyn WindowAdapter>>));
1563 }
1564
1565 #[unsafe(no_mangle)]
1567 pub unsafe extern "C" fn slint_windowrc_clone(
1568 source: *const WindowAdapterRcOpaque,
1569 target: *mut WindowAdapterRcOpaque,
1570 ) {
1571 assert_eq!(
1572 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1573 core::mem::size_of::<WindowAdapterRcOpaque>()
1574 );
1575 let window = &*(source as *const Rc<dyn WindowAdapter>);
1576 core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window.clone());
1577 }
1578
1579 #[unsafe(no_mangle)]
1581 pub unsafe extern "C" fn slint_windowrc_show(handle: *const WindowAdapterRcOpaque) {
1582 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1583
1584 window_adapter.window().show().unwrap();
1585 }
1586
1587 #[unsafe(no_mangle)]
1589 pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) {
1590 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1591 window_adapter.window().hide().unwrap();
1592 }
1593
1594 #[unsafe(no_mangle)]
1597 pub unsafe extern "C" fn slint_windowrc_is_visible(
1598 handle: *const WindowAdapterRcOpaque,
1599 ) -> bool {
1600 let window = &*(handle as *const Rc<dyn WindowAdapter>);
1601 window.window().is_visible()
1602 }
1603
1604 #[unsafe(no_mangle)]
1606 pub unsafe extern "C" fn slint_windowrc_get_scale_factor(
1607 handle: *const WindowAdapterRcOpaque,
1608 ) -> f32 {
1609 assert_eq!(
1610 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1611 core::mem::size_of::<WindowAdapterRcOpaque>()
1612 );
1613 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1614 WindowInner::from_pub(window_adapter.window()).scale_factor()
1615 }
1616
1617 #[unsafe(no_mangle)]
1619 pub unsafe extern "C" fn slint_windowrc_set_scale_factor(
1620 handle: *const WindowAdapterRcOpaque,
1621 value: f32,
1622 ) {
1623 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1624 WindowInner::from_pub(window_adapter.window()).set_scale_factor(value)
1625 }
1626
1627 #[unsafe(no_mangle)]
1629 pub unsafe extern "C" fn slint_windowrc_get_text_input_focused(
1630 handle: *const WindowAdapterRcOpaque,
1631 ) -> bool {
1632 assert_eq!(
1633 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1634 core::mem::size_of::<WindowAdapterRcOpaque>()
1635 );
1636 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1637 WindowInner::from_pub(window_adapter.window()).text_input_focused()
1638 }
1639
1640 #[unsafe(no_mangle)]
1642 pub unsafe extern "C" fn slint_windowrc_set_text_input_focused(
1643 handle: *const WindowAdapterRcOpaque,
1644 value: bool,
1645 ) {
1646 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1647 WindowInner::from_pub(window_adapter.window()).set_text_input_focused(value)
1648 }
1649
1650 #[unsafe(no_mangle)]
1652 pub unsafe extern "C" fn slint_windowrc_set_focus_item(
1653 handle: *const WindowAdapterRcOpaque,
1654 focus_item: &ItemRc,
1655 set_focus: bool,
1656 reason: FocusReason,
1657 ) {
1658 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1659 WindowInner::from_pub(window_adapter.window()).set_focus_item(focus_item, set_focus, reason)
1660 }
1661
1662 #[unsafe(no_mangle)]
1664 pub unsafe extern "C" fn slint_windowrc_set_component(
1665 handle: *const WindowAdapterRcOpaque,
1666 component: &ItemTreeRc,
1667 ) {
1668 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1669 WindowInner::from_pub(window_adapter.window()).set_component(component)
1670 }
1671
1672 #[unsafe(no_mangle)]
1674 pub unsafe extern "C" fn slint_windowrc_show_popup(
1675 handle: *const WindowAdapterRcOpaque,
1676 popup: &ItemTreeRc,
1677 position: LogicalPosition,
1678 close_policy: PopupClosePolicy,
1679 parent_item: &ItemRc,
1680 is_menu: bool,
1681 ) -> NonZeroU32 {
1682 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1683 WindowInner::from_pub(window_adapter.window()).show_popup(
1684 popup,
1685 position,
1686 close_policy,
1687 parent_item,
1688 is_menu,
1689 )
1690 }
1691
1692 #[unsafe(no_mangle)]
1694 pub unsafe extern "C" fn slint_windowrc_close_popup(
1695 handle: *const WindowAdapterRcOpaque,
1696 popup_id: NonZeroU32,
1697 ) {
1698 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1699 WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
1700 }
1701
1702 #[unsafe(no_mangle)]
1704 pub unsafe extern "C" fn slint_windowrc_set_rendering_notifier(
1705 handle: *const WindowAdapterRcOpaque,
1706 callback: extern "C" fn(
1707 rendering_state: RenderingState,
1708 graphics_api: GraphicsAPI,
1709 user_data: *mut c_void,
1710 ),
1711 drop_user_data: extern "C" fn(user_data: *mut c_void),
1712 user_data: *mut c_void,
1713 error: *mut SetRenderingNotifierError,
1714 ) -> bool {
1715 struct CNotifier {
1716 callback: extern "C" fn(
1717 rendering_state: RenderingState,
1718 graphics_api: GraphicsAPI,
1719 user_data: *mut c_void,
1720 ),
1721 drop_user_data: extern "C" fn(*mut c_void),
1722 user_data: *mut c_void,
1723 }
1724
1725 impl Drop for CNotifier {
1726 fn drop(&mut self) {
1727 (self.drop_user_data)(self.user_data)
1728 }
1729 }
1730
1731 impl RenderingNotifier for CNotifier {
1732 fn notify(&mut self, state: RenderingState, graphics_api: &crate::api::GraphicsAPI) {
1733 let cpp_graphics_api = match graphics_api {
1734 crate::api::GraphicsAPI::NativeOpenGL { .. } => GraphicsAPI::NativeOpenGL,
1735 crate::api::GraphicsAPI::WebGL { .. } => unreachable!(), #[cfg(feature = "unstable-wgpu-26")]
1737 crate::api::GraphicsAPI::WGPU26 { .. } => GraphicsAPI::Inaccessible, };
1739 (self.callback)(state, cpp_graphics_api, self.user_data)
1740 }
1741 }
1742
1743 let window = &*(handle as *const Rc<dyn WindowAdapter>);
1744 match window.renderer().set_rendering_notifier(Box::new(CNotifier {
1745 callback,
1746 drop_user_data,
1747 user_data,
1748 })) {
1749 Ok(()) => true,
1750 Err(err) => {
1751 *error = err;
1752 false
1753 }
1754 }
1755 }
1756
1757 #[unsafe(no_mangle)]
1759 pub unsafe extern "C" fn slint_windowrc_on_close_requested(
1760 handle: *const WindowAdapterRcOpaque,
1761 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
1762 drop_user_data: extern "C" fn(user_data: *mut c_void),
1763 user_data: *mut c_void,
1764 ) {
1765 struct WithUserData {
1766 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
1767 drop_user_data: extern "C" fn(*mut c_void),
1768 user_data: *mut c_void,
1769 }
1770
1771 impl Drop for WithUserData {
1772 fn drop(&mut self) {
1773 (self.drop_user_data)(self.user_data)
1774 }
1775 }
1776
1777 impl WithUserData {
1778 fn call(&self) -> CloseRequestResponse {
1779 (self.callback)(self.user_data)
1780 }
1781 }
1782
1783 let with_user_data = WithUserData { callback, drop_user_data, user_data };
1784
1785 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1786 window_adapter.window().on_close_requested(move || with_user_data.call());
1787 }
1788
1789 #[unsafe(no_mangle)]
1791 pub unsafe extern "C" fn slint_windowrc_request_redraw(handle: *const WindowAdapterRcOpaque) {
1792 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1793 window_adapter.request_redraw();
1794 }
1795
1796 #[unsafe(no_mangle)]
1799 pub unsafe extern "C" fn slint_windowrc_position(
1800 handle: *const WindowAdapterRcOpaque,
1801 pos: &mut euclid::default::Point2D<i32>,
1802 ) {
1803 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1804 *pos = window_adapter.position().unwrap_or_default().to_euclid()
1805 }
1806
1807 #[unsafe(no_mangle)]
1811 pub unsafe extern "C" fn slint_windowrc_set_physical_position(
1812 handle: *const WindowAdapterRcOpaque,
1813 pos: &euclid::default::Point2D<i32>,
1814 ) {
1815 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1816 window_adapter.set_position(crate::api::PhysicalPosition::new(pos.x, pos.y).into());
1817 }
1818
1819 #[unsafe(no_mangle)]
1823 pub unsafe extern "C" fn slint_windowrc_set_logical_position(
1824 handle: *const WindowAdapterRcOpaque,
1825 pos: &euclid::default::Point2D<f32>,
1826 ) {
1827 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1828 window_adapter.set_position(LogicalPosition::new(pos.x, pos.y).into());
1829 }
1830
1831 #[unsafe(no_mangle)]
1834 pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
1835 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1836 window_adapter.size().to_euclid().cast()
1837 }
1838
1839 #[unsafe(no_mangle)]
1842 pub unsafe extern "C" fn slint_windowrc_set_physical_size(
1843 handle: *const WindowAdapterRcOpaque,
1844 size: &IntSize,
1845 ) {
1846 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1847 window_adapter.window().set_size(crate::api::PhysicalSize::new(size.width, size.height));
1848 }
1849
1850 #[unsafe(no_mangle)]
1853 pub unsafe extern "C" fn slint_windowrc_set_logical_size(
1854 handle: *const WindowAdapterRcOpaque,
1855 size: &Size,
1856 ) {
1857 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1858 window_adapter.window().set_size(crate::api::LogicalSize::new(size.width, size.height));
1859 }
1860
1861 #[unsafe(no_mangle)]
1863 pub unsafe extern "C" fn slint_windowrc_color_scheme(
1864 handle: *const WindowAdapterRcOpaque,
1865 ) -> ColorScheme {
1866 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1867 window_adapter
1868 .internal(crate::InternalToken)
1869 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1870 }
1871
1872 #[unsafe(no_mangle)]
1874 pub unsafe extern "C" fn slint_windowrc_supports_native_menu_bar(
1875 handle: *const WindowAdapterRcOpaque,
1876 ) -> bool {
1877 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1878 window_adapter.internal(crate::InternalToken).is_some_and(|x| x.supports_native_menu_bar())
1879 }
1880
1881 #[unsafe(no_mangle)]
1883 pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
1884 handle: *const WindowAdapterRcOpaque,
1885 menu_instance: &vtable::VRc<MenuVTable>,
1886 ) {
1887 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1888 window_adapter
1889 .internal(crate::InternalToken)
1890 .map(|x| x.setup_menubar(menu_instance.clone()));
1891 }
1892
1893 #[unsafe(no_mangle)]
1895 pub unsafe extern "C" fn slint_windowrc_show_native_popup_menu(
1896 handle: *const WindowAdapterRcOpaque,
1897 context_menu: &vtable::VRc<MenuVTable>,
1898 position: LogicalPosition,
1899 parent_item: &ItemRc,
1900 ) -> bool {
1901 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1902 WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(
1903 context_menu.clone(),
1904 position,
1905 parent_item,
1906 )
1907 }
1908
1909 #[unsafe(no_mangle)]
1911 pub unsafe extern "C" fn slint_windowrc_resolved_default_font_size(
1912 item_tree: &ItemTreeRc,
1913 ) -> f32 {
1914 WindowItem::resolved_default_font_size(item_tree.clone()).get()
1915 }
1916
1917 #[unsafe(no_mangle)]
1919 pub unsafe extern "C" fn slint_windowrc_dispatch_key_event(
1920 handle: *const WindowAdapterRcOpaque,
1921 event_type: crate::input::KeyEventType,
1922 text: &SharedString,
1923 repeat: bool,
1924 ) {
1925 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1926 window_adapter.window().0.process_key_input(crate::items::KeyEvent {
1927 text: text.clone(),
1928 repeat,
1929 event_type,
1930 ..Default::default()
1931 });
1932 }
1933
1934 #[unsafe(no_mangle)]
1936 pub unsafe extern "C" fn slint_windowrc_dispatch_pointer_event(
1937 handle: *const WindowAdapterRcOpaque,
1938 event: &crate::input::MouseEvent,
1939 ) {
1940 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1941 window_adapter.window().0.process_mouse_input(event.clone());
1942 }
1943
1944 #[unsafe(no_mangle)]
1946 pub unsafe extern "C" fn slint_windowrc_dispatch_event(
1947 handle: *const WindowAdapterRcOpaque,
1948 event: &crate::platform::WindowEvent,
1949 ) {
1950 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1951 window_adapter.window().dispatch_event(event.clone());
1952 }
1953
1954 #[unsafe(no_mangle)]
1955 pub unsafe extern "C" fn slint_windowrc_is_fullscreen(
1956 handle: *const WindowAdapterRcOpaque,
1957 ) -> bool {
1958 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1959 window_adapter.window().is_fullscreen()
1960 }
1961
1962 #[unsafe(no_mangle)]
1963 pub unsafe extern "C" fn slint_windowrc_is_minimized(
1964 handle: *const WindowAdapterRcOpaque,
1965 ) -> bool {
1966 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1967 window_adapter.window().is_minimized()
1968 }
1969
1970 #[unsafe(no_mangle)]
1971 pub unsafe extern "C" fn slint_windowrc_is_maximized(
1972 handle: *const WindowAdapterRcOpaque,
1973 ) -> bool {
1974 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1975 window_adapter.window().is_maximized()
1976 }
1977
1978 #[unsafe(no_mangle)]
1979 pub unsafe extern "C" fn slint_windowrc_set_fullscreen(
1980 handle: *const WindowAdapterRcOpaque,
1981 value: bool,
1982 ) {
1983 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1984 window_adapter.window().set_fullscreen(value)
1985 }
1986
1987 #[unsafe(no_mangle)]
1988 pub unsafe extern "C" fn slint_windowrc_set_minimized(
1989 handle: *const WindowAdapterRcOpaque,
1990 value: bool,
1991 ) {
1992 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1993 window_adapter.window().set_minimized(value)
1994 }
1995
1996 #[unsafe(no_mangle)]
1997 pub unsafe extern "C" fn slint_windowrc_set_maximized(
1998 handle: *const WindowAdapterRcOpaque,
1999 value: bool,
2000 ) {
2001 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2002 window_adapter.window().set_maximized(value)
2003 }
2004
2005 #[unsafe(no_mangle)]
2007 pub unsafe extern "C" fn slint_windowrc_take_snapshot(
2008 handle: *const WindowAdapterRcOpaque,
2009 data: &mut SharedVector<Rgba8Pixel>,
2010 width: &mut u32,
2011 height: &mut u32,
2012 ) -> bool {
2013 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2014 if let Ok(snapshot) = window_adapter.window().take_snapshot() {
2015 *data = snapshot.data.clone();
2016 *width = snapshot.width();
2017 *height = snapshot.height();
2018 true
2019 } else {
2020 false
2021 }
2022 }
2023}
2024
2025#[cfg(feature = "software-renderer")]
2026#[test]
2027fn test_empty_window() {
2028 let msw = crate::software_renderer::MinimalSoftwareWindow::new(
2035 crate::software_renderer::RepaintBufferType::NewBuffer,
2036 );
2037 msw.window().request_redraw();
2038 let mut region = None;
2039 let render_called = msw.draw_if_needed(|renderer| {
2040 let mut buffer =
2041 crate::graphics::SharedPixelBuffer::<crate::graphics::Rgb8Pixel>::new(100, 100);
2042 let stride = buffer.width() as usize;
2043 region = Some(renderer.render(buffer.make_mut_slice(), stride));
2044 });
2045 assert!(render_called);
2046 let region = region.unwrap();
2047 assert_eq!(region.bounding_box_size(), PhysicalSize::default());
2048 assert_eq!(region.bounding_box_origin(), PhysicalPosition::default());
2049}