use alloc::boxed::Box;
use alloc::rc::Rc;
use crate::component::ComponentVTable;
use crate::window::WindowRc;
#[derive(Clone)]
#[non_exhaustive]
pub enum GraphicsAPI<'a> {
NativeOpenGL {
get_proc_address: &'a dyn Fn(&str) -> *const core::ffi::c_void,
},
WebGL {
canvas_element_id: &'a str,
context_type: &'a str,
},
}
impl<'a> core::fmt::Debug for GraphicsAPI<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
GraphicsAPI::NativeOpenGL { .. } => write!(f, "GraphicsAPI::NativeOpenGL"),
GraphicsAPI::WebGL { context_type, .. } => {
write!(f, "GraphicsAPI::WebGL(context_type = {})", context_type)
}
}
}
}
#[derive(Debug, Clone)]
#[repr(C)]
#[non_exhaustive]
pub enum RenderingState {
RenderingSetup,
BeforeRendering,
AfterRendering,
RenderingTeardown,
}
pub trait RenderingNotifier {
fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI);
}
impl<F: FnMut(RenderingState, &GraphicsAPI)> RenderingNotifier for F {
fn notify(&mut self, state: RenderingState, graphics_api: &GraphicsAPI) {
self(state, graphics_api)
}
}
#[derive(Debug, Clone)]
#[repr(C)]
#[non_exhaustive]
pub enum SetRenderingNotifierError {
Unsupported,
AlreadySet,
}
#[repr(transparent)]
pub struct Window(WindowRc);
#[doc(hidden)]
impl From<WindowRc> for Window {
fn from(window: WindowRc) -> Self {
Self(window)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub enum CloseRequestResponse {
HideWindow,
KeepWindowShown,
}
impl Default for CloseRequestResponse {
fn default() -> Self {
Self::HideWindow
}
}
impl Window {
pub fn show(&self) {
self.0.show();
}
pub fn hide(&self) {
self.0.hide();
}
pub fn set_rendering_notifier(
&self,
callback: impl FnMut(RenderingState, &GraphicsAPI) + 'static,
) -> Result<(), SetRenderingNotifierError> {
self.0.set_rendering_notifier(Box::new(callback))
}
pub fn on_close_requested(&self, callback: impl FnMut() -> CloseRequestResponse + 'static) {
self.0.on_close_requested(callback);
}
pub fn request_redraw(&self) {
self.0.request_redraw();
#[cfg(any(target_arch = "wasm32", target_os = "windows"))]
crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.set_has_active_animations());
}
}
impl crate::window::WindowHandleAccess for Window {
fn window_handle(&self) -> &Rc<crate::window::Window> {
&self.0
}
}
pub trait Global<'a, Component> {
fn get(component: &'a Component) -> Self;
}
pub trait ComponentHandle {
#[doc(hidden)]
type Inner;
fn as_weak(&self) -> Weak<Self>
where
Self: Sized;
#[must_use]
fn clone_strong(&self) -> Self;
#[doc(hidden)]
fn from_inner(_: vtable::VRc<ComponentVTable, Self::Inner>) -> Self;
fn show(&self);
fn hide(&self);
fn window(&self) -> &Window;
fn run(&self);
fn global<'a, T: Global<'a, Self>>(&'a self) -> T
where
Self: Sized;
}
mod weak_handle {
use super::*;
pub struct Weak<T: ComponentHandle> {
inner: vtable::VWeak<ComponentVTable, T::Inner>,
#[cfg(feature = "std")]
thread: std::thread::ThreadId,
}
impl<T: ComponentHandle> Clone for Weak<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
#[cfg(feature = "std")]
thread: self.thread,
}
}
}
impl<T: ComponentHandle> Weak<T> {
#[doc(hidden)]
pub fn new(rc: &vtable::VRc<ComponentVTable, T::Inner>) -> Self {
Self {
inner: vtable::VRc::downgrade(rc),
#[cfg(feature = "std")]
thread: std::thread::current().id(),
}
}
pub fn upgrade(&self) -> Option<T>
where
T: ComponentHandle,
{
#[cfg(feature = "std")]
if std::thread::current().id() != self.thread {
return None;
}
self.inner.upgrade().map(T::from_inner)
}
pub fn unwrap(&self) -> T {
self.upgrade().unwrap()
}
#[cfg(feature = "std")]
pub fn upgrade_in_event_loop(&self, func: impl FnOnce(T) + Send + 'static)
where
T: 'static,
{
let weak_handle = self.clone();
super::invoke_from_event_loop(move || {
if let Some(h) = weak_handle.upgrade() {
func(h);
}
})
}
}
#[allow(unsafe_code)]
#[cfg(feature = "std")]
unsafe impl<T: ComponentHandle> Send for Weak<T> {}
}
pub use weak_handle::*;
pub fn invoke_from_event_loop(func: impl FnOnce() + Send + 'static) {
if let Some(backend) = crate::backend::instance() {
backend.post_event(alloc::boxed::Box::new(func))
} else {
panic!("slint::invoke_from_event_loop() must be called after the Slint backend is initialized.")
}
}