1use core::cell::{Cell, RefCell};
9use core::pin::Pin;
10use std::rc::Rc;
11use std::rc::Weak;
12use std::sync::Arc;
13
14use euclid::approxeq::ApproxEq;
15
16#[cfg(muda)]
17use i_slint_core::api::LogicalPosition;
18use i_slint_core::lengths::{PhysicalPx, ScaleFactor};
19use winit::event_loop::ActiveEventLoop;
20#[cfg(target_arch = "wasm32")]
21use winit::platform::web::WindowExtWebSys;
22#[cfg(target_family = "windows")]
23use winit::platform::windows::WindowExtWindows;
24
25#[cfg(muda)]
26use crate::muda::MudaType;
27use crate::renderer::WinitCompatibleRenderer;
28
29use corelib::item_tree::ItemTreeRc;
30#[cfg(enable_accesskit)]
31use corelib::item_tree::{ItemTreeRef, ItemTreeRefPin};
32use corelib::items::{ColorScheme, MouseCursor};
33#[cfg(enable_accesskit)]
34use corelib::items::{ItemRc, ItemRef};
35
36#[cfg(any(enable_accesskit, muda))]
37use crate::SlintEvent;
38use crate::{EventResult, SharedBackendData};
39use corelib::Property;
40use corelib::api::PhysicalSize;
41use corelib::layout::Orientation;
42use corelib::lengths::LogicalLength;
43use corelib::platform::{PlatformError, WindowEvent};
44use corelib::window::{WindowAdapter, WindowAdapterInternal, WindowInner};
45use corelib::{Coord, graphics::*};
46use i_slint_core::{self as corelib};
47use std::cell::OnceCell;
48#[cfg(any(enable_accesskit, muda))]
49use winit::event_loop::EventLoopProxy;
50use winit::window::{WindowAttributes, WindowButtons};
51
52pub(crate) fn position_to_winit(pos: &corelib::api::WindowPosition) -> winit::dpi::Position {
53 match pos {
54 corelib::api::WindowPosition::Logical(pos) => {
55 winit::dpi::Position::new(winit::dpi::LogicalPosition::new(pos.x, pos.y))
56 }
57 corelib::api::WindowPosition::Physical(pos) => {
58 winit::dpi::Position::new(winit::dpi::PhysicalPosition::new(pos.x, pos.y))
59 }
60 }
61}
62
63fn window_size_to_winit(size: &corelib::api::WindowSize) -> winit::dpi::Size {
64 match size {
65 corelib::api::WindowSize::Logical(size) => {
66 winit::dpi::Size::new(logical_size_to_winit(*size))
67 }
68 corelib::api::WindowSize::Physical(size) => {
69 winit::dpi::Size::new(physical_size_to_winit(*size))
70 }
71 }
72}
73
74pub fn physical_size_to_slint(size: &winit::dpi::PhysicalSize<u32>) -> corelib::api::PhysicalSize {
75 corelib::api::PhysicalSize::new(size.width, size.height)
76}
77
78fn logical_size_to_winit(s: i_slint_core::api::LogicalSize) -> winit::dpi::LogicalSize<f64> {
79 winit::dpi::LogicalSize::new(s.width as f64, s.height as f64)
80}
81
82fn physical_size_to_winit(size: PhysicalSize) -> winit::dpi::PhysicalSize<u32> {
83 winit::dpi::PhysicalSize::new(size.width, size.height)
84}
85
86fn filter_out_zero_width_or_height(
87 size: winit::dpi::LogicalSize<f64>,
88) -> winit::dpi::LogicalSize<f64> {
89 fn filter(v: f64) -> f64 {
90 if v.approx_eq(&0.) {
91 10.
93 } else {
94 v
95 }
96 }
97 winit::dpi::LogicalSize { width: filter(size.width), height: filter(size.height) }
98}
99
100fn apply_scale_factor_to_logical_sizes_in_attributes(
101 attributes: &mut WindowAttributes,
102 scale_factor: f64,
103) {
104 let fixup = |maybe_size: &mut Option<winit::dpi::Size>| {
105 if let Some(size) = maybe_size.as_mut() {
106 *size = winit::dpi::Size::Physical(size.to_physical::<u32>(scale_factor))
107 }
108 };
109
110 fixup(&mut attributes.inner_size);
111 fixup(&mut attributes.min_inner_size);
112 fixup(&mut attributes.max_inner_size);
113 fixup(&mut attributes.resize_increments);
114}
115
116fn icon_to_winit(
117 icon: corelib::graphics::Image,
118 size: euclid::Size2D<Coord, PhysicalPx>,
119) -> Option<winit::window::Icon> {
120 let image_inner: &ImageInner = (&icon).into();
121
122 let Some(pixel_buffer) = image_inner.render_to_buffer(Some(size.cast())) else {
123 return None;
124 };
125
126 let rgba_pixels: Vec<u8> = match &pixel_buffer {
128 SharedImageBuffer::RGB8(pixels) => pixels
129 .as_bytes()
130 .chunks(3)
131 .flat_map(|rgb| IntoIterator::into_iter([rgb[0], rgb[1], rgb[2], 255]))
132 .collect(),
133 SharedImageBuffer::RGBA8(pixels) => pixels.as_bytes().to_vec(),
134 SharedImageBuffer::RGBA8Premultiplied(pixels) => pixels
135 .as_bytes()
136 .chunks(4)
137 .flat_map(|rgba| {
138 let alpha = rgba[3] as u32;
139 IntoIterator::into_iter(rgba)
140 .take(3)
141 .map(move |component| (*component as u32 * alpha / 255) as u8)
142 .chain(std::iter::once(alpha as u8))
143 })
144 .collect(),
145 };
146
147 winit::window::Icon::from_rgba(rgba_pixels, pixel_buffer.width(), pixel_buffer.height()).ok()
148}
149
150fn window_is_resizable(
151 min_size: Option<corelib::api::LogicalSize>,
152 max_size: Option<corelib::api::LogicalSize>,
153) -> bool {
154 if let Some((
155 corelib::api::LogicalSize { width: min_width, height: min_height, .. },
156 corelib::api::LogicalSize { width: max_width, height: max_height, .. },
157 )) = min_size.zip(max_size)
158 {
159 min_width < max_width || min_height < max_height
160 } else {
161 true
162 }
163}
164
165enum WinitWindowOrNone {
166 HasWindow {
167 window: Arc<winit::window::Window>,
168 #[cfg(enable_accesskit)]
169 accesskit_adapter: RefCell<crate::accesskit::AccessKitAdapter>,
170 #[cfg(muda)]
171 muda_adapter: RefCell<Option<crate::muda::MudaAdapter>>,
172 #[cfg(muda)]
173 context_menu_muda_adapter: RefCell<Option<crate::muda::MudaAdapter>>,
174 #[cfg(target_os = "ios")]
175 keyboard_curve_sampler: super::ios::KeyboardCurveSampler,
176 },
177 None(RefCell<WindowAttributes>),
178}
179
180impl WinitWindowOrNone {
181 fn as_window(&self) -> Option<Arc<winit::window::Window>> {
182 match self {
183 Self::HasWindow { window, .. } => Some(window.clone()),
184 Self::None { .. } => None,
185 }
186 }
187
188 fn set_window_icon(&self, icon: Option<winit::window::Icon>) {
189 match self {
190 Self::HasWindow { window, .. } => {
191 #[cfg(target_family = "windows")]
192 window.set_taskbar_icon(icon.as_ref().cloned());
193 window.set_window_icon(icon);
194 }
195 Self::None(attributes) => attributes.borrow_mut().window_icon = icon,
196 }
197 }
198
199 fn set_title(&self, title: &str) {
200 match self {
201 Self::HasWindow { window, .. } => window.set_title(title),
202 Self::None(attributes) => attributes.borrow_mut().title = title.into(),
203 }
204 }
205
206 fn set_decorations(&self, decorations: bool) {
207 match self {
208 Self::HasWindow { window, .. } => window.set_decorations(decorations),
209 Self::None(attributes) => attributes.borrow_mut().decorations = decorations,
210 }
211 }
212
213 fn fullscreen(&self) -> Option<winit::window::Fullscreen> {
214 match self {
215 Self::HasWindow { window, .. } => window.fullscreen(),
216 Self::None(attributes) => attributes.borrow().fullscreen.clone(),
217 }
218 }
219
220 fn set_fullscreen(&self, fullscreen: Option<winit::window::Fullscreen>) {
221 match self {
222 Self::HasWindow { window, .. } => window.set_fullscreen(fullscreen),
223 Self::None(attributes) => attributes.borrow_mut().fullscreen = fullscreen,
224 }
225 }
226
227 fn set_window_level(&self, level: winit::window::WindowLevel) {
228 match self {
229 Self::HasWindow { window, .. } => window.set_window_level(level),
230 Self::None(attributes) => attributes.borrow_mut().window_level = level,
231 }
232 }
233
234 fn set_visible(&self, visible: bool) {
235 match self {
236 Self::HasWindow { window, .. } => window.set_visible(visible),
237 Self::None(attributes) => attributes.borrow_mut().visible = visible,
238 }
239 }
240
241 fn set_maximized(&self, maximized: bool) {
242 match self {
243 Self::HasWindow { window, .. } => window.set_maximized(maximized),
244 Self::None(attributes) => attributes.borrow_mut().maximized = maximized,
245 }
246 }
247
248 fn set_minimized(&self, minimized: bool) {
249 match self {
250 Self::HasWindow { window, .. } => window.set_minimized(minimized),
251 Self::None(..) => { }
253 }
254 }
255
256 fn set_resizable(&self, resizable: bool) {
257 match self {
258 Self::HasWindow { window, .. } => {
259 window.set_resizable(resizable);
260 }
261 Self::None(attributes) => attributes.borrow_mut().resizable = resizable,
262 }
263 }
264
265 fn set_min_inner_size(
266 &self,
267 min_inner_size: Option<winit::dpi::LogicalSize<f64>>,
268 scale_factor: f64,
269 ) {
270 match self {
271 Self::HasWindow { window, .. } => {
272 window
274 .set_min_inner_size(min_inner_size.map(|s| s.to_physical::<u32>(scale_factor)))
275 }
276 Self::None(attributes) => {
277 attributes.borrow_mut().min_inner_size = min_inner_size.map(|s| s.into());
279 }
280 }
281 }
282
283 fn set_max_inner_size(
284 &self,
285 max_inner_size: Option<winit::dpi::LogicalSize<f64>>,
286 scale_factor: f64,
287 ) {
288 match self {
289 Self::HasWindow { window, .. } => {
290 window
292 .set_max_inner_size(max_inner_size.map(|s| s.to_physical::<u32>(scale_factor)))
293 }
294 Self::None(attributes) => {
295 attributes.borrow_mut().max_inner_size = max_inner_size.map(|s| s.into())
297 }
298 }
299 }
300}
301
302#[derive(Default, PartialEq, Clone, Copy)]
303pub(crate) enum WindowVisibility {
304 #[default]
305 Hidden,
306 ShownFirstTime,
308 Shown,
309}
310
311pub struct WinitWindowAdapter {
314 pub shared_backend_data: Rc<SharedBackendData>,
315 window: corelib::api::Window,
316 pub(crate) self_weak: Weak<Self>,
317 pending_redraw: Cell<bool>,
318 color_scheme: OnceCell<Pin<Box<Property<ColorScheme>>>>,
319 constraints: Cell<corelib::window::LayoutConstraints>,
320 shown: Cell<WindowVisibility>,
322 window_level: Cell<winit::window::WindowLevel>,
323 maximized: Cell<bool>,
324 minimized: Cell<bool>,
325 fullscreen: Cell<bool>,
326
327 pub(crate) renderer: Box<dyn WinitCompatibleRenderer>,
328 size: Cell<PhysicalSize>,
331 pending_requested_size: Cell<Option<winit::dpi::Size>>,
333
334 has_explicit_size: Cell<bool>,
337
338 pending_resize_event_after_show: Cell<bool>,
340
341 #[cfg(target_arch = "wasm32")]
342 virtual_keyboard_helper: RefCell<Option<super::wasm_input_helper::WasmInputHelper>>,
343
344 #[cfg(any(enable_accesskit, muda))]
345 event_loop_proxy: EventLoopProxy<SlintEvent>,
346
347 pub(crate) window_event_filter: Cell<
348 Option<Box<dyn FnMut(&corelib::api::Window, &winit::event::WindowEvent) -> EventResult>>,
349 >,
350
351 winit_window_or_none: RefCell<WinitWindowOrNone>,
352 window_existence_wakers: RefCell<Vec<core::task::Waker>>,
353
354 #[cfg(not(use_winit_theme))]
355 xdg_settings_watcher: RefCell<Option<i_slint_core::future::JoinHandle<()>>>,
356
357 #[cfg(muda)]
358 menubar: RefCell<Option<vtable::VRc<i_slint_core::menus::MenuVTable>>>,
359
360 #[cfg(muda)]
361 context_menu: RefCell<Option<vtable::VRc<i_slint_core::menus::MenuVTable>>>,
362
363 #[cfg(all(muda, target_os = "macos"))]
364 muda_enable_default_menu_bar: bool,
365
366 window_icon_cache_key: RefCell<Option<ImageCacheKey>>,
369
370 frame_throttle: Box<dyn crate::frame_throttle::FrameThrottle>,
371}
372
373impl WinitWindowAdapter {
374 pub(crate) fn new(
376 shared_backend_data: Rc<SharedBackendData>,
377 renderer: Box<dyn WinitCompatibleRenderer>,
378 window_attributes: winit::window::WindowAttributes,
379 #[cfg(any(enable_accesskit, muda))] proxy: EventLoopProxy<SlintEvent>,
380 #[cfg(all(muda, target_os = "macos"))] muda_enable_default_menu_bar: bool,
381 ) -> Rc<Self> {
382 let self_rc = Rc::new_cyclic(|self_weak| Self {
383 shared_backend_data: shared_backend_data.clone(),
384 window: corelib::api::Window::new(self_weak.clone() as _),
385 self_weak: self_weak.clone(),
386 pending_redraw: Default::default(),
387 color_scheme: Default::default(),
388 constraints: Default::default(),
389 shown: Default::default(),
390 window_level: Default::default(),
391 maximized: Cell::default(),
392 minimized: Cell::default(),
393 fullscreen: Cell::default(),
394 winit_window_or_none: RefCell::new(WinitWindowOrNone::None(window_attributes.into())),
395 window_existence_wakers: RefCell::new(Vec::default()),
396 size: Cell::default(),
397 pending_requested_size: Cell::new(None),
398 has_explicit_size: Default::default(),
399 pending_resize_event_after_show: Default::default(),
400 renderer,
401 #[cfg(target_arch = "wasm32")]
402 virtual_keyboard_helper: Default::default(),
403 #[cfg(any(enable_accesskit, muda))]
404 event_loop_proxy: proxy,
405 window_event_filter: Cell::new(None),
406 #[cfg(not(use_winit_theme))]
407 xdg_settings_watcher: Default::default(),
408 #[cfg(muda)]
409 menubar: Default::default(),
410 #[cfg(muda)]
411 context_menu: Default::default(),
412 #[cfg(all(muda, target_os = "macos"))]
413 muda_enable_default_menu_bar,
414 window_icon_cache_key: Default::default(),
415 frame_throttle: crate::frame_throttle::create_frame_throttle(
416 self_weak.clone(),
417 shared_backend_data.is_wayland,
418 ),
419 });
420
421 self_rc.shared_backend_data.register_inactive_window((self_rc.clone()) as _);
422
423 self_rc
424 }
425
426 pub(crate) fn renderer(&self) -> &dyn WinitCompatibleRenderer {
427 self.renderer.as_ref()
428 }
429
430 pub fn ensure_window(
431 &self,
432 active_event_loop: &ActiveEventLoop,
433 ) -> Result<Arc<winit::window::Window>, PlatformError> {
434 #[allow(unused_mut)]
435 let mut window_attributes = match &*self.winit_window_or_none.borrow() {
436 WinitWindowOrNone::HasWindow { window, .. } => return Ok(window.clone()),
437 WinitWindowOrNone::None(attributes) => attributes.borrow().clone(),
438 };
439
440 #[cfg(all(unix, not(target_vendor = "apple")))]
441 {
442 if let Some(xdg_app_id) = WindowInner::from_pub(self.window()).xdg_app_id() {
443 #[cfg(feature = "wayland")]
444 {
445 use winit::platform::wayland::WindowAttributesExtWayland;
446 window_attributes = window_attributes.with_name(xdg_app_id.clone(), "");
447 }
448 #[cfg(feature = "x11")]
449 {
450 use winit::platform::x11::WindowAttributesExtX11;
451 window_attributes = window_attributes.with_name(xdg_app_id.clone(), "");
452 }
453 }
454 }
455
456 let show_after_creation = std::mem::replace(&mut window_attributes.visible, false);
460 let resizable = window_attributes.resizable;
461
462 let overriding_scale_factor = std::env::var("SLINT_SCALE_FACTOR")
463 .ok()
464 .and_then(|x| x.parse::<f32>().ok())
465 .filter(|f| *f > 0.);
466
467 if let Some(sf) = overriding_scale_factor {
468 apply_scale_factor_to_logical_sizes_in_attributes(&mut window_attributes, sf as f64)
469 }
470
471 #[cfg(all(muda, target_os = "windows"))]
473 if self.menubar.borrow().is_some() {
474 window_attributes = window_attributes.with_transparent(false);
475 }
476
477 let winit_window = self.renderer.resume(active_event_loop, window_attributes)?;
478
479 let scale_factor =
480 overriding_scale_factor.unwrap_or_else(|| winit_window.scale_factor() as f32);
481 self.window().try_dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor })?;
482
483 #[cfg(target_os = "ios")]
484 let (content_view, keyboard_curve_self) = {
485 use objc2::Message as _;
486 use raw_window_handle::HasWindowHandle as _;
487
488 let raw_window_handle::RawWindowHandle::UiKit(window_handle) =
489 winit_window.window_handle().unwrap().as_raw()
490 else {
491 panic!()
492 };
493 let view = unsafe { &*(window_handle.ui_view.as_ptr() as *const objc2_ui_kit::UIView) }
494 .retain();
495 (view, self.self_weak.clone())
496 };
497
498 *self.winit_window_or_none.borrow_mut() = WinitWindowOrNone::HasWindow {
499 window: winit_window.clone(),
500 #[cfg(enable_accesskit)]
501 accesskit_adapter: crate::accesskit::AccessKitAdapter::new(
502 self.self_weak.clone(),
503 active_event_loop,
504 &winit_window,
505 self.event_loop_proxy.clone(),
506 )
507 .into(),
508 #[cfg(muda)]
509 muda_adapter: RefCell::new(None),
510 #[cfg(muda)]
511 context_menu_muda_adapter: None.into(),
512 #[cfg(target_os = "ios")]
513 keyboard_curve_sampler: super::ios::KeyboardCurveSampler::new(
514 &content_view,
515 move |rect| {
516 if let Some(this) = keyboard_curve_self.upgrade() {
517 use i_slint_core::api::{LogicalPosition, LogicalSize};
518
519 this.window().set_virtual_keyboard(
520 LogicalPosition::new(rect.origin.x as _, rect.origin.y as _),
521 LogicalSize::new(rect.size.width as _, rect.size.height as _),
522 i_slint_core::InternalToken,
523 );
524 }
525 },
526 ),
527 };
528
529 #[cfg(muda)]
530 {
531 let new_muda_adapter = self.menubar.borrow().as_ref().map(|menubar| {
532 crate::muda::MudaAdapter::setup(
533 menubar,
534 &winit_window,
535 self.event_loop_proxy.clone(),
536 self.self_weak.clone(),
537 )
538 });
539 match &*self.winit_window_or_none.borrow() {
540 WinitWindowOrNone::HasWindow { muda_adapter, .. } => {
541 *muda_adapter.borrow_mut() = new_muda_adapter;
542 }
543 WinitWindowOrNone::None(_) => {
544 }
547 }
548 }
549
550 if show_after_creation {
551 self.shown.set(WindowVisibility::Hidden);
552 self.set_visibility(WindowVisibility::ShownFirstTime)?;
553 }
554
555 {
556 let mut buttons = winit_window.enabled_buttons();
560 buttons.set(WindowButtons::MAXIMIZE, resizable);
561 winit_window.set_enabled_buttons(buttons);
562 }
563
564 self.shared_backend_data
565 .register_window(winit_window.id(), (self.self_weak.upgrade().unwrap()) as _);
566
567 for waker in self.window_existence_wakers.take().into_iter() {
568 waker.wake();
569 }
570
571 Ok(winit_window)
572 }
573
574 pub(crate) fn suspend(&self) -> Result<(), PlatformError> {
575 let mut winit_window_or_none = self.winit_window_or_none.borrow_mut();
576 match *winit_window_or_none {
577 WinitWindowOrNone::HasWindow { ref window, .. } => {
578 self.renderer().suspend()?;
579
580 let last_window_rc = window.clone();
581
582 let mut attributes = Self::window_attributes().unwrap_or_default();
583 attributes.inner_size = Some(physical_size_to_winit(self.size.get()).into());
584 attributes.position = last_window_rc.outer_position().ok().map(|pos| pos.into());
585 *winit_window_or_none = WinitWindowOrNone::None(attributes.into());
586
587 if let Some(last_instance) = Arc::into_inner(last_window_rc) {
588 self.shared_backend_data.unregister_window(Some(last_instance.id()));
592 drop(last_instance);
593 } else {
594 i_slint_core::debug_log!(
595 "Slint winit backend: request to hide window failed because references to the window still exist. This could be an application issue, make sure that there are no slint::WindowHandle instances left"
596 );
597 }
598 }
599 WinitWindowOrNone::None(ref attributes) => {
600 attributes.borrow_mut().visible = false;
601 }
602 }
603
604 Ok(())
605 }
606
607 pub(crate) fn window_attributes() -> Result<WindowAttributes, PlatformError> {
608 let mut attrs = WindowAttributes::default().with_transparent(true).with_visible(false);
609
610 attrs = attrs.with_title("Slint Window".to_string());
611
612 #[cfg(target_arch = "wasm32")]
613 {
614 use winit::platform::web::WindowAttributesExtWebSys;
615
616 use wasm_bindgen::JsCast;
617
618 if let Some(html_canvas) = web_sys::window()
619 .ok_or_else(|| "winit backend: Could not retrieve DOM window".to_string())?
620 .document()
621 .ok_or_else(|| "winit backend: Could not retrieve DOM document".to_string())?
622 .get_element_by_id("canvas")
623 .and_then(|canvas_elem| canvas_elem.dyn_into::<web_sys::HtmlCanvasElement>().ok())
624 {
625 attrs = attrs
626 .with_canvas(Some(html_canvas))
627 .with_active(false);
630 }
631 };
632
633 Ok(attrs)
634 }
635
636 pub fn draw(&self) -> Result<(), PlatformError> {
638 if matches!(self.shown.get(), WindowVisibility::Hidden) {
639 return Ok(()); }
641
642 self.pending_redraw.set(false);
643
644 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
645 if self.pending_resize_event_after_show.take() {
651 self.resize_event(winit_window.inner_size())?;
652 }
653 }
654
655 let renderer = self.renderer();
656 renderer.render(self.window())?;
657
658 Ok(())
659 }
660
661 pub fn winit_window(&self) -> Option<Arc<winit::window::Window>> {
662 self.winit_window_or_none.borrow().as_window()
663 }
664
665 #[cfg(target_os = "ios")]
666 pub(crate) fn with_keyboard_curve_sampler<R>(
667 &self,
668 f: impl FnOnce(&super::ios::KeyboardCurveSampler) -> R,
669 ) -> Option<R> {
670 let winit_window_or_none = self.winit_window_or_none.borrow();
671 if let WinitWindowOrNone::HasWindow { keyboard_curve_sampler, .. } = &*winit_window_or_none
672 {
673 Some(f(keyboard_curve_sampler))
674 } else {
675 None
676 }
677 }
678
679 #[cfg(muda)]
680 pub fn rebuild_menubar(&self) {
681 let WinitWindowOrNone::HasWindow {
682 window: winit_window,
683 muda_adapter: maybe_muda_adapter,
684 ..
685 } = &*self.winit_window_or_none.borrow()
686 else {
687 return;
688 };
689 let mut maybe_muda_adapter = maybe_muda_adapter.borrow_mut();
690 let Some(muda_adapter) = maybe_muda_adapter.as_mut() else { return };
691 muda_adapter.rebuild_menu(&winit_window, self.menubar.borrow().as_ref(), MudaType::Menubar);
692 }
693
694 #[cfg(muda)]
695 pub fn muda_event(&self, entry_id: usize, muda_type: MudaType) {
696 let Ok(maybe_muda_adapter) = std::cell::Ref::filter_map(
697 self.winit_window_or_none.borrow(),
698 |winit_window_or_none| match (winit_window_or_none, muda_type) {
699 (WinitWindowOrNone::HasWindow { muda_adapter, .. }, MudaType::Menubar) => {
700 Some(muda_adapter)
701 }
702 (
703 WinitWindowOrNone::HasWindow { context_menu_muda_adapter, .. },
704 MudaType::Context,
705 ) => Some(context_menu_muda_adapter),
706 (WinitWindowOrNone::None(..), _) => None,
707 },
708 ) else {
709 return;
710 };
711 let maybe_muda_adapter = maybe_muda_adapter.borrow();
712 let Some(muda_adapter) = maybe_muda_adapter.as_ref() else { return };
713 let menu = match muda_type {
714 MudaType::Menubar => &self.menubar,
715 MudaType::Context => &self.context_menu,
716 };
717 let menu = menu.borrow();
718 let Some(menu) = menu.as_ref() else { return };
719 muda_adapter.invoke(menu, entry_id);
720 }
721
722 #[cfg(target_arch = "wasm32")]
723 pub fn input_method_focused(&self) -> bool {
724 match self.virtual_keyboard_helper.try_borrow() {
725 Ok(vkh) => vkh.as_ref().map_or(false, |h| h.has_focus()),
726 Err(_) => true,
729 }
730 }
731
732 #[cfg(not(target_arch = "wasm32"))]
733 pub fn input_method_focused(&self) -> bool {
734 false
735 }
736
737 fn resize_window(&self, size: winit::dpi::Size) -> Result<bool, PlatformError> {
740 match &*self.winit_window_or_none.borrow() {
741 WinitWindowOrNone::HasWindow { window, .. } => {
742 if let Some(size) = window.request_inner_size(size) {
743 self.resize_event(size)?;
745 Ok(true)
746 } else {
747 self.pending_requested_size.set(size.into());
748 Ok(false)
750 }
751 }
752 WinitWindowOrNone::None(attributes) => {
753 let scale_factor = self.window().scale_factor() as _;
754 attributes.borrow_mut().inner_size =
758 Some(size.to_logical::<f64>(scale_factor).into());
759 self.resize_event(size.to_physical(scale_factor))?;
760 Ok(true)
761 }
762 }
763 }
764
765 pub fn resize_event(&self, size: winit::dpi::PhysicalSize<u32>) -> Result<(), PlatformError> {
766 self.pending_resize_event_after_show.set(false);
767 if size.width > 0 && size.height > 0 {
771 let physical_size = physical_size_to_slint(&size);
772 self.size.set(physical_size);
773 self.pending_requested_size.set(None);
774 let scale_factor = WindowInner::from_pub(self.window()).scale_factor();
775
776 let size = physical_size.to_logical(scale_factor);
777 self.window().try_dispatch_event(WindowEvent::Resized { size })?;
778
779 WindowInner::from_pub(self.window())
780 .set_window_item_safe_area(self.safe_area_inset().to_logical(scale_factor));
781
782 #[cfg(target_arch = "wasm32")]
786 if let Some(html_canvas) = self
787 .winit_window_or_none
788 .borrow()
789 .as_window()
790 .and_then(|winit_window| winit_window.canvas())
791 {
792 html_canvas.set_width(physical_size.width);
793 html_canvas.set_height(physical_size.height);
794 }
795 }
796 Ok(())
797 }
798
799 pub fn set_color_scheme(&self, scheme: ColorScheme) {
800 self.color_scheme
801 .get_or_init(|| Box::pin(Property::new(ColorScheme::Unknown)))
802 .as_ref()
803 .set(scheme);
804
805 #[cfg(target_os = "windows")]
807 if let WinitWindowOrNone::HasWindow {
808 window: winit_window,
809 muda_adapter: maybe_muda_adapter,
810 ..
811 } = &*self.winit_window_or_none.borrow()
812 {
813 if let Some(muda_adapter) = maybe_muda_adapter.borrow().as_ref() {
814 muda_adapter.set_menubar_theme(&winit_window, scheme);
815 };
816 }
817
818 #[cfg(not(use_winit_theme))]
820 if let Some(winit_window) = self.winit_window() {
821 winit_window.set_theme(match scheme {
822 ColorScheme::Unknown => None,
823 ColorScheme::Dark => Some(winit::window::Theme::Dark),
824 ColorScheme::Light => Some(winit::window::Theme::Light),
825 _ => None,
826 });
827 }
828 }
829
830 pub fn window_state_event(&self) {
831 let Some(winit_window) = self.winit_window_or_none.borrow().as_window() else { return };
832
833 if let Some(minimized) = winit_window.is_minimized() {
834 self.minimized.set(minimized);
835 if minimized != self.window().is_minimized() {
836 self.window().set_minimized(minimized);
837 }
838 }
839
840 let maximized = winit_window.is_maximized();
846 if !self.window().is_minimized() {
847 self.maximized.set(maximized);
848 if maximized != self.window().is_maximized() {
849 self.window().set_maximized(maximized);
850 }
851 }
852
853 let fullscreen = winit_window.fullscreen().is_some();
857 if fullscreen != self.window().is_fullscreen() {
858 self.window().set_fullscreen(fullscreen);
859 }
860 }
861
862 #[cfg(enable_accesskit)]
863 pub(crate) fn accesskit_adapter(
864 &self,
865 ) -> Option<std::cell::Ref<'_, RefCell<crate::accesskit::AccessKitAdapter>>> {
866 std::cell::Ref::filter_map(
867 self.winit_window_or_none.try_borrow().ok()?,
868 |wor: &WinitWindowOrNone| match wor {
869 WinitWindowOrNone::HasWindow { accesskit_adapter, .. } => Some(accesskit_adapter),
870 WinitWindowOrNone::None(..) => None,
871 },
872 )
873 .ok()
874 }
875
876 #[cfg(enable_accesskit)]
877 pub(crate) fn with_access_kit_adapter_from_weak_window_adapter(
878 self_weak: Weak<Self>,
879 callback: impl FnOnce(&RefCell<crate::accesskit::AccessKitAdapter>),
880 ) {
881 let Some(self_) = self_weak.upgrade() else { return };
882 let winit_window_or_none = self_.winit_window_or_none.borrow();
883 match &*winit_window_or_none {
884 WinitWindowOrNone::HasWindow { accesskit_adapter, .. } => callback(accesskit_adapter),
885 WinitWindowOrNone::None(..) => {}
886 }
887 }
888
889 #[cfg(not(use_winit_theme))]
890 fn spawn_xdg_settings_watcher(&self) -> Option<i_slint_core::future::JoinHandle<()>> {
891 let window_inner = WindowInner::from_pub(self.window());
892 let self_weak = self.self_weak.clone();
893 window_inner
894 .context()
895 .spawn_local(async move {
896 if let Err(err) = crate::xdg_color_scheme::watch(self_weak).await {
897 i_slint_core::debug_log!("Error watching for xdg color schemes: {}", err);
898 }
899 })
900 .ok()
901 }
902
903 pub fn activation_changed(&self, is_active: bool) -> Result<(), PlatformError> {
904 let have_focus = is_active || self.input_method_focused();
905 let slint_window = self.window();
906 let runtime_window = WindowInner::from_pub(slint_window);
907 if have_focus != runtime_window.active() {
910 slint_window.try_dispatch_event(
911 corelib::platform::WindowEvent::WindowActiveChanged(have_focus),
912 )?;
913 }
914
915 #[cfg(all(muda, target_os = "macos"))]
916 {
917 if let WinitWindowOrNone::HasWindow { muda_adapter, .. } =
918 &*self.winit_window_or_none.borrow()
919 {
920 if muda_adapter.borrow().is_none()
921 && self.muda_enable_default_menu_bar
922 && self.menubar.borrow().is_none()
923 {
924 *muda_adapter.borrow_mut() =
925 Some(crate::muda::MudaAdapter::setup_default_menu_bar()?);
926 }
927
928 if let Some(muda_adapter) = muda_adapter.borrow().as_ref() {
929 muda_adapter.window_activation_changed(is_active);
930 }
931 }
932 }
933
934 Ok(())
935 }
936
937 fn set_visibility(&self, visibility: WindowVisibility) -> Result<(), PlatformError> {
938 if visibility == self.shown.get() {
939 return Ok(());
940 }
941
942 self.shown.set(visibility);
943 self.pending_resize_event_after_show.set(!matches!(visibility, WindowVisibility::Hidden));
944 self.pending_redraw.set(false);
945 if matches!(visibility, WindowVisibility::ShownFirstTime | WindowVisibility::Shown) {
946 let recreating_window = matches!(visibility, WindowVisibility::Shown);
947
948 let Some(winit_window) = self.winit_window() else {
949 self.winit_window_or_none.borrow().set_visible(true);
952 self.shared_backend_data
953 .register_inactive_window((self.self_weak.upgrade().unwrap()) as _);
954 return Ok(());
955 };
956
957 let runtime_window = WindowInner::from_pub(self.window());
958
959 let scale_factor = runtime_window.scale_factor() as f64;
960
961 let component_rc = runtime_window.component();
962 let component = ItemTreeRc::borrow_pin(&component_rc);
963
964 let layout_info_h = component.as_ref().layout_info(Orientation::Horizontal);
965 if let Some(window_item) = runtime_window.window_item() {
966 window_item.width.set(LogicalLength::new(layout_info_h.preferred_bounded()));
969 }
970 let layout_info_v = component.as_ref().layout_info(Orientation::Vertical);
971 #[allow(unused_mut)]
972 let mut preferred_size = winit::dpi::LogicalSize::new(
973 layout_info_h.preferred_bounded(),
974 layout_info_v.preferred_bounded(),
975 );
976
977 #[cfg(target_arch = "wasm32")]
978 if let Some(html_canvas) = winit_window.canvas() {
979 if !is_preferred_sized_canvas(&html_canvas)
981 && !canvas_has_explicit_size_set(&html_canvas)
982 {
983 let existing_canvas_size = winit::dpi::LogicalSize::new(
984 html_canvas.client_width() as f32,
985 html_canvas.client_height() as f32,
986 );
987 preferred_size.width = existing_canvas_size.width;
988
989 preferred_size.height = existing_canvas_size.height;
990 }
991 }
992
993 if winit_window.fullscreen().is_none()
994 && !self.has_explicit_size.get()
995 && preferred_size.width > 0 as Coord
996 && preferred_size.height > 0 as Coord
997 && !recreating_window
999 {
1000 let size = preferred_size.to_physical::<u32>(scale_factor);
1002 self.resize_window(size.into())?;
1003 };
1004
1005 winit_window.set_visible(true);
1006
1007 if let Some(color_scheme_prop) = self.color_scheme.get() {
1010 if let Some(theme) = winit_window.theme() {
1011 color_scheme_prop.as_ref().set(match theme {
1012 winit::window::Theme::Dark => ColorScheme::Dark,
1013 winit::window::Theme::Light => ColorScheme::Light,
1014 })
1015 }
1016 }
1017
1018 #[cfg(target_arch = "wasm32")]
1022 if self.pending_redraw.get() {
1023 self.draw()?;
1024 };
1025
1026 Ok(())
1027 } else {
1028 if self.winit_window_or_none.borrow().as_window().is_some_and(|winit_window| {
1030 use raw_window_handle::HasWindowHandle;
1031 winit_window.window_handle().is_ok_and(|h| {
1032 matches!(h.as_raw(), raw_window_handle::RawWindowHandle::Wayland(..))
1033 }) || std::env::var_os("SLINT_DESTROY_WINDOW_ON_HIDE").is_some()
1034 }) {
1035 self.suspend()?;
1036 } else {
1040 self.winit_window_or_none.borrow().set_visible(false);
1041 }
1042
1043 Ok(())
1048 }
1049 }
1050
1051 pub(crate) fn visibility(&self) -> WindowVisibility {
1052 self.shown.get()
1053 }
1054
1055 pub(crate) fn pending_redraw(&self) -> bool {
1056 self.pending_redraw.get()
1057 }
1058
1059 pub async fn async_winit_window(
1060 self_weak: Weak<Self>,
1061 ) -> Result<Arc<winit::window::Window>, PlatformError> {
1062 std::future::poll_fn(move |context| {
1063 let Some(self_) = self_weak.upgrade() else {
1064 return std::task::Poll::Ready(Err(format!(
1065 "Unable to obtain winit window from destroyed window"
1066 )
1067 .into()));
1068 };
1069 match self_.winit_window() {
1070 Some(window) => std::task::Poll::Ready(Ok(window)),
1071 None => {
1072 let waker = context.waker();
1073 if !self_.window_existence_wakers.borrow().iter().any(|w| w.will_wake(waker)) {
1074 self_.window_existence_wakers.borrow_mut().push(waker.clone());
1075 }
1076 std::task::Poll::Pending
1077 }
1078 }
1079 })
1080 .await
1081 }
1082}
1083
1084impl WindowAdapter for WinitWindowAdapter {
1085 fn window(&self) -> &corelib::api::Window {
1086 &self.window
1087 }
1088
1089 fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
1090 self.renderer().as_core_renderer()
1091 }
1092
1093 fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
1094 self.set_visibility(if visible {
1095 WindowVisibility::Shown
1096 } else {
1097 WindowVisibility::Hidden
1098 })
1099 }
1100
1101 fn position(&self) -> Option<corelib::api::PhysicalPosition> {
1102 match &*self.winit_window_or_none.borrow() {
1103 WinitWindowOrNone::HasWindow { window, .. } => match window.outer_position() {
1104 Ok(outer_position) => {
1105 Some(corelib::api::PhysicalPosition::new(outer_position.x, outer_position.y))
1106 }
1107 Err(_) => None,
1108 },
1109 WinitWindowOrNone::None(attributes) => {
1110 attributes.borrow().position.map(|pos| {
1111 match pos {
1112 winit::dpi::Position::Physical(phys_pos) => {
1113 corelib::api::PhysicalPosition::new(phys_pos.x, phys_pos.y)
1114 }
1115 winit::dpi::Position::Logical(logical_pos) => {
1116 corelib::api::LogicalPosition::new(
1118 logical_pos.x as _,
1119 logical_pos.y as _,
1120 )
1121 .to_physical(self.window().scale_factor())
1122 }
1123 }
1124 })
1125 }
1126 }
1127 }
1128
1129 fn set_position(&self, position: corelib::api::WindowPosition) {
1130 let winit_pos = position_to_winit(&position);
1131 match &*self.winit_window_or_none.borrow() {
1132 WinitWindowOrNone::HasWindow { window, .. } => window.set_outer_position(winit_pos),
1133 WinitWindowOrNone::None(attributes) => {
1134 attributes.borrow_mut().position = Some(winit_pos);
1135 }
1136 }
1137 }
1138
1139 fn set_size(&self, size: corelib::api::WindowSize) {
1140 self.has_explicit_size.set(true);
1141 self.resize_window(window_size_to_winit(&size)).ok();
1143 }
1144
1145 fn size(&self) -> corelib::api::PhysicalSize {
1146 self.size.get()
1147 }
1148
1149 fn request_redraw(&self) {
1150 if !self.pending_redraw.replace(true) {
1151 self.frame_throttle.request_throttled_redraw();
1152 }
1153 }
1154
1155 #[allow(clippy::unnecessary_cast)] fn update_window_properties(&self, properties: corelib::window::WindowProperties<'_>) {
1157 let Some(window_item) = WindowInner::from_pub(&self.window).window_item() else {
1158 return;
1159 };
1160 let window_item = window_item.as_pin_ref();
1161
1162 let winit_window_or_none = self.winit_window_or_none.borrow();
1163
1164 let sf = self.window().scale_factor();
1166
1167 let icon_image = window_item.icon();
1169 let icon_image_cache_key = ImageCacheKey::new((&icon_image).into());
1170 if *self.window_icon_cache_key.borrow() != icon_image_cache_key {
1171 *self.window_icon_cache_key.borrow_mut() = icon_image_cache_key;
1172 winit_window_or_none.set_window_icon(icon_to_winit(
1173 icon_image,
1174 i_slint_core::lengths::LogicalSize::new(64., 64.) * ScaleFactor::new(sf),
1175 ));
1176 }
1177 winit_window_or_none.set_title(&properties.title());
1178 winit_window_or_none.set_decorations(
1179 !window_item.no_frame() || winit_window_or_none.fullscreen().is_some(),
1180 );
1181
1182 let new_window_level = if window_item.always_on_top() {
1183 winit::window::WindowLevel::AlwaysOnTop
1184 } else {
1185 winit::window::WindowLevel::Normal
1186 };
1187 if self.window_level.replace(new_window_level) != new_window_level {
1190 winit_window_or_none.set_window_level(new_window_level);
1191 }
1192
1193 let mut width = window_item.width().get() as f32;
1194 let mut height = window_item.height().get() as f32;
1195 let mut must_resize = false;
1196 let existing_size = self.size.get().to_logical(sf);
1197
1198 if width <= 0. || height <= 0. {
1199 must_resize = true;
1200 if width <= 0. {
1201 width = existing_size.width;
1202 }
1203 if height <= 0. {
1204 height = existing_size.height;
1205 }
1206 }
1207
1208 if ((existing_size.width - width).abs() > 1. || (existing_size.height - height).abs() > 1.)
1211 && self.pending_requested_size.get().is_none()
1212 {
1213 if winit_window_or_none.fullscreen().is_none() {
1217 let immediately_resized = self
1219 .resize_window(winit::dpi::LogicalSize::new(width, height).into())
1220 .unwrap_or_default();
1221 if immediately_resized {
1222 must_resize = false;
1224 }
1225 }
1226 }
1227
1228 if must_resize {
1229 self.window()
1230 .try_dispatch_event(WindowEvent::Resized {
1231 size: i_slint_core::api::LogicalSize::new(width, height),
1232 })
1233 .unwrap();
1234 WindowInner::from_pub(self.window())
1235 .set_window_item_safe_area(window_item.safe_area_insets());
1236 }
1237
1238 let m = properties.is_fullscreen();
1239 if m != self.fullscreen.get() {
1240 if m {
1241 if winit_window_or_none.fullscreen().is_none() {
1242 winit_window_or_none
1243 .set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
1244 }
1245 } else {
1246 winit_window_or_none.set_fullscreen(None);
1247 }
1248 self.fullscreen.set(m);
1249 }
1250
1251 let m = properties.is_maximized();
1252 if m != self.maximized.get() {
1253 self.maximized.set(m);
1254 winit_window_or_none.set_maximized(m);
1255 }
1256
1257 let m = properties.is_minimized();
1258 if m != self.minimized.get() {
1259 self.minimized.set(m);
1260 winit_window_or_none.set_minimized(m);
1261 }
1262
1263 if winit_window_or_none.fullscreen().is_some() {
1268 return;
1269 }
1270
1271 let new_constraints = properties.layout_constraints();
1272 if new_constraints == self.constraints.get() {
1273 return;
1274 }
1275
1276 self.constraints.set(new_constraints);
1277
1278 let resizable = window_is_resizable(new_constraints.min, new_constraints.max);
1279 winit_window_or_none.set_resizable(resizable);
1281 let winit_min_inner =
1284 new_constraints.min.map(logical_size_to_winit).map(filter_out_zero_width_or_height);
1285 winit_window_or_none.set_min_inner_size(winit_min_inner, sf as f64);
1286 let winit_max_inner =
1287 new_constraints.max.map(logical_size_to_winit).map(filter_out_zero_width_or_height);
1288 winit_window_or_none.set_max_inner_size(winit_max_inner, sf as f64);
1289
1290 #[cfg(not(ios_and_friends))]
1292 adjust_window_size_to_satisfy_constraints(self, winit_min_inner, winit_max_inner);
1293
1294 #[cfg(target_arch = "wasm32")]
1296 if let Some(canvas) =
1297 winit_window_or_none.as_window().and_then(|winit_window| winit_window.canvas())
1298 {
1299 if is_preferred_sized_canvas(&canvas) {
1300 let pref = new_constraints.preferred;
1301 if pref.width > 0 as Coord || pref.height > 0 as Coord {
1302 self.resize_window(logical_size_to_winit(pref).into()).ok();
1304 };
1305 }
1306 }
1307 }
1308
1309 fn internal(&self, _: corelib::InternalToken) -> Option<&dyn WindowAdapterInternal> {
1310 Some(self)
1311 }
1312}
1313
1314impl WindowAdapterInternal for WinitWindowAdapter {
1315 fn set_mouse_cursor(&self, cursor: MouseCursor) {
1316 let winit_cursor = match cursor {
1317 MouseCursor::Default => winit::window::CursorIcon::Default,
1318 MouseCursor::None => winit::window::CursorIcon::Default,
1319 MouseCursor::Help => winit::window::CursorIcon::Help,
1320 MouseCursor::Pointer => winit::window::CursorIcon::Pointer,
1321 MouseCursor::Progress => winit::window::CursorIcon::Progress,
1322 MouseCursor::Wait => winit::window::CursorIcon::Wait,
1323 MouseCursor::Crosshair => winit::window::CursorIcon::Crosshair,
1324 MouseCursor::Text => winit::window::CursorIcon::Text,
1325 MouseCursor::Alias => winit::window::CursorIcon::Alias,
1326 MouseCursor::Copy => winit::window::CursorIcon::Copy,
1327 MouseCursor::Move => winit::window::CursorIcon::Move,
1328 MouseCursor::NoDrop => winit::window::CursorIcon::NoDrop,
1329 MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed,
1330 MouseCursor::Grab => winit::window::CursorIcon::Grab,
1331 MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing,
1332 MouseCursor::ColResize => winit::window::CursorIcon::ColResize,
1333 MouseCursor::RowResize => winit::window::CursorIcon::RowResize,
1334 MouseCursor::NResize => winit::window::CursorIcon::NResize,
1335 MouseCursor::EResize => winit::window::CursorIcon::EResize,
1336 MouseCursor::SResize => winit::window::CursorIcon::SResize,
1337 MouseCursor::WResize => winit::window::CursorIcon::WResize,
1338 MouseCursor::NeResize => winit::window::CursorIcon::NeResize,
1339 MouseCursor::NwResize => winit::window::CursorIcon::NwResize,
1340 MouseCursor::SeResize => winit::window::CursorIcon::SeResize,
1341 MouseCursor::SwResize => winit::window::CursorIcon::SwResize,
1342 MouseCursor::EwResize => winit::window::CursorIcon::EwResize,
1343 MouseCursor::NsResize => winit::window::CursorIcon::NsResize,
1344 MouseCursor::NeswResize => winit::window::CursorIcon::NeswResize,
1345 MouseCursor::NwseResize => winit::window::CursorIcon::NwseResize,
1346 _ => winit::window::CursorIcon::Default,
1347 };
1348 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1349 winit_window.set_cursor_visible(cursor != MouseCursor::None);
1350 winit_window.set_cursor(winit_cursor);
1351 }
1352 }
1353
1354 fn input_method_request(&self, request: corelib::window::InputMethodRequest) {
1355 #[cfg(not(target_arch = "wasm32"))]
1356 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1357 let props = match &request {
1358 corelib::window::InputMethodRequest::Enable(props) => {
1359 winit_window.set_ime_allowed(true);
1360 props
1361 }
1362 corelib::window::InputMethodRequest::Disable => {
1363 return winit_window.set_ime_allowed(false);
1364 }
1365 corelib::window::InputMethodRequest::Update(props) => props,
1366 _ => return,
1367 };
1368 winit_window.set_ime_purpose(match props.input_type {
1369 corelib::items::InputType::Password => winit::window::ImePurpose::Password,
1370 corelib::items::InputType::Text
1371 | corelib::items::InputType::Number
1372 | corelib::items::InputType::Decimal
1373 | _ => winit::window::ImePurpose::Normal,
1374 });
1375 winit_window.set_ime_cursor_area(
1376 position_to_winit(&props.cursor_rect_origin.into()),
1377 window_size_to_winit(&props.cursor_rect_size.into()),
1378 );
1379 }
1380
1381 #[cfg(target_arch = "wasm32")]
1382 match request {
1383 corelib::window::InputMethodRequest::Enable(..) => {
1384 let mut vkh = self.virtual_keyboard_helper.borrow_mut();
1385 let Some(canvas) =
1386 self.winit_window().and_then(|winit_window| winit_window.canvas())
1387 else {
1388 return;
1389 };
1390 let h = vkh.get_or_insert_with(|| {
1391 super::wasm_input_helper::WasmInputHelper::new(self.self_weak.clone(), canvas)
1392 });
1393 h.show();
1394 }
1395 corelib::window::InputMethodRequest::Disable => {
1396 if let Some(h) = &*self.virtual_keyboard_helper.borrow() {
1397 h.hide()
1398 }
1399 }
1400 _ => {}
1401 };
1402 }
1403
1404 fn color_scheme(&self) -> ColorScheme {
1405 self.color_scheme
1406 .get_or_init(|| {
1407 Box::pin(Property::new({
1408 cfg_if::cfg_if! {
1409 if #[cfg(use_winit_theme)] {
1410 self.winit_window_or_none
1411 .borrow()
1412 .as_window()
1413 .and_then(|window| window.theme())
1414 .map_or(ColorScheme::Unknown, |theme| match theme {
1415 winit::window::Theme::Dark => ColorScheme::Dark,
1416 winit::window::Theme::Light => ColorScheme::Light,
1417 })
1418 } else {
1419 if let Some(old_watch) = self.xdg_settings_watcher.replace(self.spawn_xdg_settings_watcher()) {
1420 old_watch.abort()
1421 }
1422 ColorScheme::Unknown
1423 }
1424 }
1425 }))
1426 })
1427 .as_ref()
1428 .get()
1429 }
1430
1431 #[cfg(muda)]
1432 fn supports_native_menu_bar(&self) -> bool {
1433 true
1434 }
1435
1436 #[cfg(muda)]
1437 fn setup_menubar(&self, menubar: vtable::VRc<i_slint_core::menus::MenuVTable>) {
1438 self.menubar.replace(Some(menubar));
1439
1440 if let WinitWindowOrNone::HasWindow { muda_adapter, .. } =
1441 &*self.winit_window_or_none.borrow()
1442 {
1443 drop(muda_adapter.borrow_mut().take());
1445 muda_adapter.replace(Some(crate::muda::MudaAdapter::setup(
1446 self.menubar.borrow().as_ref().unwrap(),
1447 &self.winit_window().unwrap(),
1448 self.event_loop_proxy.clone(),
1449 self.self_weak.clone(),
1450 )));
1451 }
1452 }
1453
1454 #[cfg(muda)]
1455 fn show_native_popup_menu(
1456 &self,
1457 context_menu_item: vtable::VRc<i_slint_core::menus::MenuVTable>,
1458 position: LogicalPosition,
1459 ) -> bool {
1460 self.context_menu.replace(Some(context_menu_item));
1461
1462 if let WinitWindowOrNone::HasWindow { context_menu_muda_adapter, .. } =
1463 &*self.winit_window_or_none.borrow()
1464 {
1465 drop(context_menu_muda_adapter.borrow_mut().take());
1467 if let Some(new_adapter) = crate::muda::MudaAdapter::show_context_menu(
1468 self.context_menu.borrow().as_ref().unwrap(),
1469 &self.winit_window().unwrap(),
1470 position,
1471 self.event_loop_proxy.clone(),
1472 ) {
1473 context_menu_muda_adapter.replace(Some(new_adapter));
1474 return true;
1475 }
1476 }
1477 false
1478 }
1479
1480 #[cfg(enable_accesskit)]
1481 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {
1482 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1483 accesskit_adapter_cell.borrow_mut().handle_focus_item_change();
1484 }
1485
1486 #[cfg(enable_accesskit)]
1487 fn register_item_tree(&self, _: ItemTreeRefPin) {
1488 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1489 if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
1491 a.reload_tree();
1492 };
1493 }
1494
1495 #[cfg(enable_accesskit)]
1496 fn unregister_item_tree(
1497 &self,
1498 component: ItemTreeRef,
1499 _: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
1500 ) {
1501 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1502 if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
1503 a.unregister_item_tree(component);
1504 };
1505 }
1506
1507 #[cfg(feature = "raw-window-handle-06")]
1508 fn window_handle_06_rc(
1509 &self,
1510 ) -> Result<Arc<dyn raw_window_handle::HasWindowHandle>, raw_window_handle::HandleError> {
1511 self.winit_window_or_none
1512 .borrow()
1513 .as_window()
1514 .map_or(Err(raw_window_handle::HandleError::Unavailable), |window| Ok(window))
1515 }
1516
1517 #[cfg(feature = "raw-window-handle-06")]
1518 fn display_handle_06_rc(
1519 &self,
1520 ) -> Result<Arc<dyn raw_window_handle::HasDisplayHandle>, raw_window_handle::HandleError> {
1521 self.winit_window_or_none
1522 .borrow()
1523 .as_window()
1524 .map_or(Err(raw_window_handle::HandleError::Unavailable), |window| Ok(window))
1525 }
1526
1527 fn bring_to_front(&self) -> Result<(), PlatformError> {
1528 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1529 winit_window.set_minimized(false);
1530 winit_window.focus_window();
1531 }
1532 Ok(())
1533 }
1534
1535 #[cfg(target_os = "ios")]
1536 fn safe_area_inset(&self) -> i_slint_core::lengths::PhysicalEdges {
1537 self.winit_window_or_none
1538 .borrow()
1539 .as_window()
1540 .and_then(|window| {
1541 let outer_position = window.outer_position().ok()?;
1542 let inner_position = window.inner_position().ok()?;
1543 let outer_size = window.outer_size();
1544 let inner_size = window.inner_size();
1545 Some(i_slint_core::lengths::PhysicalEdges::new(
1546 inner_position.y - outer_position.y,
1547 outer_size.height as i32
1548 - (inner_size.height as i32)
1549 - (inner_position.y - outer_position.y),
1550 inner_position.x - outer_position.x,
1551 outer_size.width as i32
1552 - (inner_size.width as i32)
1553 - (inner_position.x - outer_position.x),
1554 ))
1555 })
1556 .unwrap_or_default()
1557 }
1558}
1559
1560impl Drop for WinitWindowAdapter {
1561 fn drop(&mut self) {
1562 self.shared_backend_data.unregister_window(
1563 self.winit_window_or_none.borrow().as_window().map(|winit_window| winit_window.id()),
1564 );
1565
1566 #[cfg(not(use_winit_theme))]
1567 if let Some(xdg_watch_future) = self.xdg_settings_watcher.take() {
1568 xdg_watch_future.abort();
1569 }
1570 }
1571}
1572
1573#[cfg(not(ios_and_friends))]
1575fn adjust_window_size_to_satisfy_constraints(
1576 adapter: &WinitWindowAdapter,
1577 min_size: Option<winit::dpi::LogicalSize<f64>>,
1578 max_size: Option<winit::dpi::LogicalSize<f64>>,
1579) {
1580 let sf = adapter.window().scale_factor() as f64;
1581 let Some(current_size) = adapter
1582 .pending_requested_size
1583 .get()
1584 .or_else(|| {
1585 let existing_adapter_size = adapter.size.get();
1586 (existing_adapter_size.width != 0 && existing_adapter_size.height != 0)
1587 .then(|| physical_size_to_winit(existing_adapter_size).into())
1588 })
1589 .map(|s| s.to_logical::<f64>(sf))
1590 else {
1591 return;
1592 };
1593
1594 let mut window_size = current_size;
1595 if let Some(min_size) = min_size {
1596 let min_size = min_size.cast();
1597 window_size.width = window_size.width.max(min_size.width);
1598 window_size.height = window_size.height.max(min_size.height);
1599 }
1600
1601 if let Some(max_size) = max_size {
1602 let max_size = max_size.cast();
1603 window_size.width = window_size.width.min(max_size.width);
1604 window_size.height = window_size.height.min(max_size.height);
1605 }
1606
1607 if window_size != current_size {
1608 adapter.resize_window(window_size.into()).ok();
1610 }
1611}
1612
1613#[cfg(target_family = "wasm")]
1614fn is_preferred_sized_canvas(canvas: &web_sys::HtmlCanvasElement) -> bool {
1615 canvas
1616 .dataset()
1617 .get("slintAutoResizeToPreferred")
1618 .and_then(|val_str| val_str.parse::<bool>().ok())
1619 .unwrap_or_default()
1620}
1621
1622#[cfg(target_family = "wasm")]
1623fn canvas_has_explicit_size_set(canvas: &web_sys::HtmlCanvasElement) -> bool {
1624 let style = canvas.style();
1625 if !style.get_property_value("width").unwrap_or_default().is_empty()
1626 || !style.get_property_value("height").unwrap_or_default().is_empty()
1627 {
1628 return true;
1629 }
1630
1631 let Some(window) = web_sys::window() else {
1632 return false;
1633 };
1634 let Some(computed_style) = window.get_computed_style(&canvas).ok().flatten() else {
1635 return false;
1636 };
1637
1638 computed_style.get_property_value("width").ok().as_deref() != Some("auto")
1639 || computed_style.get_property_value("height").ok().as_deref() != Some("auto")
1640}