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