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;
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::api::PhysicalSize;
40use corelib::layout::Orientation;
41use corelib::lengths::LogicalLength;
42use corelib::platform::{PlatformError, WindowEvent};
43use corelib::window::{WindowAdapter, WindowAdapterInternal, WindowInner};
44use corelib::Property;
45use corelib::{graphics::*, Coord};
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 },
175 None(RefCell<WindowAttributes>),
176}
177
178impl WinitWindowOrNone {
179 fn as_window(&self) -> Option<Arc<winit::window::Window>> {
180 match self {
181 Self::HasWindow { window, .. } => Some(window.clone()),
182 Self::None { .. } => None,
183 }
184 }
185
186 fn set_window_icon(&self, icon: Option<winit::window::Icon>) {
187 match self {
188 Self::HasWindow { window, .. } => {
189 #[cfg(target_family = "windows")]
190 window.set_taskbar_icon(icon.as_ref().cloned());
191 window.set_window_icon(icon);
192 }
193 Self::None(attributes) => attributes.borrow_mut().window_icon = icon,
194 }
195 }
196
197 fn set_title(&self, title: &str) {
198 match self {
199 Self::HasWindow { window, .. } => window.set_title(title),
200 Self::None(attributes) => attributes.borrow_mut().title = title.into(),
201 }
202 }
203
204 fn set_decorations(&self, decorations: bool) {
205 match self {
206 Self::HasWindow { window, .. } => window.set_decorations(decorations),
207 Self::None(attributes) => attributes.borrow_mut().decorations = decorations,
208 }
209 }
210
211 fn fullscreen(&self) -> Option<winit::window::Fullscreen> {
212 match self {
213 Self::HasWindow { window, .. } => window.fullscreen(),
214 Self::None(attributes) => attributes.borrow().fullscreen.clone(),
215 }
216 }
217
218 fn set_fullscreen(&self, fullscreen: Option<winit::window::Fullscreen>) {
219 match self {
220 Self::HasWindow { window, .. } => window.set_fullscreen(fullscreen),
221 Self::None(attributes) => attributes.borrow_mut().fullscreen = fullscreen,
222 }
223 }
224
225 fn set_window_level(&self, level: winit::window::WindowLevel) {
226 match self {
227 Self::HasWindow { window, .. } => window.set_window_level(level),
228 Self::None(attributes) => attributes.borrow_mut().window_level = level,
229 }
230 }
231
232 fn set_visible(&self, visible: bool) {
233 match self {
234 Self::HasWindow { window, .. } => window.set_visible(visible),
235 Self::None(attributes) => attributes.borrow_mut().visible = visible,
236 }
237 }
238
239 fn set_maximized(&self, maximized: bool) {
240 match self {
241 Self::HasWindow { window, .. } => window.set_maximized(maximized),
242 Self::None(attributes) => attributes.borrow_mut().maximized = maximized,
243 }
244 }
245
246 fn set_minimized(&self, minimized: bool) {
247 match self {
248 Self::HasWindow { window, .. } => window.set_minimized(minimized),
249 Self::None(..) => { }
251 }
252 }
253
254 fn set_resizable(&self, resizable: bool) {
255 match self {
256 Self::HasWindow { window, .. } => {
257 window.set_resizable(resizable);
258 }
259 Self::None(attributes) => attributes.borrow_mut().resizable = resizable,
260 }
261 }
262
263 fn set_min_inner_size(
264 &self,
265 min_inner_size: Option<winit::dpi::LogicalSize<f64>>,
266 scale_factor: f64,
267 ) {
268 match self {
269 Self::HasWindow { window, .. } => {
270 window
272 .set_min_inner_size(min_inner_size.map(|s| s.to_physical::<u32>(scale_factor)))
273 }
274 Self::None(attributes) => {
275 attributes.borrow_mut().min_inner_size = min_inner_size.map(|s| s.into());
277 }
278 }
279 }
280
281 fn set_max_inner_size(
282 &self,
283 max_inner_size: Option<winit::dpi::LogicalSize<f64>>,
284 scale_factor: f64,
285 ) {
286 match self {
287 Self::HasWindow { window, .. } => {
288 window
290 .set_max_inner_size(max_inner_size.map(|s| s.to_physical::<u32>(scale_factor)))
291 }
292 Self::None(attributes) => {
293 attributes.borrow_mut().max_inner_size = max_inner_size.map(|s| s.into())
295 }
296 }
297 }
298}
299
300#[derive(Default, PartialEq, Clone, Copy)]
301pub(crate) enum WindowVisibility {
302 #[default]
303 Hidden,
304 ShownFirstTime,
306 Shown,
307}
308
309pub struct WinitWindowAdapter {
312 pub shared_backend_data: Rc<SharedBackendData>,
313 window: OnceCell<corelib::api::Window>,
314 pub(crate) self_weak: Weak<Self>,
315 pending_redraw: Cell<bool>,
316 color_scheme: OnceCell<Pin<Box<Property<ColorScheme>>>>,
317 constraints: Cell<corelib::window::LayoutConstraints>,
318 shown: Cell<WindowVisibility>,
320 window_level: Cell<winit::window::WindowLevel>,
321 maximized: Cell<bool>,
322 minimized: Cell<bool>,
323 fullscreen: Cell<bool>,
324
325 pub(crate) renderer: Box<dyn WinitCompatibleRenderer>,
326 size: Cell<PhysicalSize>,
329 pending_requested_size: Cell<Option<winit::dpi::Size>>,
331
332 has_explicit_size: Cell<bool>,
335
336 pending_resize_event_after_show: Cell<bool>,
338
339 #[cfg(target_arch = "wasm32")]
340 virtual_keyboard_helper: RefCell<Option<super::wasm_input_helper::WasmInputHelper>>,
341
342 #[cfg(any(enable_accesskit, muda))]
343 event_loop_proxy: EventLoopProxy<SlintEvent>,
344
345 pub(crate) window_event_filter: Cell<
346 Option<Box<dyn FnMut(&corelib::api::Window, &winit::event::WindowEvent) -> EventResult>>,
347 >,
348
349 winit_window_or_none: RefCell<WinitWindowOrNone>,
350 window_existence_wakers: RefCell<Vec<core::task::Waker>>,
351
352 #[cfg(not(use_winit_theme))]
353 xdg_settings_watcher: RefCell<Option<i_slint_core::future::JoinHandle<()>>>,
354
355 #[cfg(muda)]
356 menubar: RefCell<Option<vtable::VRc<i_slint_core::menus::MenuVTable>>>,
357
358 #[cfg(muda)]
359 context_menu: RefCell<Option<vtable::VRc<i_slint_core::menus::MenuVTable>>>,
360
361 #[cfg(all(muda, target_os = "macos"))]
362 muda_enable_default_menu_bar: bool,
363
364 window_icon_cache_key: RefCell<Option<ImageCacheKey>>,
367
368 frame_throttle: Box<dyn crate::frame_throttle::FrameThrottle>,
369}
370
371impl WinitWindowAdapter {
372 pub(crate) fn new(
374 shared_backend_data: Rc<SharedBackendData>,
375 renderer: Box<dyn WinitCompatibleRenderer>,
376 window_attributes: winit::window::WindowAttributes,
377 #[cfg(any(enable_accesskit, muda))] proxy: EventLoopProxy<SlintEvent>,
378 #[cfg(all(muda, target_os = "macos"))] muda_enable_default_menu_bar: bool,
379 ) -> Rc<Self> {
380 let self_rc = Rc::new_cyclic(|self_weak| Self {
381 shared_backend_data: shared_backend_data.clone(),
382 window: OnceCell::from(corelib::api::Window::new(self_weak.clone() as _)),
383 self_weak: self_weak.clone(),
384 pending_redraw: Default::default(),
385 color_scheme: Default::default(),
386 constraints: Default::default(),
387 shown: Default::default(),
388 window_level: Default::default(),
389 maximized: Cell::default(),
390 minimized: Cell::default(),
391 fullscreen: Cell::default(),
392 winit_window_or_none: RefCell::new(WinitWindowOrNone::None(window_attributes.into())),
393 window_existence_wakers: RefCell::new(Vec::default()),
394 size: Cell::default(),
395 pending_requested_size: Cell::new(None),
396 has_explicit_size: Default::default(),
397 pending_resize_event_after_show: Default::default(),
398 renderer,
399 #[cfg(target_arch = "wasm32")]
400 virtual_keyboard_helper: Default::default(),
401 #[cfg(any(enable_accesskit, muda))]
402 event_loop_proxy: proxy,
403 window_event_filter: Cell::new(None),
404 #[cfg(not(use_winit_theme))]
405 xdg_settings_watcher: Default::default(),
406 #[cfg(muda)]
407 menubar: Default::default(),
408 #[cfg(muda)]
409 context_menu: Default::default(),
410 #[cfg(all(muda, target_os = "macos"))]
411 muda_enable_default_menu_bar,
412 window_icon_cache_key: Default::default(),
413 frame_throttle: crate::frame_throttle::create_frame_throttle(
414 self_weak.clone(),
415 shared_backend_data.is_wayland,
416 ),
417 });
418
419 self_rc.shared_backend_data.register_inactive_window((self_rc.clone()) as _);
420
421 self_rc
422 }
423
424 fn renderer(&self) -> &dyn WinitCompatibleRenderer {
425 self.renderer.as_ref()
426 }
427
428 pub fn ensure_window(
429 &self,
430 active_event_loop: &ActiveEventLoop,
431 ) -> Result<Arc<winit::window::Window>, PlatformError> {
432 #[allow(unused_mut)]
433 let mut window_attributes = match &*self.winit_window_or_none.borrow() {
434 WinitWindowOrNone::HasWindow { window, .. } => return Ok(window.clone()),
435 WinitWindowOrNone::None(attributes) => attributes.borrow().clone(),
436 };
437
438 #[cfg(all(unix, not(target_vendor = "apple")))]
439 {
440 if let Some(xdg_app_id) = WindowInner::from_pub(self.window()).xdg_app_id() {
441 #[cfg(feature = "wayland")]
442 {
443 use winit::platform::wayland::WindowAttributesExtWayland;
444 window_attributes = window_attributes.with_name(xdg_app_id.clone(), "");
445 }
446 #[cfg(feature = "x11")]
447 {
448 use winit::platform::x11::WindowAttributesExtX11;
449 window_attributes = window_attributes.with_name(xdg_app_id.clone(), "");
450 }
451 }
452 }
453
454 let mut winit_window_or_none = self.winit_window_or_none.borrow_mut();
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 *winit_window_or_none = WinitWindowOrNone::HasWindow {
484 window: winit_window.clone(),
485 #[cfg(enable_accesskit)]
486 accesskit_adapter: crate::accesskit::AccessKitAdapter::new(
487 self.self_weak.clone(),
488 active_event_loop,
489 &winit_window,
490 self.event_loop_proxy.clone(),
491 )
492 .into(),
493 #[cfg(muda)]
494 muda_adapter: self
495 .menubar
496 .borrow()
497 .as_ref()
498 .map(|menubar| {
499 crate::muda::MudaAdapter::setup(
500 menubar,
501 &winit_window,
502 self.event_loop_proxy.clone(),
503 self.self_weak.clone(),
504 )
505 })
506 .into(),
507 #[cfg(muda)]
508 context_menu_muda_adapter: None.into(),
509 };
510
511 drop(winit_window_or_none);
512
513 if show_after_creation {
514 self.shown.set(WindowVisibility::Hidden);
515 self.set_visibility(WindowVisibility::ShownFirstTime)?;
516 }
517
518 {
519 let mut buttons = winit_window.enabled_buttons();
523 buttons.set(WindowButtons::MAXIMIZE, resizable);
524 winit_window.set_enabled_buttons(buttons);
525 }
526
527 self.shared_backend_data
528 .register_window(winit_window.id(), (self.self_weak.upgrade().unwrap()) as _);
529
530 for waker in self.window_existence_wakers.take().into_iter() {
531 waker.wake();
532 }
533
534 Ok(winit_window)
535 }
536
537 pub(crate) fn suspend(&self) -> Result<(), PlatformError> {
538 let mut winit_window_or_none = self.winit_window_or_none.borrow_mut();
539 match *winit_window_or_none {
540 WinitWindowOrNone::HasWindow { ref window, .. } => {
541 self.renderer().suspend()?;
542
543 let last_window_rc = window.clone();
544
545 let mut attributes = Self::window_attributes().unwrap_or_default();
546 attributes.inner_size = Some(physical_size_to_winit(self.size.get()).into());
547 attributes.position = last_window_rc.outer_position().ok().map(|pos| pos.into());
548 *winit_window_or_none = WinitWindowOrNone::None(attributes.into());
549
550 if let Some(last_instance) = Arc::into_inner(last_window_rc) {
551 self.shared_backend_data.unregister_window(Some(last_instance.id()));
555 drop(last_instance);
556 } else {
557 i_slint_core::debug_log!(
558 "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"
559 );
560 }
561 }
562 WinitWindowOrNone::None(ref attributes) => {
563 attributes.borrow_mut().visible = false;
564 }
565 }
566
567 Ok(())
568 }
569
570 pub(crate) fn window_attributes() -> Result<WindowAttributes, PlatformError> {
571 let mut attrs = WindowAttributes::default().with_transparent(true).with_visible(false);
572
573 attrs = attrs.with_title("Slint Window".to_string());
574
575 #[cfg(target_arch = "wasm32")]
576 {
577 use winit::platform::web::WindowAttributesExtWebSys;
578
579 use wasm_bindgen::JsCast;
580
581 if let Some(html_canvas) = web_sys::window()
582 .ok_or_else(|| "winit backend: Could not retrieve DOM window".to_string())?
583 .document()
584 .ok_or_else(|| "winit backend: Could not retrieve DOM document".to_string())?
585 .get_element_by_id("canvas")
586 .and_then(|canvas_elem| canvas_elem.dyn_into::<web_sys::HtmlCanvasElement>().ok())
587 {
588 attrs = attrs
589 .with_canvas(Some(html_canvas))
590 .with_active(false);
593 }
594 };
595
596 Ok(attrs)
597 }
598
599 pub fn draw(&self) -> Result<(), PlatformError> {
601 if matches!(self.shown.get(), WindowVisibility::Hidden) {
602 return Ok(()); }
604
605 self.pending_redraw.set(false);
606
607 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
608 if self.pending_resize_event_after_show.take() {
614 self.resize_event(winit_window.inner_size())?;
615 }
616 }
617
618 let renderer = self.renderer();
619 renderer.render(self.window())?;
620
621 Ok(())
622 }
623
624 pub fn winit_window(&self) -> Option<Arc<winit::window::Window>> {
625 self.winit_window_or_none.borrow().as_window()
626 }
627
628 #[cfg(muda)]
629 pub fn rebuild_menubar(&self) {
630 let WinitWindowOrNone::HasWindow {
631 window: winit_window,
632 muda_adapter: maybe_muda_adapter,
633 ..
634 } = &*self.winit_window_or_none.borrow()
635 else {
636 return;
637 };
638 let mut maybe_muda_adapter = maybe_muda_adapter.borrow_mut();
639 let Some(muda_adapter) = maybe_muda_adapter.as_mut() else { return };
640 muda_adapter.rebuild_menu(&winit_window, self.menubar.borrow().as_ref(), MudaType::Menubar);
641 }
642
643 #[cfg(muda)]
644 pub fn muda_event(&self, entry_id: usize, muda_type: MudaType) {
645 let Ok(maybe_muda_adapter) = std::cell::Ref::filter_map(
646 self.winit_window_or_none.borrow(),
647 |winit_window_or_none| match (winit_window_or_none, muda_type) {
648 (WinitWindowOrNone::HasWindow { muda_adapter, .. }, MudaType::Menubar) => {
649 Some(muda_adapter)
650 }
651 (
652 WinitWindowOrNone::HasWindow { context_menu_muda_adapter, .. },
653 MudaType::Context,
654 ) => Some(context_menu_muda_adapter),
655 (WinitWindowOrNone::None(..), _) => None,
656 },
657 ) else {
658 return;
659 };
660 let maybe_muda_adapter = maybe_muda_adapter.borrow();
661 let Some(muda_adapter) = maybe_muda_adapter.as_ref() else { return };
662 let menu = match muda_type {
663 MudaType::Menubar => &self.menubar,
664 MudaType::Context => &self.context_menu,
665 };
666 let menu = menu.borrow();
667 let Some(menu) = menu.as_ref() else { return };
668 muda_adapter.invoke(menu, entry_id);
669 }
670
671 #[cfg(target_arch = "wasm32")]
672 pub fn input_method_focused(&self) -> bool {
673 match self.virtual_keyboard_helper.try_borrow() {
674 Ok(vkh) => vkh.as_ref().map_or(false, |h| h.has_focus()),
675 Err(_) => true,
678 }
679 }
680
681 #[cfg(not(target_arch = "wasm32"))]
682 pub fn input_method_focused(&self) -> bool {
683 false
684 }
685
686 fn resize_window(&self, size: winit::dpi::Size) -> Result<bool, PlatformError> {
689 match &*self.winit_window_or_none.borrow() {
690 WinitWindowOrNone::HasWindow { window, .. } => {
691 if let Some(size) = window.request_inner_size(size) {
692 self.resize_event(size)?;
694 Ok(true)
695 } else {
696 self.pending_requested_size.set(size.into());
697 Ok(false)
699 }
700 }
701 WinitWindowOrNone::None(attributes) => {
702 let scale_factor = self.window().scale_factor() as _;
703 attributes.borrow_mut().inner_size =
707 Some(size.to_logical::<f64>(scale_factor).into());
708 self.resize_event(size.to_physical(scale_factor))?;
709 Ok(true)
710 }
711 }
712 }
713
714 pub fn resize_event(&self, size: winit::dpi::PhysicalSize<u32>) -> Result<(), PlatformError> {
715 self.pending_resize_event_after_show.set(false);
716 if size.width > 0 && size.height > 0 {
720 let physical_size = physical_size_to_slint(&size);
721 self.size.set(physical_size);
722 self.pending_requested_size.set(None);
723 let scale_factor = WindowInner::from_pub(self.window()).scale_factor();
724 self.window().try_dispatch_event(WindowEvent::Resized {
725 size: physical_size.to_logical(scale_factor),
726 })?;
727
728 #[cfg(target_arch = "wasm32")]
732 if let Some(html_canvas) = self
733 .winit_window_or_none
734 .borrow()
735 .as_window()
736 .and_then(|winit_window| winit_window.canvas())
737 {
738 html_canvas.set_width(physical_size.width);
739 html_canvas.set_height(physical_size.height);
740 }
741 }
742 Ok(())
743 }
744
745 pub fn set_color_scheme(&self, scheme: ColorScheme) {
746 self.color_scheme
747 .get_or_init(|| Box::pin(Property::new(ColorScheme::Unknown)))
748 .as_ref()
749 .set(scheme);
750 #[cfg(not(use_winit_theme))]
752 if let Some(winit_window) = self.winit_window() {
753 winit_window.set_theme(match scheme {
754 ColorScheme::Unknown => None,
755 ColorScheme::Dark => Some(winit::window::Theme::Dark),
756 ColorScheme::Light => Some(winit::window::Theme::Light),
757 });
758 }
759 }
760
761 pub fn window_state_event(&self) {
762 let Some(winit_window) = self.winit_window_or_none.borrow().as_window() else { return };
763
764 if let Some(minimized) = winit_window.is_minimized() {
765 self.minimized.set(minimized);
766 if minimized != self.window().is_minimized() {
767 self.window().set_minimized(minimized);
768 }
769 }
770
771 let maximized = winit_window.is_maximized();
777 if !self.window().is_minimized() {
778 self.maximized.set(maximized);
779 if maximized != self.window().is_maximized() {
780 self.window().set_maximized(maximized);
781 }
782 }
783
784 let fullscreen = winit_window.fullscreen().is_some();
788 if fullscreen != self.window().is_fullscreen() {
789 self.window().set_fullscreen(fullscreen);
790 }
791 }
792
793 #[cfg(enable_accesskit)]
794 pub(crate) fn accesskit_adapter(
795 &self,
796 ) -> Option<std::cell::Ref<'_, RefCell<crate::accesskit::AccessKitAdapter>>> {
797 std::cell::Ref::filter_map(
798 self.winit_window_or_none.try_borrow().ok()?,
799 |wor: &WinitWindowOrNone| match wor {
800 WinitWindowOrNone::HasWindow { accesskit_adapter, .. } => Some(accesskit_adapter),
801 WinitWindowOrNone::None(..) => None,
802 },
803 )
804 .ok()
805 }
806
807 #[cfg(enable_accesskit)]
808 pub(crate) fn with_access_kit_adapter_from_weak_window_adapter(
809 self_weak: Weak<Self>,
810 callback: impl FnOnce(&RefCell<crate::accesskit::AccessKitAdapter>),
811 ) {
812 let Some(self_) = self_weak.upgrade() else { return };
813 let winit_window_or_none = self_.winit_window_or_none.borrow();
814 match &*winit_window_or_none {
815 WinitWindowOrNone::HasWindow { accesskit_adapter, .. } => callback(accesskit_adapter),
816 WinitWindowOrNone::None(..) => {}
817 }
818 }
819
820 #[cfg(not(use_winit_theme))]
821 fn spawn_xdg_settings_watcher(&self) -> Option<i_slint_core::future::JoinHandle<()>> {
822 let window_inner = WindowInner::from_pub(self.window());
823 let self_weak = self.self_weak.clone();
824 window_inner
825 .context()
826 .spawn_local(async move {
827 if let Err(err) = crate::xdg_color_scheme::watch(self_weak).await {
828 i_slint_core::debug_log!("Error watching for xdg color schemes: {}", err);
829 }
830 })
831 .ok()
832 }
833
834 pub fn activation_changed(&self, is_active: bool) -> Result<(), PlatformError> {
835 let have_focus = is_active || self.input_method_focused();
836 let slint_window = self.window();
837 let runtime_window = WindowInner::from_pub(slint_window);
838 if have_focus != runtime_window.active() {
841 slint_window.try_dispatch_event(
842 corelib::platform::WindowEvent::WindowActiveChanged(have_focus),
843 )?;
844 }
845
846 #[cfg(all(muda, target_os = "macos"))]
847 {
848 if let WinitWindowOrNone::HasWindow { muda_adapter, .. } =
849 &*self.winit_window_or_none.borrow()
850 {
851 if muda_adapter.borrow().is_none()
852 && self.muda_enable_default_menu_bar
853 && self.menubar.borrow().is_none()
854 {
855 *muda_adapter.borrow_mut() =
856 Some(crate::muda::MudaAdapter::setup_default_menu_bar()?);
857 }
858
859 if let Some(muda_adapter) = muda_adapter.borrow().as_ref() {
860 muda_adapter.window_activation_changed(is_active);
861 }
862 }
863 }
864
865 Ok(())
866 }
867
868 fn set_visibility(&self, visibility: WindowVisibility) -> Result<(), PlatformError> {
869 if visibility == self.shown.get() {
870 return Ok(());
871 }
872
873 self.shown.set(visibility);
874 self.pending_resize_event_after_show.set(!matches!(visibility, WindowVisibility::Hidden));
875 self.pending_redraw.set(false);
876 if matches!(visibility, WindowVisibility::ShownFirstTime | WindowVisibility::Shown) {
877 let recreating_window = matches!(visibility, WindowVisibility::Shown);
878
879 let Some(winit_window) = self.winit_window() else {
880 self.winit_window_or_none.borrow().set_visible(true);
883 self.shared_backend_data
884 .register_inactive_window((self.self_weak.upgrade().unwrap()) as _);
885 return Ok(());
886 };
887
888 let runtime_window = WindowInner::from_pub(self.window());
889
890 let scale_factor = runtime_window.scale_factor() as f64;
891
892 let component_rc = runtime_window.component();
893 let component = ItemTreeRc::borrow_pin(&component_rc);
894
895 let layout_info_h = component.as_ref().layout_info(Orientation::Horizontal);
896 if let Some(window_item) = runtime_window.window_item() {
897 window_item.width.set(LogicalLength::new(layout_info_h.preferred_bounded()));
900 }
901 let layout_info_v = component.as_ref().layout_info(Orientation::Vertical);
902 #[allow(unused_mut)]
903 let mut preferred_size = winit::dpi::LogicalSize::new(
904 layout_info_h.preferred_bounded(),
905 layout_info_v.preferred_bounded(),
906 );
907
908 #[cfg(target_arch = "wasm32")]
909 if let Some(html_canvas) = winit_window.canvas() {
910 if !is_preferred_sized_canvas(&html_canvas)
912 && !canvas_has_explicit_size_set(&html_canvas)
913 {
914 let existing_canvas_size = winit::dpi::LogicalSize::new(
915 html_canvas.client_width() as f32,
916 html_canvas.client_height() as f32,
917 );
918 preferred_size.width = existing_canvas_size.width;
919
920 preferred_size.height = existing_canvas_size.height;
921 }
922 }
923
924 if winit_window.fullscreen().is_none()
925 && !self.has_explicit_size.get()
926 && preferred_size.width > 0 as Coord
927 && preferred_size.height > 0 as Coord
928 && !recreating_window
930 {
931 let size = preferred_size.to_physical::<u32>(scale_factor);
933 self.resize_window(size.into())?;
934 };
935
936 winit_window.set_visible(true);
937
938 if let Some(color_scheme_prop) = self.color_scheme.get() {
941 if let Some(theme) = winit_window.theme() {
942 color_scheme_prop.as_ref().set(match theme {
943 winit::window::Theme::Dark => ColorScheme::Dark,
944 winit::window::Theme::Light => ColorScheme::Light,
945 })
946 }
947 }
948
949 #[cfg(target_arch = "wasm32")]
953 if self.pending_redraw.get() {
954 self.draw()?;
955 };
956
957 Ok(())
958 } else {
959 if self.winit_window_or_none.borrow().as_window().is_some_and(|winit_window| {
961 use raw_window_handle::HasWindowHandle;
962 winit_window.window_handle().is_ok_and(|h| {
963 matches!(h.as_raw(), raw_window_handle::RawWindowHandle::Wayland(..))
964 }) || std::env::var_os("SLINT_DESTROY_WINDOW_ON_HIDE").is_some()
965 }) {
966 self.suspend()?;
967 } else {
971 self.winit_window_or_none.borrow().set_visible(false);
972 }
973
974 Ok(())
979 }
980 }
981
982 pub(crate) fn visibility(&self) -> WindowVisibility {
983 self.shown.get()
984 }
985
986 pub(crate) fn pending_redraw(&self) -> bool {
987 self.pending_redraw.get()
988 }
989
990 pub async fn async_winit_window(
991 self_weak: Weak<Self>,
992 ) -> Result<Arc<winit::window::Window>, PlatformError> {
993 std::future::poll_fn(move |context| {
994 let Some(self_) = self_weak.upgrade() else {
995 return std::task::Poll::Ready(Err(format!(
996 "Unable to obtain winit window from destroyed window"
997 )
998 .into()));
999 };
1000 match self_.winit_window() {
1001 Some(window) => std::task::Poll::Ready(Ok(window)),
1002 None => {
1003 let waker = context.waker();
1004 if !self_.window_existence_wakers.borrow().iter().any(|w| w.will_wake(waker)) {
1005 self_.window_existence_wakers.borrow_mut().push(waker.clone());
1006 }
1007 std::task::Poll::Pending
1008 }
1009 }
1010 })
1011 .await
1012 }
1013}
1014
1015impl WindowAdapter for WinitWindowAdapter {
1016 fn window(&self) -> &corelib::api::Window {
1017 self.window.get().unwrap()
1018 }
1019
1020 fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
1021 self.renderer().as_core_renderer()
1022 }
1023
1024 fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
1025 self.set_visibility(if visible {
1026 WindowVisibility::Shown
1027 } else {
1028 WindowVisibility::Hidden
1029 })
1030 }
1031
1032 fn position(&self) -> Option<corelib::api::PhysicalPosition> {
1033 match &*self.winit_window_or_none.borrow() {
1034 WinitWindowOrNone::HasWindow { window, .. } => match window.outer_position() {
1035 Ok(outer_position) => {
1036 Some(corelib::api::PhysicalPosition::new(outer_position.x, outer_position.y))
1037 }
1038 Err(_) => None,
1039 },
1040 WinitWindowOrNone::None(attributes) => {
1041 attributes.borrow().position.map(|pos| {
1042 match pos {
1043 winit::dpi::Position::Physical(phys_pos) => {
1044 corelib::api::PhysicalPosition::new(phys_pos.x, phys_pos.y)
1045 }
1046 winit::dpi::Position::Logical(logical_pos) => {
1047 corelib::api::LogicalPosition::new(
1049 logical_pos.x as _,
1050 logical_pos.y as _,
1051 )
1052 .to_physical(self.window().scale_factor())
1053 }
1054 }
1055 })
1056 }
1057 }
1058 }
1059
1060 fn set_position(&self, position: corelib::api::WindowPosition) {
1061 let winit_pos = position_to_winit(&position);
1062 match &*self.winit_window_or_none.borrow() {
1063 WinitWindowOrNone::HasWindow { window, .. } => window.set_outer_position(winit_pos),
1064 WinitWindowOrNone::None(attributes) => {
1065 attributes.borrow_mut().position = Some(winit_pos);
1066 }
1067 }
1068 }
1069
1070 fn set_size(&self, size: corelib::api::WindowSize) {
1071 self.has_explicit_size.set(true);
1072 self.resize_window(window_size_to_winit(&size)).ok();
1074 }
1075
1076 fn size(&self) -> corelib::api::PhysicalSize {
1077 self.size.get()
1078 }
1079
1080 fn request_redraw(&self) {
1081 if !self.pending_redraw.replace(true) {
1082 self.frame_throttle.request_throttled_redraw();
1083 }
1084 }
1085
1086 #[allow(clippy::unnecessary_cast)] fn update_window_properties(&self, properties: corelib::window::WindowProperties<'_>) {
1088 let Some(window_item) =
1089 self.window.get().and_then(|w| WindowInner::from_pub(w).window_item())
1090 else {
1091 return;
1092 };
1093 let window_item = window_item.as_pin_ref();
1094
1095 let winit_window_or_none = self.winit_window_or_none.borrow();
1096
1097 let sf = self.window().scale_factor();
1099
1100 let icon_image = window_item.icon();
1102 let icon_image_cache_key = ImageCacheKey::new((&icon_image).into());
1103 if *self.window_icon_cache_key.borrow() != icon_image_cache_key {
1104 *self.window_icon_cache_key.borrow_mut() = icon_image_cache_key;
1105 winit_window_or_none.set_window_icon(icon_to_winit(
1106 icon_image,
1107 i_slint_core::lengths::LogicalSize::new(64., 64.) * ScaleFactor::new(sf),
1108 ));
1109 }
1110 winit_window_or_none.set_title(&properties.title());
1111 winit_window_or_none.set_decorations(
1112 !window_item.no_frame() || winit_window_or_none.fullscreen().is_some(),
1113 );
1114
1115 let new_window_level = if window_item.always_on_top() {
1116 winit::window::WindowLevel::AlwaysOnTop
1117 } else {
1118 winit::window::WindowLevel::Normal
1119 };
1120 if self.window_level.replace(new_window_level) != new_window_level {
1123 winit_window_or_none.set_window_level(new_window_level);
1124 }
1125
1126 let mut width = window_item.width().get() as f32;
1127 let mut height = window_item.height().get() as f32;
1128 let mut must_resize = false;
1129 let existing_size = self.size.get().to_logical(sf);
1130
1131 if width <= 0. || height <= 0. {
1132 must_resize = true;
1133 if width <= 0. {
1134 width = existing_size.width;
1135 }
1136 if height <= 0. {
1137 height = existing_size.height;
1138 }
1139 }
1140
1141 if ((existing_size.width - width).abs() > 1. || (existing_size.height - height).abs() > 1.)
1144 && self.pending_requested_size.get().is_none()
1145 {
1146 if winit_window_or_none.fullscreen().is_none() {
1150 let immediately_resized = self
1152 .resize_window(winit::dpi::LogicalSize::new(width, height).into())
1153 .unwrap_or_default();
1154 if immediately_resized {
1155 must_resize = false;
1157 }
1158 }
1159 }
1160
1161 if must_resize {
1162 self.window()
1163 .try_dispatch_event(WindowEvent::Resized {
1164 size: i_slint_core::api::LogicalSize::new(width, height),
1165 })
1166 .unwrap();
1167 }
1168
1169 let m = properties.is_fullscreen();
1170 if m != self.fullscreen.get() {
1171 if m {
1172 if winit_window_or_none.fullscreen().is_none() {
1173 winit_window_or_none
1174 .set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
1175 }
1176 } else {
1177 winit_window_or_none.set_fullscreen(None);
1178 }
1179 self.fullscreen.set(m);
1180 }
1181
1182 let m = properties.is_maximized();
1183 if m != self.maximized.get() {
1184 self.maximized.set(m);
1185 winit_window_or_none.set_maximized(m);
1186 }
1187
1188 let m = properties.is_minimized();
1189 if m != self.minimized.get() {
1190 self.minimized.set(m);
1191 winit_window_or_none.set_minimized(m);
1192 }
1193
1194 if winit_window_or_none.fullscreen().is_some() {
1199 return;
1200 }
1201
1202 let new_constraints = properties.layout_constraints();
1203 if new_constraints == self.constraints.get() {
1204 return;
1205 }
1206
1207 self.constraints.set(new_constraints);
1208
1209 let resizable = window_is_resizable(new_constraints.min, new_constraints.max);
1210 winit_window_or_none.set_resizable(resizable);
1212 let winit_min_inner =
1215 new_constraints.min.map(logical_size_to_winit).map(filter_out_zero_width_or_height);
1216 winit_window_or_none.set_min_inner_size(winit_min_inner, sf as f64);
1217 let winit_max_inner =
1218 new_constraints.max.map(logical_size_to_winit).map(filter_out_zero_width_or_height);
1219 winit_window_or_none.set_max_inner_size(winit_max_inner, sf as f64);
1220
1221 #[cfg(not(ios_and_friends))]
1223 adjust_window_size_to_satisfy_constraints(self, winit_min_inner, winit_max_inner);
1224
1225 #[cfg(target_arch = "wasm32")]
1227 if let Some(canvas) =
1228 winit_window_or_none.as_window().and_then(|winit_window| winit_window.canvas())
1229 {
1230 if is_preferred_sized_canvas(&canvas) {
1231 let pref = new_constraints.preferred;
1232 if pref.width > 0 as Coord || pref.height > 0 as Coord {
1233 self.resize_window(logical_size_to_winit(pref).into()).ok();
1235 };
1236 }
1237 }
1238 }
1239
1240 fn internal(&self, _: corelib::InternalToken) -> Option<&dyn WindowAdapterInternal> {
1241 Some(self)
1242 }
1243}
1244
1245impl WindowAdapterInternal for WinitWindowAdapter {
1246 fn set_mouse_cursor(&self, cursor: MouseCursor) {
1247 let winit_cursor = match cursor {
1248 MouseCursor::Default => winit::window::CursorIcon::Default,
1249 MouseCursor::None => winit::window::CursorIcon::Default,
1250 MouseCursor::Help => winit::window::CursorIcon::Help,
1251 MouseCursor::Pointer => winit::window::CursorIcon::Pointer,
1252 MouseCursor::Progress => winit::window::CursorIcon::Progress,
1253 MouseCursor::Wait => winit::window::CursorIcon::Wait,
1254 MouseCursor::Crosshair => winit::window::CursorIcon::Crosshair,
1255 MouseCursor::Text => winit::window::CursorIcon::Text,
1256 MouseCursor::Alias => winit::window::CursorIcon::Alias,
1257 MouseCursor::Copy => winit::window::CursorIcon::Copy,
1258 MouseCursor::Move => winit::window::CursorIcon::Move,
1259 MouseCursor::NoDrop => winit::window::CursorIcon::NoDrop,
1260 MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed,
1261 MouseCursor::Grab => winit::window::CursorIcon::Grab,
1262 MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing,
1263 MouseCursor::ColResize => winit::window::CursorIcon::ColResize,
1264 MouseCursor::RowResize => winit::window::CursorIcon::RowResize,
1265 MouseCursor::NResize => winit::window::CursorIcon::NResize,
1266 MouseCursor::EResize => winit::window::CursorIcon::EResize,
1267 MouseCursor::SResize => winit::window::CursorIcon::SResize,
1268 MouseCursor::WResize => winit::window::CursorIcon::WResize,
1269 MouseCursor::NeResize => winit::window::CursorIcon::NeResize,
1270 MouseCursor::NwResize => winit::window::CursorIcon::NwResize,
1271 MouseCursor::SeResize => winit::window::CursorIcon::SeResize,
1272 MouseCursor::SwResize => winit::window::CursorIcon::SwResize,
1273 MouseCursor::EwResize => winit::window::CursorIcon::EwResize,
1274 MouseCursor::NsResize => winit::window::CursorIcon::NsResize,
1275 MouseCursor::NeswResize => winit::window::CursorIcon::NeswResize,
1276 MouseCursor::NwseResize => winit::window::CursorIcon::NwseResize,
1277 };
1278 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1279 winit_window.set_cursor_visible(cursor != MouseCursor::None);
1280 winit_window.set_cursor(winit_cursor);
1281 }
1282 }
1283
1284 fn input_method_request(&self, request: corelib::window::InputMethodRequest) {
1285 #[cfg(not(target_arch = "wasm32"))]
1286 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1287 let props = match &request {
1288 corelib::window::InputMethodRequest::Enable(props) => {
1289 winit_window.set_ime_allowed(true);
1290 props
1291 }
1292 corelib::window::InputMethodRequest::Disable => {
1293 return winit_window.set_ime_allowed(false);
1294 }
1295 corelib::window::InputMethodRequest::Update(props) => props,
1296 _ => return,
1297 };
1298 winit_window.set_ime_purpose(match props.input_type {
1299 corelib::items::InputType::Password => winit::window::ImePurpose::Password,
1300 _ => winit::window::ImePurpose::Normal,
1301 });
1302 winit_window.set_ime_cursor_area(
1303 position_to_winit(&props.cursor_rect_origin.into()),
1304 window_size_to_winit(&props.cursor_rect_size.into()),
1305 );
1306 }
1307
1308 #[cfg(target_arch = "wasm32")]
1309 match request {
1310 corelib::window::InputMethodRequest::Enable(..) => {
1311 let mut vkh = self.virtual_keyboard_helper.borrow_mut();
1312 let Some(canvas) =
1313 self.winit_window().and_then(|winit_window| winit_window.canvas())
1314 else {
1315 return;
1316 };
1317 let h = vkh.get_or_insert_with(|| {
1318 super::wasm_input_helper::WasmInputHelper::new(self.self_weak.clone(), canvas)
1319 });
1320 h.show();
1321 }
1322 corelib::window::InputMethodRequest::Disable => {
1323 if let Some(h) = &*self.virtual_keyboard_helper.borrow() {
1324 h.hide()
1325 }
1326 }
1327 _ => {}
1328 };
1329 }
1330
1331 fn as_any(&self) -> &dyn std::any::Any {
1332 self
1333 }
1334
1335 fn color_scheme(&self) -> ColorScheme {
1336 self.color_scheme
1337 .get_or_init(|| {
1338 Box::pin(Property::new({
1339 cfg_if::cfg_if! {
1340 if #[cfg(use_winit_theme)] {
1341 self.winit_window_or_none
1342 .borrow()
1343 .as_window()
1344 .and_then(|window| window.theme())
1345 .map_or(ColorScheme::Unknown, |theme| match theme {
1346 winit::window::Theme::Dark => ColorScheme::Dark,
1347 winit::window::Theme::Light => ColorScheme::Light,
1348 })
1349 } else {
1350 if let Some(old_watch) = self.xdg_settings_watcher.replace(self.spawn_xdg_settings_watcher()) {
1351 old_watch.abort()
1352 }
1353 ColorScheme::Unknown
1354 }
1355 }
1356 }))
1357 })
1358 .as_ref()
1359 .get()
1360 }
1361
1362 #[cfg(muda)]
1363 fn supports_native_menu_bar(&self) -> bool {
1364 true
1365 }
1366
1367 #[cfg(muda)]
1368 fn setup_menubar(&self, menubar: vtable::VRc<i_slint_core::menus::MenuVTable>) {
1369 self.menubar.replace(Some(menubar));
1370
1371 if let WinitWindowOrNone::HasWindow { muda_adapter, .. } =
1372 &*self.winit_window_or_none.borrow()
1373 {
1374 drop(muda_adapter.borrow_mut().take());
1376 muda_adapter.replace(Some(crate::muda::MudaAdapter::setup(
1377 self.menubar.borrow().as_ref().unwrap(),
1378 &self.winit_window().unwrap(),
1379 self.event_loop_proxy.clone(),
1380 self.self_weak.clone(),
1381 )));
1382 }
1383 }
1384
1385 #[cfg(muda)]
1386 fn show_native_popup_menu(
1387 &self,
1388 context_menu_item: vtable::VRc<i_slint_core::menus::MenuVTable>,
1389 position: LogicalPosition,
1390 ) -> bool {
1391 self.context_menu.replace(Some(context_menu_item));
1392
1393 if let WinitWindowOrNone::HasWindow { context_menu_muda_adapter, .. } =
1394 &*self.winit_window_or_none.borrow()
1395 {
1396 drop(context_menu_muda_adapter.borrow_mut().take());
1398 if let Some(new_adapter) = crate::muda::MudaAdapter::show_context_menu(
1399 self.context_menu.borrow().as_ref().unwrap(),
1400 &self.winit_window().unwrap(),
1401 position,
1402 self.event_loop_proxy.clone(),
1403 ) {
1404 context_menu_muda_adapter.replace(Some(new_adapter));
1405 return true;
1406 }
1407 }
1408 false
1409 }
1410
1411 #[cfg(enable_accesskit)]
1412 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {
1413 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1414 accesskit_adapter_cell.borrow_mut().handle_focus_item_change();
1415 }
1416
1417 #[cfg(enable_accesskit)]
1418 fn register_item_tree(&self) {
1419 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1420 if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
1422 a.reload_tree();
1423 };
1424 }
1425
1426 #[cfg(enable_accesskit)]
1427 fn unregister_item_tree(
1428 &self,
1429 component: ItemTreeRef,
1430 _: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
1431 ) {
1432 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1433 if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
1434 a.unregister_item_tree(component);
1435 };
1436 }
1437
1438 #[cfg(feature = "raw-window-handle-06")]
1439 fn window_handle_06_rc(
1440 &self,
1441 ) -> Result<Arc<dyn raw_window_handle::HasWindowHandle>, raw_window_handle::HandleError> {
1442 self.winit_window_or_none
1443 .borrow()
1444 .as_window()
1445 .map_or(Err(raw_window_handle::HandleError::Unavailable), |window| Ok(window))
1446 }
1447
1448 #[cfg(feature = "raw-window-handle-06")]
1449 fn display_handle_06_rc(
1450 &self,
1451 ) -> Result<Arc<dyn raw_window_handle::HasDisplayHandle>, raw_window_handle::HandleError> {
1452 self.winit_window_or_none
1453 .borrow()
1454 .as_window()
1455 .map_or(Err(raw_window_handle::HandleError::Unavailable), |window| Ok(window))
1456 }
1457
1458 fn bring_to_front(&self) -> Result<(), PlatformError> {
1459 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1460 winit_window.set_minimized(false);
1461 winit_window.focus_window();
1462 }
1463 Ok(())
1464 }
1465}
1466
1467impl Drop for WinitWindowAdapter {
1468 fn drop(&mut self) {
1469 self.shared_backend_data.unregister_window(
1470 self.winit_window_or_none.borrow().as_window().map(|winit_window| winit_window.id()),
1471 );
1472
1473 #[cfg(not(use_winit_theme))]
1474 if let Some(xdg_watch_future) = self.xdg_settings_watcher.take() {
1475 xdg_watch_future.abort();
1476 }
1477 }
1478}
1479
1480#[cfg(not(ios_and_friends))]
1482fn adjust_window_size_to_satisfy_constraints(
1483 adapter: &WinitWindowAdapter,
1484 min_size: Option<winit::dpi::LogicalSize<f64>>,
1485 max_size: Option<winit::dpi::LogicalSize<f64>>,
1486) {
1487 let sf = adapter.window().scale_factor() as f64;
1488 let Some(current_size) = adapter
1489 .pending_requested_size
1490 .get()
1491 .or_else(|| {
1492 let existing_adapter_size = adapter.size.get();
1493 (existing_adapter_size.width != 0 && existing_adapter_size.height != 0)
1494 .then(|| physical_size_to_winit(existing_adapter_size).into())
1495 })
1496 .map(|s| s.to_logical::<f64>(sf))
1497 else {
1498 return;
1499 };
1500
1501 let mut window_size = current_size;
1502 if let Some(min_size) = min_size {
1503 let min_size = min_size.cast();
1504 window_size.width = window_size.width.max(min_size.width);
1505 window_size.height = window_size.height.max(min_size.height);
1506 }
1507
1508 if let Some(max_size) = max_size {
1509 let max_size = max_size.cast();
1510 window_size.width = window_size.width.min(max_size.width);
1511 window_size.height = window_size.height.min(max_size.height);
1512 }
1513
1514 if window_size != current_size {
1515 adapter.resize_window(window_size.into()).ok();
1517 }
1518}
1519
1520#[cfg(target_family = "wasm")]
1521fn is_preferred_sized_canvas(canvas: &web_sys::HtmlCanvasElement) -> bool {
1522 canvas
1523 .dataset()
1524 .get("slintAutoResizeToPreferred")
1525 .and_then(|val_str| val_str.parse::<bool>().ok())
1526 .unwrap_or_default()
1527}
1528
1529#[cfg(target_family = "wasm")]
1530fn canvas_has_explicit_size_set(canvas: &web_sys::HtmlCanvasElement) -> bool {
1531 let style = canvas.style();
1532 if !style.get_property_value("width").unwrap_or_default().is_empty()
1533 || !style.get_property_value("height").unwrap_or_default().is_empty()
1534 {
1535 return true;
1536 }
1537
1538 let Some(window) = web_sys::window() else {
1539 return false;
1540 };
1541 let Some(computed_style) = window.get_computed_style(&canvas).ok().flatten() else {
1542 return false;
1543 };
1544
1545 computed_style.get_property_value("width").ok().as_deref() != Some("auto")
1546 || computed_style.get_property_value("height").ok().as_deref() != Some("auto")
1547}