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