slint/internal/backends/winit/glwindow.rs
Simon Hausmann 155b1443e2 Add support for reacting to dark/light mode theme changes with winit
On macOS and Windows, use winit's `Window::theme()` instead of
dark-light, to detect the theme.

Always react to WindowEvent::ThemeChange. It's delivered on macOS,
Windows, and the web.
2023-02-17 16:11:44 +01:00

781 lines
32 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
//! This module contains the GraphicsWindow that used to be within corelib.
// cspell:ignore borderless corelib nesw webgl winit winsys xlib
use core::cell::{Cell, RefCell};
use core::pin::Pin;
use std::rc::{Rc, Weak};
use crate::event_loop::WinitWindow;
use crate::renderer::WinitCompatibleRenderer;
use const_field_offset::FieldOffsets;
use corelib::component::ComponentRc;
use corelib::graphics::euclid::num::Zero;
use corelib::items::MouseCursor;
use corelib::layout::Orientation;
use corelib::lengths::{LogicalLength, LogicalPoint, LogicalSize};
use corelib::platform::PlatformError;
use corelib::window::{WindowAdapter, WindowAdapterSealed, WindowInner};
use corelib::Property;
use corelib::{graphics::*, Coord};
use i_slint_core as corelib;
fn position_to_winit(pos: &corelib::api::WindowPosition) -> winit::dpi::Position {
match pos {
corelib::api::WindowPosition::Logical(pos) => {
winit::dpi::Position::new(winit::dpi::LogicalPosition::new(pos.x, pos.y))
}
corelib::api::WindowPosition::Physical(pos) => {
winit::dpi::Position::new(winit::dpi::PhysicalPosition::new(pos.x, pos.y))
}
}
}
fn window_size_to_slint(size: &corelib::api::WindowSize) -> winit::dpi::Size {
match size {
corelib::api::WindowSize::Logical(size) => {
winit::dpi::Size::new(winit::dpi::LogicalSize::new(size.width, size.height))
}
corelib::api::WindowSize::Physical(size) => {
winit::dpi::Size::new(winit::dpi::PhysicalSize::new(size.width, size.height))
}
}
}
fn physical_size_to_slint(size: &winit::dpi::PhysicalSize<u32>) -> corelib::api::PhysicalSize {
corelib::api::PhysicalSize::new(size.width, size.height)
}
fn icon_to_winit(icon: corelib::graphics::Image) -> Option<winit::window::Icon> {
let image_inner: &ImageInner = (&icon).into();
let pixel_buffer = match image_inner {
ImageInner::EmbeddedImage { buffer, .. } => buffer.clone(),
_ => return None,
};
// This could become a method in SharedPixelBuffer...
let rgba_pixels: Vec<u8> = match &pixel_buffer {
SharedImageBuffer::RGB8(pixels) => pixels
.as_bytes()
.chunks(3)
.flat_map(|rgb| IntoIterator::into_iter([rgb[0], rgb[1], rgb[2], 255]))
.collect(),
SharedImageBuffer::RGBA8(pixels) => pixels.as_bytes().to_vec(),
SharedImageBuffer::RGBA8Premultiplied(pixels) => pixels
.as_bytes()
.chunks(4)
.flat_map(|rgba| {
let alpha = rgba[3] as u32;
IntoIterator::into_iter(rgba)
.take(3)
.map(move |component| (*component as u32 * alpha / 255) as u8)
.chain(std::iter::once(alpha as u8))
})
.collect(),
};
winit::window::Icon::from_rgba(rgba_pixels, pixel_buffer.width(), pixel_buffer.height()).ok()
}
/// GraphicsWindow is an implementation of the [WindowAdapter][`crate::eventloop::WindowAdapter`] trait. This is
/// typically instantiated by entry factory functions of the different graphics back ends.
pub(crate) struct GLWindow<Renderer: WinitCompatibleRenderer + 'static> {
window: corelib::api::Window,
self_weak: Weak<Self>,
map_state: RefCell<GraphicsWindowBackendState>,
currently_pressed_key_code: std::cell::Cell<Option<winit::event::VirtualKeyCode>>,
pending_redraw: Cell<bool>,
in_resize_event: Cell<bool>,
dark_color_scheme: once_cell::unsync::OnceCell<Pin<Box<Property<bool>>>>,
renderer: Renderer,
#[cfg(target_arch = "wasm32")]
canvas_id: String,
#[cfg(target_arch = "wasm32")]
virtual_keyboard_helper: RefCell<Option<super::wasm_input_helper::WasmInputHelper>>,
}
impl<Renderer: WinitCompatibleRenderer + 'static> GLWindow<Renderer> {
/// Creates a new reference-counted instance.
///
/// Arguments:
/// * `graphics_backend_factory`: The factor function stored in the GraphicsWindow that's called when the state
/// of the window changes to mapped. The event loop and window builder parameters can be used to create a
/// backing window.
pub(crate) fn new(#[cfg(target_arch = "wasm32")] canvas_id: String) -> Rc<dyn WindowAdapter> {
let self_rc = Rc::new_cyclic(|self_weak| Self {
window: corelib::api::Window::new(self_weak.clone() as _),
self_weak: self_weak.clone(),
map_state: RefCell::new(GraphicsWindowBackendState::Unmapped {
requested_position: None,
requested_size: None,
}),
currently_pressed_key_code: Default::default(),
pending_redraw: Cell::new(false),
in_resize_event: Cell::new(false),
dark_color_scheme: Default::default(),
renderer: Renderer::new(&(self_weak.clone() as _)),
#[cfg(target_arch = "wasm32")]
canvas_id,
#[cfg(target_arch = "wasm32")]
virtual_keyboard_helper: Default::default(),
});
self_rc as _
}
fn is_mapped(&self) -> bool {
matches!(&*self.map_state.borrow(), GraphicsWindowBackendState::Mapped { .. })
}
fn borrow_mapped_window(&self) -> Option<std::cell::Ref<MappedWindow>> {
if self.is_mapped() {
std::cell::Ref::map(self.map_state.borrow(), |state| match state {
GraphicsWindowBackendState::Unmapped{..} => {
panic!("borrow_mapped_window must be called after checking if the window is mapped")
}
GraphicsWindowBackendState::Mapped(window) => window,
}).into()
} else {
None
}
}
fn unmap(&self) {
let old_mapped = match self.map_state.replace(GraphicsWindowBackendState::Unmapped {
requested_position: None,
requested_size: None,
}) {
GraphicsWindowBackendState::Unmapped { .. } => return,
GraphicsWindowBackendState::Mapped(old_mapped) => old_mapped,
};
crate::event_loop::unregister_window(old_mapped.winit_window.id());
self.renderer.hide();
}
fn call_with_event_loop(&self, callback: fn(&Self)) {
// With wasm, winit's `run()` consumes the event loop and access to it from outside the event handler yields
// loop and thus ends up trying to create a new event loop instance, which panics in winit. Instead, forward
// the call to be invoked from within the event loop
#[cfg(target_arch = "wasm32")]
corelib::api::invoke_from_event_loop({
let self_weak = send_wrapper::SendWrapper::new(self.self_weak.clone());
move || {
if let Some(this) = self_weak.take().upgrade() {
callback(&this)
}
}
})
.unwrap();
#[cfg(not(target_arch = "wasm32"))]
callback(self)
}
}
impl<Renderer: WinitCompatibleRenderer + 'static> WinitWindow for GLWindow<Renderer> {
fn take_pending_redraw(&self) -> bool {
self.pending_redraw.take()
}
fn currently_pressed_key_code(&self) -> &Cell<Option<winit::event::VirtualKeyCode>> {
&self.currently_pressed_key_code
}
/// Draw the items of the specified `component` in the given window.
fn draw(&self) -> bool {
let window = match self.borrow_mapped_window() {
Some(window) => window,
None => return false, // caller bug, doesn't make sense to call draw() when not mapped
};
self.pending_redraw.set(false);
self.renderer.render(physical_size_to_slint(&window.winit_window.inner_size()));
self.pending_redraw.get()
}
fn with_window_handle(&self, callback: &mut dyn FnMut(&winit::window::Window)) {
if let Some(mapped_window) = self.borrow_mapped_window() {
callback(&mapped_window.winit_window);
}
}
fn constraints(&self) -> (corelib::layout::LayoutInfo, corelib::layout::LayoutInfo) {
self.borrow_mapped_window().map(|window| window.constraints.get()).unwrap_or_default()
}
fn set_constraints(
&self,
constraints: (corelib::layout::LayoutInfo, corelib::layout::LayoutInfo),
) {
if let Some(window) = self.borrow_mapped_window() {
window.constraints.set(constraints);
}
}
#[cfg(target_arch = "wasm32")]
fn input_method_focused(&self) -> bool {
match self.virtual_keyboard_helper.try_borrow() {
Ok(vkh) => vkh.as_ref().map_or(false, |h| h.has_focus()),
// the only location in which the virtual_keyboard_helper is mutably borrowed is from
// show_virtual_keyboard, which means we have the focus
Err(_) => true,
}
}
fn resize_event(&self, size: winit::dpi::PhysicalSize<u32>) {
// slint::Window::set_size will call set_size() on this type, which would call
// set_inner_size on the winit Window. On Windows that triggers an new resize event
// in the next event loop iteration for mysterious reasons, with slightly different sizes.
// I suspect a bug in the way the frame decorations are added and subtracted from the size
// we provide.
// Work around it with this guard that prevents us from calling set_inner_size again.
assert!(!self.in_resize_event.get());
self.in_resize_event.set(true);
scopeguard::defer! { self.in_resize_event.set(false); }
// When a window is minimized on Windows, we get a move event to an off-screen position
// and a resize even with a zero size. Don't forward that, especially not to the renderer,
// which might panic when trying to create a zero-sized surface.
if size.width > 0 && size.height > 0 {
let physical_size = physical_size_to_slint(&size);
self.window.set_size(physical_size);
self.renderer.resize_event(physical_size);
}
}
fn set_dark_color_scheme(&self, dark_mode: bool) {
self.dark_color_scheme
.get_or_init(|| Box::pin(Property::new(false)))
.as_ref()
.set(dark_mode)
}
}
impl<Renderer: WinitCompatibleRenderer + 'static> WindowAdapter for GLWindow<Renderer> {
fn window(&self) -> &corelib::api::Window {
&self.window
}
}
impl<Renderer: WinitCompatibleRenderer + 'static> WindowAdapterSealed for GLWindow<Renderer> {
fn request_redraw(&self) {
self.pending_redraw.set(true);
self.with_window_handle(&mut |window| window.request_redraw())
}
fn request_window_properties_update(&self) {
self.call_with_event_loop(|self_| {
self_.with_window_handle(&mut |window| {
let window_id = window.id();
crate::event_loop::with_window_target(|event_loop| {
event_loop.event_loop_proxy().send_event(
crate::event_loop::CustomEvent::UpdateWindowProperties(window_id),
)
})
.ok();
})
});
}
fn apply_window_properties(&self, window_item: Pin<&i_slint_core::items::WindowItem>) {
// Make the unwrap() calls on self.borrow_mapped_window*() safe
if !self.is_mapped() {
return;
}
let mut width = window_item.width().get() as f32;
let mut height = window_item.height().get() as f32;
let mut must_resize = false;
self.with_window_handle(&mut |winit_window| {
winit_window.set_window_icon(icon_to_winit(window_item.icon()));
winit_window.set_title(&window_item.title());
winit_window
.set_decorations(!window_item.no_frame() || winit_window.fullscreen().is_some());
if width <= 0. || height <= 0. {
must_resize = true;
let winit_size =
winit_window.inner_size().to_logical(self.window.scale_factor() as f64);
if width <= 0. {
width = winit_size.width;
}
if height <= 0. {
height = winit_size.height;
}
}
let existing_size: winit::dpi::LogicalSize<f32> =
winit_window.inner_size().to_logical(self.window.scale_factor().into());
if (existing_size.width - width).abs() > 1.
|| (existing_size.height - height).abs() > 1.
{
// If we're in fullscreen state, don't try to resize the window but maintain the surface
// size we've been assigned to from the windowing system. Weston/Wayland don't like it
// when we create a surface that's bigger than the screen due to constraints (#532).
if winit_window.fullscreen().is_none() {
winit_window.set_inner_size(winit::dpi::LogicalSize::new(width, height));
}
}
});
if must_resize {
self.window.set_size(i_slint_core::api::LogicalSize::new(width, height));
}
}
fn apply_geometry_constraint(
&self,
constraints_horizontal: corelib::layout::LayoutInfo,
constraints_vertical: corelib::layout::LayoutInfo,
) {
self.with_window_handle(&mut |winit_window| {
// If we're in fullscreen state, don't try to resize the window but maintain the surface
// size we've been assigned to from the windowing system. Weston/Wayland don't like it
// when we create a surface that's bigger than the screen due to constraints (#532).
if winit_window.fullscreen().is_some() {
return;
}
if (constraints_horizontal, constraints_vertical) != self.constraints() {
let min_width = constraints_horizontal.min.min(constraints_horizontal.max) as f32;
let min_height = constraints_vertical.min.min(constraints_vertical.max) as f32;
let max_width = constraints_horizontal.max.max(constraints_horizontal.min) as f32;
let max_height = constraints_vertical.max.max(constraints_vertical.min) as f32;
let sf = self.window.scale_factor();
winit_window.set_resizable(true);
winit_window.set_min_inner_size(if min_width > 0. || min_height > 0. {
Some(winit::dpi::PhysicalSize::new(min_width * sf, min_height * sf))
} else {
None
});
winit_window.set_max_inner_size(
if max_width < i32::MAX as f32 || max_height < i32::MAX as f32 {
Some(winit::dpi::PhysicalSize::new(
(max_width * sf).min(65535.),
(max_height * sf).min(65535.),
))
} else {
None
},
);
self.set_constraints((constraints_horizontal, constraints_vertical));
winit_window.set_resizable(min_width < max_width || min_height < max_height);
#[cfg(target_arch = "wasm32")]
{
// set_max_inner_size / set_min_inner_size don't work on wasm, so apply the size manually
let existing_size: winit::dpi::LogicalSize<f32> =
winit_window.inner_size().to_logical(sf as f64);
if !(min_width..=max_width).contains(&(existing_size.width))
|| !(min_height..=max_height).contains(&(existing_size.height))
{
let new_size = winit::dpi::LogicalSize::new(
existing_size.width.min(max_width).max(min_width),
existing_size.height.min(max_height).max(min_height),
);
winit_window.set_inner_size(new_size);
}
}
}
});
}
fn show(&self) -> Result<(), PlatformError> {
self.call_with_event_loop(|self_| {
let (requested_position, requested_size) = match &*self_.map_state.borrow() {
GraphicsWindowBackendState::Unmapped { requested_position, requested_size } => {
(requested_position.clone(), requested_size.clone())
}
GraphicsWindowBackendState::Mapped(_) => return,
};
let mut window_builder = winit::window::WindowBuilder::new();
let runtime_window = WindowInner::from_pub(&self_.window);
let component_rc = runtime_window.component();
let component = ComponentRc::borrow_pin(&component_rc);
window_builder = if let Some(window_item) =
runtime_window.window_item().as_ref().map(|i| i.as_pin_ref())
{
window_builder
.with_title(window_item.title().to_string())
.with_resizable(
window_item.height() <= LogicalLength::zero()
|| window_item.width() <= LogicalLength::zero(),
)
.with_decorations(!window_item.no_frame())
.with_window_icon(icon_to_winit(window_item.icon()))
} else {
window_builder.with_title("Slint Window".to_string())
};
let scale_factor_override = runtime_window.scale_factor();
// If the scale factor was already set programmatically, use that
// else, use the SLINT_SCALE_FACTOR if set, otherwise use the one from winit
let scale_factor_override = if scale_factor_override > 1. {
Some(scale_factor_override as f64)
} else {
std::env::var("SLINT_SCALE_FACTOR")
.ok()
.and_then(|x| x.parse::<f64>().ok())
.filter(|f| *f > 0.)
};
let into_size = |s: winit::dpi::LogicalSize<Coord>| -> winit::dpi::Size {
if let Some(f) = scale_factor_override {
s.to_physical::<f32>(f).into()
} else {
s.into()
}
};
let layout_info_h = component.as_ref().layout_info(Orientation::Horizontal);
if let Some(window_item) = runtime_window.window_item() {
// Setting the width to its preferred size before querying the vertical layout info
// is important in case the height depends on the width
window_item.width.set(LogicalLength::new(layout_info_h.preferred_bounded()));
}
let layout_info_v = component.as_ref().layout_info(Orientation::Vertical);
#[allow(unused_mut)]
let mut s = winit::dpi::LogicalSize::new(
layout_info_h.preferred_bounded(),
layout_info_v.preferred_bounded(),
);
#[cfg(target_arch = "wasm32")]
{
use wasm_bindgen::JsCast;
let canvas = web_sys::window()
.unwrap()
.document()
.unwrap()
.get_element_by_id(&self_.canvas_id)
.unwrap()
.dyn_into::<web_sys::HtmlCanvasElement>()
.unwrap();
let existing_canvas_size = winit::dpi::LogicalSize::new(
canvas.client_width() as f32,
canvas.client_height() as f32,
);
// Try to maintain the existing size of the canvas element. A window created with winit
// on the web will always have 1024x768 as size otherwise.
if s.width <= 0. {
s.width = existing_canvas_size.width;
}
if s.height <= 0. {
s.height = existing_canvas_size.height;
}
}
let window_builder = if std::env::var("SLINT_FULLSCREEN").is_ok() {
window_builder.with_fullscreen(Some(winit::window::Fullscreen::Borderless(None)))
} else {
if layout_info_h.min >= 1. as Coord || layout_info_v.min >= 1. as Coord {
window_builder = window_builder.with_min_inner_size(into_size(
winit::dpi::LogicalSize::new(layout_info_h.min, layout_info_v.min),
))
}
if layout_info_h.max < Coord::MAX || layout_info_v.max < Coord::MAX {
window_builder = window_builder.with_max_inner_size(into_size(
winit::dpi::LogicalSize::new(layout_info_h.max, layout_info_v.max),
))
}
if let Some(requested_size) = &requested_size {
// It would be nice to bound this with our constraints, but those are in logical coordinates
// and we don't know the scale factor yet...
if let Some(sf) = scale_factor_override {
let physical_size = requested_size.to_physical(sf as f32);
window_builder.with_inner_size(winit::dpi::Size::new(
winit::dpi::PhysicalSize::new(
physical_size.width,
physical_size.height,
),
))
} else {
window_builder.with_inner_size(window_size_to_slint(&requested_size))
}
} else if s.width > 0 as Coord && s.height > 0 as Coord {
// Make sure that the window's inner size is in sync with the root window item's
// width/height.
runtime_window.set_window_item_geometry(LogicalSize::new(s.width, s.height));
window_builder.with_inner_size(into_size(s))
} else {
window_builder
}
};
let window_builder = if let Some(pos) = &requested_position {
window_builder.with_position(position_to_winit(pos))
} else {
window_builder
};
#[cfg(target_arch = "wasm32")]
let window_builder = {
use wasm_bindgen::JsCast;
let canvas = web_sys::window()
.unwrap()
.document()
.unwrap()
.get_element_by_id(&self_.canvas_id)
.unwrap()
.dyn_into::<web_sys::HtmlCanvasElement>()
.unwrap();
use winit::platform::web::WindowBuilderExtWebSys;
window_builder.with_canvas(Some(canvas.clone()))
};
let winit_window = Rc::new(crate::event_loop::with_window_target(|event_loop| {
window_builder.build(event_loop.event_loop_target()).unwrap()
}));
self_.renderer.show(
&winit_window,
#[cfg(target_arch = "wasm32")]
&self_.canvas_id,
);
WindowInner::from_pub(&self_.window).set_scale_factor(
scale_factor_override.unwrap_or_else(|| winit_window.scale_factor()) as _,
);
// On wasm, with_inner_size on the WindowBuilder don't have effect, so apply manually
#[cfg(target_arch = "wasm32")]
if s.width > 0 as Coord && s.height > 0 as Coord {
winit_window.set_inner_size(s);
}
let id = winit_window.id();
self_.map_state.replace(GraphicsWindowBackendState::Mapped(MappedWindow {
constraints: Default::default(),
winit_window,
}));
crate::event_loop::register_window(id, self_.self_weak.upgrade().unwrap());
});
Ok(())
}
fn hide(&self) {
self.call_with_event_loop(|self_| {
self_.unmap();
/* FIXME:
if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() {
existing_blinker.stop();
}*/
crate::event_loop::with_window_target(|event_loop| {
event_loop
.event_loop_proxy()
.send_event(crate::event_loop::CustomEvent::WindowHidden)
})
.unwrap();
});
}
fn set_mouse_cursor(&self, cursor: MouseCursor) {
let winit_cursor = match cursor {
MouseCursor::Default => winit::window::CursorIcon::Default,
MouseCursor::None => winit::window::CursorIcon::Default,
MouseCursor::Help => winit::window::CursorIcon::Help,
MouseCursor::Pointer => winit::window::CursorIcon::Hand,
MouseCursor::Progress => winit::window::CursorIcon::Progress,
MouseCursor::Wait => winit::window::CursorIcon::Wait,
MouseCursor::Crosshair => winit::window::CursorIcon::Crosshair,
MouseCursor::Text => winit::window::CursorIcon::Text,
MouseCursor::Alias => winit::window::CursorIcon::Alias,
MouseCursor::Copy => winit::window::CursorIcon::Copy,
MouseCursor::Move => winit::window::CursorIcon::Move,
MouseCursor::NoDrop => winit::window::CursorIcon::NoDrop,
MouseCursor::NotAllowed => winit::window::CursorIcon::NotAllowed,
MouseCursor::Grab => winit::window::CursorIcon::Grab,
MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing,
MouseCursor::ColResize => winit::window::CursorIcon::ColResize,
MouseCursor::RowResize => winit::window::CursorIcon::RowResize,
MouseCursor::NResize => winit::window::CursorIcon::NResize,
MouseCursor::EResize => winit::window::CursorIcon::EResize,
MouseCursor::SResize => winit::window::CursorIcon::SResize,
MouseCursor::WResize => winit::window::CursorIcon::WResize,
MouseCursor::NeResize => winit::window::CursorIcon::NeResize,
MouseCursor::NwResize => winit::window::CursorIcon::NwResize,
MouseCursor::SeResize => winit::window::CursorIcon::SeResize,
MouseCursor::SwResize => winit::window::CursorIcon::SwResize,
MouseCursor::EwResize => winit::window::CursorIcon::EwResize,
MouseCursor::NsResize => winit::window::CursorIcon::NsResize,
MouseCursor::NeswResize => winit::window::CursorIcon::NeswResize,
MouseCursor::NwseResize => winit::window::CursorIcon::NwseResize,
};
self.with_window_handle(&mut |winit_window| {
winit_window.set_cursor_visible(cursor != MouseCursor::None);
winit_window.set_cursor_icon(winit_cursor);
});
}
fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
self.renderer.as_core_renderer()
}
fn enable_input_method(&self, _it: corelib::items::InputType) {
#[cfg(target_arch = "wasm32")]
{
let mut vkh = self.virtual_keyboard_helper.borrow_mut();
let h = vkh.get_or_insert_with(|| {
let canvas = self.renderer.html_canvas_element();
super::wasm_input_helper::WasmInputHelper::new(self.self_weak.clone(), canvas)
});
h.show();
}
#[cfg(not(target_arch = "wasm32"))]
self.with_window_handle(&mut |winit_window| {
winit_window.set_ime_allowed(matches!(_it, corelib::items::InputType::Text))
});
}
fn disable_input_method(&self) {
#[cfg(target_arch = "wasm32")]
if let Some(h) = &*self.virtual_keyboard_helper.borrow() {
h.hide()
}
#[cfg(not(target_arch = "wasm32"))]
self.with_window_handle(&mut |winit_window| winit_window.set_ime_allowed(false));
}
fn set_ime_position(&self, ime_pos: LogicalPoint) {
self.with_window_handle(&mut |winit_window| {
winit_window.set_ime_position(winit::dpi::LogicalPosition::new(ime_pos.x, ime_pos.y))
})
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn position(&self) -> corelib::api::PhysicalPosition {
match &*self.map_state.borrow() {
GraphicsWindowBackendState::Unmapped { requested_position, .. } => requested_position
.as_ref()
.map(|p| p.to_physical(self.window.scale_factor()))
.unwrap_or_default(),
GraphicsWindowBackendState::Mapped(mapped_window) => {
match mapped_window.winit_window.outer_position() {
Ok(outer_position) => {
corelib::api::PhysicalPosition::new(outer_position.x, outer_position.y)
}
Err(_) => Default::default(),
}
}
}
}
fn set_position(&self, position: corelib::api::WindowPosition) {
match &mut *self.map_state.borrow_mut() {
GraphicsWindowBackendState::Unmapped { requested_position, .. } => {
*requested_position = Some(position)
}
GraphicsWindowBackendState::Mapped(mapped_window) => {
mapped_window.winit_window.set_outer_position(position_to_winit(&position))
}
}
}
fn set_size(&self, size: corelib::api::WindowSize) {
if self.in_resize_event.get() {
return;
}
if let Ok(mut map_state) = self.map_state.try_borrow_mut() {
match &mut *map_state {
GraphicsWindowBackendState::Unmapped { requested_size, .. } => {
*requested_size = Some(size)
}
GraphicsWindowBackendState::Mapped(mapped_window) => {
mapped_window.winit_window.set_inner_size(window_size_to_slint(&size));
}
}
}
}
fn dark_color_scheme(&self) -> bool {
self.dark_color_scheme
.get_or_init(|| {
Box::pin(Property::new({
cfg_if::cfg_if! {
if #[cfg(use_winit_theme)] {
self.borrow_mapped_window().and_then(|mapped_window| {
mapped_window
.winit_window
.theme()
.map(|theme| theme == winit::window::Theme::Dark)
})
.unwrap_or_default()
} else {
dark_light::detect() == dark_light::Mode::Dark
}
}
}))
})
.as_ref()
.get()
}
fn is_visible(&self) -> bool {
if let Some(mapped_window) = self.borrow_mapped_window() {
mapped_window.winit_window.is_visible().unwrap_or(true)
} else {
false
}
}
}
impl<Renderer: WinitCompatibleRenderer + 'static> Drop for GLWindow<Renderer> {
fn drop(&mut self) {
self.unmap();
}
}
struct MappedWindow {
constraints: Cell<(corelib::layout::LayoutInfo, corelib::layout::LayoutInfo)>,
winit_window: Rc<winit::window::Window>,
}
enum GraphicsWindowBackendState {
Unmapped {
requested_position: Option<corelib::api::WindowPosition>,
requested_size: Option<corelib::api::WindowSize>,
},
Mapped(MappedWindow),
}
#[derive(FieldOffsets)]
#[repr(C)]
#[pin]
struct WindowProperties {
scale_factor: Property<f32>,
}
impl Default for WindowProperties {
fn default() -> Self {
Self { scale_factor: Property::new(1.0) }
}
}