1#![warn(missing_docs)]
7use crate::api::{
9 CloseRequestResponse, LogicalPosition, PhysicalPosition, PhysicalSize, PlatformError, Window,
10 WindowPosition, WindowSize,
11};
12use crate::graphics::Color;
13use crate::input::{
14 ClickState, FocusEvent, FocusReason, InternalKeyEvent, KeyEventResult, KeyEventType, Keys,
15 MouseEvent, MouseInputState, PointerEventButton, TextCursorBlinker, TouchPhase, TouchState,
16 key_codes,
17};
18use crate::item_tree::{
19 ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeRefPin, ItemTreeVTable, ItemTreeWeak, ItemWeak,
20 ParentItemTraversalMode,
21};
22use crate::items::{ColorScheme, InputType, ItemRef, MenuEntry, MouseCursor, PopupClosePolicy};
23use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, SizeLengths};
24use crate::menus::MenuVTable;
25use crate::properties::{Property, PropertyTracker};
26use crate::renderer::Renderer;
27use crate::{Callback, SharedString, SharedVector};
28use alloc::boxed::Box;
29use alloc::rc::{Rc, Weak};
30use alloc::vec::Vec;
31use core::cell::{Cell, RefCell};
32use core::num::NonZeroU32;
33use core::pin::Pin;
34use euclid::num::Zero;
35use vtable::{VRc, VRcMapped};
36
37pub mod popup;
38
39fn next_focus_item(item: ItemRc) -> ItemRc {
40 item.next_focus_item()
41}
42
43fn previous_focus_item(item: ItemRc) -> ItemRc {
44 item.previous_focus_item()
45}
46
47pub trait WindowAdapter {
74 fn window(&self) -> &Window;
76
77 fn set_visible(&self, _visible: bool) -> Result<(), PlatformError> {
79 Ok(())
80 }
81
82 fn position(&self) -> Option<PhysicalPosition> {
89 None
90 }
91 fn set_position(&self, _position: WindowPosition) {}
98
99 fn set_size(&self, _size: WindowSize) {}
110
111 fn size(&self) -> PhysicalSize;
113
114 fn request_redraw(&self) {}
126
127 fn renderer(&self) -> &dyn Renderer;
132
133 fn update_window_properties(&self, _properties: WindowProperties<'_>) {}
139
140 #[doc(hidden)]
141 fn internal(&self, _: crate::InternalToken) -> Option<&dyn WindowAdapterInternal> {
142 None
143 }
144
145 #[cfg(feature = "raw-window-handle-06")]
147 fn window_handle_06(
148 &self,
149 ) -> Result<raw_window_handle_06::WindowHandle<'_>, raw_window_handle_06::HandleError> {
150 Err(raw_window_handle_06::HandleError::NotSupported)
151 }
152
153 #[cfg(feature = "raw-window-handle-06")]
155 fn display_handle_06(
156 &self,
157 ) -> Result<raw_window_handle_06::DisplayHandle<'_>, raw_window_handle_06::HandleError> {
158 Err(raw_window_handle_06::HandleError::NotSupported)
159 }
160}
161
162#[doc(hidden)]
167pub trait WindowAdapterInternal: core::any::Any {
168 fn register_item_tree(&self, _: ItemTreeRefPin) {}
170
171 fn unregister_item_tree(
174 &self,
175 _component: ItemTreeRef,
176 _items: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
177 ) {
178 }
179
180 fn create_popup(&self, _geometry: LogicalRect) -> Option<Rc<dyn WindowAdapter>> {
187 None
188 }
189
190 fn set_mouse_cursor(&self, _cursor: MouseCursor) {}
193
194 fn input_method_request(&self, _: InputMethodRequest) {}
196
197 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {}
200
201 fn color_scheme(&self) -> ColorScheme {
203 ColorScheme::Unknown
204 }
205
206 fn accent_color(&self) -> Color {
208 Color::default()
209 }
210
211 fn supports_native_menu_bar(&self) -> bool {
213 false
214 }
215
216 fn setup_menubar(&self, _menubar: vtable::VRc<MenuVTable>) {}
217
218 fn show_native_popup_menu(
219 &self,
220 _context_menu_item: vtable::VRc<MenuVTable>,
221 _position: LogicalPosition,
222 ) -> bool {
223 false
224 }
225
226 #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
228 fn window_handle_06_rc(
229 &self,
230 ) -> Result<
231 std::sync::Arc<dyn raw_window_handle_06::HasWindowHandle>,
232 raw_window_handle_06::HandleError,
233 > {
234 Err(raw_window_handle_06::HandleError::NotSupported)
235 }
236
237 #[cfg(all(feature = "std", feature = "raw-window-handle-06"))]
239 fn display_handle_06_rc(
240 &self,
241 ) -> Result<
242 std::sync::Arc<dyn raw_window_handle_06::HasDisplayHandle>,
243 raw_window_handle_06::HandleError,
244 > {
245 Err(raw_window_handle_06::HandleError::NotSupported)
246 }
247
248 fn bring_to_front(&self) -> Result<(), PlatformError> {
250 Ok(())
251 }
252
253 fn safe_area_inset(&self) -> crate::lengths::PhysicalEdges {
256 Default::default()
257 }
258}
259
260#[non_exhaustive]
263#[derive(Debug, Clone)]
264pub enum InputMethodRequest {
265 Enable(InputMethodProperties),
267 Update(InputMethodProperties),
269 Disable,
271}
272
273#[non_exhaustive]
275#[derive(Clone, Default, Debug)]
276pub struct InputMethodProperties {
277 pub text: SharedString,
281 pub cursor_position: usize,
283 pub anchor_position: Option<usize>,
286 pub preedit_text: SharedString,
290 pub preedit_offset: usize,
292 pub cursor_rect_origin: LogicalPosition,
294 pub cursor_rect_size: crate::api::LogicalSize,
296 pub anchor_point: LogicalPosition,
298 pub input_type: InputType,
300 pub clip_rect: Option<LogicalRect>,
302}
303
304#[non_exhaustive]
306#[derive(Copy, Clone, Debug, PartialEq, Default)]
307pub struct LayoutConstraints {
308 pub min: Option<crate::api::LogicalSize>,
310 pub max: Option<crate::api::LogicalSize>,
312 pub preferred: crate::api::LogicalSize,
314}
315
316pub struct WindowProperties<'a>(&'a WindowInner);
319
320impl WindowProperties<'_> {
321 pub fn title(&self) -> SharedString {
323 self.0.window_item().map(|w| w.as_pin_ref().title()).unwrap_or_default()
324 }
325
326 pub fn background(&self) -> crate::Brush {
328 self.0
329 .window_item()
330 .map(|w: VRcMapped<ItemTreeVTable, crate::items::WindowItem>| {
331 w.as_pin_ref().background()
332 })
333 .unwrap_or_default()
334 }
335
336 pub fn layout_constraints(&self) -> LayoutConstraints {
338 let component = self.0.component();
339 let component = ItemTreeRc::borrow_pin(&component);
340 let h = component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
341 let v = component.as_ref().layout_info(crate::layout::Orientation::Vertical);
342 let (min, max) = crate::layout::min_max_size_for_layout_constraints(h, v);
343 LayoutConstraints {
344 min,
345 max,
346 preferred: crate::api::LogicalSize::new(
347 h.preferred_bounded() as f32,
348 v.preferred_bounded() as f32,
349 ),
350 }
351 }
352
353 #[deprecated(note = "Please use `is_fullscreen` instead")]
355 pub fn fullscreen(&self) -> bool {
356 self.is_fullscreen()
357 }
358
359 pub fn is_fullscreen(&self) -> bool {
361 self.0.is_fullscreen()
362 }
363
364 pub fn is_maximized(&self) -> bool {
366 self.0.maximized.get()
367 }
368
369 pub fn is_minimized(&self) -> bool {
371 self.0.minimized.get()
372 }
373}
374
375struct WindowPropertiesTracker {
376 window_adapter_weak: Weak<dyn WindowAdapter>,
377}
378
379impl crate::properties::PropertyDirtyHandler for WindowPropertiesTracker {
380 fn notify(self: Pin<&Self>) {
381 let win = self.window_adapter_weak.clone();
382 crate::timers::Timer::single_shot(Default::default(), move || {
383 if let Some(window_adapter) = win.upgrade() {
384 WindowInner::from_pub(window_adapter.window()).update_window_properties();
385 };
386 })
387 }
388}
389
390struct WindowRedrawTracker {
391 window_adapter_weak: Weak<dyn WindowAdapter>,
392}
393
394impl crate::properties::PropertyDirtyHandler for WindowRedrawTracker {
395 fn notify(self: Pin<&Self>) {
396 if let Some(window_adapter) = self.window_adapter_weak.upgrade() {
397 window_adapter.request_redraw();
398 };
399 }
400}
401
402#[derive(Clone)]
404pub enum PopupWindowLocation {
405 TopLevel(Rc<dyn WindowAdapter>),
407 ChildWindow(LogicalPoint),
409}
410
411#[derive(Clone)]
414pub struct PopupWindow {
415 pub popup_id: NonZeroU32,
417 pub location: PopupWindowLocation,
419 pub component: ItemTreeRc,
421 pub close_policy: PopupClosePolicy,
423 focus_item_in_parent: ItemWeak,
425 pub parent_item: ItemWeak,
427 is_menu: bool,
430}
431
432#[pin_project::pin_project]
433struct WindowPinnedFields {
434 #[pin]
435 redraw_tracker: PropertyTracker<false, WindowRedrawTracker>,
436 #[pin]
438 window_properties_tracker: PropertyTracker<true, WindowPropertiesTracker>,
439 #[pin]
440 scale_factor: Property<f32>,
441 #[pin]
442 active: Property<bool>,
443 #[pin]
444 text_input_focused: Property<bool>,
445 #[pin]
446 menubar_shortcuts: Property<SharedVector<MenuEntry>>,
447}
448
449pub struct WindowInner {
451 window_adapter_weak: Weak<dyn WindowAdapter>,
452 component: RefCell<ItemTreeWeak>,
453 strong_component_ref: RefCell<Option<ItemTreeRc>>,
455 mouse_input_state: Cell<MouseInputState>,
456 touch_state: RefCell<TouchState>,
457
458 pub focus_item: RefCell<crate::item_tree::ItemWeak>,
460 pub(crate) last_ime_text: RefCell<SharedString>,
462 pub(crate) prevent_focus_change: Cell<bool>,
467 cursor_blinker: RefCell<pin_weak::rc::PinWeak<crate::input::TextCursorBlinker>>,
468
469 pinned_fields: Pin<Box<WindowPinnedFields>>,
470 maximized: Cell<bool>,
471 minimized: Cell<bool>,
472
473 menubar: RefCell<Option<vtable::VWeak<MenuVTable>>>,
474
475 active_popups: RefCell<Vec<PopupWindow>>,
477 next_popup_id: Cell<NonZeroU32>,
478 had_popup_on_press: Cell<bool>,
479 close_requested: Callback<(), CloseRequestResponse>,
480 click_state: ClickState,
481 ctx: core::cell::OnceCell<crate::SlintContext>,
482}
483
484impl Drop for WindowInner {
485 fn drop(&mut self) {
486 if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() {
487 existing_blinker.stop();
488 }
489 }
490}
491
492impl WindowInner {
493 pub fn new(window_adapter_weak: Weak<dyn WindowAdapter>) -> Self {
495 #![allow(unused_mut)]
496
497 let mut window_properties_tracker =
498 PropertyTracker::new_with_dirty_handler(WindowPropertiesTracker {
499 window_adapter_weak: window_adapter_weak.clone(),
500 });
501
502 let mut redraw_tracker = PropertyTracker::new_with_dirty_handler(WindowRedrawTracker {
503 window_adapter_weak: window_adapter_weak.clone(),
504 });
505
506 #[cfg(slint_debug_property)]
507 {
508 window_properties_tracker
509 .set_debug_name("i_slint_core::Window::window_properties_tracker".into());
510 redraw_tracker.set_debug_name("i_slint_core::Window::redraw_tracker".into());
511 }
512
513 Self {
514 window_adapter_weak,
515 component: Default::default(),
516 strong_component_ref: Default::default(),
517 mouse_input_state: Default::default(),
518 touch_state: Default::default(),
519 pinned_fields: Box::pin(WindowPinnedFields {
520 redraw_tracker,
521 window_properties_tracker,
522 scale_factor: Property::new_named(1., "i_slint_core::Window::scale_factor"),
523 active: Property::new_named(false, "i_slint_core::Window::active"),
524 text_input_focused: Property::new_named(
525 false,
526 "i_slint_core::Window::text_input_focused",
527 ),
528 menubar_shortcuts: Property::new_named(
529 SharedVector::default(),
530 "i_slint_core::Window::menubar_shortcuts",
531 ),
532 }),
533 maximized: Cell::new(false),
534 minimized: Cell::new(false),
535 focus_item: Default::default(),
536 last_ime_text: Default::default(),
537 cursor_blinker: Default::default(),
538 active_popups: Default::default(),
539 next_popup_id: Cell::new(NonZeroU32::MIN),
540 had_popup_on_press: Default::default(),
541 close_requested: Default::default(),
542 click_state: ClickState::default(),
543 prevent_focus_change: Default::default(),
544 ctx: Default::default(),
545 menubar: Default::default(),
546 }
547 }
548
549 pub fn set_component(&self, component: &ItemTreeRc) {
552 self.close_all_popups();
553 self.focus_item.replace(Default::default());
554 self.mouse_input_state.replace(Default::default());
555 self.touch_state.replace(Default::default());
556 self.component.replace(ItemTreeRc::downgrade(component));
557 self.pinned_fields.window_properties_tracker.set_dirty(); let window_adapter = self.window_adapter();
559 window_adapter.renderer().set_window_adapter(&window_adapter);
560 let scale_factor = self.scale_factor();
561 self.set_window_item_geometry(window_adapter.size().to_logical(scale_factor).to_euclid());
562 let inset = window_adapter
563 .internal(crate::InternalToken)
564 .map(|internal| internal.safe_area_inset())
565 .unwrap_or_default();
566 self.set_window_item_safe_area(inset.to_logical(scale_factor));
567 window_adapter.request_redraw();
568 let weak = Rc::downgrade(&window_adapter);
569 crate::timers::Timer::single_shot(Default::default(), move || {
570 if let Some(window_adapter) = weak.upgrade() {
571 WindowInner::from_pub(window_adapter.window()).update_window_properties();
572 }
573 })
574 }
575
576 pub fn component(&self) -> ItemTreeRc {
579 self.component.borrow().upgrade().unwrap()
580 }
581
582 pub fn try_component(&self) -> Option<ItemTreeRc> {
584 self.component.borrow().upgrade()
585 }
586
587 pub fn active_popups(&self) -> core::cell::Ref<'_, [PopupWindow]> {
589 core::cell::Ref::map(self.active_popups.borrow(), |v| v.as_slice())
590 }
591
592 pub fn process_mouse_input(&self, mut event: MouseEvent) {
595 crate::animations::update_animations();
596
597 let Some(item_tree) = self.try_component() else { return };
598
599 event = self.click_state.check_repeat(event, self.context().platform().click_interval());
601
602 let window_adapter = self.window_adapter();
603 let mut mouse_input_state = self.mouse_input_state.take();
604
605 let old_cursor = core::mem::replace(&mut mouse_input_state.cursor, MouseCursor::Default);
606
607 if let Some(mut drop_event) = mouse_input_state.drag_data.clone() {
608 match &event {
609 MouseEvent::Released { position, button: PointerEventButton::Left, .. } => {
610 drop_event.position = crate::lengths::logical_position_to_api(*position);
611 event = MouseEvent::Drop(drop_event);
612 mouse_input_state.drag_data = None;
613 }
614 MouseEvent::Moved { position, .. } => {
615 drop_event.position = crate::lengths::logical_position_to_api(*position);
616 mouse_input_state.cursor = MouseCursor::NoDrop;
617 event = MouseEvent::DragMove(drop_event);
618 }
619 MouseEvent::Exit => {
620 mouse_input_state.drag_data = None;
621 }
622 _ => {}
623 }
624 }
625
626 let pressed_event = matches!(event, MouseEvent::Pressed { .. });
627 let released_event = matches!(event, MouseEvent::Released { .. });
628
629 let last_top_item = mouse_input_state.top_item_including_delayed();
630 if released_event {
631 mouse_input_state =
632 crate::input::process_delayed_event(&window_adapter, mouse_input_state);
633 }
634
635 let mut root_adapter = None;
637 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut root_adapter);
638 let root_adapter = root_adapter.unwrap_or_else(|| window_adapter.clone());
639 let active_popups = &WindowInner::from_pub(root_adapter.window()).active_popups;
640 let native_popup_index = active_popups.borrow().iter().position(|p| {
641 if let PopupWindowLocation::TopLevel(wa) = &p.location {
642 Rc::ptr_eq(wa, &window_adapter)
643 } else {
644 false
645 }
646 });
647
648 if pressed_event {
649 self.had_popup_on_press.set(!active_popups.borrow().is_empty());
650 }
651
652 let mut popup_to_close = active_popups.borrow().last().and_then(|popup| {
653 let mouse_inside_popup = || {
654 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
655 event.position().is_none_or(|pos| {
656 ItemTreeRc::borrow_pin(&popup.component)
657 .as_ref()
658 .item_geometry(0)
659 .contains(pos - coordinates.to_vector())
660 })
661 } else {
662 native_popup_index.is_some_and(|idx| idx == active_popups.borrow().len() - 1)
663 && event.position().is_none_or(|pos| {
664 ItemTreeRc::borrow_pin(&item_tree)
665 .as_ref()
666 .item_geometry(0)
667 .contains(pos)
668 })
669 }
670 };
671 match popup.close_policy {
672 PopupClosePolicy::CloseOnClick => {
673 let mouse_inside_popup = mouse_inside_popup();
674 (mouse_inside_popup && released_event && self.had_popup_on_press.get())
675 || (!mouse_inside_popup && pressed_event)
676 }
677 PopupClosePolicy::CloseOnClickOutside => !mouse_inside_popup() && pressed_event,
678 PopupClosePolicy::NoAutoClose => false,
679 }
680 .then_some(popup.popup_id)
681 });
682
683 mouse_input_state = if let Some(mut event) =
684 crate::input::handle_mouse_grab(&event, &window_adapter, &mut mouse_input_state)
685 {
686 let mut item_tree = self.component.borrow().upgrade();
687 let mut offset = LogicalPoint::default();
688 let mut menubar_item = None;
689 for (idx, popup) in active_popups.borrow().iter().enumerate().rev() {
690 item_tree = None;
691 menubar_item = None;
692 if let PopupWindowLocation::ChildWindow(coordinates) = &popup.location {
693 let geom = ItemTreeRc::borrow_pin(&popup.component).as_ref().item_geometry(0);
694 let mouse_inside_popup = event
695 .position()
696 .is_none_or(|pos| geom.contains(pos - coordinates.to_vector()));
697 if mouse_inside_popup {
698 item_tree = Some(popup.component.clone());
699 offset = *coordinates;
700 break;
701 }
702 } else if native_popup_index.is_some_and(|i| i == idx) {
703 item_tree = self.component.borrow().upgrade();
704 break;
705 }
706
707 if !popup.is_menu {
708 break;
709 } else if popup_to_close.is_some() {
710 popup_to_close = Some(popup.popup_id);
712 }
713
714 menubar_item = popup.parent_item.upgrade();
715 }
716
717 let root = match menubar_item {
718 None => item_tree.map(|item_tree| ItemRc::new_root(item_tree.clone())),
719 Some(menubar_item) => {
720 event.translate(
721 menubar_item
722 .map_to_item_tree(Default::default(), &self.component())
723 .to_vector(),
724 );
725 menubar_item.parent_item(ParentItemTraversalMode::StopAtPopups)
726 }
727 };
728
729 if let Some(root) = root {
730 event.translate(-offset.to_vector());
731 let mut new_input_state = crate::input::process_mouse_input(
732 root,
733 &event,
734 &window_adapter,
735 mouse_input_state,
736 );
737 new_input_state.offset = offset;
738 new_input_state
739 } else {
740 let mut new_input_state = MouseInputState::default();
742 crate::input::send_exit_events(
743 &mouse_input_state,
744 &mut new_input_state,
745 event.position(),
746 &window_adapter,
747 );
748 new_input_state
749 }
750 } else {
751 mouse_input_state
752 };
753
754 if last_top_item != mouse_input_state.top_item_including_delayed() {
755 self.click_state.reset();
756 self.click_state.check_repeat(event, self.context().platform().click_interval());
757 }
758
759 if old_cursor != mouse_input_state.cursor
760 && let Some(window_adapter) = window_adapter.internal(crate::InternalToken)
761 {
762 window_adapter.set_mouse_cursor(mouse_input_state.cursor);
763 }
764
765 self.mouse_input_state.set(mouse_input_state);
766
767 if let Some(popup_id) = popup_to_close {
768 WindowInner::from_pub(root_adapter.window()).close_popup(popup_id);
769 }
770
771 crate::properties::ChangeTracker::run_change_handlers();
772 }
773
774 pub fn process_touch_input(&self, id: u64, position: LogicalPoint, phase: TouchPhase) {
782 let events = self.touch_state.borrow_mut().process(id, position, phase);
783 for event in events.into_iter() {
784 self.process_mouse_input(event);
785 }
786 }
787
788 pub(crate) fn process_delayed_event(&self) {
790 self.mouse_input_state.set(crate::input::process_delayed_event(
791 &self.window_adapter(),
792 self.mouse_input_state.take(),
793 ));
794 }
795
796 pub fn process_key_input(
802 &self,
803 mut internal_key_event: InternalKeyEvent,
804 ) -> crate::input::KeyEventResult {
805 #[cfg(feature = "shared-parley")]
810 {
811 let normalizer = icu_normalizer::ComposingNormalizer::new_nfc();
812 let normalized = normalizer.normalize(&internal_key_event.key_event.text);
813 if let alloc::borrow::Cow::Owned(normalized) = normalized {
816 internal_key_event.key_event.text = normalized.into();
817 }
818 }
819
820 if let Some(updated_modifier) = self.context().0.modifiers.get().state_update(
821 internal_key_event.event_type == KeyEventType::KeyPressed,
822 &internal_key_event.key_event.text,
823 ) {
824 self.context().0.modifiers.set(updated_modifier);
826 }
827
828 internal_key_event.key_event.modifiers =
829 self.context().0.modifiers.get().modifiers_for(&internal_key_event);
830
831 if self.process_menubar_shortcuts(&internal_key_event) == KeyEventResult::EventAccepted {
835 crate::properties::ChangeTracker::run_change_handlers();
836 return crate::input::KeyEventResult::EventAccepted;
837 }
838
839 let mut item = self.focus_item.borrow().clone().upgrade();
840
841 if item.as_ref().is_some_and(|i| !i.is_visible()) {
842 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation));
844 item = None;
845 }
846
847 let item_list = {
848 let mut tmp = Vec::new();
849 let mut item = item.clone();
850
851 while let Some(i) = item {
852 tmp.push(i.clone());
853 item = i.parent_item(ParentItemTraversalMode::StopAtPopups);
854 }
855
856 tmp
857 };
858
859 for i in item_list.iter().rev() {
861 if i.borrow().as_ref().capture_key_event(&internal_key_event, &self.window_adapter(), i)
862 == crate::input::KeyEventResult::EventAccepted
863 {
864 crate::properties::ChangeTracker::run_change_handlers();
865 return crate::input::KeyEventResult::EventAccepted;
866 }
867 }
868
869 drop(item_list);
870
871 while let Some(focus_item) = item {
873 if focus_item.borrow().as_ref().key_event(
874 &internal_key_event,
875 &self.window_adapter(),
876 &focus_item,
877 ) == crate::input::KeyEventResult::EventAccepted
878 {
879 crate::properties::ChangeTracker::run_change_handlers();
880 return crate::input::KeyEventResult::EventAccepted;
881 }
882 item = focus_item.parent_item(ParentItemTraversalMode::StopAtPopups);
883 }
884
885 let extra_mod = internal_key_event.key_event.modifiers.control
887 || internal_key_event.key_event.modifiers.meta
888 || internal_key_event.key_event.modifiers.alt;
889 if internal_key_event.key_event.text.starts_with(key_codes::Tab)
890 && !internal_key_event.key_event.modifiers.shift
891 && !extra_mod
892 && internal_key_event.event_type == KeyEventType::KeyPressed
893 {
894 self.focus_next_item();
895 crate::properties::ChangeTracker::run_change_handlers();
896 return crate::input::KeyEventResult::EventAccepted;
897 } else if (internal_key_event.key_event.text.starts_with(key_codes::Backtab)
898 || (internal_key_event.key_event.text.starts_with(key_codes::Tab)
899 && internal_key_event.key_event.modifiers.shift))
900 && internal_key_event.event_type == KeyEventType::KeyPressed
901 && !extra_mod
902 {
903 self.focus_previous_item();
904 crate::properties::ChangeTracker::run_change_handlers();
905 return crate::input::KeyEventResult::EventAccepted;
906 } else if internal_key_event.event_type == KeyEventType::KeyPressed
907 && internal_key_event.key_event.text.starts_with(key_codes::Escape)
908 {
909 let mut adapter = self.window_adapter();
913 let item_tree = self.component();
914 let mut a = None;
915 ItemTreeRc::borrow_pin(&item_tree).as_ref().window_adapter(false, &mut a);
916 if let Some(a) = a {
917 adapter = a;
918 }
919 let window = WindowInner::from_pub(adapter.window());
920
921 let close_on_escape = if let Some(popup) = window.active_popups.borrow().last() {
922 popup.close_policy == PopupClosePolicy::CloseOnClick
923 || popup.close_policy == PopupClosePolicy::CloseOnClickOutside
924 } else {
925 false
926 };
927
928 if close_on_escape {
929 window.close_top_popup();
930 }
931 crate::properties::ChangeTracker::run_change_handlers();
932 return crate::input::KeyEventResult::EventAccepted;
933 }
934
935 crate::properties::ChangeTracker::run_change_handlers();
936 crate::input::KeyEventResult::EventIgnored
937 }
938
939 fn process_menubar_shortcuts(
940 &self,
941 internal_key_event: &InternalKeyEvent,
942 ) -> crate::input::KeyEventResult {
943 let event_type = internal_key_event.event_type;
944 let menubar = self.menubar.borrow().as_ref().and_then(vtable::VWeak::upgrade);
945
946 if (event_type == KeyEventType::KeyReleased || event_type == KeyEventType::KeyPressed)
947 && let Some(menubar) = menubar
948 {
949 let shortcuts = self.pinned_fields.as_ref().project_ref().menubar_shortcuts.get();
950 let mut matches = shortcuts
951 .into_iter()
952 .filter(|entry| entry.shortcut.matches(&internal_key_event.key_event));
953 if let Some(entry) = matches.next() {
954 if internal_key_event.event_type == KeyEventType::KeyPressed {
955 VRc::borrow(&menubar).activate(&entry);
956 if matches.next().is_some() {
957 crate::debug_log!(
958 "Warning: Ambiguous menubar shortcut: {}",
959 entry.shortcut
960 );
961 }
962 }
963 return crate::input::KeyEventResult::EventAccepted;
964 }
965 }
966 crate::input::KeyEventResult::EventIgnored
967 }
968
969 pub fn set_cursor_blink_binding(&self, prop: &crate::Property<bool>) {
971 let existing_blinker = self.cursor_blinker.borrow().clone();
972
973 let blinker = existing_blinker.upgrade().unwrap_or_else(|| {
974 let new_blinker = TextCursorBlinker::new();
975 *self.cursor_blinker.borrow_mut() =
976 pin_weak::rc::PinWeak::downgrade(new_blinker.clone());
977 new_blinker
978 });
979
980 TextCursorBlinker::set_binding(
981 blinker,
982 prop,
983 self.context().platform().cursor_flash_cycle(),
984 );
985 }
986
987 pub fn set_focus_item(&self, new_focus_item: &ItemRc, set_focus: bool, reason: FocusReason) {
990 if self.prevent_focus_change.get() {
991 return;
992 }
993
994 let popup_wa = self.active_popups.borrow().last().and_then(|p| match &p.location {
995 PopupWindowLocation::TopLevel(wa) => Some(wa.clone()),
996 PopupWindowLocation::ChildWindow(..) => None,
997 });
998 if let Some(popup_wa) = popup_wa {
999 popup_wa.window().0.set_focus_item(new_focus_item, set_focus, reason);
1001 return;
1002 }
1003
1004 let current_focus_item = self.focus_item.borrow().clone();
1005 if let Some(current_focus_item_rc) = current_focus_item.upgrade() {
1006 if set_focus {
1007 if current_focus_item_rc == *new_focus_item {
1008 return;
1010 }
1011 } else if current_focus_item_rc != *new_focus_item {
1012 return;
1014 }
1015 }
1016
1017 let old = self.take_focus_item(&FocusEvent::FocusOut(reason));
1018 let new = if set_focus {
1019 self.move_focus(new_focus_item.clone(), next_focus_item, reason)
1020 } else {
1021 None
1022 };
1023 let window_adapter = self.window_adapter();
1024 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1025 window_adapter.handle_focus_change(old, new);
1026 }
1027 }
1028
1029 fn take_focus_item(&self, event: &FocusEvent) -> Option<ItemRc> {
1033 let focus_item = self.focus_item.take();
1034 assert!(matches!(event, FocusEvent::FocusOut(_)));
1035
1036 if let Some(focus_item_rc) = focus_item.upgrade() {
1037 focus_item_rc.borrow().as_ref().focus_event(
1038 event,
1039 &self.window_adapter(),
1040 &focus_item_rc,
1041 );
1042 Some(focus_item_rc)
1043 } else {
1044 None
1045 }
1046 }
1047
1048 fn publish_focus_item(
1052 &self,
1053 item: &Option<ItemRc>,
1054 reason: FocusReason,
1055 ) -> crate::input::FocusEventResult {
1056 match item {
1057 Some(item) => {
1058 *self.focus_item.borrow_mut() = item.downgrade();
1059 let result = item.borrow().as_ref().focus_event(
1060 &FocusEvent::FocusIn(reason),
1061 &self.window_adapter(),
1062 item,
1063 );
1064 if result == crate::input::FocusEventResult::FocusAccepted {
1066 item.try_scroll_into_visible();
1067 }
1068
1069 result
1070 }
1071 None => {
1072 *self.focus_item.borrow_mut() = Default::default();
1073 crate::input::FocusEventResult::FocusAccepted }
1075 }
1076 }
1077
1078 fn move_focus(
1079 &self,
1080 start_item: ItemRc,
1081 forward: impl Fn(ItemRc) -> ItemRc,
1082 reason: FocusReason,
1083 ) -> Option<ItemRc> {
1084 let mut current_item = start_item;
1085 let mut visited = Vec::new();
1086
1087 loop {
1088 let can_receive_focus = match reason {
1089 FocusReason::Programmatic => true,
1090 FocusReason::TabNavigation => current_item.is_visible_or_clipped_by_flickable(),
1091 _ => current_item.is_visible(),
1092 };
1093 if can_receive_focus
1094 && self.publish_focus_item(&Some(current_item.clone()), reason)
1095 == crate::input::FocusEventResult::FocusAccepted
1096 {
1097 return Some(current_item); }
1099 visited.push(current_item.clone());
1100 current_item = forward(current_item);
1101
1102 if visited.contains(¤t_item) {
1103 return None; }
1105 }
1106 }
1107
1108 pub fn focus_next_item(&self) {
1110 let start_item = self
1111 .take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation))
1112 .map(next_focus_item)
1113 .unwrap_or_else(|| {
1114 ItemRc::new(
1115 self.active_popups
1116 .borrow()
1117 .last()
1118 .map_or_else(|| self.component(), |p| p.component.clone()),
1119 0,
1120 )
1121 });
1122 let end_item =
1123 self.move_focus(start_item.clone(), next_focus_item, FocusReason::TabNavigation);
1124 let window_adapter = self.window_adapter();
1125 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1126 window_adapter.handle_focus_change(Some(start_item), end_item);
1127 }
1128 }
1129
1130 pub fn focus_previous_item(&self) {
1132 let start_item = previous_focus_item(
1133 self.take_focus_item(&FocusEvent::FocusOut(FocusReason::TabNavigation)).unwrap_or_else(
1134 || {
1135 ItemRc::new(
1136 self.active_popups
1137 .borrow()
1138 .last()
1139 .map_or_else(|| self.component(), |p| p.component.clone()),
1140 0,
1141 )
1142 },
1143 ),
1144 );
1145 let end_item =
1146 self.move_focus(start_item.clone(), previous_focus_item, FocusReason::TabNavigation);
1147 let window_adapter = self.window_adapter();
1148 if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1149 window_adapter.handle_focus_change(Some(start_item), end_item);
1150 }
1151 }
1152
1153 pub fn set_active(&self, have_focus: bool) {
1159 self.pinned_fields.as_ref().project_ref().active.set(have_focus);
1160
1161 let event = if have_focus {
1162 FocusEvent::FocusIn(FocusReason::WindowActivation)
1163 } else {
1164 FocusEvent::FocusOut(FocusReason::WindowActivation)
1165 };
1166
1167 if let Some(focus_item) = self.focus_item.borrow().upgrade() {
1168 focus_item.borrow().as_ref().focus_event(&event, &self.window_adapter(), &focus_item);
1169 }
1170
1171 if !have_focus {
1174 self.context().0.modifiers.take();
1175 }
1176 }
1177
1178 pub fn active(&self) -> bool {
1181 self.pinned_fields.as_ref().project_ref().active.get()
1182 }
1183
1184 pub fn update_window_properties(&self) {
1187 let window_adapter = self.window_adapter();
1188
1189 self.pinned_fields
1192 .as_ref()
1193 .project_ref()
1194 .window_properties_tracker
1195 .evaluate_as_dependency_root(|| {
1196 window_adapter.update_window_properties(WindowProperties(self));
1197 });
1198 }
1199
1200 pub fn draw_contents<T>(
1204 &self,
1205 render_components: impl FnOnce(&[(ItemTreeWeak, LogicalPoint)]) -> T,
1206 ) -> Option<T> {
1207 let component_weak = ItemTreeRc::downgrade(&self.try_component()?);
1208 Some(self.pinned_fields.as_ref().project_ref().redraw_tracker.evaluate_as_dependency_root(
1209 || {
1210 if !self
1211 .active_popups
1212 .borrow()
1213 .iter()
1214 .any(|p| matches!(p.location, PopupWindowLocation::ChildWindow(..)))
1215 {
1216 render_components(&[(component_weak, LogicalPoint::default())])
1217 } else {
1218 let borrow = self.active_popups.borrow();
1219 let mut item_trees = Vec::with_capacity(borrow.len() + 1);
1220 item_trees.push((component_weak, LogicalPoint::default()));
1221 for popup in borrow.iter() {
1222 if let PopupWindowLocation::ChildWindow(location) = &popup.location {
1226 item_trees.push((ItemTreeRc::downgrade(&popup.component), *location));
1227 }
1228 }
1229 drop(borrow);
1230 render_components(&item_trees)
1231 }
1232 },
1233 ))
1234 }
1235
1236 pub fn show(&self) -> Result<(), PlatformError> {
1239 if let Some(component) = self.try_component() {
1240 let was_visible = self.strong_component_ref.replace(Some(component)).is_some();
1241 if !was_visible {
1242 *(self.context().0.window_count.borrow_mut()) += 1;
1243 }
1244 }
1245
1246 self.update_window_properties();
1247 self.window_adapter().set_visible(true)?;
1248 let size = self.window_adapter().size();
1251 let scale_factor = self.scale_factor();
1252 self.set_window_item_geometry(size.to_logical(scale_factor).to_euclid());
1253 let inset = self
1254 .window_adapter()
1255 .internal(crate::InternalToken)
1256 .map(|internal| internal.safe_area_inset())
1257 .unwrap_or_default();
1258 self.set_window_item_safe_area(inset.to_logical(scale_factor));
1259 self.window_adapter().renderer().resize(size).unwrap();
1260 if let Some(hook) = self.context().0.window_shown_hook.borrow_mut().as_mut() {
1261 hook(&self.window_adapter());
1262 }
1263 Ok(())
1264 }
1265
1266 pub fn hide(&self) -> Result<(), PlatformError> {
1268 let result = self.window_adapter().set_visible(false);
1269 let was_visible = self.strong_component_ref.borrow_mut().take().is_some();
1270 if was_visible {
1271 let mut count = self.context().0.window_count.borrow_mut();
1272 *count -= 1;
1273 if *count <= 0 {
1274 drop(count);
1275 let _ = self.context().event_loop_proxy().and_then(|p| p.quit_event_loop().ok());
1276 }
1277 }
1278 result
1279 }
1280
1281 pub fn color_scheme(&self) -> ColorScheme {
1283 self.window_adapter()
1284 .internal(crate::InternalToken)
1285 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1286 }
1287
1288 pub fn accent_color(&self) -> Color {
1290 self.window_adapter()
1291 .internal(crate::InternalToken)
1292 .map_or(Color::default(), |x| x.accent_color())
1293 }
1294
1295 pub fn supports_native_menu_bar(&self) -> bool {
1297 self.window_adapter()
1298 .internal(crate::InternalToken)
1299 .is_some_and(|x| x.supports_native_menu_bar())
1300 }
1301
1302 pub fn setup_menubar(&self, menubar: vtable::VRc<MenuVTable>) {
1304 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1305 x.setup_menubar(menubar);
1306 }
1307 }
1308
1309 pub fn setup_menubar_shortcuts(&self, menubar: VRc<MenuVTable>) {
1314 *self.menubar.borrow_mut() = Some(VRc::downgrade(&menubar));
1315 let weak = VRc::downgrade(&menubar);
1316 self.pinned_fields.menubar_shortcuts.set_binding(move || {
1317 fn flatten_menu(
1318 root: vtable::VRef<'_, MenuVTable>,
1319 parent: Option<&MenuEntry>,
1320 ) -> SharedVector<MenuEntry> {
1321 let mut menu_entries = Default::default();
1322 root.sub_menu(parent, &mut menu_entries);
1323
1324 let mut result = menu_entries.clone();
1325
1326 for entry in menu_entries {
1327 result.extend(flatten_menu(root, Some(&entry)).into_iter());
1328 }
1329 result
1330 }
1331
1332 let Some(menubar) = weak.upgrade() else {
1333 return SharedVector::default();
1334 };
1335 flatten_menu(VRc::borrow(&menubar), None)
1336 .into_iter()
1337 .filter(|entry| entry.enabled && entry.shortcut != Keys::default())
1338 .collect()
1339 });
1340 }
1341
1342 pub fn show_popup(
1346 &self,
1347 popup_componentrc: &ItemTreeRc,
1348 position: LogicalPosition,
1349 close_policy: PopupClosePolicy,
1350 parent_item: &ItemRc,
1351 is_menu: bool,
1352 ) -> NonZeroU32 {
1353 let position = parent_item
1354 .map_to_native_window(parent_item.geometry().origin + position.to_euclid().to_vector());
1355 let popup_component = ItemTreeRc::borrow_pin(popup_componentrc);
1356 let popup_root = popup_component.as_ref().get_item_ref(0);
1357
1358 let (mut w, mut h) = if let Some(window_item) =
1359 ItemRef::downcast_pin::<crate::items::WindowItem>(popup_root)
1360 {
1361 (window_item.width(), window_item.height())
1362 } else {
1363 (LogicalLength::zero(), LogicalLength::zero())
1364 };
1365
1366 let layout_info_h =
1367 popup_component.as_ref().layout_info(crate::layout::Orientation::Horizontal);
1368 let layout_info_v =
1369 popup_component.as_ref().layout_info(crate::layout::Orientation::Vertical);
1370
1371 if w <= LogicalLength::zero() {
1372 w = LogicalLength::new(layout_info_h.preferred);
1373 }
1374 if h <= LogicalLength::zero() {
1375 h = LogicalLength::new(layout_info_v.preferred);
1376 }
1377 w = w.max(LogicalLength::new(layout_info_h.min)).min(LogicalLength::new(layout_info_h.max));
1378 h = h.max(LogicalLength::new(layout_info_v.min)).min(LogicalLength::new(layout_info_v.max));
1379
1380 let size = crate::lengths::LogicalSize::from_lengths(w, h);
1381
1382 if let Some(window_item) = ItemRef::downcast_pin(popup_root) {
1383 let width_property =
1384 crate::items::WindowItem::FIELD_OFFSETS.width().apply_pin(window_item);
1385 let height_property =
1386 crate::items::WindowItem::FIELD_OFFSETS.height().apply_pin(window_item);
1387 width_property.set(size.width_length());
1388 height_property.set(size.height_length());
1389 };
1390
1391 let popup_id = self.next_popup_id.get();
1392 self.next_popup_id.set(self.next_popup_id.get().checked_add(1).unwrap());
1393
1394 let siblings: Vec<_> = self
1396 .active_popups
1397 .borrow()
1398 .iter()
1399 .filter(|p| p.parent_item == parent_item.downgrade())
1400 .map(|p| p.popup_id)
1401 .collect();
1402
1403 for sibling in siblings {
1404 self.close_popup(sibling);
1405 }
1406
1407 let root_of = |mut item_tree: ItemTreeRc| loop {
1408 if ItemRc::new_root(item_tree.clone()).downcast::<crate::items::WindowItem>().is_some()
1409 {
1410 return item_tree;
1411 }
1412 let mut r = crate::item_tree::ItemWeak::default();
1413 ItemTreeRc::borrow_pin(&item_tree).as_ref().parent_node(&mut r);
1414 match r.upgrade() {
1415 None => return item_tree,
1416 Some(x) => item_tree = x.item_tree().clone(),
1417 }
1418 };
1419
1420 let parent_root_item_tree = root_of(parent_item.item_tree().clone());
1421 let parent_window_adapter = if let Some(parent_popup) = self
1422 .active_popups
1423 .borrow()
1424 .iter()
1425 .find(|p| ItemTreeRc::ptr_eq(&p.component, &parent_root_item_tree))
1426 {
1427 match &parent_popup.location {
1429 PopupWindowLocation::TopLevel(wa) => wa.clone(),
1430 PopupWindowLocation::ChildWindow(_) => self.window_adapter(),
1431 }
1432 } else {
1433 self.window_adapter()
1434 };
1435
1436 let location = match parent_window_adapter
1439 .internal(crate::InternalToken)
1440 .and_then(|x| x.create_popup(LogicalRect::new(position, size)))
1441 {
1442 None => {
1443 let clip = LogicalRect::new(
1444 LogicalPoint::new(0.0 as crate::Coord, 0.0 as crate::Coord),
1445 self.window_adapter().size().to_logical(self.scale_factor()).to_euclid(),
1446 );
1447 let rect = popup::place_popup(
1448 popup::Placement::Fixed(LogicalRect::new(position, size)),
1449 &Some(clip),
1450 );
1451 self.window_adapter().request_redraw();
1452 PopupWindowLocation::ChildWindow(rect.origin)
1453 }
1454 Some(window_adapter) => {
1455 WindowInner::from_pub(window_adapter.window()).set_component(popup_componentrc);
1456 PopupWindowLocation::TopLevel(window_adapter)
1457 }
1458 };
1459
1460 let focus_item = self
1461 .take_focus_item(&FocusEvent::FocusOut(FocusReason::PopupActivation))
1462 .map(|item| item.downgrade())
1463 .unwrap_or_default();
1464
1465 self.active_popups.borrow_mut().push(PopupWindow {
1466 popup_id,
1467 location,
1468 component: popup_componentrc.clone(),
1469 close_policy,
1470 focus_item_in_parent: focus_item,
1471 parent_item: parent_item.downgrade(),
1472 is_menu,
1473 });
1474
1475 popup_id
1476 }
1477
1478 pub fn show_native_popup_menu(
1484 &self,
1485 context_menu_item: vtable::VRc<MenuVTable>,
1486 position: LogicalPosition,
1487 parent_item: &ItemRc,
1488 ) -> bool {
1489 if let Some(x) = self.window_adapter().internal(crate::InternalToken) {
1490 let position = parent_item.map_to_native_window(
1491 parent_item.geometry().origin + position.to_euclid().to_vector(),
1492 );
1493 let position = crate::lengths::logical_position_to_api(position);
1494 x.show_native_popup_menu(context_menu_item, position)
1495 } else {
1496 false
1497 }
1498 }
1499
1500 fn close_popup_impl(&self, current_popup: &PopupWindow) {
1502 match ¤t_popup.location {
1503 PopupWindowLocation::ChildWindow(offset) => {
1504 let popup_region = crate::properties::evaluate_no_tracking(|| {
1506 let popup_component = ItemTreeRc::borrow_pin(¤t_popup.component);
1507 popup_component.as_ref().item_geometry(0)
1508 })
1509 .translate(offset.to_vector());
1510
1511 if !popup_region.is_empty() {
1512 let window_adapter = self.window_adapter();
1513 window_adapter.renderer().mark_dirty_region(popup_region.into());
1514 window_adapter.request_redraw();
1515 }
1516 }
1517 PopupWindowLocation::TopLevel(adapter) => {
1518 let _ = adapter.set_visible(false);
1519 }
1520 }
1521 if let Some(focus) = current_popup.focus_item_in_parent.upgrade() {
1522 self.set_focus_item(&focus, true, FocusReason::PopupActivation);
1523 }
1524 }
1525
1526 pub fn close_popup(&self, popup_id: NonZeroU32) {
1528 let mut active_popups = self.active_popups.borrow_mut();
1529 let maybe_index = active_popups.iter().position(|popup| popup.popup_id == popup_id);
1530
1531 if let Some(popup_index) = maybe_index {
1532 let p = active_popups.remove(popup_index);
1533 drop(active_popups);
1534 self.close_popup_impl(&p);
1535 if p.is_menu {
1536 while self.active_popups.borrow().get(popup_index).is_some_and(|p| p.is_menu) {
1538 let p = self.active_popups.borrow_mut().remove(popup_index);
1539 self.close_popup_impl(&p);
1540 }
1541 }
1542 }
1543 }
1544
1545 pub fn close_all_popups(&self) {
1547 for popup in self.active_popups.take() {
1548 self.close_popup_impl(&popup);
1549 }
1550 }
1551
1552 pub fn close_top_popup(&self) {
1554 let popup = self.active_popups.borrow_mut().pop();
1555 if let Some(popup) = popup {
1556 self.close_popup_impl(&popup);
1557 }
1558 }
1559
1560 pub fn scale_factor(&self) -> f32 {
1562 self.pinned_fields.as_ref().project_ref().scale_factor.get()
1563 }
1564
1565 pub(crate) fn set_scale_factor(&self, factor: f32) {
1567 if !self.pinned_fields.scale_factor.is_constant() {
1568 self.pinned_fields.scale_factor.set(factor)
1569 }
1570 }
1571
1572 pub fn set_const_scale_factor(&self, factor: f32) {
1575 if !self.pinned_fields.scale_factor.is_constant() {
1576 self.pinned_fields.scale_factor.set(factor);
1577 self.pinned_fields.scale_factor.set_constant();
1578 }
1579 }
1580
1581 pub fn text_input_focused(&self) -> bool {
1583 self.pinned_fields.as_ref().project_ref().text_input_focused.get()
1584 }
1585
1586 pub fn set_text_input_focused(&self, value: bool) {
1588 if !value && let Some(window_adapter) = self.window_adapter().internal(crate::InternalToken)
1589 {
1590 window_adapter.input_method_request(InputMethodRequest::Disable);
1591 }
1592 self.pinned_fields.text_input_focused.set(value)
1593 }
1594
1595 pub fn is_visible(&self) -> bool {
1597 self.strong_component_ref.borrow().is_some()
1598 }
1599
1600 pub fn window_item_rc(&self) -> Option<ItemRc> {
1603 self.try_component().and_then(|component_rc| {
1604 let item_rc = ItemRc::new_root(component_rc);
1605 if item_rc.downcast::<crate::items::WindowItem>().is_some() {
1606 Some(item_rc)
1607 } else {
1608 None
1609 }
1610 })
1611 }
1612
1613 pub fn window_item(&self) -> Option<VRcMapped<ItemTreeVTable, crate::items::WindowItem>> {
1615 self.try_component().and_then(|component_rc| {
1616 ItemRc::new_root(component_rc).downcast::<crate::items::WindowItem>()
1617 })
1618 }
1619
1620 pub(crate) fn set_window_item_geometry(&self, size: crate::lengths::LogicalSize) {
1623 if let Some(component_rc) = self.try_component() {
1624 let component = ItemTreeRc::borrow_pin(&component_rc);
1625 let root_item = component.as_ref().get_item_ref(0);
1626 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1627 {
1628 window_item.width.set(size.width_length());
1629 window_item.height.set(size.height_length());
1630 }
1631 }
1632 }
1633
1634 pub fn set_window_item_safe_area(&self, inset: crate::lengths::LogicalEdges) {
1636 if let Some(component_rc) = self.try_component() {
1637 let component = ItemTreeRc::borrow_pin(&component_rc);
1638 let root_item = component.as_ref().get_item_ref(0);
1639 if let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
1640 {
1641 window_item.safe_area_insets.set(inset);
1642 }
1643 }
1644 }
1645
1646 pub(crate) fn set_window_item_virtual_keyboard(
1647 &self,
1648 origin: crate::lengths::LogicalPoint,
1649 size: crate::lengths::LogicalSize,
1650 ) {
1651 let Some(component_rc) = self.try_component() else {
1652 return;
1653 };
1654 let component = ItemTreeRc::borrow_pin(&component_rc);
1655 let root_item = component.as_ref().get_item_ref(0);
1656 let Some(window_item) = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item) else {
1657 return;
1658 };
1659 window_item.virtual_keyboard_position.set(origin);
1660 window_item.virtual_keyboard_size.set(size);
1661 if let Some(focus_item) = self.focus_item.borrow().upgrade() {
1662 focus_item.try_scroll_into_visible();
1663 }
1664 }
1665
1666 pub(crate) fn window_item_virtual_keyboard(
1667 &self,
1668 ) -> Option<(crate::lengths::LogicalPoint, crate::lengths::LogicalSize)> {
1669 let component_rc = self.try_component()?;
1670 let component = ItemTreeRc::borrow_pin(&component_rc);
1671 let root_item = component.as_ref().get_item_ref(0);
1672 let window_item = ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)?;
1673 Some((window_item.virtual_keyboard_position(), window_item.virtual_keyboard_size()))
1674 }
1675
1676 pub fn on_close_requested(&self, mut callback: impl FnMut() -> CloseRequestResponse + 'static) {
1678 self.close_requested.set_handler(move |()| callback());
1679 }
1680
1681 pub fn request_close(&self) -> bool {
1685 match self.close_requested.call(&()) {
1686 CloseRequestResponse::HideWindow => true,
1687 CloseRequestResponse::KeepWindowShown => false,
1688 }
1689 }
1690
1691 pub fn is_fullscreen(&self) -> bool {
1693 if let Some(window_item) = self.window_item() {
1694 window_item.as_pin_ref().full_screen()
1695 } else {
1696 false
1697 }
1698 }
1699
1700 pub fn set_fullscreen(&self, enabled: bool) {
1702 if let Some(window_item) = self.window_item() {
1703 window_item.as_pin_ref().full_screen.set(enabled);
1704 self.update_window_properties()
1705 }
1706 }
1707
1708 pub fn is_maximized(&self) -> bool {
1710 self.maximized.get()
1711 }
1712
1713 pub fn set_maximized(&self, maximized: bool) {
1715 self.maximized.set(maximized);
1716 self.update_window_properties()
1717 }
1718
1719 pub fn is_minimized(&self) -> bool {
1721 self.minimized.get()
1722 }
1723
1724 pub fn set_minimized(&self, minimized: bool) {
1726 self.minimized.set(minimized);
1727 self.update_window_properties()
1728 }
1729
1730 pub fn xdg_app_id(&self) -> Option<SharedString> {
1732 self.context().xdg_app_id()
1733 }
1734
1735 pub fn window_adapter(&self) -> Rc<dyn WindowAdapter> {
1737 self.window_adapter_weak.upgrade().unwrap()
1738 }
1739
1740 pub fn from_pub(window: &crate::api::Window) -> &Self {
1742 &window.0
1743 }
1744
1745 pub fn context(&self) -> &crate::SlintContext {
1747 self.ctx
1748 .get_or_init(|| crate::context::GLOBAL_CONTEXT.with(|ctx| ctx.get().unwrap().clone()))
1749 }
1750
1751 pub fn set_context(&self, ctx: crate::SlintContext) {
1754 self.ctx.set(ctx).map_err(|_| ()).expect("context shouldn't have been set before")
1755 }
1756}
1757
1758pub type WindowAdapterRc = Rc<dyn WindowAdapter>;
1760
1761#[cfg(feature = "ffi")]
1764pub mod ffi {
1765 #![allow(unsafe_code)]
1766 #![allow(clippy::missing_safety_doc)]
1767 #![allow(missing_docs)]
1768
1769 use super::*;
1770 use crate::SharedVector;
1771 use crate::api::{RenderingNotifier, RenderingState, SetRenderingNotifierError};
1772 use crate::graphics::Size;
1773 use crate::graphics::{IntSize, Rgba8Pixel};
1774 use crate::items::WindowItem;
1775
1776 #[repr(u8)]
1779 pub enum GraphicsAPI {
1780 NativeOpenGL,
1782 Inaccessible,
1784 }
1785
1786 #[allow(non_camel_case_types)]
1787 type c_void = ();
1788
1789 #[repr(C)]
1791 pub struct WindowAdapterRcOpaque(*const c_void, *const c_void);
1792
1793 #[unsafe(no_mangle)]
1795 pub unsafe extern "C" fn slint_windowrc_drop(handle: *mut WindowAdapterRcOpaque) {
1796 unsafe {
1797 assert_eq!(
1798 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1799 core::mem::size_of::<WindowAdapterRcOpaque>()
1800 );
1801 assert_eq!(
1802 core::mem::size_of::<Option<Rc<dyn WindowAdapter>>>(),
1803 core::mem::size_of::<WindowAdapterRcOpaque>()
1804 );
1805 drop(core::ptr::read(handle as *mut Option<Rc<dyn WindowAdapter>>));
1806 }
1807 }
1808
1809 #[unsafe(no_mangle)]
1811 pub unsafe extern "C" fn slint_windowrc_clone(
1812 source: *const WindowAdapterRcOpaque,
1813 target: *mut WindowAdapterRcOpaque,
1814 ) {
1815 unsafe {
1816 assert_eq!(
1817 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1818 core::mem::size_of::<WindowAdapterRcOpaque>()
1819 );
1820 let window = &*(source as *const Rc<dyn WindowAdapter>);
1821 core::ptr::write(target as *mut Rc<dyn WindowAdapter>, window.clone());
1822 }
1823 }
1824
1825 #[unsafe(no_mangle)]
1827 pub unsafe extern "C" fn slint_windowrc_show(handle: *const WindowAdapterRcOpaque) {
1828 unsafe {
1829 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1830
1831 window_adapter.window().show().unwrap();
1832 }
1833 }
1834
1835 #[unsafe(no_mangle)]
1837 pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) {
1838 unsafe {
1839 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1840 window_adapter.window().hide().unwrap();
1841 }
1842 }
1843
1844 #[unsafe(no_mangle)]
1847 pub unsafe extern "C" fn slint_windowrc_is_visible(
1848 handle: *const WindowAdapterRcOpaque,
1849 ) -> bool {
1850 unsafe {
1851 let window = &*(handle as *const Rc<dyn WindowAdapter>);
1852 window.window().is_visible()
1853 }
1854 }
1855
1856 #[unsafe(no_mangle)]
1858 pub unsafe extern "C" fn slint_windowrc_get_scale_factor(
1859 handle: *const WindowAdapterRcOpaque,
1860 ) -> f32 {
1861 unsafe {
1862 assert_eq!(
1863 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1864 core::mem::size_of::<WindowAdapterRcOpaque>()
1865 );
1866 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1867 WindowInner::from_pub(window_adapter.window()).scale_factor()
1868 }
1869 }
1870
1871 #[unsafe(no_mangle)]
1873 pub unsafe extern "C" fn slint_windowrc_set_const_scale_factor(
1874 handle: *const WindowAdapterRcOpaque,
1875 value: f32,
1876 ) {
1877 unsafe {
1878 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1879 WindowInner::from_pub(window_adapter.window()).set_const_scale_factor(value)
1880 }
1881 }
1882
1883 #[unsafe(no_mangle)]
1885 pub unsafe extern "C" fn slint_windowrc_get_text_input_focused(
1886 handle: *const WindowAdapterRcOpaque,
1887 ) -> bool {
1888 unsafe {
1889 assert_eq!(
1890 core::mem::size_of::<Rc<dyn WindowAdapter>>(),
1891 core::mem::size_of::<WindowAdapterRcOpaque>()
1892 );
1893 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1894 WindowInner::from_pub(window_adapter.window()).text_input_focused()
1895 }
1896 }
1897
1898 #[unsafe(no_mangle)]
1900 pub unsafe extern "C" fn slint_windowrc_set_text_input_focused(
1901 handle: *const WindowAdapterRcOpaque,
1902 value: bool,
1903 ) {
1904 unsafe {
1905 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1906 WindowInner::from_pub(window_adapter.window()).set_text_input_focused(value)
1907 }
1908 }
1909
1910 #[unsafe(no_mangle)]
1912 pub unsafe extern "C" fn slint_windowrc_set_focus_item(
1913 handle: *const WindowAdapterRcOpaque,
1914 focus_item: &ItemRc,
1915 set_focus: bool,
1916 reason: FocusReason,
1917 ) {
1918 unsafe {
1919 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1920 WindowInner::from_pub(window_adapter.window())
1921 .set_focus_item(focus_item, set_focus, reason)
1922 }
1923 }
1924
1925 #[unsafe(no_mangle)]
1927 pub unsafe extern "C" fn slint_windowrc_set_component(
1928 handle: *const WindowAdapterRcOpaque,
1929 component: &ItemTreeRc,
1930 ) {
1931 unsafe {
1932 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1933 WindowInner::from_pub(window_adapter.window()).set_component(component)
1934 }
1935 }
1936
1937 #[unsafe(no_mangle)]
1939 pub unsafe extern "C" fn slint_windowrc_show_popup(
1940 handle: *const WindowAdapterRcOpaque,
1941 popup: &ItemTreeRc,
1942 position: LogicalPosition,
1943 close_policy: PopupClosePolicy,
1944 parent_item: &ItemRc,
1945 is_menu: bool,
1946 ) -> NonZeroU32 {
1947 unsafe {
1948 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1949 WindowInner::from_pub(window_adapter.window()).show_popup(
1950 popup,
1951 position,
1952 close_policy,
1953 parent_item,
1954 is_menu,
1955 )
1956 }
1957 }
1958
1959 #[unsafe(no_mangle)]
1961 pub unsafe extern "C" fn slint_windowrc_close_popup(
1962 handle: *const WindowAdapterRcOpaque,
1963 popup_id: NonZeroU32,
1964 ) {
1965 unsafe {
1966 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
1967 WindowInner::from_pub(window_adapter.window()).close_popup(popup_id);
1968 }
1969 }
1970
1971 #[unsafe(no_mangle)]
1973 pub unsafe extern "C" fn slint_windowrc_set_rendering_notifier(
1974 handle: *const WindowAdapterRcOpaque,
1975 callback: extern "C" fn(
1976 rendering_state: RenderingState,
1977 graphics_api: GraphicsAPI,
1978 user_data: *mut c_void,
1979 ),
1980 drop_user_data: extern "C" fn(user_data: *mut c_void),
1981 user_data: *mut c_void,
1982 error: *mut SetRenderingNotifierError,
1983 ) -> bool {
1984 unsafe {
1985 struct CNotifier {
1986 callback: extern "C" fn(
1987 rendering_state: RenderingState,
1988 graphics_api: GraphicsAPI,
1989 user_data: *mut c_void,
1990 ),
1991 drop_user_data: extern "C" fn(*mut c_void),
1992 user_data: *mut c_void,
1993 }
1994
1995 impl Drop for CNotifier {
1996 fn drop(&mut self) {
1997 (self.drop_user_data)(self.user_data)
1998 }
1999 }
2000
2001 impl RenderingNotifier for CNotifier {
2002 fn notify(
2003 &mut self,
2004 state: RenderingState,
2005 graphics_api: &crate::api::GraphicsAPI,
2006 ) {
2007 let cpp_graphics_api = match graphics_api {
2008 crate::api::GraphicsAPI::NativeOpenGL { .. } => GraphicsAPI::NativeOpenGL,
2009 crate::api::GraphicsAPI::WebGL { .. } => unreachable!(), #[cfg(feature = "unstable-wgpu-27")]
2011 crate::api::GraphicsAPI::WGPU27 { .. } => GraphicsAPI::Inaccessible, #[cfg(feature = "unstable-wgpu-28")]
2013 crate::api::GraphicsAPI::WGPU28 { .. } => GraphicsAPI::Inaccessible, };
2015 (self.callback)(state, cpp_graphics_api, self.user_data)
2016 }
2017 }
2018
2019 let window = &*(handle as *const Rc<dyn WindowAdapter>);
2020 match window.renderer().set_rendering_notifier(Box::new(CNotifier {
2021 callback,
2022 drop_user_data,
2023 user_data,
2024 })) {
2025 Ok(()) => true,
2026 Err(err) => {
2027 *error = err;
2028 false
2029 }
2030 }
2031 }
2032 }
2033
2034 #[unsafe(no_mangle)]
2036 pub unsafe extern "C" fn slint_windowrc_on_close_requested(
2037 handle: *const WindowAdapterRcOpaque,
2038 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
2039 drop_user_data: extern "C" fn(user_data: *mut c_void),
2040 user_data: *mut c_void,
2041 ) {
2042 unsafe {
2043 struct WithUserData {
2044 callback: extern "C" fn(user_data: *mut c_void) -> CloseRequestResponse,
2045 drop_user_data: extern "C" fn(*mut c_void),
2046 user_data: *mut c_void,
2047 }
2048
2049 impl Drop for WithUserData {
2050 fn drop(&mut self) {
2051 (self.drop_user_data)(self.user_data)
2052 }
2053 }
2054
2055 impl WithUserData {
2056 fn call(&self) -> CloseRequestResponse {
2057 (self.callback)(self.user_data)
2058 }
2059 }
2060
2061 let with_user_data = WithUserData { callback, drop_user_data, user_data };
2062
2063 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2064 window_adapter.window().on_close_requested(move || with_user_data.call());
2065 }
2066 }
2067
2068 #[unsafe(no_mangle)]
2070 pub unsafe extern "C" fn slint_windowrc_request_redraw(handle: *const WindowAdapterRcOpaque) {
2071 unsafe {
2072 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2073 window_adapter.request_redraw();
2074 }
2075 }
2076
2077 #[unsafe(no_mangle)]
2080 pub unsafe extern "C" fn slint_windowrc_position(
2081 handle: *const WindowAdapterRcOpaque,
2082 pos: &mut euclid::default::Point2D<i32>,
2083 ) {
2084 unsafe {
2085 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2086 *pos = window_adapter.position().unwrap_or_default().to_euclid()
2087 }
2088 }
2089
2090 #[unsafe(no_mangle)]
2094 pub unsafe extern "C" fn slint_windowrc_set_physical_position(
2095 handle: *const WindowAdapterRcOpaque,
2096 pos: &euclid::default::Point2D<i32>,
2097 ) {
2098 unsafe {
2099 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2100 window_adapter.set_position(crate::api::PhysicalPosition::new(pos.x, pos.y).into());
2101 }
2102 }
2103
2104 #[unsafe(no_mangle)]
2108 pub unsafe extern "C" fn slint_windowrc_set_logical_position(
2109 handle: *const WindowAdapterRcOpaque,
2110 pos: &euclid::default::Point2D<f32>,
2111 ) {
2112 unsafe {
2113 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2114 window_adapter.set_position(LogicalPosition::new(pos.x, pos.y).into());
2115 }
2116 }
2117
2118 #[unsafe(no_mangle)]
2121 pub unsafe extern "C" fn slint_windowrc_size(handle: *const WindowAdapterRcOpaque) -> IntSize {
2122 unsafe {
2123 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2124 window_adapter.size().to_euclid().cast()
2125 }
2126 }
2127
2128 #[unsafe(no_mangle)]
2131 pub unsafe extern "C" fn slint_windowrc_set_physical_size(
2132 handle: *const WindowAdapterRcOpaque,
2133 size: &IntSize,
2134 ) {
2135 unsafe {
2136 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2137 window_adapter
2138 .window()
2139 .set_size(crate::api::PhysicalSize::new(size.width, size.height));
2140 }
2141 }
2142
2143 #[unsafe(no_mangle)]
2146 pub unsafe extern "C" fn slint_windowrc_set_logical_size(
2147 handle: *const WindowAdapterRcOpaque,
2148 size: &Size,
2149 ) {
2150 unsafe {
2151 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2152 window_adapter.window().set_size(crate::api::LogicalSize::new(size.width, size.height));
2153 }
2154 }
2155
2156 #[unsafe(no_mangle)]
2158 pub unsafe extern "C" fn slint_windowrc_color_scheme(
2159 handle: *const WindowAdapterRcOpaque,
2160 ) -> ColorScheme {
2161 unsafe {
2162 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2163 window_adapter
2164 .internal(crate::InternalToken)
2165 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
2166 }
2167 }
2168
2169 #[unsafe(no_mangle)]
2171 pub unsafe extern "C" fn slint_windowrc_accent_color(
2172 handle: *const WindowAdapterRcOpaque,
2173 out: &mut Color,
2174 ) {
2175 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2176 *out = window_adapter
2177 .internal(crate::InternalToken)
2178 .map_or(Color::default(), |x| x.accent_color());
2179 }
2180
2181 #[unsafe(no_mangle)]
2183 pub unsafe extern "C" fn slint_windowrc_supports_native_menu_bar(
2184 handle: *const WindowAdapterRcOpaque,
2185 ) -> bool {
2186 unsafe {
2187 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2188 window_adapter
2189 .internal(crate::InternalToken)
2190 .is_some_and(|x| x.supports_native_menu_bar())
2191 }
2192 }
2193
2194 #[unsafe(no_mangle)]
2196 pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
2197 handle: *const WindowAdapterRcOpaque,
2198 menu_instance: &vtable::VRc<MenuVTable>,
2199 ) {
2200 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2201 let window = window_adapter.window();
2202 window.0.setup_menubar(vtable::VRc::clone(menu_instance));
2203 }
2204
2205 #[unsafe(no_mangle)]
2206 pub unsafe extern "C" fn slint_windowrc_setup_menu_bar_shortcuts(
2207 handle: *const WindowAdapterRcOpaque,
2208 menu_instance: &vtable::VRc<MenuVTable>,
2209 ) {
2210 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2211 let window = window_adapter.window();
2212 window.0.setup_menubar_shortcuts(vtable::VRc::clone(menu_instance));
2213 }
2214
2215 #[unsafe(no_mangle)]
2217 pub unsafe extern "C" fn slint_windowrc_show_native_popup_menu(
2218 handle: *const WindowAdapterRcOpaque,
2219 context_menu: &vtable::VRc<MenuVTable>,
2220 position: LogicalPosition,
2221 parent_item: &ItemRc,
2222 ) -> bool {
2223 unsafe {
2224 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2225 WindowInner::from_pub(window_adapter.window()).show_native_popup_menu(
2226 context_menu.clone(),
2227 position,
2228 parent_item,
2229 )
2230 }
2231 }
2232
2233 #[unsafe(no_mangle)]
2235 pub unsafe extern "C" fn slint_windowrc_resolved_default_font_size(
2236 item_tree: &ItemTreeRc,
2237 ) -> f32 {
2238 WindowItem::resolved_default_font_size(item_tree.clone()).get()
2239 }
2240
2241 #[unsafe(no_mangle)]
2243 pub unsafe extern "C" fn slint_windowrc_dispatch_key_event(
2244 handle: *const WindowAdapterRcOpaque,
2245 event_type: crate::input::KeyEventType,
2246 text: &SharedString,
2247 repeat: bool,
2248 ) {
2249 unsafe {
2250 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2251 window_adapter.window().0.process_key_input(InternalKeyEvent {
2252 event_type,
2253 key_event: crate::items::KeyEvent {
2254 text: text.clone(),
2255 repeat,
2256 ..Default::default()
2257 },
2258 ..Default::default()
2259 });
2260 }
2261 }
2262
2263 #[unsafe(no_mangle)]
2265 pub unsafe extern "C" fn slint_windowrc_dispatch_pointer_event(
2266 handle: *const WindowAdapterRcOpaque,
2267 event: &crate::input::MouseEvent,
2268 ) {
2269 unsafe {
2270 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2271 window_adapter.window().0.process_mouse_input(event.clone());
2272 }
2273 }
2274
2275 #[unsafe(no_mangle)]
2277 pub unsafe extern "C" fn slint_windowrc_dispatch_event(
2278 handle: *const WindowAdapterRcOpaque,
2279 event: &crate::platform::WindowEvent,
2280 ) {
2281 unsafe {
2282 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2283 window_adapter.window().dispatch_event(event.clone());
2284 }
2285 }
2286
2287 #[unsafe(no_mangle)]
2288 pub unsafe extern "C" fn slint_windowrc_is_fullscreen(
2289 handle: *const WindowAdapterRcOpaque,
2290 ) -> bool {
2291 unsafe {
2292 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2293 window_adapter.window().is_fullscreen()
2294 }
2295 }
2296
2297 #[unsafe(no_mangle)]
2298 pub unsafe extern "C" fn slint_windowrc_is_minimized(
2299 handle: *const WindowAdapterRcOpaque,
2300 ) -> bool {
2301 unsafe {
2302 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2303 window_adapter.window().is_minimized()
2304 }
2305 }
2306
2307 #[unsafe(no_mangle)]
2308 pub unsafe extern "C" fn slint_windowrc_is_maximized(
2309 handle: *const WindowAdapterRcOpaque,
2310 ) -> bool {
2311 unsafe {
2312 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2313 window_adapter.window().is_maximized()
2314 }
2315 }
2316
2317 #[unsafe(no_mangle)]
2318 pub unsafe extern "C" fn slint_windowrc_set_fullscreen(
2319 handle: *const WindowAdapterRcOpaque,
2320 value: bool,
2321 ) {
2322 unsafe {
2323 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2324 window_adapter.window().set_fullscreen(value)
2325 }
2326 }
2327
2328 #[unsafe(no_mangle)]
2329 pub unsafe extern "C" fn slint_windowrc_set_minimized(
2330 handle: *const WindowAdapterRcOpaque,
2331 value: bool,
2332 ) {
2333 unsafe {
2334 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2335 window_adapter.window().set_minimized(value)
2336 }
2337 }
2338
2339 #[unsafe(no_mangle)]
2340 pub unsafe extern "C" fn slint_windowrc_set_maximized(
2341 handle: *const WindowAdapterRcOpaque,
2342 value: bool,
2343 ) {
2344 unsafe {
2345 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2346 window_adapter.window().set_maximized(value)
2347 }
2348 }
2349
2350 #[unsafe(no_mangle)]
2352 pub unsafe extern "C" fn slint_windowrc_take_snapshot(
2353 handle: *const WindowAdapterRcOpaque,
2354 data: &mut SharedVector<Rgba8Pixel>,
2355 width: &mut u32,
2356 height: &mut u32,
2357 ) -> bool {
2358 unsafe {
2359 let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
2360 if let Ok(snapshot) = window_adapter.window().take_snapshot() {
2361 *data = snapshot.data.clone();
2362 *width = snapshot.width();
2363 *height = snapshot.height();
2364 true
2365 } else {
2366 false
2367 }
2368 }
2369 }
2370}
2371
2372#[cfg(all(feature = "ffi", feature = "raw-window-handle-06"))]
2374pub mod ffi_window {
2375 #![allow(unsafe_code)]
2376 #![allow(clippy::missing_safety_doc)]
2377
2378 use super::ffi::WindowAdapterRcOpaque;
2379 use super::*;
2380 use std::ffi::c_void;
2381 use std::ptr::null_mut;
2382 use std::sync::Arc;
2383
2384 fn has_window_handle(
2386 handle: *const WindowAdapterRcOpaque,
2387 ) -> Option<Arc<dyn raw_window_handle_06::HasWindowHandle>> {
2388 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2389 let window_adapter = window_adapter.internal(crate::InternalToken)?;
2390 window_adapter.window_handle_06_rc().ok()
2391 }
2392
2393 fn has_display_handle(
2395 handle: *const WindowAdapterRcOpaque,
2396 ) -> Option<Arc<dyn raw_window_handle_06::HasDisplayHandle>> {
2397 let window_adapter = unsafe { &*(handle as *const Rc<dyn WindowAdapter>) };
2398 let window_adapter = window_adapter.internal(crate::InternalToken)?;
2399 window_adapter.display_handle_06_rc().ok()
2400 }
2401
2402 #[unsafe(no_mangle)]
2404 pub unsafe extern "C" fn slint_windowrc_hwnd_win32(
2405 handle: *const WindowAdapterRcOpaque,
2406 ) -> *mut c_void {
2407 use raw_window_handle_06::HasWindowHandle;
2408
2409 if let Some(has_window_handle) = has_window_handle(handle)
2410 && let Ok(window_handle) = has_window_handle.window_handle()
2411 && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
2412 {
2413 isize::from(win32.hwnd) as *mut c_void
2414 } else {
2415 null_mut()
2416 }
2417 }
2418
2419 #[unsafe(no_mangle)]
2421 pub unsafe extern "C" fn slint_windowrc_hinstance_win32(
2422 handle: *const WindowAdapterRcOpaque,
2423 ) -> *mut c_void {
2424 use raw_window_handle_06::HasWindowHandle;
2425
2426 if let Some(has_window_handle) = has_window_handle(handle)
2427 && let Ok(window_handle) = has_window_handle.window_handle()
2428 && let raw_window_handle_06::RawWindowHandle::Win32(win32) = window_handle.as_raw()
2429 {
2430 win32
2431 .hinstance
2432 .map(|hinstance| isize::from(hinstance) as *mut c_void)
2433 .unwrap_or_default()
2434 } else {
2435 null_mut()
2436 }
2437 }
2438
2439 #[unsafe(no_mangle)]
2441 pub unsafe extern "C" fn slint_windowrc_wlsurface_wayland(
2442 handle: *const WindowAdapterRcOpaque,
2443 ) -> *mut c_void {
2444 use raw_window_handle_06::HasWindowHandle;
2445
2446 if let Some(has_window_handle) = has_window_handle(handle)
2447 && let Ok(window_handle) = has_window_handle.window_handle()
2448 && let raw_window_handle_06::RawWindowHandle::Wayland(wayland) = window_handle.as_raw()
2449 {
2450 wayland.surface.as_ptr()
2451 } else {
2452 null_mut()
2453 }
2454 }
2455
2456 #[unsafe(no_mangle)]
2458 pub unsafe extern "C" fn slint_windowrc_wldisplay_wayland(
2459 handle: *const WindowAdapterRcOpaque,
2460 ) -> *mut c_void {
2461 use raw_window_handle_06::HasDisplayHandle;
2462
2463 if let Some(has_display_handle) = has_display_handle(handle)
2464 && let Ok(display_handle) = has_display_handle.display_handle()
2465 && let raw_window_handle_06::RawDisplayHandle::Wayland(wayland) =
2466 display_handle.as_raw()
2467 {
2468 wayland.display.as_ptr()
2469 } else {
2470 null_mut()
2471 }
2472 }
2473
2474 #[unsafe(no_mangle)]
2476 pub unsafe extern "C" fn slint_windowrc_nsview_appkit(
2477 handle: *const WindowAdapterRcOpaque,
2478 ) -> *mut c_void {
2479 use raw_window_handle_06::HasWindowHandle;
2480
2481 if let Some(has_window_handle) = has_window_handle(handle)
2482 && let Ok(window_handle) = has_window_handle.window_handle()
2483 && let raw_window_handle_06::RawWindowHandle::AppKit(appkit) = window_handle.as_raw()
2484 {
2485 appkit.ns_view.as_ptr()
2486 } else {
2487 null_mut()
2488 }
2489 }
2490}