slint/internal/backends/winit/lib.rs
Olivier Goffart 0ba468c236 Use a builtin function to access the dark-style
instead of a property on NativeStyleMetrics
2022-10-19 15:23:41 +02:00

243 lines
8.6 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
#![doc = include_str!("README.md")]
#![doc(html_logo_url = "https://slint-ui.com/logo/slint-logo-square-light.svg")]
extern crate alloc;
use i_slint_core::platform::EventLoopProxy;
use i_slint_core::window::WindowAdapter;
use renderer::WinitCompatibleRenderer;
use std::rc::Rc;
mod glwindow;
use glwindow::*;
#[cfg(any(feature = "renderer-winit-femtovg", skia_backend_opengl))]
mod glcontext;
#[cfg(any(feature = "renderer-winit-femtovg", skia_backend_opengl))]
use glcontext::*;
pub(crate) mod event_loop;
mod renderer {
use std::rc::Weak;
use i_slint_core::window::WindowAdapter;
#[cfg(any(feature = "renderer-winit-femtovg", enable_skia_renderer))]
mod boxshadowcache;
pub(crate) trait WinitCompatibleRenderer: i_slint_core::renderer::Renderer {
type Canvas: WinitCompatibleCanvas;
const NAME: &'static str;
fn new(
window_adapter_weak: &Weak<dyn WindowAdapter>,
#[cfg(target_arch = "wasm32")] canvas_id: String,
) -> Self;
fn create_canvas(&self, window_builder: winit::window::WindowBuilder) -> Self::Canvas;
fn release_canvas(&self, canvas: Self::Canvas);
fn render(&self, canvas: &Self::Canvas, window: &dyn WindowAdapter);
}
pub(crate) trait WinitCompatibleCanvas {
fn component_destroyed(&self, component: i_slint_core::component::ComponentRef);
fn with_window_handle<T>(&self, callback: impl FnOnce(&winit::window::Window) -> T) -> T;
fn resize_event(&self);
#[cfg(target_arch = "wasm32")]
fn html_canvas_element(&self) -> std::cell::Ref<web_sys::HtmlCanvasElement>;
}
#[cfg(feature = "renderer-winit-femtovg")]
pub(crate) mod femtovg;
#[cfg(enable_skia_renderer)]
pub(crate) mod skia;
#[cfg(feature = "renderer-winit-software")]
pub(crate) mod sw;
}
#[cfg(target_arch = "wasm32")]
pub(crate) mod wasm_input_helper;
#[cfg(target_arch = "wasm32")]
pub fn create_gl_window_with_canvas_id(canvas_id: String) -> Rc<dyn WindowAdapter> {
GLWindow::<crate::renderer::femtovg::FemtoVGRenderer>::new(canvas_id)
}
fn window_factory_fn<R: WinitCompatibleRenderer + 'static>() -> Rc<dyn WindowAdapter> {
GLWindow::<R>::new(
#[cfg(target_arch = "wasm32")]
"canvas".into(),
)
}
cfg_if::cfg_if! {
if #[cfg(feature = "renderer-winit-femtovg")] {
type DefaultRenderer = renderer::femtovg::FemtoVGRenderer;
} else if #[cfg(enable_skia_renderer)] {
type DefaultRenderer = renderer::skia::SkiaRenderer;
} else if #[cfg(feature = "renderer-winit-software")] {
type DefaultRenderer = renderer::sw::SoftwareRenderer<0>;
} else {
compile_error!("Please select a feature to build with the winit backend: `renderer-winit-femtovg`, `renderer-winit-skia`, `renderer-winit-skia-opengl` or `renderer-winit-software`");
}
}
#[doc(hidden)]
#[cold]
#[cfg(not(target_arch = "wasm32"))]
pub fn use_modules() {}
pub type NativeWidgets = ();
pub type NativeGlobals = ();
pub const HAS_NATIVE_STYLE: bool = false;
pub mod native_widgets {}
pub struct Backend {
window_factory_fn: fn() -> Rc<dyn WindowAdapter>,
}
impl Backend {
pub fn new(renderer_name: Option<&str>) -> Self {
let window_factory_fn = match renderer_name {
#[cfg(feature = "renderer-winit-femtovg")]
Some("gl") | Some("femtovg") => window_factory_fn::<renderer::femtovg::FemtoVGRenderer>,
#[cfg(enable_skia_renderer)]
Some("skia") => window_factory_fn::<renderer::skia::SkiaRenderer>,
#[cfg(feature = "renderer-winit-software")]
Some("sw") | Some("software") => window_factory_fn::<renderer::sw::SoftwareRenderer<0>>,
None => window_factory_fn::<DefaultRenderer>,
Some(renderer_name) => {
eprintln!(
"slint winit: unrecognized renderer {}, falling back to {}",
renderer_name,
DefaultRenderer::NAME
);
window_factory_fn::<DefaultRenderer>
}
};
Self { window_factory_fn }
}
}
impl i_slint_core::platform::Platform for Backend {
fn create_window_adapter(&self) -> Rc<dyn WindowAdapter> {
(self.window_factory_fn)()
}
#[doc(hidden)]
fn set_event_loop_quit_on_last_window_closed(&self, quit_on_last_window_closed: bool) {
event_loop::QUIT_ON_LAST_WINDOW_CLOSED
.store(quit_on_last_window_closed, std::sync::atomic::Ordering::Relaxed);
}
fn run_event_loop(&self) {
crate::event_loop::run();
}
fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
struct Proxy;
impl EventLoopProxy for Proxy {
fn quit_event_loop(&self) -> Result<(), i_slint_core::api::EventLoopError> {
crate::event_loop::with_window_target(|event_loop| {
event_loop
.event_loop_proxy()
.send_event(crate::event_loop::CustomEvent::Exit)
.map_err(|_| i_slint_core::api::EventLoopError::EventLoopTerminated)
})
}
fn invoke_from_event_loop(
&self,
event: Box<dyn FnOnce() + Send>,
) -> Result<(), i_slint_core::api::EventLoopError> {
let e = crate::event_loop::CustomEvent::UserEvent(event);
#[cfg(not(target_arch = "wasm32"))]
crate::event_loop::GLOBAL_PROXY
.get_or_init(Default::default)
.lock()
.unwrap()
.send_event(e)?;
#[cfg(target_arch = "wasm32")]
{
crate::event_loop::GLOBAL_PROXY.with(|global_proxy| {
let mut maybe_proxy = global_proxy.borrow_mut();
let proxy = maybe_proxy.get_or_insert_with(Default::default);
// Calling send_event is usually done by winit at the bottom of the stack,
// in event handlers, and thus winit might decide to process the event
// immediately within that stack.
// To prevent re-entrancy issues that might happen by getting the application
// event processed on top of the current stack, set winit in Poll mode so that
// events are queued and process on top of a clean stack during a requested animation
// frame a few moments later.
// This also allows batching multiple post_event calls and redraw their state changes
// all at once.
proxy
.send_event(crate::event_loop::CustomEvent::WakeEventLoopWorkaround)?;
proxy.send_event(e)?;
Ok(())
})?
}
Ok(())
}
}
Some(Box::new(Proxy))
}
fn set_clipboard_text(&self, text: &str) {
crate::event_loop::with_window_target(|event_loop_target| {
event_loop_target.clipboard().set_contents(text.into()).ok()
});
}
fn clipboard_text(&self) -> Option<String> {
crate::event_loop::with_window_target(|event_loop_target| {
event_loop_target.clipboard().get_contents().ok()
})
}
}
pub(crate) trait WindowSystemName {
fn winsys_name(&self) -> &'static str;
}
impl WindowSystemName for winit::window::Window {
fn winsys_name(&self) -> &'static str {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
let winsys = "HTML Canvas";
} else if #[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))] {
use winit::platform::unix::WindowExtUnix;
let mut winsys = "unknown";
#[cfg(feature = "x11")]
if self.xlib_window().is_some() {
winsys = "x11";
}
#[cfg(feature = "wayland")]
if self.wayland_surface().is_some() {
winsys = "wayland"
}
} else if #[cfg(target_os = "windows")] {
let winsys = "windows";
} else if #[cfg(target_os = "macos")] {
let winsys = "macos";
} else {
let winsys = "unknown";
}
}
winsys
}
}