1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
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;
9#[cfg(target_arch = "wasm32")]
10use core::cell::RefCell;
11use core::pin::Pin;
12use std::rc::Rc;
13#[cfg(target_arch = "wasm32")]
14use std::rc::Weak;
15
16#[cfg(target_arch = "wasm32")]
17use winit::platform::web::WindowExtWebSys;
18
19use crate::renderer::WinitCompatibleRenderer;
20use const_field_offset::FieldOffsets;
21
22use corelib::item_tree::ItemTreeRc;
23#[cfg(enable_accesskit)]
24use corelib::item_tree::ItemTreeRef;
25use corelib::items::MouseCursor;
26#[cfg(enable_accesskit)]
27use corelib::items::{ItemRc, ItemRef};
28
29use corelib::api::PhysicalSize;
30use corelib::layout::Orientation;
31use corelib::lengths::LogicalLength;
32use corelib::platform::{PlatformError, WindowEvent};
33use corelib::window::{WindowAdapter, WindowAdapterInternal, WindowInner};
34use corelib::Property;
35use corelib::{graphics::*, Coord};
36use i_slint_core as corelib;
37use once_cell::unsync::OnceCell;
38use winit::window::WindowBuilder;
39
40fn position_to_winit(pos: &corelib::api::WindowPosition) -> winit::dpi::Position {
41 match pos {
42 corelib::api::WindowPosition::Logical(pos: &LogicalPosition) => {
43 winit::dpi::Position::new(position:winit::dpi::LogicalPosition::new(pos.x, pos.y))
44 }
45 corelib::api::WindowPosition::Physical(pos: &PhysicalPosition) => {
46 winit::dpi::Position::new(position:winit::dpi::PhysicalPosition::new(pos.x, pos.y))
47 }
48 }
49}
50
51fn window_size_to_winit(size: &corelib::api::WindowSize) -> winit::dpi::Size {
52 match size {
53 corelib::api::WindowSize::Logical(size: &LogicalSize) => {
54 winit::dpi::Size::new(size:winit::dpi::LogicalSize::new(size.width, size.height))
55 }
56 corelib::api::WindowSize::Physical(size: &PhysicalSize) => {
57 winit::dpi::Size::new(size:winit::dpi::PhysicalSize::new(size.width, size.height))
58 }
59 }
60}
61
62pub fn physical_size_to_slint(size: &winit::dpi::PhysicalSize<u32>) -> corelib::api::PhysicalSize {
63 corelib::api::PhysicalSize::new(size.width, size.height)
64}
65
66fn icon_to_winit(icon: corelib::graphics::Image) -> Option<winit::window::Icon> {
67 let image_inner: &ImageInner = (&icon).into();
68
69 let pixel_buffer = match image_inner {
70 ImageInner::EmbeddedImage { buffer, .. } => buffer.clone(),
71 _ => return None,
72 };
73
74 // This could become a method in SharedPixelBuffer...
75 let rgba_pixels: Vec<u8> = match &pixel_buffer {
76 SharedImageBuffer::RGB8(pixels) => pixels
77 .as_bytes()
78 .chunks(3)
79 .flat_map(|rgb| IntoIterator::into_iter([rgb[0], rgb[1], rgb[2], 255]))
80 .collect(),
81 SharedImageBuffer::RGBA8(pixels) => pixels.as_bytes().to_vec(),
82 SharedImageBuffer::RGBA8Premultiplied(pixels) => pixels
83 .as_bytes()
84 .chunks(4)
85 .flat_map(|rgba| {
86 let alpha = rgba[3] as u32;
87 IntoIterator::into_iter(rgba)
88 .take(3)
89 .map(move |component| (*component as u32 * alpha / 255) as u8)
90 .chain(std::iter::once(alpha as u8))
91 })
92 .collect(),
93 };
94
95 winit::window::Icon::from_rgba(rgba_pixels, pixel_buffer.width(), pixel_buffer.height()).ok()
96}
97
98fn window_is_resizable(
99 min_size: Option<corelib::api::LogicalSize>,
100 max_size: Option<corelib::api::LogicalSize>,
101) -> bool {
102 if let Some((
103 corelib::api::LogicalSize { width: min_width: f32, height: min_height: f32, .. },
104 corelib::api::LogicalSize { width: max_width: f32, height: max_height: f32, .. },
105 )) = min_size.zip(max_size)
106 {
107 min_width < max_width || min_height < max_height
108 } else {
109 true
110 }
111}
112
113/// GraphicsWindow is an implementation of the [WindowAdapter][`crate::eventloop::WindowAdapter`] trait. This is
114/// typically instantiated by entry factory functions of the different graphics back ends.
115pub struct WinitWindowAdapter {
116 window: OnceCell<corelib::api::Window>,
117 #[cfg(target_arch = "wasm32")]
118 self_weak: Weak<Self>,
119 pending_redraw: Cell<bool>,
120 dark_color_scheme: OnceCell<Pin<Box<Property<bool>>>>,
121 constraints: Cell<corelib::window::LayoutConstraints>,
122 shown: Cell<bool>,
123 window_level: Cell<winit::window::WindowLevel>,
124 maximized: Cell<bool>,
125 minimized: Cell<bool>,
126 fullscreen: Cell<bool>,
127
128 pub(crate) renderer: Box<dyn WinitCompatibleRenderer>,
129 /// We cache the size because winit_window.inner_size() can return different value between calls (eg, on X11)
130 /// And we wan see the newer value before the Resized event was received, leading to inconsistencies
131 size: Cell<PhysicalSize>,
132
133 /// Whether the size has been set explicitly via `set_size`
134 has_explicit_size: Cell<bool>,
135
136 #[cfg(target_arch = "wasm32")]
137 virtual_keyboard_helper: RefCell<Option<super::wasm_input_helper::WasmInputHelper>>,
138
139 #[cfg(enable_accesskit)]
140 pub accesskit_adapter: crate::accesskit::AccessKitAdapter,
141
142 winit_window: Rc<winit::window::Window>, // Last field so that any previously provided window handles are still valid in the drop impl of the renderers, etc.
143}
144
145impl WinitWindowAdapter {
146 /// Creates a new reference-counted instance.
147 pub(crate) fn new(
148 renderer: Box<dyn WinitCompatibleRenderer>,
149 winit_window: Rc<winit::window::Window>,
150 ) -> Rc<Self> {
151 let self_rc = Rc::new_cyclic(|self_weak| Self {
152 window: OnceCell::with_value(corelib::api::Window::new(self_weak.clone() as _)),
153 #[cfg(target_arch = "wasm32")]
154 self_weak: self_weak.clone(),
155 pending_redraw: Default::default(),
156 dark_color_scheme: Default::default(),
157 constraints: Default::default(),
158 shown: Default::default(),
159 window_level: Default::default(),
160 maximized: Cell::default(),
161 minimized: Cell::default(),
162 fullscreen: Cell::default(),
163 winit_window: winit_window.clone(),
164 size: Default::default(),
165 has_explicit_size: Default::default(),
166 renderer,
167 #[cfg(target_arch = "wasm32")]
168 virtual_keyboard_helper: Default::default(),
169 #[cfg(enable_accesskit)]
170 accesskit_adapter: crate::accesskit::AccessKitAdapter::new(
171 self_weak.clone(),
172 &winit_window,
173 ),
174 });
175
176 let id = self_rc.winit_window().id();
177 crate::event_loop::register_window(id, (self_rc.clone()) as _);
178
179 let scale_factor = std::env::var("SLINT_SCALE_FACTOR")
180 .ok()
181 .and_then(|x| x.parse::<f32>().ok())
182 .filter(|f| *f > 0.)
183 .unwrap_or_else(|| self_rc.winit_window().scale_factor() as f32);
184 self_rc.window().dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor });
185
186 self_rc
187 }
188
189 fn renderer(&self) -> &dyn WinitCompatibleRenderer {
190 self.renderer.as_ref()
191 }
192
193 pub(crate) fn window_builder(
194 #[cfg(target_arch = "wasm32")] canvas_id: &str,
195 ) -> Result<WindowBuilder, PlatformError> {
196 let mut window_builder = WindowBuilder::new().with_transparent(true).with_visible(false);
197
198 window_builder = window_builder.with_title("Slint Window".to_string());
199
200 #[cfg(target_arch = "wasm32")]
201 {
202 use winit::platform::web::WindowBuilderExtWebSys;
203
204 use wasm_bindgen::JsCast;
205
206 let html_canvas = web_sys::window()
207 .ok_or_else(|| "winit backend: Could not retrieve DOM window".to_string())?
208 .document()
209 .ok_or_else(|| "winit backend: Could not retrieve DOM document".to_string())?
210 .get_element_by_id(canvas_id)
211 .ok_or_else(|| {
212 format!(
213 "winit backend: Could not retrieve existing HTML Canvas element '{}'",
214 canvas_id
215 )
216 })?
217 .dyn_into::<web_sys::HtmlCanvasElement>()
218 .map_err(|_| {
219 format!(
220 "winit backend: Specified DOM element '{}' is not a HTML Canvas",
221 canvas_id
222 )
223 })?;
224 window_builder = window_builder
225 .with_canvas(Some(html_canvas))
226 // Don't activate the window by default, as that will cause the page to scroll,
227 // ignoring any existing anchors.
228 .with_active(false)
229 };
230
231 Ok(window_builder)
232 }
233
234 /// Draw the items of the specified `component` in the given window.
235 pub fn draw(&self) -> Result<(), PlatformError> {
236 if !self.shown.get() {
237 return Ok(()); // caller bug, doesn't make sense to call draw() when not shown
238 }
239
240 self.pending_redraw.set(false);
241
242 let renderer = self.renderer();
243 renderer.render(self.window())?;
244
245 Ok(())
246 }
247
248 fn with_window_handle(&self, callback: &mut dyn FnMut(&winit::window::Window)) {
249 callback(&self.winit_window());
250 }
251
252 pub fn winit_window(&self) -> Rc<winit::window::Window> {
253 self.winit_window.clone()
254 }
255
256 #[cfg(target_arch = "wasm32")]
257 pub fn input_method_focused(&self) -> bool {
258 match self.virtual_keyboard_helper.try_borrow() {
259 Ok(vkh) => vkh.as_ref().map_or(false, |h| h.has_focus()),
260 // the only location in which the virtual_keyboard_helper is mutably borrowed is from
261 // show_virtual_keyboard, which means we have the focus
262 Err(_) => true,
263 }
264 }
265
266 #[cfg(not(target_arch = "wasm32"))]
267 pub fn input_method_focused(&self) -> bool {
268 false
269 }
270
271 // Requests for the window to be resized. Returns true if the window was resized immediately,
272 // or if it will be resized later (false).
273 fn resize_window(&self, size: winit::dpi::Size) -> Result<bool, PlatformError> {
274 if let Some(size) = self.winit_window().request_inner_size(size) {
275 // On wayland we might not get a WindowEvent::Resized, so resize the EGL surface right away.
276 self.resize_event(size)?;
277 Ok(true)
278 } else {
279 // None means that we'll get a `WindowEvent::Resized` later
280 Ok(false)
281 }
282 }
283
284 pub fn resize_event(&self, size: winit::dpi::PhysicalSize<u32>) -> Result<(), PlatformError> {
285 // When a window is minimized on Windows, we get a move event to an off-screen position
286 // and a resize even with a zero size. Don't forward that, especially not to the renderer,
287 // which might panic when trying to create a zero-sized surface.
288 if size.width > 0 && size.height > 0 {
289 let physical_size = physical_size_to_slint(&size);
290 self.size.set(physical_size);
291 let scale_factor = WindowInner::from_pub(self.window()).scale_factor();
292 self.window().dispatch_event(WindowEvent::Resized {
293 size: physical_size.to_logical(scale_factor),
294 });
295
296 // Workaround fox winit not sync'ing CSS size of the canvas (the size shown on the browser)
297 // with the width/height attribute (the size of the viewport/GL surface)
298 // If they're not in sync, the UI would be shown as scaled
299 #[cfg(target_arch = "wasm32")]
300 if let Some(html_canvas) = self.winit_window.canvas() {
301 html_canvas.set_width(physical_size.width);
302 html_canvas.set_height(physical_size.height);
303 }
304 }
305 Ok(())
306 }
307
308 pub fn set_dark_color_scheme(&self, dark_mode: bool) {
309 self.dark_color_scheme
310 .get_or_init(|| Box::pin(Property::new(false)))
311 .as_ref()
312 .set(dark_mode)
313 }
314
315 pub fn window_state_event(&self) {
316 if let Some(minimized) = self.winit_window.is_minimized() {
317 self.minimized.set(minimized);
318 if minimized != self.window().is_minimized() {
319 self.window().set_minimized(minimized);
320 }
321 }
322
323 // The method winit::Window::is_maximized returns false when the window
324 // is minimized, even if it was previously maximized. We have to ensure
325 // that we only update the internal maximized state when the window is
326 // not minimized. Otherwise, the window would be restored in a
327 // non-maximized state even if it was maximized before being minimized.
328 let maximized = self.winit_window.is_maximized();
329 if !self.window().is_minimized() {
330 self.maximized.set(maximized);
331 if maximized != self.window().is_maximized() {
332 self.window().set_maximized(maximized);
333 }
334 }
335
336 // NOTE: Fullscreen overrides maximized so if both are true then the
337 // window will remain in fullscreen. Fullscreen must be false to switch
338 // to maximized.
339 let fullscreen = self.winit_window.fullscreen().is_some();
340 if fullscreen != self.window().is_fullscreen() {
341 self.window().set_fullscreen(fullscreen);
342 }
343 }
344}
345
346impl WindowAdapter for WinitWindowAdapter {
347 fn window(&self) -> &corelib::api::Window {
348 self.window.get().unwrap()
349 }
350
351 fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
352 self.renderer().as_core_renderer()
353 }
354
355 fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
356 if visible == self.shown.get() {
357 return Ok(());
358 }
359
360 self.shown.set(visible);
361 if visible {
362 let winit_window = self.winit_window();
363
364 let runtime_window = WindowInner::from_pub(self.window());
365
366 let scale_factor = runtime_window.scale_factor() as f64;
367
368 let component_rc = runtime_window.component();
369 let component = ItemTreeRc::borrow_pin(&component_rc);
370
371 let layout_info_h = component.as_ref().layout_info(Orientation::Horizontal);
372 if let Some(window_item) = runtime_window.window_item() {
373 // Setting the width to its preferred size before querying the vertical layout info
374 // is important in case the height depends on the width
375 window_item.width.set(LogicalLength::new(layout_info_h.preferred_bounded()));
376 }
377 let layout_info_v = component.as_ref().layout_info(Orientation::Vertical);
378 #[allow(unused_mut)]
379 let mut preferred_size = winit::dpi::LogicalSize::new(
380 layout_info_h.preferred_bounded(),
381 layout_info_v.preferred_bounded(),
382 );
383
384 #[cfg(target_arch = "wasm32")]
385 if let Some(html_canvas) = winit_window.canvas() {
386 let existing_canvas_size = winit::dpi::LogicalSize::new(
387 html_canvas.client_width() as f32,
388 html_canvas.client_height() as f32,
389 );
390 // Try to maintain the existing size of the canvas element, if any
391 if existing_canvas_size.width > 0. {
392 preferred_size.width = existing_canvas_size.width;
393 }
394 if existing_canvas_size.height > 0. {
395 preferred_size.height = existing_canvas_size.height;
396 }
397 }
398
399 if winit_window.fullscreen().is_none()
400 && !self.has_explicit_size.get()
401 && preferred_size.width > 0 as Coord
402 && preferred_size.height > 0 as Coord
403 {
404 // use the Slint's window Scale factor to take in account the override
405 let size = preferred_size.to_physical::<u32>(scale_factor);
406 self.resize_window(size.into())?;
407 };
408
409 winit_window.set_visible(true);
410
411 // Make sure the dark color scheme property is up-to-date, as it may have been queried earlier when
412 // the window wasn't mapped yet.
413 if let Some(dark_color_scheme_prop) = self.dark_color_scheme.get() {
414 if let Some(theme) = winit_window.theme() {
415 dark_color_scheme_prop.as_ref().set(theme == winit::window::Theme::Dark)
416 }
417 }
418
419 // In wasm a request_redraw() issued before show() results in a draw() even when the window
420 // isn't visible, as opposed to regular windowing systems. The compensate for the lost draw,
421 // explicitly render the first frame on show().
422 #[cfg(target_arch = "wasm32")]
423 if self.pending_redraw.get() {
424 self.draw()?;
425 };
426
427 Ok(())
428 } else {
429 self.winit_window().set_visible(false);
430
431 /* FIXME:
432 if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() {
433 existing_blinker.stop();
434 }*/
435 Ok(())
436 }
437 }
438
439 fn position(&self) -> Option<corelib::api::PhysicalPosition> {
440 match self.winit_window().outer_position() {
441 Ok(outer_position) => {
442 Some(corelib::api::PhysicalPosition::new(outer_position.x, outer_position.y))
443 }
444 Err(_) => None,
445 }
446 }
447
448 fn set_position(&self, position: corelib::api::WindowPosition) {
449 self.winit_window().set_outer_position(position_to_winit(&position))
450 }
451
452 fn set_size(&self, size: corelib::api::WindowSize) {
453 self.has_explicit_size.set(true);
454 // TODO: don't ignore error, propgate to caller
455 self.resize_window(window_size_to_winit(&size)).ok();
456 }
457
458 fn size(&self) -> corelib::api::PhysicalSize {
459 self.size.get()
460 }
461
462 fn request_redraw(&self) {
463 if !self.pending_redraw.replace(true) {
464 self.winit_window.request_redraw()
465 }
466 }
467
468 #[allow(clippy::unnecessary_cast)] // Coord is used!
469 fn update_window_properties(&self, properties: corelib::window::WindowProperties<'_>) {
470 let Some(window_item) =
471 self.window.get().and_then(|w| WindowInner::from_pub(w).window_item())
472 else {
473 return;
474 };
475 let window_item = window_item.as_pin_ref();
476
477 let winit_window = self.winit_window();
478
479 let mut width = window_item.width().get() as f32;
480 let mut height = window_item.height().get() as f32;
481
482 let mut must_resize = false;
483
484 winit_window.set_window_icon(icon_to_winit(window_item.icon()));
485 winit_window.set_title(&properties.title());
486 winit_window
487 .set_decorations(!window_item.no_frame() || winit_window.fullscreen().is_some());
488 let new_window_level = if window_item.always_on_top() {
489 winit::window::WindowLevel::AlwaysOnTop
490 } else {
491 winit::window::WindowLevel::Normal
492 };
493 // Only change the window level if it changes, to avoid https://github.com/slint-ui/slint/issues/3280
494 // (Ubuntu 20.04's window manager always bringing the window to the front on x11)
495 if self.window_level.replace(new_window_level) != new_window_level {
496 winit_window.set_window_level(new_window_level);
497 }
498
499 if width <= 0. || height <= 0. {
500 must_resize = true;
501
502 let winit_size =
503 winit_window.inner_size().to_logical(self.window().scale_factor() as f64);
504
505 if width <= 0. {
506 width = winit_size.width;
507 }
508 if height <= 0. {
509 height = winit_size.height;
510 }
511 }
512
513 let existing_size = self.size().to_logical(self.window().scale_factor());
514
515 if (existing_size.width - width).abs() > 1. || (existing_size.height - height).abs() > 1. {
516 // If we're in fullscreen state, don't try to resize the window but maintain the surface
517 // size we've been assigned to from the windowing system. Weston/Wayland don't like it
518 // when we create a surface that's bigger than the screen due to constraints (#532).
519 if winit_window.fullscreen().is_none() {
520 // TODO: don't ignore error, propgate to caller
521 let immediately_resized = self
522 .resize_window(winit::dpi::LogicalSize::new(width, height).into())
523 .unwrap_or_default();
524 if immediately_resized {
525 // The resize event was already dispatched
526 must_resize = false;
527 }
528 }
529 }
530
531 if must_resize {
532 self.window().dispatch_event(WindowEvent::Resized {
533 size: i_slint_core::api::LogicalSize::new(width, height),
534 });
535 }
536
537 self.with_window_handle(&mut |winit_window| {
538 let m = properties.is_fullscreen();
539 if m != self.fullscreen.get() {
540 if m {
541 if winit_window.fullscreen().is_none() {
542 winit_window
543 .set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
544 }
545 } else {
546 winit_window.set_fullscreen(None);
547 }
548 }
549
550 let m = properties.is_maximized();
551 if m != self.maximized.get() {
552 self.maximized.set(m);
553 winit_window.set_maximized(m);
554 }
555
556 let m = properties.is_minimized();
557 if m != self.minimized.get() {
558 self.minimized.set(m);
559 winit_window.set_minimized(m);
560 }
561
562 // If we're in fullscreen, don't try to resize the window but
563 // maintain the surface size we've been assigned to from the
564 // windowing system. Weston/Wayland don't like it when we create a
565 // surface that's bigger than the screen due to constraints (#532).
566 if winit_window.fullscreen().is_some() {
567 return;
568 }
569
570 let new_constraints = properties.layout_constraints();
571 if new_constraints == self.constraints.get() {
572 return;
573 }
574
575 self.constraints.set(new_constraints);
576
577 // Use our scale factor instead of winit's logical size to take a scale factor override into account.
578 let sf = self.window().scale_factor();
579
580 let into_size = |s: corelib::api::LogicalSize| -> winit::dpi::PhysicalSize<f32> {
581 winit::dpi::LogicalSize::new(s.width, s.height).to_physical(sf as f64)
582 };
583
584 let resizable = window_is_resizable(new_constraints.min, new_constraints.max);
585 // we must call set_resizable before setting the min and max size otherwise setting the min and max size don't work on X11
586 winit_window.set_resizable(resizable);
587 let winit_min_inner = new_constraints.min.map(into_size);
588 winit_window.set_min_inner_size(winit_min_inner);
589 let winit_max_inner = new_constraints.max.map(into_size);
590 winit_window.set_max_inner_size(winit_max_inner);
591
592 adjust_window_size_to_satisfy_constraints(self, winit_min_inner, winit_max_inner);
593
594 // Auto-resize to the preferred size if users (SlintPad) requests it
595 #[cfg(target_arch = "wasm32")]
596 if let Some(canvas) = winit_window.canvas() {
597 if canvas
598 .dataset()
599 .get("slintAutoResizeToPreferred")
600 .and_then(|val_str| val_str.parse().ok())
601 .unwrap_or_default()
602 {
603 let pref_width = new_constraints.preferred.width;
604 let pref_height = new_constraints.preferred.height;
605 if pref_width > 0 as Coord || pref_height > 0 as Coord {
606 // TODO: don't ignore error, propgate to caller
607 self.resize_window(
608 winit::dpi::LogicalSize::new(pref_width, pref_height).into(),
609 )
610 .ok();
611 };
612 }
613 }
614 });
615 }
616
617 fn internal(&self, _: corelib::InternalToken) -> Option<&dyn WindowAdapterInternal> {
618 Some(self)
619 }
620}
621
622impl WindowAdapterInternal for WinitWindowAdapter {
623 fn set_mouse_cursor(&self, cursor: MouseCursor) {
624 let winit_cursor = match cursor {
625 MouseCursor::Default => winit::window::CursorIcon::Default,
626 MouseCursor::None => winit::window::CursorIcon::Default,
627 MouseCursor::Help => winit::window::CursorIcon::Help,
628 MouseCursor::Pointer => winit::window::CursorIcon::Pointer,
629 MouseCursor::Progress => winit::window::CursorIcon::Progress,
630 MouseCursor::Wait => winit::window::CursorIcon::Wait,
631 MouseCursor::Crosshair => winit::window::CursorIcon::Crosshair,
632 MouseCursor::Text => winit::window::CursorIcon::Text,
633 MouseCursor::Alias => winit::window::CursorIcon::Alias,
634 MouseCursor::Copy => winit::window::CursorIcon::Copy,
635 MouseCursor::Move => winit::window::CursorIcon::Move,
636 MouseCursor::NoDrop => winit::window::CursorIcon::NoDrop,
637 MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed,
638 MouseCursor::Grab => winit::window::CursorIcon::Grab,
639 MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing,
640 MouseCursor::ColResize => winit::window::CursorIcon::ColResize,
641 MouseCursor::RowResize => winit::window::CursorIcon::RowResize,
642 MouseCursor::NResize => winit::window::CursorIcon::NResize,
643 MouseCursor::EResize => winit::window::CursorIcon::EResize,
644 MouseCursor::SResize => winit::window::CursorIcon::SResize,
645 MouseCursor::WResize => winit::window::CursorIcon::WResize,
646 MouseCursor::NeResize => winit::window::CursorIcon::NeResize,
647 MouseCursor::NwResize => winit::window::CursorIcon::NwResize,
648 MouseCursor::SeResize => winit::window::CursorIcon::SeResize,
649 MouseCursor::SwResize => winit::window::CursorIcon::SwResize,
650 MouseCursor::EwResize => winit::window::CursorIcon::EwResize,
651 MouseCursor::NsResize => winit::window::CursorIcon::NsResize,
652 MouseCursor::NeswResize => winit::window::CursorIcon::NeswResize,
653 MouseCursor::NwseResize => winit::window::CursorIcon::NwseResize,
654 };
655 self.with_window_handle(&mut |winit_window| {
656 winit_window.set_cursor_visible(cursor != MouseCursor::None);
657 winit_window.set_cursor_icon(winit_cursor);
658 });
659 }
660
661 fn input_method_request(&self, request: corelib::window::InputMethodRequest) {
662 #[cfg(not(target_arch = "wasm32"))]
663 self.with_window_handle(&mut |winit_window| {
664 let props = match &request {
665 corelib::window::InputMethodRequest::Enable(props) => {
666 winit_window.set_ime_allowed(true);
667 props
668 }
669 corelib::window::InputMethodRequest::Disable => {
670 return winit_window.set_ime_allowed(false)
671 }
672 corelib::window::InputMethodRequest::Update(props) => props,
673 _ => return,
674 };
675 winit_window.set_ime_purpose(match props.input_type {
676 corelib::items::InputType::Password => winit::window::ImePurpose::Password,
677 _ => winit::window::ImePurpose::Normal,
678 });
679 winit_window.set_ime_cursor_area(
680 position_to_winit(&props.cursor_rect_origin.into()),
681 window_size_to_winit(&props.cursor_rect_size.into()),
682 );
683 });
684
685 #[cfg(target_arch = "wasm32")]
686 match request {
687 corelib::window::InputMethodRequest::Enable(..) => {
688 let mut vkh = self.virtual_keyboard_helper.borrow_mut();
689 let Some(canvas) = self.winit_window().canvas() else { return };
690 let h = vkh.get_or_insert_with(|| {
691 super::wasm_input_helper::WasmInputHelper::new(self.self_weak.clone(), canvas)
692 });
693 h.show();
694 }
695 corelib::window::InputMethodRequest::Disable => {
696 if let Some(h) = &*self.virtual_keyboard_helper.borrow() {
697 h.hide()
698 }
699 }
700 _ => {}
701 };
702 }
703
704 fn as_any(&self) -> &dyn std::any::Any {
705 self
706 }
707
708 fn dark_color_scheme(&self) -> bool {
709 self.dark_color_scheme
710 .get_or_init(|| {
711 Box::pin(Property::new({
712 self.winit_window()
713 .theme()
714 .map_or(false, |theme| theme == winit::window::Theme::Dark)
715 }))
716 })
717 .as_ref()
718 .get()
719 }
720
721 #[cfg(enable_accesskit)]
722 fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {
723 self.accesskit_adapter.handle_focus_item_change();
724 }
725
726 #[cfg(enable_accesskit)]
727 fn register_item_tree(&self) {
728 self.accesskit_adapter.register_item_tree();
729 }
730
731 #[cfg(enable_accesskit)]
732 fn unregister_item_tree(
733 &self,
734 _component: ItemTreeRef,
735 _: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
736 ) {
737 self.accesskit_adapter.unregister_item_tree(_component);
738 }
739}
740
741impl Drop for WinitWindowAdapter {
742 fn drop(&mut self) {
743 crate::event_loop::unregister_window(self.winit_window().id());
744 }
745}
746
747#[derive(FieldOffsets)]
748#[repr(C)]
749#[pin]
750struct WindowProperties {
751 scale_factor: Property<f32>,
752}
753
754impl Default for WindowProperties {
755 fn default() -> Self {
756 Self { scale_factor: Property::new(1.0) }
757 }
758}
759
760// Winit doesn't automatically resize the window to satisfy constraints. Qt does it though, and so do we here.
761fn adjust_window_size_to_satisfy_constraints(
762 winit_window: &WinitWindowAdapter,
763 min_size: Option<winit::dpi::PhysicalSize<f32>>,
764 max_size: Option<winit::dpi::PhysicalSize<f32>>,
765) {
766 let mut window_size: PhysicalSize = winit_window.size();
767
768 if let Some(min_size: PhysicalSize) = min_size {
769 let min_size: PhysicalSize = min_size.cast();
770 window_size.width = window_size.width.max(min_size.width);
771 window_size.height = window_size.height.max(min_size.height);
772 }
773
774 if let Some(max_size: PhysicalSize) = max_size {
775 let max_size: PhysicalSize = max_size.cast();
776 window_size.width = window_size.width.min(max_size.width);
777 window_size.height = window_size.height.min(max_size.height);
778 }
779
780 if window_size != winit_window.size() {
781 // TODO: don't ignore error, propgate to caller
782 winit_windowResult
783 .resize_window(
784 size:winit::dpi::PhysicalSize::new(window_size.width, window_size.height).into(),
785 )
786 .ok();
787 }
788}
789

Provided by KDAB

Privacy Policy