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) {
1129 let _ = self.draw();
1130 }
1131
1132 winit_window.set_visible(true);
1133
1134 if let Some(theme) = winit_window.theme() {
1137 self.set_color_scheme(match theme {
1138 winit::window::Theme::Dark => ColorScheme::Dark,
1139 winit::window::Theme::Light => ColorScheme::Light,
1140 });
1141 }
1142
1143 #[cfg(target_arch = "wasm32")]
1147 if self.pending_redraw.get() {
1148 self.draw()?;
1149 };
1150
1151 #[cfg(ios_and_friends)]
1155 self.request_redraw();
1156
1157 Ok(())
1158 } else {
1159 if self.winit_window_or_none.borrow().as_window().is_some_and(|winit_window| {
1161 use raw_window_handle::HasWindowHandle;
1162 winit_window.window_handle().is_ok_and(|h| {
1163 matches!(h.as_raw(), raw_window_handle::RawWindowHandle::Wayland(..))
1164 }) || std::env::var_os("SLINT_DESTROY_WINDOW_ON_HIDE").is_some()
1165 }) {
1166 self.suspend()?;
1167 } else {
1171 self.winit_window_or_none.borrow().set_visible(false);
1172 }
1173
1174 Ok(())
1179 }
1180 }
1181
1182 pub(crate) fn visibility(&self) -> WindowVisibility {
1183 self.shown.get()
1184 }
1185
1186 pub(crate) fn pending_redraw(&self) -> bool {
1187 self.pending_redraw.get()
1188 }
1189
1190 pub async fn async_winit_window(
1191 self_weak: Weak<Self>,
1192 ) -> Result<Arc<winit::window::Window>, PlatformError> {
1193 std::future::poll_fn(move |context| {
1194 let Some(self_) = self_weak.upgrade() else {
1195 return std::task::Poll::Ready(Err(
1196 "Unable to obtain winit window from destroyed window".to_string().into(),
1197 ));
1198 };
1199 match self_.winit_window() {
1200 Some(window) => std::task::Poll::Ready(Ok(window)),
1201 None => {
1202 let waker = context.waker();
1203 if !self_.window_existence_wakers.borrow().iter().any(|w| w.will_wake(waker)) {
1204 self_.window_existence_wakers.borrow_mut().push(waker.clone());
1205 }
1206 std::task::Poll::Pending
1207 }
1208 }
1209 })
1210 .await
1211 }
1212}
1213
1214impl WindowAdapter for WinitWindowAdapter {
1215 fn window(&self) -> &corelib::api::Window {
1216 &self.window
1217 }
1218
1219 fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
1220 self.renderer().as_core_renderer()
1221 }
1222
1223 fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
1224 self.set_visibility(if visible {
1225 WindowVisibility::Shown
1226 } else {
1227 WindowVisibility::Hidden
1228 })
1229 }
1230
1231 fn position(&self) -> Option<corelib::api::PhysicalPosition> {
1232 match &*self.winit_window_or_none.borrow() {
1233 WinitWindowOrNone::HasWindow { window, .. } => match window.outer_position() {
1234 Ok(outer_position) => {
1235 Some(corelib::api::PhysicalPosition::new(outer_position.x, outer_position.y))
1236 }
1237 Err(_) => None,
1238 },
1239 WinitWindowOrNone::None(attributes) => {
1240 attributes.borrow().position.map(|pos| {
1241 match pos {
1242 winit::dpi::Position::Physical(phys_pos) => {
1243 corelib::api::PhysicalPosition::new(phys_pos.x, phys_pos.y)
1244 }
1245 winit::dpi::Position::Logical(logical_pos) => {
1246 corelib::api::LogicalPosition::new(
1248 logical_pos.x as _,
1249 logical_pos.y as _,
1250 )
1251 .to_physical(self.window().scale_factor())
1252 }
1253 }
1254 })
1255 }
1256 }
1257 }
1258
1259 fn set_position(&self, position: corelib::api::WindowPosition) {
1260 let winit_pos = position_to_winit(&position);
1261 match &*self.winit_window_or_none.borrow() {
1262 WinitWindowOrNone::HasWindow { window, .. } => window.set_outer_position(winit_pos),
1263 WinitWindowOrNone::None(attributes) => {
1264 attributes.borrow_mut().position = Some(winit_pos);
1265 }
1266 }
1267 }
1268
1269 fn set_size(&self, size: corelib::api::WindowSize) {
1270 self.has_explicit_size.set(true);
1271 self.resize_window(window_size_to_winit(&size)).ok();
1273 }
1274
1275 fn size(&self) -> corelib::api::PhysicalSize {
1276 self.size.get()
1277 }
1278
1279 fn request_redraw(&self) {
1280 if !self.pending_redraw.replace(true)
1281 && let WinitWindowOrNone::HasWindow { window, frame_throttle, .. } =
1282 &*self.winit_window_or_none.borrow()
1283 {
1284 frame_throttle.request_throttled_redraw(window);
1285 }
1286 }
1287
1288 #[allow(clippy::unnecessary_cast)] fn update_window_properties(&self, properties: corelib::window::WindowProperties<'_>) {
1290 let Some(window_item) = WindowInner::from_pub(&self.window).window_item() else {
1291 return;
1292 };
1293 let window_item = window_item.as_pin_ref();
1294
1295 let winit_window_or_none = self.winit_window_or_none.borrow();
1296
1297 let sf = self.window().scale_factor();
1299
1300 let icon_image = window_item.icon();
1302 let icon_image_cache_key = ImageCacheKey::new((&icon_image).into());
1303 if *self.window_icon_cache_key.borrow() != icon_image_cache_key {
1304 *self.window_icon_cache_key.borrow_mut() = icon_image_cache_key;
1305 winit_window_or_none.set_window_icon(icon_to_winit(
1306 icon_image,
1307 i_slint_core::lengths::LogicalSize::new(64., 64.) * ScaleFactor::new(sf),
1308 ));
1309 }
1310 winit_window_or_none.set_title(&properties.title());
1311 winit_window_or_none.set_decorations(
1312 !window_item.no_frame() || winit_window_or_none.fullscreen().is_some(),
1313 );
1314
1315 let new_window_level = if window_item.always_on_top() {
1316 winit::window::WindowLevel::AlwaysOnTop
1317 } else {
1318 winit::window::WindowLevel::Normal
1319 };
1320 if self.window_level.replace(new_window_level) != new_window_level {
1323 winit_window_or_none.set_window_level(new_window_level);
1324 }
1325
1326 let mut width = window_item.width().get() as f32;
1327 let mut height = window_item.height().get() as f32;
1328 let mut must_resize = false;
1329 let existing_size = self.size.get().to_logical(sf);
1330
1331 if width <= 0. || height <= 0. {
1332 must_resize = true;
1333 if width <= 0. {
1334 width = existing_size.width;
1335 }
1336 if height <= 0. {
1337 height = existing_size.height;
1338 }
1339 }
1340
1341 if ((existing_size.width - width).abs() > 1. || (existing_size.height - height).abs() > 1.)
1344 && self.pending_requested_size.get().is_none()
1345 {
1346 if winit_window_or_none.fullscreen().is_none() {
1350 let immediately_resized = self
1352 .resize_window(winit::dpi::LogicalSize::new(width, height).into())
1353 .unwrap_or_default();
1354 if immediately_resized {
1355 must_resize = false;
1357 }
1358 }
1359 }
1360
1361 if must_resize {
1362 self.window()
1363 .try_dispatch_event(WindowEvent::Resized {
1364 size: i_slint_core::api::LogicalSize::new(width, height),
1365 })
1366 .unwrap();
1367 WindowInner::from_pub(self.window())
1368 .set_window_item_safe_area(window_item.safe_area_insets());
1369 }
1370
1371 let m = properties.is_fullscreen();
1372 if m != self.fullscreen.get() {
1373 if m {
1374 if winit_window_or_none.fullscreen().is_none() {
1375 winit_window_or_none
1376 .set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
1377 }
1378 } else {
1379 winit_window_or_none.set_fullscreen(None);
1380 }
1381 self.fullscreen.set(m);
1382 }
1383
1384 let m = properties.is_maximized();
1385 if m != self.maximized.get() {
1386 self.maximized.set(m);
1387 winit_window_or_none.set_maximized(m);
1388 }
1389
1390 let m = properties.is_minimized();
1391 if m != self.minimized.get() {
1392 self.minimized.set(m);
1393 winit_window_or_none.set_minimized(m);
1394 }
1395
1396 if winit_window_or_none.fullscreen().is_some() {
1401 return;
1402 }
1403
1404 let new_constraints = properties.layout_constraints();
1405 if new_constraints == self.constraints.get() {
1406 return;
1407 }
1408
1409 self.constraints.set(new_constraints);
1410
1411 let resizable = window_is_resizable(new_constraints.min, new_constraints.max);
1412 winit_window_or_none.set_resizable(resizable);
1414 let winit_min_inner =
1417 new_constraints.min.map(logical_size_to_winit).map(filter_out_zero_width_or_height);
1418 winit_window_or_none.set_min_inner_size(winit_min_inner, sf as f64);
1419 let winit_max_inner =
1420 new_constraints.max.map(logical_size_to_winit).map(filter_out_zero_width_or_height);
1421 winit_window_or_none.set_max_inner_size(winit_max_inner, sf as f64);
1422
1423 #[cfg(not(ios_and_friends))]
1425 adjust_window_size_to_satisfy_constraints(self, winit_min_inner, winit_max_inner);
1426
1427 #[cfg(target_arch = "wasm32")]
1429 if let Some(canvas) =
1430 winit_window_or_none.as_window().and_then(|winit_window| winit_window.canvas())
1431 {
1432 if is_preferred_sized_canvas(&canvas) {
1433 let pref = new_constraints.preferred;
1434 if pref.width > 0 as Coord || pref.height > 0 as Coord {
1435 self.resize_window(logical_size_to_winit(pref).into()).ok();
1437 };
1438 }
1439 }
1440 }
1441
1442 fn internal(&self, _: corelib::InternalToken) -> Option<&dyn WindowAdapterInternal> {
1443 Some(self)
1444 }
1445}
1446
1447impl WindowAdapterInternal for WinitWindowAdapter {
1448 fn set_mouse_cursor(&self, cursor: MouseCursor) {
1449 let winit_cursor = match cursor {
1450 MouseCursor::Default => winit::window::CursorIcon::Default,
1451 MouseCursor::None => winit::window::CursorIcon::Default,
1452 MouseCursor::Help => winit::window::CursorIcon::Help,
1453 MouseCursor::Pointer => winit::window::CursorIcon::Pointer,
1454 MouseCursor::Progress => winit::window::CursorIcon::Progress,
1455 MouseCursor::Wait => winit::window::CursorIcon::Wait,
1456 MouseCursor::Crosshair => winit::window::CursorIcon::Crosshair,
1457 MouseCursor::Text => winit::window::CursorIcon::Text,
1458 MouseCursor::Alias => winit::window::CursorIcon::Alias,
1459 MouseCursor::Copy => winit::window::CursorIcon::Copy,
1460 MouseCursor::Move => winit::window::CursorIcon::Move,
1461 MouseCursor::NoDrop => winit::window::CursorIcon::NoDrop,
1462 MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed,
1463 MouseCursor::Grab => winit::window::CursorIcon::Grab,
1464 MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing,
1465 MouseCursor::ColResize => winit::window::CursorIcon::ColResize,
1466 MouseCursor::RowResize => winit::window::CursorIcon::RowResize,
1467 MouseCursor::NResize => winit::window::CursorIcon::NResize,
1468 MouseCursor::EResize => winit::window::CursorIcon::EResize,
1469 MouseCursor::SResize => winit::window::CursorIcon::SResize,
1470 MouseCursor::WResize => winit::window::CursorIcon::WResize,
1471 MouseCursor::NeResize => winit::window::CursorIcon::NeResize,
1472 MouseCursor::NwResize => winit::window::CursorIcon::NwResize,
1473 MouseCursor::SeResize => winit::window::CursorIcon::SeResize,
1474 MouseCursor::SwResize => winit::window::CursorIcon::SwResize,
1475 MouseCursor::EwResize => winit::window::CursorIcon::EwResize,
1476 MouseCursor::NsResize => winit::window::CursorIcon::NsResize,
1477 MouseCursor::NeswResize => winit::window::CursorIcon::NeswResize,
1478 MouseCursor::NwseResize => winit::window::CursorIcon::NwseResize,
1479 _ => winit::window::CursorIcon::Default,
1480 };
1481 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1482 winit_window.set_cursor_visible(cursor != MouseCursor::None);
1483 winit_window.set_cursor(winit_cursor);
1484 }
1485 }
1486
1487 fn input_method_request(&self, request: corelib::window::InputMethodRequest) {
1488 #[cfg(not(target_arch = "wasm32"))]
1489 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1490 let props = match &request {
1491 corelib::window::InputMethodRequest::Enable(props) => {
1492 winit_window.set_ime_allowed(true);
1493 props
1494 }
1495 corelib::window::InputMethodRequest::Disable => {
1496 return winit_window.set_ime_allowed(false);
1497 }
1498 corelib::window::InputMethodRequest::Update(props) => props,
1499 _ => return,
1500 };
1501 winit_window.set_ime_purpose(match props.input_type {
1502 corelib::items::InputType::Password => winit::window::ImePurpose::Password,
1503 corelib::items::InputType::Text
1504 | corelib::items::InputType::Number
1505 | corelib::items::InputType::Decimal
1506 | corelib::items::InputType::Search
1507 | _ => winit::window::ImePurpose::Normal,
1508 });
1509 winit_window.set_ime_cursor_area(
1510 position_to_winit(&props.cursor_rect_origin.into()),
1511 window_size_to_winit(&props.cursor_rect_size.into()),
1512 );
1513 }
1514
1515 #[cfg(target_arch = "wasm32")]
1516 match request {
1517 corelib::window::InputMethodRequest::Enable(..) => {
1518 let mut vkh = self.virtual_keyboard_helper.borrow_mut();
1519 let Some(canvas) =
1520 self.winit_window().and_then(|winit_window| winit_window.canvas())
1521 else {
1522 return;
1523 };
1524 let h = vkh.get_or_insert_with(|| {
1525 super::wasm_input_helper::WasmInputHelper::new(self.self_weak.clone(), canvas)
1526 });
1527 h.show();
1528 }
1529 corelib::window::InputMethodRequest::Disable => {
1530 if let Some(h) = &*self.virtual_keyboard_helper.borrow() {
1531 h.hide()
1532 }
1533 }
1534 _ => {}
1535 };
1536 }
1537
1538 #[cfg(muda)]
1539 fn supports_native_menu_bar(&self) -> bool {
1540 true
1541 }
1542
1543 #[cfg(muda)]
1544 fn setup_menubar(&self, menubar: vtable::VRc<i_slint_core::menus::MenuVTable>) {
1545 self.menubar.replace(Some(menubar));
1546
1547 if let WinitWindowOrNone::HasWindow { muda_adapter, .. } =
1548 &*self.winit_window_or_none.borrow()
1549 {
1550 drop(muda_adapter.borrow_mut().take());
1552 muda_adapter.replace(Some(crate::muda::MudaAdapter::setup(
1553 self.menubar.borrow().as_ref().unwrap(),
1554 &self.winit_window().unwrap(),
1555 self.event_loop_proxy.clone(),
1556 self.self_weak.clone(),
1557 )));
1558 }
1559 }
1560
1561 #[cfg(muda)]
1562 fn show_native_popup_menu(
1563 &self,
1564 context_menu_item: vtable::VRc<i_slint_core::menus::MenuVTable>,
1565 position: LogicalPosition,
1566 ) -> bool {
1567 self.context_menu.replace(Some(context_menu_item));
1568
1569 if let WinitWindowOrNone::HasWindow { context_menu_muda_adapter, .. } =
1570 &*self.winit_window_or_none.borrow()
1571 {
1572 drop(context_menu_muda_adapter.borrow_mut().take());
1574 if let Some(new_adapter) = crate::muda::MudaAdapter::show_context_menu(
1575 self.context_menu.borrow().as_ref().unwrap(),
1576 &self.winit_window().unwrap(),
1577 position,
1578 self.event_loop_proxy.clone(),
1579 ) {
1580 context_menu_muda_adapter.replace(Some(new_adapter));
1581 return true;
1582 }
1583 }
1584 false
1585 }
1586
1587 #[cfg(enable_accesskit)]
1588 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {
1589 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1590 accesskit_adapter_cell.borrow_mut().handle_focus_item_change();
1591 }
1592
1593 #[cfg(enable_accesskit)]
1594 fn register_item_tree(&self, _: ItemTreeRefPin) {
1595 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1596 if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
1598 a.reload_tree();
1599 };
1600 }
1601
1602 #[cfg(enable_accesskit)]
1603 fn unregister_item_tree(
1604 &self,
1605 component: ItemTreeRef,
1606 _: &mut dyn Iterator<Item = core::pin::Pin<ItemRef<'_>>>,
1607 ) {
1608 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1609 if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
1610 a.unregister_item_tree(component);
1611 };
1612 }
1613
1614 #[cfg(feature = "raw-window-handle-06")]
1615 fn window_handle_06_rc(
1616 &self,
1617 ) -> Result<Arc<dyn raw_window_handle::HasWindowHandle>, raw_window_handle::HandleError> {
1618 Ok(self
1619 .winit_window_or_none
1620 .borrow()
1621 .as_window()
1622 .ok_or(raw_window_handle::HandleError::Unavailable)?)
1623 }
1624
1625 #[cfg(feature = "raw-window-handle-06")]
1626 fn display_handle_06_rc(
1627 &self,
1628 ) -> Result<Arc<dyn raw_window_handle::HasDisplayHandle>, raw_window_handle::HandleError> {
1629 Ok(self
1630 .winit_window_or_none
1631 .borrow()
1632 .as_window()
1633 .ok_or(raw_window_handle::HandleError::Unavailable)?)
1634 }
1635
1636 fn bring_to_front(&self) -> Result<(), PlatformError> {
1637 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1638 winit_window.set_minimized(false);
1639 winit_window.focus_window();
1640 }
1641 Ok(())
1642 }
1643
1644 #[cfg(target_os = "ios")]
1645 fn safe_area_inset(&self) -> i_slint_core::lengths::PhysicalEdges {
1646 self.winit_window_or_none
1647 .borrow()
1648 .as_window()
1649 .and_then(|window| {
1650 let outer_position = window.outer_position().ok()?;
1651 let inner_position = window.inner_position().ok()?;
1652 let outer_size = window.outer_size();
1653 let inner_size = window.inner_size();
1654 Some(i_slint_core::lengths::PhysicalEdges::new(
1655 inner_position.y - outer_position.y,
1656 outer_size.height as i32
1657 - (inner_size.height as i32)
1658 - (inner_position.y - outer_position.y),
1659 inner_position.x - outer_position.x,
1660 outer_size.width as i32
1661 - (inner_size.width as i32)
1662 - (inner_position.x - outer_position.x),
1663 ))
1664 })
1665 .unwrap_or_default()
1666 }
1667}
1668
1669impl Drop for WinitWindowAdapter {
1670 fn drop(&mut self) {
1671 self.shared_backend_data.unregister_window(
1672 self.winit_window_or_none.borrow().as_window().map(|winit_window| winit_window.id()),
1673 );
1674
1675 #[cfg(target_os = "macos")]
1676 if let Some(observer) = self.macos_color_observer.get() {
1677 unsafe {
1678 objc2_foundation::NSNotificationCenter::defaultCenter()
1679 .removeObserver((*observer).as_ref());
1680 }
1681 }
1682 }
1683}
1684
1685#[cfg(not(ios_and_friends))]
1687fn adjust_window_size_to_satisfy_constraints(
1688 adapter: &WinitWindowAdapter,
1689 min_size: Option<winit::dpi::LogicalSize<f64>>,
1690 max_size: Option<winit::dpi::LogicalSize<f64>>,
1691) {
1692 let sf = adapter.window().scale_factor() as f64;
1693 let current_size = adapter
1694 .pending_requested_size
1695 .get()
1696 .unwrap_or_else(|| {
1697 let existing_adapter_size = adapter.size.get();
1698 physical_size_to_winit(existing_adapter_size).into()
1699 })
1700 .to_logical::<f64>(sf);
1701
1702 let mut window_size = current_size;
1703 if let Some(min_size) = min_size {
1704 let min_size = min_size.cast();
1705 window_size.width = window_size.width.max(min_size.width);
1706 window_size.height = window_size.height.max(min_size.height);
1707 }
1708
1709 if let Some(max_size) = max_size {
1710 let max_size = max_size.cast();
1711 window_size.width = window_size.width.min(max_size.width);
1712 window_size.height = window_size.height.min(max_size.height);
1713 }
1714
1715 if window_size != current_size {
1716 adapter.resize_window(window_size.into()).ok();
1718 }
1719}
1720
1721#[cfg(target_family = "wasm")]
1722fn is_preferred_sized_canvas(canvas: &web_sys::HtmlCanvasElement) -> bool {
1723 canvas
1724 .dataset()
1725 .get("slintAutoResizeToPreferred")
1726 .and_then(|val_str| val_str.parse::<bool>().ok())
1727 .unwrap_or_default()
1728}
1729
1730#[cfg(target_family = "wasm")]
1731fn canvas_has_explicit_size_set(canvas: &web_sys::HtmlCanvasElement) -> bool {
1732 let style = canvas.style();
1733 if !style.get_property_value("width").unwrap_or_default().is_empty()
1734 || !style.get_property_value("height").unwrap_or_default().is_empty()
1735 {
1736 return true;
1737 }
1738
1739 let Some(window) = web_sys::window() else {
1740 return false;
1741 };
1742 let Some(computed_style) = window.get_computed_style(&canvas).ok().flatten() else {
1743 return false;
1744 };
1745
1746 computed_style.get_property_value("width").ok().as_deref() != Some("auto")
1747 || computed_style.get_property_value("height").ok().as_deref() != Some("auto")
1748}