use core::cell::{Cell, RefCell};
use core::pin::Pin;
use std::rc::Rc;
use std::rc::Weak;
#[cfg(target_arch = "wasm32")]
use winit::platform::web::WindowExtWebSys;
#[cfg(target_family = "windows")]
use winit::platform::windows::WindowAttributesExtWindows;
#[cfg(target_family = "windows")]
use winit::platform::windows::WindowExtWindows;
use crate::renderer::WinitCompatibleRenderer;
use corelib::item_tree::ItemTreeRc;
#[cfg(enable_accesskit)]
use corelib::item_tree::ItemTreeRef;
use corelib::items::{ColorScheme, MouseCursor};
#[cfg(enable_accesskit)]
use corelib::items::{ItemRc, ItemRef};
#[cfg(enable_accesskit)]
use crate::SlintUserEvent;
use crate::WinitWindowEventResult;
use corelib::api::PhysicalSize;
use corelib::layout::Orientation;
use corelib::lengths::LogicalLength;
use corelib::platform::{PlatformError, WindowEvent};
use corelib::window::{WindowAdapter, WindowAdapterInternal, WindowInner};
use corelib::Property;
use corelib::{graphics::*, Coord};
use i_slint_core::{self as corelib, graphics::RequestedGraphicsAPI};
use once_cell::unsync::OnceCell;
#[cfg(enable_accesskit)]
use winit::event_loop::EventLoopProxy;
use winit::window::{WindowAttributes, WindowButtons};
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_winit(size: &corelib::api::WindowSize) -> winit::dpi::Size {
match size {
corelib::api::WindowSize::Logical(size) => {
winit::dpi::Size::new(logical_size_to_winit(*size))
}
corelib::api::WindowSize::Physical(size) => {
winit::dpi::Size::new(physical_size_to_winit(*size))
}
}
}
pub fn physical_size_to_slint(size: &winit::dpi::PhysicalSize<u32>) -> corelib::api::PhysicalSize {
corelib::api::PhysicalSize::new(size.width, size.height)
}
fn logical_size_to_winit(s: i_slint_core::api::LogicalSize) -> winit::dpi::LogicalSize<f32> {
winit::dpi::LogicalSize::new(s.width, s.height)
}
fn physical_size_to_winit(size: PhysicalSize) -> winit::dpi::PhysicalSize<u32> {
winit::dpi::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,
};
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()
}
fn window_is_resizable(
min_size: Option<corelib::api::LogicalSize>,
max_size: Option<corelib::api::LogicalSize>,
) -> bool {
if let Some((
corelib::api::LogicalSize { width: min_width, height: min_height, .. },
corelib::api::LogicalSize { width: max_width, height: max_height, .. },
)) = min_size.zip(max_size)
{
min_width < max_width || min_height < max_height
} else {
true
}
}
enum WinitWindowOrNone {
HasWindow {
window: Rc<winit::window::Window>,
#[cfg(enable_accesskit)]
accesskit_adapter: RefCell<crate::accesskit::AccessKitAdapter>,
},
None(RefCell<WindowAttributes>),
}
impl WinitWindowOrNone {
fn as_window(&self) -> Option<Rc<winit::window::Window>> {
match self {
Self::HasWindow { window, .. } => Some(window.clone()),
Self::None { .. } => None,
}
}
fn set_window_icon(&self, icon: Option<winit::window::Icon>) {
match self {
Self::HasWindow { window, .. } => {
#[cfg(target_family = "windows")]
window.set_taskbar_icon(icon.as_ref().cloned());
window.set_window_icon(icon);
}
Self::None(attributes) => attributes.borrow_mut().window_icon = icon,
}
}
fn set_title(&self, title: &str) {
match self {
Self::HasWindow { window, .. } => window.set_title(title),
Self::None(attributes) => attributes.borrow_mut().title = title.into(),
}
}
fn set_decorations(&self, decorations: bool) {
match self {
Self::HasWindow { window, .. } => window.set_decorations(decorations),
Self::None(attributes) => attributes.borrow_mut().decorations = decorations,
}
}
fn fullscreen(&self) -> Option<winit::window::Fullscreen> {
match self {
Self::HasWindow { window, .. } => window.fullscreen(),
Self::None(attributes) => attributes.borrow().fullscreen.clone(),
}
}
fn set_fullscreen(&self, fullscreen: Option<winit::window::Fullscreen>) {
match self {
Self::HasWindow { window, .. } => window.set_fullscreen(fullscreen),
Self::None(attributes) => attributes.borrow_mut().fullscreen = fullscreen,
}
}
fn set_window_level(&self, level: winit::window::WindowLevel) {
match self {
Self::HasWindow { window, .. } => window.set_window_level(level),
Self::None(attributes) => attributes.borrow_mut().window_level = level,
}
}
fn set_visible(&self, visible: bool) {
match self {
Self::HasWindow { window, .. } => window.set_visible(visible),
Self::None(attributes) => attributes.borrow_mut().visible = visible,
}
}
fn set_maximized(&self, maximized: bool) {
match self {
Self::HasWindow { window, .. } => window.set_maximized(maximized),
Self::None(attributes) => attributes.borrow_mut().maximized = maximized,
}
}
fn set_minimized(&self, minimized: bool) {
match self {
Self::HasWindow { window, .. } => window.set_minimized(minimized),
Self::None(..) => {
}
}
}
fn set_resizable(&self, resizable: bool) {
match self {
Self::HasWindow { window, .. } => {
window.set_resizable(resizable);
let mut buttons = window.enabled_buttons();
buttons.set(WindowButtons::MAXIMIZE, resizable);
window.set_enabled_buttons(buttons);
}
Self::None(attributes) => attributes.borrow_mut().resizable = resizable,
}
}
fn set_min_inner_size<S: Into<winit::dpi::Size>>(&self, min_inner_size: Option<S>) {
match self {
Self::HasWindow { window, .. } => window.set_min_inner_size(min_inner_size),
Self::None(attributes) => {
attributes.borrow_mut().min_inner_size = min_inner_size.map(|s| s.into());
}
}
}
fn set_max_inner_size<S: Into<winit::dpi::Size>>(&self, max_inner_size: Option<S>) {
match self {
Self::HasWindow { window, .. } => window.set_max_inner_size(max_inner_size),
Self::None(attributes) => {
attributes.borrow_mut().max_inner_size = max_inner_size.map(|s| s.into())
}
}
}
}
pub struct WinitWindowAdapter {
window: OnceCell<corelib::api::Window>,
self_weak: Weak<Self>,
pending_redraw: Cell<bool>,
color_scheme: OnceCell<Pin<Box<Property<ColorScheme>>>>,
constraints: Cell<corelib::window::LayoutConstraints>,
shown: Cell<bool>,
window_level: Cell<winit::window::WindowLevel>,
maximized: Cell<bool>,
minimized: Cell<bool>,
fullscreen: Cell<bool>,
pub(crate) renderer: Box<dyn WinitCompatibleRenderer>,
requested_graphics_api: Option<RequestedGraphicsAPI>,
size: Cell<PhysicalSize>,
pending_requested_size: Cell<Option<winit::dpi::Size>>,
has_explicit_size: Cell<bool>,
pending_resize_event_after_show: Cell<bool>,
#[cfg(target_arch = "wasm32")]
virtual_keyboard_helper: RefCell<Option<super::wasm_input_helper::WasmInputHelper>>,
#[cfg(enable_accesskit)]
event_loop_proxy: EventLoopProxy<SlintUserEvent>,
pub(crate) window_event_filter: Cell<
Option<
Box<
dyn FnMut(
&corelib::api::Window,
&winit::event::WindowEvent,
) -> WinitWindowEventResult,
>,
>,
>,
winit_window_or_none: RefCell<WinitWindowOrNone>,
#[cfg(not(use_winit_theme))]
xdg_settings_watcher: RefCell<Option<i_slint_core::future::JoinHandle<()>>>,
}
impl WinitWindowAdapter {
pub(crate) fn new(
renderer: Box<dyn WinitCompatibleRenderer>,
window_attributes: winit::window::WindowAttributes,
requested_graphics_api: Option<RequestedGraphicsAPI>,
#[cfg(enable_accesskit)] proxy: EventLoopProxy<SlintUserEvent>,
) -> Result<Rc<Self>, PlatformError> {
let self_rc = Rc::new_cyclic(|self_weak| Self {
window: OnceCell::with_value(corelib::api::Window::new(self_weak.clone() as _)),
self_weak: self_weak.clone(),
pending_redraw: Default::default(),
color_scheme: Default::default(),
constraints: Default::default(),
shown: Default::default(),
window_level: Default::default(),
maximized: Cell::default(),
minimized: Cell::default(),
fullscreen: Cell::default(),
winit_window_or_none: RefCell::new(WinitWindowOrNone::None(window_attributes.into())),
size: Cell::default(),
pending_requested_size: Cell::new(None),
has_explicit_size: Default::default(),
pending_resize_event_after_show: Default::default(),
renderer,
requested_graphics_api,
#[cfg(target_arch = "wasm32")]
virtual_keyboard_helper: Default::default(),
#[cfg(enable_accesskit)]
event_loop_proxy: proxy,
window_event_filter: Cell::new(None),
#[cfg(not(use_winit_theme))]
xdg_settings_watcher: Default::default(),
});
let winit_window = self_rc.ensure_window()?;
debug_assert!(!self_rc.renderer.is_suspended());
self_rc.size.set(physical_size_to_slint(&winit_window.inner_size()));
let id = winit_window.id();
crate::event_loop::register_window(id, (self_rc.clone()) as _);
let scale_factor = std::env::var("SLINT_SCALE_FACTOR")
.ok()
.and_then(|x| x.parse::<f32>().ok())
.filter(|f| *f > 0.)
.unwrap_or_else(|| winit_window.scale_factor() as f32);
self_rc.window().dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor });
Ok(self_rc)
}
fn renderer(&self) -> &dyn WinitCompatibleRenderer {
self.renderer.as_ref()
}
pub fn ensure_window(&self) -> Result<Rc<winit::window::Window>, PlatformError> {
#[allow(unused_mut)]
let mut window_attributes = match &*self.winit_window_or_none.borrow() {
WinitWindowOrNone::HasWindow { window, .. } => return Ok(window.clone()),
WinitWindowOrNone::None(attributes) => attributes.borrow().clone(),
};
#[cfg(all(unix, not(target_vendor = "apple")))]
{
if let Some(xdg_app_id) = WindowInner::from_pub(self.window()).xdg_app_id() {
#[cfg(feature = "wayland")]
{
use winit::platform::wayland::WindowAttributesExtWayland;
window_attributes = window_attributes.with_name(xdg_app_id.clone(), "");
}
#[cfg(feature = "x11")]
{
use winit::platform::x11::WindowAttributesExtX11;
window_attributes = window_attributes.with_name(xdg_app_id.clone(), "");
}
}
}
let mut winit_window_or_none = self.winit_window_or_none.borrow_mut();
let winit_window =
self.renderer.resume(window_attributes, self.requested_graphics_api.clone())?;
*winit_window_or_none = WinitWindowOrNone::HasWindow {
window: winit_window.clone(),
#[cfg(enable_accesskit)]
accesskit_adapter: crate::accesskit::AccessKitAdapter::new(
self.self_weak.clone(),
&winit_window,
self.event_loop_proxy.clone(),
)
.into(),
};
crate::event_loop::register_window(
winit_window.id(),
(self.self_weak.upgrade().unwrap()) as _,
);
Ok(winit_window)
}
fn suspend(&self) -> Result<(), PlatformError> {
let mut winit_window_or_none = self.winit_window_or_none.borrow_mut();
match *winit_window_or_none {
WinitWindowOrNone::HasWindow { ref window, .. } => {
self.renderer().suspend()?;
let last_window_rc = window.clone();
let mut attributes = Self::window_attributes(
#[cfg(target_arch = "wasm32")]
"canvas",
)
.unwrap_or_default();
attributes.inner_size = Some(physical_size_to_winit(self.size.get()).into());
attributes.position = last_window_rc.outer_position().ok().map(|pos| pos.into());
*winit_window_or_none = WinitWindowOrNone::None(attributes.into());
if let Some(last_instance) = Rc::into_inner(last_window_rc) {
crate::event_loop::unregister_window(last_instance.id());
drop(last_instance);
} else {
i_slint_core::debug_log!(
"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"
);
}
}
WinitWindowOrNone::None(ref attributes) => {
attributes.borrow_mut().visible = false;
}
}
Ok(())
}
pub(crate) fn window_attributes(
#[cfg(target_arch = "wasm32")] canvas_id: &str,
) -> Result<WindowAttributes, PlatformError> {
let mut attrs = WindowAttributes::default().with_transparent(true).with_visible(false);
attrs = attrs.with_title("Slint Window".to_string());
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowAttributesExtWebSys;
use wasm_bindgen::JsCast;
let html_canvas = web_sys::window()
.ok_or_else(|| "winit backend: Could not retrieve DOM window".to_string())?
.document()
.ok_or_else(|| "winit backend: Could not retrieve DOM document".to_string())?
.get_element_by_id(canvas_id)
.ok_or_else(|| {
format!(
"winit backend: Could not retrieve existing HTML Canvas element '{}'",
canvas_id
)
})?
.dyn_into::<web_sys::HtmlCanvasElement>()
.map_err(|_| {
format!(
"winit backend: Specified DOM element '{}' is not a HTML Canvas",
canvas_id
)
})?;
attrs = attrs
.with_canvas(Some(html_canvas))
.with_active(false)
};
Ok(attrs)
}
pub fn draw(&self) -> Result<(), PlatformError> {
if !self.shown.get() {
return Ok(()); }
self.pending_redraw.set(false);
if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
if self.pending_resize_event_after_show.take() {
self.resize_event(winit_window.inner_size())?;
}
}
let renderer = self.renderer();
renderer.render(self.window())?;
Ok(())
}
pub fn winit_window(&self) -> Option<Rc<winit::window::Window>> {
self.winit_window_or_none.borrow().as_window()
}
#[cfg(target_arch = "wasm32")]
pub 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()),
Err(_) => true,
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn input_method_focused(&self) -> bool {
false
}
fn resize_window(&self, size: winit::dpi::Size) -> Result<bool, PlatformError> {
match &*self.winit_window_or_none.borrow() {
WinitWindowOrNone::HasWindow { window, .. } => {
if let Some(size) = window.request_inner_size(size) {
self.resize_event(size)?;
Ok(true)
} else {
Ok(false)
}
}
WinitWindowOrNone::None(attributes) => {
attributes.borrow_mut().inner_size = Some(size);
self.resize_event(size.to_physical(self.window().scale_factor() as _))?;
Ok(true)
}
}
}
pub fn resize_event(&self, size: winit::dpi::PhysicalSize<u32>) -> Result<(), PlatformError> {
self.pending_resize_event_after_show.set(false);
if size.width > 0 && size.height > 0 {
let physical_size = physical_size_to_slint(&size);
self.size.set(physical_size);
let scale_factor = WindowInner::from_pub(self.window()).scale_factor();
self.window().dispatch_event(WindowEvent::Resized {
size: physical_size.to_logical(scale_factor),
});
#[cfg(target_arch = "wasm32")]
if let Some(html_canvas) = self
.winit_window_or_none
.borrow()
.as_window()
.and_then(|winit_window| winit_window.canvas())
{
html_canvas.set_width(physical_size.width);
html_canvas.set_height(physical_size.height);
}
}
Ok(())
}
pub fn set_color_scheme(&self, scheme: ColorScheme) {
self.color_scheme
.get_or_init(|| Box::pin(Property::new(ColorScheme::Unknown)))
.as_ref()
.set(scheme);
#[cfg(not(use_winit_theme))]
if let Some(winit_window) = self.winit_window() {
winit_window.set_theme(match scheme {
ColorScheme::Unknown => None,
ColorScheme::Dark => Some(winit::window::Theme::Dark),
ColorScheme::Light => Some(winit::window::Theme::Light),
});
}
}
pub fn window_state_event(&self) {
let Some(winit_window) = self.winit_window_or_none.borrow().as_window() else { return };
if let Some(minimized) = winit_window.is_minimized() {
self.minimized.set(minimized);
if minimized != self.window().is_minimized() {
self.window().set_minimized(minimized);
}
}
let maximized = winit_window.is_maximized();
if !self.window().is_minimized() {
self.maximized.set(maximized);
if maximized != self.window().is_maximized() {
self.window().set_maximized(maximized);
}
}
let fullscreen = winit_window.fullscreen().is_some();
if fullscreen != self.window().is_fullscreen() {
self.window().set_fullscreen(fullscreen);
}
}
#[cfg(enable_accesskit)]
pub(crate) fn accesskit_adapter(
&self,
) -> Option<std::cell::Ref<'_, RefCell<crate::accesskit::AccessKitAdapter>>> {
std::cell::Ref::filter_map(self.winit_window_or_none.borrow(), |wor: &WinitWindowOrNone| {
match wor {
WinitWindowOrNone::HasWindow { accesskit_adapter, .. } => Some(accesskit_adapter),
WinitWindowOrNone::None(..) => None,
}
})
.ok()
}
#[cfg(enable_accesskit)]
pub(crate) fn with_access_kit_adapter_from_weak_window_adapter(
self_weak: Weak<Self>,
callback: impl FnOnce(&RefCell<crate::accesskit::AccessKitAdapter>),
) {
let Some(self_) = self_weak.upgrade() else { return };
let winit_window_or_none = self_.winit_window_or_none.borrow();
match &*winit_window_or_none {
WinitWindowOrNone::HasWindow { accesskit_adapter, .. } => callback(&accesskit_adapter),
WinitWindowOrNone::None(..) => {}
}
}
#[cfg(not(use_winit_theme))]
fn spawn_xdg_settings_watcher(&self) -> Option<i_slint_core::future::JoinHandle<()>> {
let window_inner = WindowInner::from_pub(self.window());
let self_weak = self.self_weak.clone();
window_inner
.context()
.spawn_local(async move {
let Ok(settings) = ashpd::desktop::settings::Settings::new().await else { return };
let Ok(initial_color_scheme_value) = settings.color_scheme().await else { return };
let convert = |ashpd_color_scheme| match ashpd_color_scheme {
ashpd::desktop::settings::ColorScheme::NoPreference => ColorScheme::Unknown,
ashpd::desktop::settings::ColorScheme::PreferDark => ColorScheme::Dark,
ashpd::desktop::settings::ColorScheme::PreferLight => ColorScheme::Light,
};
if let Some(window) = self_weak.upgrade() {
window.set_color_scheme(convert(initial_color_scheme_value));
}
let Ok(mut color_scheme_stream) = settings.receive_color_scheme_changed().await
else {
return;
};
loop {
use futures::stream::StreamExt;
let Some(new_scheme) = color_scheme_stream.next().await else { break };
if let Some(window) = self_weak.upgrade() {
window.set_color_scheme(convert(new_scheme));
}
}
})
.ok()
}
}
impl WindowAdapter for WinitWindowAdapter {
fn window(&self) -> &corelib::api::Window {
self.window.get().unwrap()
}
fn renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
self.renderer().as_core_renderer()
}
fn set_visible(&self, visible: bool) -> Result<(), PlatformError> {
if visible == self.shown.get() {
return Ok(());
}
self.shown.set(visible);
self.pending_resize_event_after_show.set(visible);
self.pending_redraw.set(false);
if visible {
let recreating_window = self.winit_window_or_none.borrow().as_window().is_none();
let winit_window = self.ensure_window()?;
let runtime_window = WindowInner::from_pub(self.window());
let scale_factor = runtime_window.scale_factor() as f64;
let component_rc = runtime_window.component();
let component = ItemTreeRc::borrow_pin(&component_rc);
let layout_info_h = component.as_ref().layout_info(Orientation::Horizontal);
if let Some(window_item) = runtime_window.window_item() {
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 preferred_size = winit::dpi::LogicalSize::new(
layout_info_h.preferred_bounded(),
layout_info_v.preferred_bounded(),
);
#[cfg(target_arch = "wasm32")]
if let Some(html_canvas) = winit_window.canvas() {
let existing_canvas_size = winit::dpi::LogicalSize::new(
html_canvas.client_width() as f32,
html_canvas.client_height() as f32,
);
if existing_canvas_size.width > 0. {
preferred_size.width = existing_canvas_size.width;
}
if existing_canvas_size.height > 0. {
preferred_size.height = existing_canvas_size.height;
}
}
if winit_window.fullscreen().is_none()
&& !self.has_explicit_size.get()
&& preferred_size.width > 0 as Coord
&& preferred_size.height > 0 as Coord
&& !recreating_window
{
let size = preferred_size.to_physical::<u32>(scale_factor);
self.resize_window(size.into())?;
};
winit_window.set_visible(true);
if let Some(color_scheme_prop) = self.color_scheme.get() {
if let Some(theme) = winit_window.theme() {
color_scheme_prop.as_ref().set(match theme {
winit::window::Theme::Dark => ColorScheme::Dark,
winit::window::Theme::Light => ColorScheme::Light,
})
}
}
#[cfg(target_arch = "wasm32")]
if self.pending_redraw.get() {
self.draw()?;
};
Ok(())
} else {
crate::event_loop::with_window_target(|event_loop| {
if event_loop.is_wayland()
|| std::env::var_os("SLINT_DESTROY_WINDOW_ON_HIDE").is_some()
{
self.suspend()?;
} else {
self.winit_window_or_none.borrow().set_visible(false);
}
Ok(())
})?;
Ok(())
}
}
fn position(&self) -> Option<corelib::api::PhysicalPosition> {
match &*self.winit_window_or_none.borrow() {
WinitWindowOrNone::HasWindow { window, .. } => match window.outer_position() {
Ok(outer_position) => {
Some(corelib::api::PhysicalPosition::new(outer_position.x, outer_position.y))
}
Err(_) => None,
},
WinitWindowOrNone::None(attributes) => {
attributes.borrow().position.and_then(|pos| {
match pos {
winit::dpi::Position::Physical(phys_pos) => {
Some(corelib::api::PhysicalPosition::new(phys_pos.x, phys_pos.y))
}
winit::dpi::Position::Logical(logical_pos) => {
Some(
corelib::api::LogicalPosition::new(
logical_pos.x as _,
logical_pos.y as _,
)
.to_physical(self.window().scale_factor()),
)
}
}
})
}
}
}
fn set_position(&self, position: corelib::api::WindowPosition) {
let winit_pos = position_to_winit(&position);
match &*self.winit_window_or_none.borrow() {
WinitWindowOrNone::HasWindow { window, .. } => window.set_outer_position(winit_pos),
WinitWindowOrNone::None(attributes) => {
attributes.borrow_mut().position = Some(winit_pos);
}
}
}
fn set_size(&self, size: corelib::api::WindowSize) {
self.has_explicit_size.set(true);
self.resize_window(window_size_to_winit(&size)).ok();
}
fn size(&self) -> corelib::api::PhysicalSize {
self.size.get()
}
fn request_redraw(&self) {
if !self.pending_redraw.replace(true) {
if let Some(window) = self.winit_window_or_none.borrow().as_window() {
window.request_redraw()
}
}
}
#[allow(clippy::unnecessary_cast)] fn update_window_properties(&self, properties: corelib::window::WindowProperties<'_>) {
let Some(window_item) =
self.window.get().and_then(|w| WindowInner::from_pub(w).window_item())
else {
return;
};
let window_item = window_item.as_pin_ref();
let winit_window_or_none = self.winit_window_or_none.borrow();
winit_window_or_none.set_window_icon(icon_to_winit(window_item.icon()));
winit_window_or_none.set_title(&properties.title());
winit_window_or_none.set_decorations(
!window_item.no_frame() || winit_window_or_none.fullscreen().is_some(),
);
let new_window_level = if window_item.always_on_top() {
winit::window::WindowLevel::AlwaysOnTop
} else {
winit::window::WindowLevel::Normal
};
if self.window_level.replace(new_window_level) != new_window_level {
winit_window_or_none.set_window_level(new_window_level);
}
let sf = self.window().scale_factor();
let mut width = window_item.width().get() as f32;
let mut height = window_item.height().get() as f32;
let mut must_resize = false;
let existing_size = self.size.get().to_logical(sf);
if width <= 0. || height <= 0. {
must_resize = true;
if width <= 0. {
width = existing_size.width;
}
if height <= 0. {
height = existing_size.height;
}
}
if ((existing_size.width - width).abs() > 1. || (existing_size.height - height).abs() > 1.)
&& self.pending_requested_size.get().is_none()
{
if winit_window_or_none.fullscreen().is_none() {
let immediately_resized = self
.resize_window(winit::dpi::LogicalSize::new(width, height).into())
.unwrap_or_default();
if immediately_resized {
must_resize = false;
}
}
}
if must_resize {
self.window().dispatch_event(WindowEvent::Resized {
size: i_slint_core::api::LogicalSize::new(width, height),
});
}
let m = properties.is_fullscreen();
if m != self.fullscreen.get() {
if m {
if winit_window_or_none.fullscreen().is_none() {
winit_window_or_none
.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
}
} else {
winit_window_or_none.set_fullscreen(None);
}
self.fullscreen.set(m);
}
let m = properties.is_maximized();
if m != self.maximized.get() {
self.maximized.set(m);
winit_window_or_none.set_maximized(m);
}
let m = properties.is_minimized();
if m != self.minimized.get() {
self.minimized.set(m);
winit_window_or_none.set_minimized(m);
}
if winit_window_or_none.fullscreen().is_some() {
return;
}
let new_constraints = properties.layout_constraints();
if new_constraints == self.constraints.get() {
return;
}
self.constraints.set(new_constraints);
let into_size = |s: corelib::api::LogicalSize| -> winit::dpi::PhysicalSize<f32> {
logical_size_to_winit(s).to_physical(sf as f64)
};
let resizable = window_is_resizable(new_constraints.min, new_constraints.max);
winit_window_or_none.set_resizable(resizable);
let winit_min_inner = new_constraints.min.map(into_size);
winit_window_or_none.set_min_inner_size(winit_min_inner);
let winit_max_inner = new_constraints.max.map(into_size);
winit_window_or_none.set_max_inner_size(winit_max_inner);
adjust_window_size_to_satisfy_constraints(self, winit_min_inner, winit_max_inner);
#[cfg(target_arch = "wasm32")]
if let Some(canvas) =
winit_window_or_none.as_window().and_then(|winit_window| winit_window.canvas())
{
if canvas
.dataset()
.get("slintAutoResizeToPreferred")
.and_then(|val_str| val_str.parse().ok())
.unwrap_or_default()
{
let pref = new_constraints.preferred;
if pref.width > 0 as Coord || pref.height > 0 as Coord {
self.resize_window(logical_size_to_winit(pref).into()).ok();
};
}
}
}
fn internal(&self, _: corelib::InternalToken) -> Option<&dyn WindowAdapterInternal> {
Some(self)
}
}
impl WindowAdapterInternal for WinitWindowAdapter {
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::Pointer,
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,
};
if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
winit_window.set_cursor_visible(cursor != MouseCursor::None);
winit_window.set_cursor(winit_cursor);
}
}
fn input_method_request(&self, request: corelib::window::InputMethodRequest) {
#[cfg(not(target_arch = "wasm32"))]
if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
let props = match &request {
corelib::window::InputMethodRequest::Enable(props) => {
winit_window.set_ime_allowed(true);
props
}
corelib::window::InputMethodRequest::Disable => {
return winit_window.set_ime_allowed(false);
}
corelib::window::InputMethodRequest::Update(props) => props,
_ => return,
};
winit_window.set_ime_purpose(match props.input_type {
corelib::items::InputType::Password => winit::window::ImePurpose::Password,
_ => winit::window::ImePurpose::Normal,
});
winit_window.set_ime_cursor_area(
position_to_winit(&props.cursor_rect_origin.into()),
window_size_to_winit(&props.cursor_rect_size.into()),
);
}
#[cfg(target_arch = "wasm32")]
match request {
corelib::window::InputMethodRequest::Enable(..) => {
let mut vkh = self.virtual_keyboard_helper.borrow_mut();
let Some(canvas) =
self.winit_window().and_then(|winit_window| winit_window.canvas())
else {
return;
};
let h = vkh.get_or_insert_with(|| {
super::wasm_input_helper::WasmInputHelper::new(self.self_weak.clone(), canvas)
});
h.show();
}
corelib::window::InputMethodRequest::Disable => {
if let Some(h) = &*self.virtual_keyboard_helper.borrow() {
h.hide()
}
}
_ => {}
};
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn color_scheme(&self) -> ColorScheme {
self.color_scheme
.get_or_init(|| {
Box::pin(Property::new({
cfg_if::cfg_if! {
if #[cfg(use_winit_theme)] {
self.winit_window_or_none
.borrow()
.as_window()
.and_then(|window| window.theme())
.map_or(ColorScheme::Unknown, |theme| match theme {
winit::window::Theme::Dark => ColorScheme::Dark,
winit::window::Theme::Light => ColorScheme::Light,
})
} else {
if let Some(old_watch) = self.xdg_settings_watcher.replace(self.spawn_xdg_settings_watcher()) {
old_watch.abort()
}
ColorScheme::Unknown
}
}
}))
})
.as_ref()
.get()
}
#[cfg(enable_accesskit)]
fn handle_focus_change(&self, _old: Option<ItemRc>, _new: Option<ItemRc>) {
let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
accesskit_adapter_cell.borrow_mut().handle_focus_item_change();
}
#[cfg(enable_accesskit)]
fn register_item_tree(&self) {
let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
a.reload_tree();
};
}
#[cfg(enable_accesskit)]
fn unregister_item_tree(
&self,
component: ItemTreeRef,
_: &mut dyn Iterator<Item = Pin<ItemRef<'_>>>,
) {
let Some(accesskit_adapter_cell) = self.accesskit_adapter() else { return };
if let Ok(mut a) = accesskit_adapter_cell.try_borrow_mut() {
a.unregister_item_tree(component);
};
}
#[cfg(feature = "raw-window-handle-06")]
fn window_handle_06_rc(
&self,
) -> Result<Rc<dyn raw_window_handle::HasWindowHandle>, raw_window_handle::HandleError> {
self.winit_window_or_none
.borrow()
.as_window()
.map_or(Err(raw_window_handle::HandleError::Unavailable), |window| Ok(window))
}
#[cfg(feature = "raw-window-handle-06")]
fn display_handle_06_rc(
&self,
) -> Result<Rc<dyn raw_window_handle::HasDisplayHandle>, raw_window_handle::HandleError> {
self.winit_window_or_none
.borrow()
.as_window()
.map_or(Err(raw_window_handle::HandleError::Unavailable), |window| Ok(window))
}
fn bring_to_front(&self) -> Result<(), PlatformError> {
if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
winit_window.set_minimized(false);
winit_window.focus_window();
}
Ok(())
}
}
impl Drop for WinitWindowAdapter {
fn drop(&mut self) {
if let Some(winit_window) = self.winit_window_or_none.borrow().as_window() {
crate::event_loop::unregister_window(winit_window.id());
}
#[cfg(not(use_winit_theme))]
if let Some(xdg_watch_future) = self.xdg_settings_watcher.take() {
xdg_watch_future.abort();
}
}
}
fn adjust_window_size_to_satisfy_constraints(
adapter: &WinitWindowAdapter,
min_size: Option<winit::dpi::PhysicalSize<f32>>,
max_size: Option<winit::dpi::PhysicalSize<f32>>,
) {
let current_size = adapter
.pending_requested_size
.get()
.map(|s| s.to_physical(adapter.window().scale_factor() as f64))
.unwrap_or_else(|| physical_size_to_winit(adapter.size.get()));
let mut window_size = current_size;
if let Some(min_size) = min_size {
let min_size = min_size.cast();
window_size.width = window_size.width.max(min_size.width);
window_size.height = window_size.height.max(min_size.height);
}
if let Some(max_size) = max_size {
let max_size = max_size.cast();
window_size.width = window_size.width.min(max_size.width);
window_size.height = window_size.height.min(max_size.height);
}
if window_size != current_size {
adapter.resize_window(window_size.into()).ok();
}
}