1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! This module contains the GraphicsWindow that used to be within corelib.
5
6// cspell:ignore accesskit borderless corelib nesw webgl winit winsys xlib
7
8use core::cell::{Cell, RefCell};
9use core::pin::Pin;
10use std::rc::Rc;
11use std::rc::Weak;
12
13#[cfg(target_arch = "wasm32")]
14use winit::platform::web::WindowExtWebSys;
15#[cfg(target_family = "windows")]
16use winit::platform::windows::WindowExtWindows;
17
18use crate::renderer::WinitCompatibleRenderer;
19
20use corelib::item_tree::ItemTreeRc;
21#[cfg(enable_accesskit)]
22use corelib::item_tree::ItemTreeRef;
23use corelib::items::{ColorScheme, MouseCursor};
24#[cfg(enable_accesskit)]
25use corelib::items::{ItemRc, ItemRef};
26
27#[cfg(any(enable_accesskit, muda))]
28use crate::SlintUserEvent;
29use crate::{SharedBackendData, WinitWindowEventResult};
30use corelib::api::PhysicalSize;
31use corelib::layout::Orientation;
32use corelib::lengths::LogicalLength;
33use corelib::platform::{PlatformError, WindowEvent};
34use corelib::window::{WindowAdapter, WindowAdapterInternal, WindowInner};
35use corelib::Property;
36use corelib::{graphics::*, Coord};
37use i_slint_core::{self as corelib, graphics::RequestedGraphicsAPI};
38use std::cell::OnceCell;
39#[cfg(any(enable_accesskit, muda))]
40use winit::event_loop::EventLoopProxy;
41use winit::window::{WindowAttributes, WindowButtons};
42
43fn position_to_winit(pos: &corelib::api::WindowPosition) -> winit::dpi::Position {
44 match pos {
45 corelib::api::WindowPosition::Logical(pos: &LogicalPosition) => {
46 winit::dpi::Position::new(position:winit::dpi::LogicalPosition::new(pos.x, pos.y))
47 }
48 corelib::api::WindowPosition::Physical(pos: &PhysicalPosition) => {
49 winit::dpi::Position::new(position:winit::dpi::PhysicalPosition::new(pos.x, pos.y))
50 }
51 }
52}
53
54fn window_size_to_winit(size: &corelib::api::WindowSize) -> winit::dpi::Size {
55 match size {
56 corelib::api::WindowSize::Logical(size: &LogicalSize) => {
57 winit::dpi::Size::new(size:logical_size_to_winit(*size))
58 }
59 corelib::api::WindowSize::Physical(size: &PhysicalSize) => {
60 winit::dpi::Size::new(size:physical_size_to_winit(*size))
61 }
62 }
63}
64
65pub fn physical_size_to_slint(size: &winit::dpi::PhysicalSize<u32>) -> corelib::api::PhysicalSize {
66 corelib::api::PhysicalSize::new(size.width, size.height)
67}
68
69fn logical_size_to_winit(s: i_slint_core::api::LogicalSize) -> winit::dpi::LogicalSize<f32> {
70 winit::dpi::LogicalSize::new(s.width, s.height)
71}
72
73fn physical_size_to_winit(size: PhysicalSize) -> winit::dpi::PhysicalSize<u32> {
74 winit::dpi::PhysicalSize::new(size.width, size.height)
75}
76
77fn icon_to_winit(icon: corelib::graphics::Image) -> Option<winit::window::Icon> {
78 let image_inner: &ImageInner = (&icon).into();
79
80 let pixel_buffer = match image_inner {
81 ImageInner::EmbeddedImage { buffer, .. } => buffer.clone(),
82 _ => return None,
83 };
84
85 // This could become a method in SharedPixelBuffer...
86 let rgba_pixels: Vec<u8> = match &pixel_buffer {
87 SharedImageBuffer::RGB8(pixels) => pixels
88 .as_bytes()
89 .chunks(3)
90 .flat_map(|rgb| IntoIterator::into_iter([rgb[0], rgb[1], rgb[2], 255]))
91 .collect(),
92 SharedImageBuffer::RGBA8(pixels) => pixels.as_bytes().to_vec(),
93 SharedImageBuffer::RGBA8Premultiplied(pixels) => pixels
94 .as_bytes()
95 .chunks(4)
96 .flat_map(|rgba| {
97 let alpha = rgba[3] as u32;
98 IntoIterator::into_iter(rgba)
99 .take(3)
100 .map(move |component| (*component as u32 * alpha / 255) as u8)
101 .chain(std::iter::once(alpha as u8))
102 })
103 .collect(),
104 };
105
106 winit::window::Icon::from_rgba(rgba_pixels, pixel_buffer.width(), pixel_buffer.height()).ok()
107}
108
109fn window_is_resizable(
110 min_size: Option<corelib::api::LogicalSize>,
111 max_size: Option<corelib::api::LogicalSize>,
112) -> bool {
113 if let Some((
114 corelib::api::LogicalSize { width: min_width: f32, height: min_height: f32, .. },
115 corelib::api::LogicalSize { width: max_width: f32, height: max_height: f32, .. },
116 )) = min_size.zip(max_size)
117 {
118 min_width < max_width || min_height < max_height
119 } else {
120 true
121 }
122}
123
124enum WinitWindowOrNone {
125 HasWindow {
126 window: Rc<winit::window::Window>,
127 #[cfg(enable_accesskit)]
128 accesskit_adapter: RefCell<crate::accesskit::AccessKitAdapter>,
129 #[cfg(muda)]
130 muda_adapter: RefCell<Option<crate::muda::MudaAdapter>>,
131 },
132 None(RefCell<WindowAttributes>),
133}
134
135impl WinitWindowOrNone {
136 fn as_window(&self) -> Option<Rc<winit::window::Window>> {
137 match self {
138 Self::HasWindow { window, .. } => Some(window.clone()),
139 Self::None { .. } => None,
140 }
141 }
142
143 fn set_window_icon(&self, icon: Option<winit::window::Icon>) {
144 match self {
145 Self::HasWindow { window, .. } => {
146 #[cfg(target_family = "windows")]
147 window.set_taskbar_icon(icon.as_ref().cloned());
148 window.set_window_icon(icon);
149 }
150 Self::None(attributes) => attributes.borrow_mut().window_icon = icon,
151 }
152 }
153
154 fn set_title(&self, title: &str) {
155 match self {
156 Self::HasWindow { window, .. } => window.set_title(title),
157 Self::None(attributes) => attributes.borrow_mut().title = title.into(),
158 }
159 }
160
161 fn set_decorations(&self, decorations: bool) {
162 match self {
163 Self::HasWindow { window, .. } => window.set_decorations(decorations),
164 Self::None(attributes) => attributes.borrow_mut().decorations = decorations,
165 }
166 }
167
168 fn fullscreen(&self) -> Option<winit::window::Fullscreen> {
169 match self {
170 Self::HasWindow { window, .. } => window.fullscreen(),
171 Self::None(attributes) => attributes.borrow().fullscreen.clone(),
172 }
173 }
174
175 fn set_fullscreen(&self, fullscreen: Option<winit::window::Fullscreen>) {
176 match self {
177 Self::HasWindow { window, .. } => window.set_fullscreen(fullscreen),
178 Self::None(attributes) => attributes.borrow_mut().fullscreen = fullscreen,
179 }
180 }
181
182 fn set_window_level(&self, level: winit::window::WindowLevel) {
183 match self {
184 Self::HasWindow { window, .. } => window.set_window_level(level),
185 Self::None(attributes) => attributes.borrow_mut().window_level = level,
186 }
187 }
188
189 fn set_visible(&self, visible: bool) {
190 match self {
191 Self::HasWindow { window, .. } => window.set_visible(visible),
192 Self::None(attributes) => attributes.borrow_mut().visible = visible,
193 }
194 }
195
196 fn set_maximized(&self, maximized: bool) {
197 match self {
198 Self::HasWindow { window, .. } => window.set_maximized(maximized),
199 Self::None(attributes) => attributes.borrow_mut().maximized = maximized,
200 }
201 }
202
203 fn set_minimized(&self, minimized: bool) {
204 match self {
205 Self::HasWindow { window, .. } => window.set_minimized(minimized),
206 Self::None(..) => { /* TODO: winit is missing attributes.borrow_mut().minimized = minimized*/
207 }
208 }
209 }
210
211 fn set_resizable(&self, resizable: bool) {
212 match self {
213 Self::HasWindow { window, .. } => {
214 window.set_resizable(resizable);
215
216 // Workaround for winit bug #2990
217 // Non-resizable windows can still contain a maximize button,
218 // so we'd have to additionally remove the button.
219 let mut buttons = window.enabled_buttons();
220 buttons.set(WindowButtons::MAXIMIZE, resizable);
221 window.set_enabled_buttons(buttons);
222 }
223 Self::None(attributes) => attributes.borrow_mut().resizable = resizable,
224 }
225 }
226
227 fn set_min_inner_size<S: Into<winit::dpi::Size>>(&self, min_inner_size: Option<S>) {
228 match self {
229 Self::HasWindow { window, .. } => window.set_min_inner_size(min_inner_size),
230 Self::None(attributes) => {
231 attributes.borrow_mut().min_inner_size = min_inner_size.map(|s| s.into());
232 }
233 }
234 }
235
236 fn set_max_inner_size<S: Into<winit::dpi::Size>>(&self, max_inner_size: Option<S>) {
237 match self {
238 Self::HasWindow { window, .. } => window.set_max_inner_size(max_inner_size),
239 Self::None(attributes) => {
240 attributes.borrow_mut().max_inner_size = max_inner_size.map(|s| s.into())
241 }
242 }
243 }
244}
245
246/// GraphicsWindow is an implementation of the [WindowAdapter][`crate::eventloop::WindowAdapter`] trait. This is
247/// typically instantiated by entry factory functions of the different graphics back ends.
248pub struct WinitWindowAdapter {
249 pub shared_backend_data: Rc<SharedBackendData>,
250 window: OnceCell<corelib::api::Window>,
251 self_weak: Weak<Self>,
252 pending_redraw: Cell<bool>,
253 color_scheme: OnceCell<Pin<Box<Property<ColorScheme>>>>,
254 constraints: Cell<corelib::window::LayoutConstraints>,
255 shown: Cell<bool>,
256 window_level: Cell<winit::window::WindowLevel>,
257 maximized: Cell<bool>,
258 minimized: Cell<bool>,
259 fullscreen: Cell<bool>,
260
261 pub(crate) renderer: Box<dyn WinitCompatibleRenderer>,
262 requested_graphics_api: Option<RequestedGraphicsAPI>,
263 /// We cache the size because winit_window.inner_size() can return different value between calls (eg, on X11)
264 /// And we wan see the newer value before the Resized event was received, leading to inconsistencies
265 size: Cell<PhysicalSize>,
266 /// We requested a size to be set, but we didn't get the resize event from winit yet
267 pending_requested_size: Cell<Option<winit::dpi::Size>>,
268
269 /// Whether the size has been set explicitly via `set_size`.
270 /// If that's the case, we should't resize to the preferred size in set_visible
271 has_explicit_size: Cell<bool>,
272
273 /// Indicate whether we've ever received a resize event from winit after showing the window.
274 pending_resize_event_after_show: Cell<bool>,
275
276 #[cfg(target_arch = "wasm32")]
277 virtual_keyboard_helper: RefCell<Option<super::wasm_input_helper::WasmInputHelper>>,
278
279 #[cfg(any(enable_accesskit, muda))]
280 event_loop_proxy: EventLoopProxy<SlintUserEvent>,
281
282 pub(crate) window_event_filter: Cell<
283 Option<
284 Box<
285 dyn FnMut(
286 &corelib::api::Window,
287 &winit::event::WindowEvent,
288 ) -> WinitWindowEventResult,
289 >,
290 >,
291 >,
292
293 winit_window_or_none: RefCell<WinitWindowOrNone>,
294
295 #[cfg(not(use_winit_theme))]
296 xdg_settings_watcher: RefCell<Option<i_slint_core::future::JoinHandle<()>>>,
297
298 #[cfg(muda)]
299 menubar: RefCell<Option<vtable::VBox<i_slint_core::menus::MenuVTable>>>,
300
301 #[cfg(all(muda, target_os = "macos"))]
302 muda_enable_default_menu_bar: bool,
303}
304
305impl WinitWindowAdapter {
306 /// Creates a new reference-counted instance.
307 pub(crate) fn new(
308 shared_backend_data: Rc<SharedBackendData>,
309 renderer: Box<dyn WinitCompatibleRenderer>,
310 window_attributes: winit::window::WindowAttributes,
311 requested_graphics_api: Option<RequestedGraphicsAPI>,
312 #[cfg(any(enable_accesskit, muda))] proxy: EventLoopProxy<SlintUserEvent>,
313 #[cfg(all(muda, target_os = "macos"))] muda_enable_default_menu_bar: bool,
314 ) -> Result<Rc<Self>, PlatformError> {
315 let self_rc = Rc::new_cyclic(|self_weak| Self {
316 shared_backend_data,
317 window: OnceCell::from(corelib::api::Window::new(self_weak.clone() as _)),
318 self_weak: self_weak.clone(),
319 pending_redraw: Default::default(),
320 color_scheme: Default::default(),
321 constraints: Default::default(),
322 shown: Default::default(),
323 window_level: Default::default(),
324 maximized: Cell::default(),
325 minimized: Cell::default(),
326 fullscreen: Cell::default(),
327 winit_window_or_none: RefCell::new(WinitWindowOrNone::None(window_attributes.into())),
328 size: Cell::default(),
329 pending_requested_size: Cell::new(None),
330 has_explicit_size: Default::default(),
331 pending_resize_event_after_show: Default::default(),
332 renderer,
333 requested_graphics_api,
334 #[cfg(target_arch = "wasm32")]
335 virtual_keyboard_helper: Default::default(),
336 #[cfg(any(enable_accesskit, muda))]
337 event_loop_proxy: proxy,
338 window_event_filter: Cell::new(None),
339 #[cfg(not(use_winit_theme))]
340 xdg_settings_watcher: Default::default(),
341 #[cfg(muda)]
342 menubar: Default::default(),
343 #[cfg(all(muda, target_os = "macos"))]
344 muda_enable_default_menu_bar,
345 });
346
347 let winit_window = self_rc
348 .shared_backend_data
349 .with_event_loop(|event_loop| Ok(self_rc.ensure_window(event_loop)?))?;
350 debug_assert!(!self_rc.renderer.is_suspended());
351 self_rc.size.set(physical_size_to_slint(&winit_window.inner_size()));
352
353 let id = winit_window.id();
354 self_rc.shared_backend_data.register_window(id, (self_rc.clone()) as _);
355
356 let scale_factor = std::env::var("SLINT_SCALE_FACTOR")
357 .ok()
358 .and_then(|x| x.parse::<f32>().ok())
359 .filter(|f| *f > 0.)
360 .unwrap_or_else(|| winit_window.scale_factor() as f32);
361 self_rc.window().try_dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor })?;
362
363 Ok(self_rc)
364 }
365
366 fn renderer(&self) -> &dyn WinitCompatibleRenderer {
367 self.renderer.as_ref()
368 }
369
370 pub fn ensure_window(
371 &self,
372 event_loop: &dyn crate::event_loop::EventLoopInterface,
373 ) -> Result<Rc<winit::window::Window>, PlatformError> {
374 #[allow(unused_mut)]
375 let mut window_attributes = match &*self.winit_window_or_none.borrow() {
376 WinitWindowOrNone::HasWindow { window, .. } => return Ok(window.clone()),
377 WinitWindowOrNone::None(attributes) => attributes.borrow().clone(),
378 };
379
380 #[cfg(all(unix, not(target_vendor = "apple")))]
381 {
382 if let Some(xdg_app_id) = WindowInner::from_pub(self.window()).xdg_app_id() {
383 #[cfg(feature = "wayland")]
384 {
385 use winit::platform::wayland::WindowAttributesExtWayland;
386 window_attributes = window_attributes.with_name(xdg_app_id.clone(), "");
387 }
388 #[cfg(feature = "x11")]
389 {
390 use winit::platform::x11::WindowAttributesExtX11;
391 window_attributes = window_attributes.with_name(xdg_app_id.clone(), "");
392 }
393 }
394 }
395
396 let mut winit_window_or_none = self.winit_window_or_none.borrow_mut();
397
398 let winit_window = self.renderer.resume(
399 event_loop,
400 window_attributes,
401 self.requested_graphics_api.clone(),
402 )?;
403
404 *winit_window_or_none = WinitWindowOrNone::HasWindow {
405 window: winit_window.clone(),
406 #[cfg(enable_accesskit)]
407 accesskit_adapter: crate::accesskit::AccessKitAdapter::new(
408 self.self_weak.clone(),
409 &winit_window,
410 self.event_loop_proxy.clone(),
411 )
412 .into(),
413 #[cfg(muda)]
414 muda_adapter: self
415 .menubar
416 .borrow()
417 .as_ref()
418 .map(|menubar| {
419 crate::muda::MudaAdapter::setup(
420 menubar,
421 &self.winit_window().unwrap(),
422 self.event_loop_proxy.clone(),
423 self.self_weak.clone(),
424 )
425 })
426 .into(),
427 };
428
429 self.shared_backend_data
430 .register_window(winit_window.id(), (self.self_weak.upgrade().unwrap()) as _);
431
432 Ok(winit_window)
433 }
434
435 fn suspend(&self) -> Result<(), PlatformError> {
436 let mut winit_window_or_none = self.winit_window_or_none.borrow_mut();
437 match *winit_window_or_none {
438 WinitWindowOrNone::HasWindow { ref window, .. } => {
439 self.renderer().suspend()?;
440
441 let last_window_rc = window.clone();
442
443 let mut attributes = Self::window_attributes().unwrap_or_default();
444 attributes.inner_size = Some(physical_size_to_winit(self.size.get()).into());
445 attributes.position = last_window_rc.outer_position().ok().map(|pos| pos.into());
446 *winit_window_or_none = WinitWindowOrNone::None(attributes.into());
447
448 if let Some(last_instance) = Rc::into_inner(last_window_rc) {
449 self.shared_backend_data.unregister_window(last_instance.id());
450 drop(last_instance);
451 } else {
452 i_slint_core::debug_log!(
453 "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"
454 );
455 }
456 }
457 WinitWindowOrNone::None(ref attributes) => {
458 attributes.borrow_mut().visible = false;
459 }
460 }
461
462 Ok(())
463 }
464
465 pub(crate) fn window_attributes() -> Result<WindowAttributes, PlatformError> {
466 let mut attrs = WindowAttributes::default().with_transparent(true).with_visible(false);
467
468 attrs = attrs.with_title("Slint Window".to_string());
469
470 #[cfg(target_arch = "wasm32")]
471 {
472 use winit::platform::web::WindowAttributesExtWebSys;
473
474 use wasm_bindgen::JsCast;
475
476 if let Some(html_canvas) = web_sys::window()
477 .ok_or_else(|| "winit backend: Could not retrieve DOM window".to_string())?
478 .document()
479 .ok_or_else(|| "winit backend: Could not retrieve DOM document".to_string())?
480 .get_element_by_id("canvas")
481 .and_then(|canvas_elem| canvas_elem.dyn_into::<web_sys::HtmlCanvasElement>().ok())
482 {
483 attrs = attrs
484 .with_canvas(Some(html_canvas))
485 // Don't activate the window by default, as that will cause the page to scroll,
486 // ignoring any existing anchors.
487 .with_active(false);
488 }
489 };
490
491 Ok(attrs)
492 }
493
494 /// Draw the items of the specified `component` in the given window.
495 pub fn draw(&self) -> Result<(), PlatformError> {
496 if !self.shown.get() {
497 return Ok(()); // caller bug, doesn't make sense to call draw() when not shown
498 }
499
500 self.pending_redraw.set(false);
501
502 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
503 // on macOS we sometimes don't get a resize event after calling
504 // request_inner_size(), it returning None (promising a resize event), and then delivering RedrawRequested. To work around this,
505 // catch up here to ensure the renderer can resize the surface correctly.
506 // Note: On displays with a scale factor != 1, we get a scale factor change
507 // event and a resize event, so all is good.
508 if self.pending_resize_event_after_show.take() {
509 self.resize_event(winit_window.inner_size())?;
510 }
511 }
512
513 let renderer = self.renderer();
514 renderer.render(self.window())?;
515
516 Ok(())
517 }
518
519 pub fn winit_window(&self) -> Option<Rc<winit::window::Window>> {
520 self.winit_window_or_none.borrow().as_window()
521 }
522
523 #[cfg(muda)]
524 pub fn rebuild_menubar(&self) {
525 let WinitWindowOrNone::HasWindow {
526 window: winit_window,
527 muda_adapter: maybe_muda_adapter,
528 ..
529 } = &*self.winit_window_or_none.borrow()
530 else {
531 return;
532 };
533 let mut maybe_muda_adapter = maybe_muda_adapter.borrow_mut();
534 let Some(muda_adapter) = maybe_muda_adapter.as_mut() else { return };
535 muda_adapter.rebuild_menu(&winit_window, self.menubar.borrow().as_ref());
536 }
537
538 #[cfg(muda)]
539 pub fn muda_event(&self, entry_id: usize) {
540 let Ok(maybe_muda_adapter) = std::cell::Ref::filter_map(
541 self.winit_window_or_none.borrow(),
542 |winit_window_or_none| match winit_window_or_none {
543 WinitWindowOrNone::HasWindow { muda_adapter, .. } => Some(muda_adapter),
544 WinitWindowOrNone::None(..) => None,
545 },
546 ) else {
547 return;
548 };
549 let maybe_muda_adapter = maybe_muda_adapter.borrow();
550 let Some(muda_adapter) = maybe_muda_adapter.as_ref() else { return };
551 let menubar = self.menubar.borrow();
552 let Some(menubar) = menubar.as_ref() else { return };
553 muda_adapter.invoke(menubar, entry_id);
554 }
555
556 #[cfg(target_arch = "wasm32")]
557 pub fn input_method_focused(&self) -> bool {
558 match self.virtual_keyboard_helper.try_borrow() {
559 Ok(vkh) => vkh.as_ref().map_or(false, |h| h.has_focus()),
560 // the only location in which the virtual_keyboard_helper is mutably borrowed is from
561 // show_virtual_keyboard, which means we have the focus
562 Err(_) => true,
563 }
564 }
565
566 #[cfg(not(target_arch = "wasm32"))]
567 pub fn input_method_focused(&self) -> bool {
568 false
569 }
570
571 // Requests for the window to be resized. Returns true if the window was resized immediately,
572 // or if it will be resized later (false).
573 fn resize_window(&self, size: winit::dpi::Size) -> Result<bool, PlatformError> {
574 match &*self.winit_window_or_none.borrow() {
575 WinitWindowOrNone::HasWindow { window, .. } => {
576 if let Some(size) = window.request_inner_size(size) {
577 // On wayland we might not get a WindowEvent::Resized, so resize the EGL surface right away.
578 self.resize_event(size)?;
579 Ok(true)
580 } else {
581 // None means that we'll get a `WindowEvent::Resized` later
582 Ok(false)
583 }
584 }
585 WinitWindowOrNone::None(attributes) => {
586 attributes.borrow_mut().inner_size = Some(size);
587 self.resize_event(size.to_physical(self.window().scale_factor() as _))?;
588 Ok(true)
589 }
590 }
591 }
592
593 pub fn resize_event(&self, size: winit::dpi::PhysicalSize<u32>) -> Result<(), PlatformError> {
594 self.pending_resize_event_after_show.set(false);
595 // When a window is minimized on Windows, we get a move event to an off-screen position
596 // and a resize even with a zero size. Don't forward that, especially not to the renderer,
597 // which might panic when trying to create a zero-sized surface.
598 if size.width > 0 && size.height > 0 {
599 let physical_size = physical_size_to_slint(&size);
600 self.size.set(physical_size);
601 let scale_factor = WindowInner::from_pub(self.window()).scale_factor();
602 self.window().try_dispatch_event(WindowEvent::Resized {
603 size: physical_size.to_logical(scale_factor),
604 })?;
605
606 // Workaround fox winit not sync'ing CSS size of the canvas (the size shown on the browser)
607 // with the width/height attribute (the size of the viewport/GL surface)
608 // If they're not in sync, the UI would be shown as scaled
609 #[cfg(target_arch = "wasm32")]
610 if let Some(html_canvas) = self
611 .winit_window_or_none
612 .borrow()
613 .as_window()
614 .and_then(|winit_window| winit_window.canvas())
615 {
616 html_canvas.set_width(physical_size.width);
617 html_canvas.set_height(physical_size.height);
618 }
619 }
620 Ok(())
621 }
622
623 pub fn set_color_scheme(&self, scheme: ColorScheme) {
624 self.color_scheme
625 .get_or_init(|| Box::pin(Property::new(ColorScheme::Unknown)))
626 .as_ref()
627 .set(scheme);
628 // Inform winit about the selected color theme, so that the window decoration is drawn correctly.
629 #[cfg(not(use_winit_theme))]
630 if let Some(winit_window) = self.winit_window() {
631 winit_window.set_theme(match scheme {
632 ColorScheme::Unknown => None,
633 ColorScheme::Dark => Some(winit::window::Theme::Dark),
634 ColorScheme::Light => Some(winit::window::Theme::Light),
635 });
636 }
637 }
638
639 pub fn window_state_event(&self) {
640 let Some(winit_window) = self.winit_window_or_none.borrow().as_window() else { return };
641
642 if let Some(minimized) = winit_window.is_minimized() {
643 self.minimized.set(minimized);
644 if minimized != self.window().is_minimized() {
645 self.window().set_minimized(minimized);
646 }
647 }
648
649 // The method winit::Window::is_maximized returns false when the window
650 // is minimized, even if it was previously maximized. We have to ensure
651 // that we only update the internal maximized state when the window is
652 // not minimized. Otherwise, the window would be restored in a
653 // non-maximized state even if it was maximized before being minimized.
654 let maximized = winit_window.is_maximized();
655 if !self.window().is_minimized() {
656 self.maximized.set(maximized);
657 if maximized != self.window().is_maximized() {
658 self.window().set_maximized(maximized);
659 }
660 }
661
662 // NOTE: Fullscreen overrides maximized so if both are true then the
663 // window will remain in fullscreen. Fullscreen must be false to switch
664 // to maximized.
665 let fullscreen = winit_window.fullscreen().is_some();
666 if fullscreen != self.window().is_fullscreen() {
667 self.window().set_fullscreen(fullscreen);
668 }
669 }
670
671 #[cfg(enable_accesskit)]
672 pub(crate) fn accesskit_adapter(
673 &self,
674 ) -> Option<std::cell::Ref<'_, RefCell<crate::accesskit::AccessKitAdapter>>> {
675 std::cell::Ref::filter_map(self.winit_window_or_none.borrow(), |wor: &WinitWindowOrNone| {
676 match wor {
677 WinitWindowOrNone::HasWindow { accesskit_adapter, .. } => Some(accesskit_adapter),
678 WinitWindowOrNone::None(..) => None,
679 }
680 })
681 .ok()
682 }
683
684 #[cfg(enable_accesskit)]
685 pub(crate) fn with_access_kit_adapter_from_weak_window_adapter(
686 self_weak: Weak<Self>,
687 callback: impl FnOnce(&RefCell<crate::accesskit::AccessKitAdapter>),
688 ) {
689 let Some(self_) = self_weak.upgrade() else { return };
690 let winit_window_or_none = self_.winit_window_or_none.borrow();
691 match &*winit_window_or_none {
692 WinitWindowOrNone::HasWindow { accesskit_adapter, .. } => callback(accesskit_adapter),
693 WinitWindowOrNone::None(..) => {}
694 }
695 }
696
697 #[cfg(not(use_winit_theme))]
698 fn spawn_xdg_settings_watcher(&self) -> Option<i_slint_core::future::JoinHandle<()>> {
699 let window_inner = WindowInner::from_pub(self.window());
700 let self_weak = self.self_weak.clone();
701 window_inner
702 .context()
703 .spawn_local(async move {
704 if let Err(err) = crate::xdg_color_scheme::watch(self_weak).await {
705 i_slint_core::debug_log!("Error watching for xdg color schemes: {}", err);
706 }
707 })
708 .ok()
709 }
710
711 pub fn activation_changed(&self, is_active: bool) -> Result<(), PlatformError> {
712 let have_focus = is_active || self.input_method_focused();
713 let slint_window = self.window();
714 let runtime_window = WindowInner::from_pub(slint_window);
715 // We don't render popups as separate windows yet, so treat
716 // focus to be the same as being active.
717 if have_focus != runtime_window.active() {
718 slint_window.try_dispatch_event(
719 corelib::platform::WindowEvent::WindowActiveChanged(have_focus),
720 )?;
721 }
722
723 #[cfg(all(muda, target_os = "macos"))]
724 {
725 if let WinitWindowOrNone::HasWindow { muda_adapter, .. } =
726 &*self.winit_window_or_none.borrow()
727 {
728 if muda_adapter.borrow().is_none()
729 && self.muda_enable_default_menu_bar
730 && self.menubar.borrow().is_none()
731 {
732 *muda_adapter.borrow_mut() =
733 Some(crate::muda::MudaAdapter::setup_default_menu_bar()?);
734 }
735
736 if let Some(muda_adapter) = muda_adapter.borrow().as_ref() {
737 muda_adapter.window_activation_changed(is_active);
738 }
739 }
740 }
741
742 Ok(())
743 }
744}
745
746impl WindowAdapter for WinitWindowAdapter {
747 fn window(&self) -> &corelib::api::Window {
748 self.window.get().unwrap()
749 }
750
751 fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
752 self.renderer().as_core_renderer()
753 }
754
755 fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
756 if visible == self.shown.get() {
757 return Ok(());
758 }
759
760 self.shown.set(visible);
761 self.pending_resize_event_after_show.set(visible);
762 self.pending_redraw.set(false);
763 if visible {
764 let recreating_window = self.winit_window_or_none.borrow().as_window().is_none();
765
766 let winit_window = self
767 .shared_backend_data
768 .with_event_loop(|event_loop| Ok(self.ensure_window(event_loop)?))?;
769
770 let runtime_window = WindowInner::from_pub(self.window());
771
772 let scale_factor = runtime_window.scale_factor() as f64;
773
774 let component_rc = runtime_window.component();
775 let component = ItemTreeRc::borrow_pin(&component_rc);
776
777 let layout_info_h = component.as_ref().layout_info(Orientation::Horizontal);
778 if let Some(window_item) = runtime_window.window_item() {
779 // Setting the width to its preferred size before querying the vertical layout info
780 // is important in case the height depends on the width
781 window_item.width.set(LogicalLength::new(layout_info_h.preferred_bounded()));
782 }
783 let layout_info_v = component.as_ref().layout_info(Orientation::Vertical);
784 #[allow(unused_mut)]
785 let mut preferred_size = winit::dpi::LogicalSize::new(
786 layout_info_h.preferred_bounded(),
787 layout_info_v.preferred_bounded(),
788 );
789
790 #[cfg(target_arch = "wasm32")]
791 if let Some(html_canvas) = winit_window.canvas() {
792 let existing_canvas_size = winit::dpi::LogicalSize::new(
793 html_canvas.client_width() as f32,
794 html_canvas.client_height() as f32,
795 );
796 // Try to maintain the existing size of the canvas element, if any
797 if existing_canvas_size.width > 0. {
798 preferred_size.width = existing_canvas_size.width;
799 }
800 if existing_canvas_size.height > 0. {
801 preferred_size.height = existing_canvas_size.height;
802 }
803 }
804
805 if winit_window.fullscreen().is_none()
806 && !self.has_explicit_size.get()
807 && preferred_size.width > 0 as Coord
808 && preferred_size.height > 0 as Coord
809 // Don't set the preferred size as the user may have resized the window
810 && !recreating_window
811 {
812 // use the Slint's window Scale factor to take in account the override
813 let size = preferred_size.to_physical::<u32>(scale_factor);
814 self.resize_window(size.into())?;
815 };
816
817 winit_window.set_visible(true);
818
819 // Make sure the dark color scheme property is up-to-date, as it may have been queried earlier when
820 // the window wasn't mapped yet.
821 if let Some(color_scheme_prop) = self.color_scheme.get() {
822 if let Some(theme) = winit_window.theme() {
823 color_scheme_prop.as_ref().set(match theme {
824 winit::window::Theme::Dark => ColorScheme::Dark,
825 winit::window::Theme::Light => ColorScheme::Light,
826 })
827 }
828 }
829
830 // In wasm a request_redraw() issued before show() results in a draw() even when the window
831 // isn't visible, as opposed to regular windowing systems. The compensate for the lost draw,
832 // explicitly render the first frame on show().
833 #[cfg(target_arch = "wasm32")]
834 if self.pending_redraw.get() {
835 self.draw()?;
836 };
837
838 Ok(())
839 } else {
840 // Wayland doesn't support hiding a window, only destroying it entirely.
841 if self.winit_window_or_none.borrow().as_window().is_some_and(|winit_window| {
842 use raw_window_handle::HasWindowHandle;
843 winit_window.window_handle().is_ok_and(|h| {
844 matches!(h.as_raw(), raw_window_handle::RawWindowHandle::Wayland(..))
845 }) || std::env::var_os("SLINT_DESTROY_WINDOW_ON_HIDE").is_some()
846 }) {
847 self.suspend()?;
848 } else {
849 self.winit_window_or_none.borrow().set_visible(false);
850 }
851
852 /* FIXME:
853 if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() {
854 existing_blinker.stop();
855 }*/
856 Ok(())
857 }
858 }
859
860 fn position(&self) -> Option<corelib::api::PhysicalPosition> {
861 match &*self.winit_window_or_none.borrow() {
862 WinitWindowOrNone::HasWindow { window, .. } => match window.outer_position() {
863 Ok(outer_position) => {
864 Some(corelib::api::PhysicalPosition::new(outer_position.x, outer_position.y))
865 }
866 Err(_) => None,
867 },
868 WinitWindowOrNone::None(attributes) => {
869 attributes.borrow().position.map(|pos| {
870 match pos {
871 winit::dpi::Position::Physical(phys_pos) => {
872 corelib::api::PhysicalPosition::new(phys_pos.x, phys_pos.y)
873 }
874 winit::dpi::Position::Logical(logical_pos) => {
875 // Best effort: Use the last known scale factor
876 corelib::api::LogicalPosition::new(
877 logical_pos.x as _,
878 logical_pos.y as _,
879 )
880 .to_physical(self.window().scale_factor())
881 }
882 }
883 })
884 }
885 }
886 }
887
888 fn set_position(&self, position: corelib::api::WindowPosition) {
889 let winit_pos = position_to_winit(&position);
890 match &*self.winit_window_or_none.borrow() {
891 WinitWindowOrNone::HasWindow { window, .. } => window.set_outer_position(winit_pos),
892 WinitWindowOrNone::None(attributes) => {
893 attributes.borrow_mut().position = Some(winit_pos);
894 }
895 }
896 }
897
898 fn set_size(&self, size: corelib::api::WindowSize) {
899 self.has_explicit_size.set(true);
900 // TODO: don't ignore error, propgate to caller
901 self.resize_window(window_size_to_winit(&size)).ok();
902 }
903
904 fn size(&self) -> corelib::api::PhysicalSize {
905 self.size.get()
906 }
907
908 fn request_redraw(&self) {
909 if !self.pending_redraw.replace(true) {
910 if let Some(window) = self.winit_window_or_none.borrow().as_window() {
911 window.request_redraw()
912 }
913 }
914 }
915
916 #[allow(clippy::unnecessary_cast)] // Coord is used!
917 fn update_window_properties(&self, properties: corelib::window::WindowProperties<'_>) {
918 let Some(window_item) =
919 self.window.get().and_then(|w| WindowInner::from_pub(w).window_item())
920 else {
921 return;
922 };
923 let window_item = window_item.as_pin_ref();
924
925 let winit_window_or_none = self.winit_window_or_none.borrow();
926
927 winit_window_or_none.set_window_icon(icon_to_winit(window_item.icon()));
928 winit_window_or_none.set_title(&properties.title());
929 winit_window_or_none.set_decorations(
930 !window_item.no_frame() || winit_window_or_none.fullscreen().is_some(),
931 );
932
933 let new_window_level = if window_item.always_on_top() {
934 winit::window::WindowLevel::AlwaysOnTop
935 } else {
936 winit::window::WindowLevel::Normal
937 };
938 // Only change the window level if it changes, to avoid https://github.com/slint-ui/slint/issues/3280
939 // (Ubuntu 20.04's window manager always bringing the window to the front on x11)
940 if self.window_level.replace(new_window_level) != new_window_level {
941 winit_window_or_none.set_window_level(new_window_level);
942 }
943
944 // Use our scale factor instead of winit's logical size to take a scale factor override into account.
945 let sf = self.window().scale_factor();
946
947 let mut width = window_item.width().get() as f32;
948 let mut height = window_item.height().get() as f32;
949 let mut must_resize = false;
950 let existing_size = self.size.get().to_logical(sf);
951
952 if width <= 0. || height <= 0. {
953 must_resize = true;
954 if width <= 0. {
955 width = existing_size.width;
956 }
957 if height <= 0. {
958 height = existing_size.height;
959 }
960 }
961
962 // Adjust the size of the window to the value of the width and height property (if these property are changed from .slint).
963 // But not if there is a pending resize in flight as that resize will reset these properties back
964 if ((existing_size.width - width).abs() > 1. || (existing_size.height - height).abs() > 1.)
965 && self.pending_requested_size.get().is_none()
966 {
967 // If we're in fullscreen state, don't try to resize the window but maintain the surface
968 // size we've been assigned to from the windowing system. Weston/Wayland don't like it
969 // when we create a surface that's bigger than the screen due to constraints (#532).
970 if winit_window_or_none.fullscreen().is_none() {
971 // TODO: don't ignore error, propgate to caller
972 let immediately_resized = self
973 .resize_window(winit::dpi::LogicalSize::new(width, height).into())
974 .unwrap_or_default();
975 if immediately_resized {
976 // The resize event was already dispatched
977 must_resize = false;
978 }
979 }
980 }
981
982 if must_resize {
983 self.window()
984 .try_dispatch_event(WindowEvent::Resized {
985 size: i_slint_core::api::LogicalSize::new(width, height),
986 })
987 .unwrap();
988 }
989
990 let m = properties.is_fullscreen();
991 if m != self.fullscreen.get() {
992 if m {
993 if winit_window_or_none.fullscreen().is_none() {
994 winit_window_or_none
995 .set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
996 }
997 } else {
998 winit_window_or_none.set_fullscreen(None);
999 }
1000 self.fullscreen.set(m);
1001 }
1002
1003 let m = properties.is_maximized();
1004 if m != self.maximized.get() {
1005 self.maximized.set(m);
1006 winit_window_or_none.set_maximized(m);
1007 }
1008
1009 let m = properties.is_minimized();
1010 if m != self.minimized.get() {
1011 self.minimized.set(m);
1012 winit_window_or_none.set_minimized(m);
1013 }
1014
1015 // If we're in fullscreen, don't try to resize the window but
1016 // maintain the surface size we've been assigned to from the
1017 // windowing system. Weston/Wayland don't like it when we create a
1018 // surface that's bigger than the screen due to constraints (#532).
1019 if winit_window_or_none.fullscreen().is_some() {
1020 return;
1021 }
1022
1023 let new_constraints = properties.layout_constraints();
1024 if new_constraints == self.constraints.get() {
1025 return;
1026 }
1027
1028 self.constraints.set(new_constraints);
1029
1030 let into_size = |s: corelib::api::LogicalSize| -> winit::dpi::PhysicalSize<f32> {
1031 logical_size_to_winit(s).to_physical(sf as f64)
1032 };
1033
1034 let resizable = window_is_resizable(new_constraints.min, new_constraints.max);
1035 // we must call set_resizable before setting the min and max size otherwise setting the min and max size don't work on X11
1036 winit_window_or_none.set_resizable(resizable);
1037 let winit_min_inner = new_constraints.min.map(into_size);
1038 winit_window_or_none.set_min_inner_size(winit_min_inner);
1039 let winit_max_inner = new_constraints.max.map(into_size);
1040 winit_window_or_none.set_max_inner_size(winit_max_inner);
1041
1042 adjust_window_size_to_satisfy_constraints(self, winit_min_inner, winit_max_inner);
1043
1044 // Auto-resize to the preferred size if users (SlintPad) requests it
1045 #[cfg(target_arch = "wasm32")]
1046 if let Some(canvas) =
1047 winit_window_or_none.as_window().and_then(|winit_window| winit_window.canvas())
1048 {
1049 if canvas
1050 .dataset()
1051 .get("slintAutoResizeToPreferred")
1052 .and_then(|val_str| val_str.parse().ok())
1053 .unwrap_or_default()
1054 {
1055 let pref = new_constraints.preferred;
1056 if pref.width > 0 as Coord || pref.height > 0 as Coord {
1057 // TODO: don't ignore error, propgate to caller
1058 self.resize_window(logical_size_to_winit(pref).into()).ok();
1059 };
1060 }
1061 }
1062 }
1063
1064 fn internal(&self, _: corelib::InternalToken) -> Option<&dyn WindowAdapterInternal> {
1065 Some(self)
1066 }
1067}
1068
1069impl WindowAdapterInternal for WinitWindowAdapter {
1070 fn set_mouse_cursor(&self, cursor: MouseCursor) {
1071 let winit_cursor = match cursor {
1072 MouseCursor::Default => winit::window::CursorIcon::Default,
1073 MouseCursor::None => winit::window::CursorIcon::Default,
1074 MouseCursor::Help => winit::window::CursorIcon::Help,
1075 MouseCursor::Pointer => winit::window::CursorIcon::Pointer,
1076 MouseCursor::Progress => winit::window::CursorIcon::Progress,
1077 MouseCursor::Wait => winit::window::CursorIcon::Wait,
1078 MouseCursor::Crosshair => winit::window::CursorIcon::Crosshair,
1079 MouseCursor::Text => winit::window::CursorIcon::Text,
1080 MouseCursor::Alias => winit::window::CursorIcon::Alias,
1081 MouseCursor::Copy => winit::window::CursorIcon::Copy,
1082 MouseCursor::Move => winit::window::CursorIcon::Move,
1083 MouseCursor::NoDrop => winit::window::CursorIcon::NoDrop,
1084 MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed,
1085 MouseCursor::Grab => winit::window::CursorIcon::Grab,
1086 MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing,
1087 MouseCursor::ColResize => winit::window::CursorIcon::ColResize,
1088 MouseCursor::RowResize => winit::window::CursorIcon::RowResize,
1089 MouseCursor::NResize => winit::window::CursorIcon::NResize,
1090 MouseCursor::EResize => winit::window::CursorIcon::EResize,
1091 MouseCursor::SResize => winit::window::CursorIcon::SResize,
1092 MouseCursor::WResize => winit::window::CursorIcon::WResize,
1093 MouseCursor::NeResize => winit::window::CursorIcon::NeResize,
1094 MouseCursor::NwResize => winit::window::CursorIcon::NwResize,
1095 MouseCursor::SeResize => winit::window::CursorIcon::SeResize,
1096 MouseCursor::SwResize => winit::window::CursorIcon::SwResize,
1097 MouseCursor::EwResize => winit::window::CursorIcon::EwResize,
1098 MouseCursor::NsResize => winit::window::CursorIcon::NsResize,
1099 MouseCursor::NeswResize => winit::window::CursorIcon::NeswResize,
1100 MouseCursor::NwseResize => winit::window::CursorIcon::NwseResize,
1101 };
1102 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1103 winit_window.set_cursor_visible(cursor != MouseCursor::None);
1104 winit_window.set_cursor(winit_cursor);
1105 }
1106 }
1107
1108 fn input_method_request(&self, request: corelib::window::InputMethodRequest) {
1109 #[cfg(not(target_arch = "wasm32"))]
1110 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1111 let props = match &request {
1112 corelib::window::InputMethodRequest::Enable(props) => {
1113 winit_window.set_ime_allowed(true);
1114 props
1115 }
1116 corelib::window::InputMethodRequest::Disable => {
1117 return winit_window.set_ime_allowed(false);
1118 }
1119 corelib::window::InputMethodRequest::Update(props) => props,
1120 _ => return,
1121 };
1122 winit_window.set_ime_purpose(match props.input_type {
1123 corelib::items::InputType::Password => winit::window::ImePurpose::Password,
1124 _ => winit::window::ImePurpose::Normal,
1125 });
1126 winit_window.set_ime_cursor_area(
1127 position_to_winit(&props.cursor_rect_origin.into()),
1128 window_size_to_winit(&props.cursor_rect_size.into()),
1129 );
1130 }
1131
1132 #[cfg(target_arch = "wasm32")]
1133 match request {
1134 corelib::window::InputMethodRequest::Enable(..) => {
1135 let mut vkh = self.virtual_keyboard_helper.borrow_mut();
1136 let Some(canvas) =
1137 self.winit_window().and_then(|winit_window| winit_window.canvas())
1138 else {
1139 return;
1140 };
1141 let h = vkh.get_or_insert_with(|| {
1142 super::wasm_input_helper::WasmInputHelper::new(self.self_weak.clone(), canvas)
1143 });
1144 h.show();
1145 }
1146 corelib::window::InputMethodRequest::Disable => {
1147 if let Some(h) = &*self.virtual_keyboard_helper.borrow() {
1148 h.hide()
1149 }
1150 }
1151 _ => {}
1152 };
1153 }
1154
1155 fn as_any(&self) -> &dyn std::any::Any {
1156 self
1157 }
1158
1159 fn color_scheme(&self) -> ColorScheme {
1160 self.color_scheme
1161 .get_or_init(|| {
1162 Box::pin(Property::new({
1163 cfg_if::cfg_if! {
1164 if #[cfg(use_winit_theme)] {
1165 self.winit_window_or_none
1166 .borrow()
1167 .as_window()
1168 .and_then(|window| window.theme())
1169 .map_or(ColorScheme::Unknown, |theme| match theme {
1170 winit::window::Theme::Dark => ColorScheme::Dark,
1171 winit::window::Theme::Light => ColorScheme::Light,
1172 })
1173 } else {
1174 if let Some(old_watch) = self.xdg_settings_watcher.replace(self.spawn_xdg_settings_watcher()) {
1175 old_watch.abort()
1176 }
1177 ColorScheme::Unknown
1178 }
1179 }
1180 }))
1181 })
1182 .as_ref()
1183 .get()
1184 }
1185
1186 #[cfg(muda)]
1187 fn supports_native_menu_bar(&self) -> bool {
1188 true
1189 }
1190
1191 #[cfg(muda)]
1192 fn setup_menubar(&self, menubar: vtable::VBox<i_slint_core::menus::MenuVTable>) {
1193 self.menubar.replace(Some(menubar));
1194
1195 if let WinitWindowOrNone::HasWindow { muda_adapter, .. } =
1196 &*self.winit_window_or_none.borrow()
1197 {
1198 // On Windows, we must destroy the muda menu before re-creating a new one
1199 drop(muda_adapter.borrow_mut().take());
1200 muda_adapter.replace(Some(crate::muda::MudaAdapter::setup(
1201 self.menubar.borrow().as_ref().unwrap(),
1202 &self.winit_window().unwrap(),
1203 self.event_loop_proxy.clone(),
1204 self.self_weak.clone(),
1205 )));
1206 }
1207 }
1208
1209 #[cfg(enable_accesskit)]
1210 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {
1211 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1212 accesskit_adapter_cell.borrow_mut().handle_focus_item_change();
1213 }
1214
1215 #[cfg(enable_accesskit)]
1216 fn register_item_tree(&self) {
1217 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1218 // If the accesskit_adapter is already borrowed, this means the new items were created when the tree was built and there is no need to re-visit them
1219 if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
1220 a.reload_tree();
1221 };
1222 }
1223
1224 #[cfg(enable_accesskit)]
1225 fn unregister_item_tree(
1226 &self,
1227 component: ItemTreeRef,
1228 _: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
1229 ) {
1230 let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
1231 if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
1232 a.unregister_item_tree(component);
1233 };
1234 }
1235
1236 #[cfg(feature = "raw-window-handle-06")]
1237 fn window_handle_06_rc(
1238 &self,
1239 ) -> Result<Rc<dyn raw_window_handle::HasWindowHandle>, raw_window_handle::HandleError> {
1240 self.winit_window_or_none
1241 .borrow()
1242 .as_window()
1243 .map_or(Err(raw_window_handle::HandleError::Unavailable), |window| Ok(window))
1244 }
1245
1246 #[cfg(feature = "raw-window-handle-06")]
1247 fn display_handle_06_rc(
1248 &self,
1249 ) -> Result<Rc<dyn raw_window_handle::HasDisplayHandle>, raw_window_handle::HandleError> {
1250 self.winit_window_or_none
1251 .borrow()
1252 .as_window()
1253 .map_or(Err(raw_window_handle::HandleError::Unavailable), |window| Ok(window))
1254 }
1255
1256 fn bring_to_front(&self) -> Result<(), PlatformError> {
1257 if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
1258 winit_window.set_minimized(false);
1259 winit_window.focus_window();
1260 }
1261 Ok(())
1262 }
1263}
1264
1265impl Drop for WinitWindowAdapter {
1266 fn drop(&mut self) {
1267 if let Some(winit_window: Rc) = self.winit_window_or_none.borrow().as_window() {
1268 self.shared_backend_data.unregister_window(winit_window.id());
1269 }
1270
1271 #[cfg(not(use_winit_theme))]
1272 if let Some(xdg_watch_future: JoinHandle<()>) = self.xdg_settings_watcher.take() {
1273 xdg_watch_future.abort();
1274 }
1275 }
1276}
1277
1278// Winit doesn't automatically resize the window to satisfy constraints. Qt does it though, and so do we here.
1279fn adjust_window_size_to_satisfy_constraints(
1280 adapter: &WinitWindowAdapter,
1281 min_size: Option<winit::dpi::PhysicalSize<f32>>,
1282 max_size: Option<winit::dpi::PhysicalSize<f32>>,
1283) {
1284 let current_size = adapter
1285 .pending_requested_size
1286 .get()
1287 .map(|s| s.to_physical(adapter.window().scale_factor() as f64))
1288 .unwrap_or_else(|| physical_size_to_winit(adapter.size.get()));
1289
1290 let mut window_size = current_size;
1291 if let Some(min_size) = min_size {
1292 let min_size = min_size.cast();
1293 window_size.width = window_size.width.max(min_size.width);
1294 window_size.height = window_size.height.max(min_size.height);
1295 }
1296
1297 if let Some(max_size) = max_size {
1298 let max_size = max_size.cast();
1299 window_size.width = window_size.width.min(max_size.width);
1300 window_size.height = window_size.height.min(max_size.height);
1301 }
1302
1303 if window_size != current_size {
1304 // TODO: don't ignore error, propgate to caller
1305 adapter.resize_window(window_size.into()).ok();
1306 }
1307}
1308

Provided by KDAB

Privacy Policy