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