slint/sixtyfps_runtime/corelib/eventloop.rs
2020-08-04 09:01:30 +02:00

287 lines
11 KiB
Rust

use crate::abi::datastructures::{ComponentVTable, WindowProperties};
use std::cell::RefCell;
use std::{
pin::Pin,
rc::{Rc, Weak},
};
use vtable::*;
use crate::{graphics::Size, input::MouseEventType, properties::PropertyListenerScope};
#[cfg(not(target_arch = "wasm32"))]
use winit::platform::desktop::EventLoopExtDesktop;
pub trait GenericWindow {
fn draw(&self, component: core::pin::Pin<crate::abi::datastructures::ComponentRef>);
fn process_mouse_input(
&self,
pos: winit::dpi::PhysicalPosition<f64>,
what: MouseEventType,
component: core::pin::Pin<crate::abi::datastructures::ComponentRef>,
);
fn window_handle(&self) -> std::cell::Ref<'_, winit::window::Window>;
fn map_window(self: Rc<Self>, event_loop: &EventLoop);
fn request_redraw(&self);
fn size(&self) -> Size;
}
/// The ComponentWindow is the (rust) facing public type that can render the items
/// of components to the screen.
#[repr(C)]
#[derive(Clone)]
pub struct ComponentWindow(std::rc::Rc<dyn crate::eventloop::GenericWindow>);
impl ComponentWindow {
/// Creates a new instance of a CompomentWindow based on the given window implementation. Only used
/// internally.
pub fn new(window_impl: std::rc::Rc<dyn crate::eventloop::GenericWindow>) -> Self {
Self(window_impl)
}
/// Spins an event loop and renders the items of the provided component in this window.
pub fn run(&self, component: Pin<VRef<ComponentVTable>>, props: &WindowProperties) {
let event_loop = crate::eventloop::EventLoop::new();
self.0.clone().map_window(&event_loop);
{
let size = self.0.size();
if let Some(width_property) = props.width {
width_property.set(size.width)
}
if let Some(height_property) = props.height {
height_property.set(size.height)
}
}
event_loop.run(component, &props);
}
}
thread_local! {
static ALL_WINDOWS: RefCell<std::collections::HashMap<winit::window::WindowId, Weak<dyn GenericWindow>>> = RefCell::new(std::collections::HashMap::new());
}
pub(crate) fn register_window(id: winit::window::WindowId, window: Rc<dyn GenericWindow>) {
ALL_WINDOWS.with(|windows| {
windows.borrow_mut().insert(id, Rc::downgrade(&window));
})
}
pub(crate) fn unregister_window(id: winit::window::WindowId) {
ALL_WINDOWS.with(|windows| {
windows.borrow_mut().remove(&id);
})
}
pub struct EventLoop {
winit_loop: winit::event_loop::EventLoop<()>,
}
impl EventLoop {
pub fn new() -> Self {
Self { winit_loop: winit::event_loop::EventLoop::new() }
}
#[allow(unused_mut)] // mut need changes for wasm
pub fn run(
mut self,
component: core::pin::Pin<crate::abi::datastructures::ComponentRef>,
window_properties: &crate::abi::datastructures::WindowProperties,
) {
use winit::event::Event;
use winit::event_loop::{ControlFlow, EventLoopWindowTarget};
let layout_listener = Rc::pin(PropertyListenerScope::default());
let mut cursor_pos = winit::dpi::PhysicalPosition::new(0., 0.);
let mut run_fn = move |event: Event<()>,
_: &EventLoopWindowTarget<()>,
control_flow: &mut ControlFlow| {
*control_flow = ControlFlow::Wait;
match event {
winit::event::Event::WindowEvent {
event: winit::event::WindowEvent::CloseRequested,
..
} => *control_flow = winit::event_loop::ControlFlow::Exit,
winit::event::Event::RedrawRequested(id) => {
crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| {
driver.update_animations(instant::Instant::now());
});
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&id).map(|weakref| weakref.upgrade())
{
if layout_listener.as_ref().is_dirty() {
layout_listener
.as_ref()
.evaluate(|| component.as_ref().compute_layout())
}
window.draw(component);
}
});
}
winit::event::Event::WindowEvent {
event: winit::event::WindowEvent::Resized(size),
window_id,
} => {
if let Some(width_property) = window_properties.width {
width_property.set(size.width as f32)
}
if let Some(height_property) = window_properties.height {
height_property.set(size.height as f32)
}
if let Some(scale_factor_property) = window_properties.scale_factor {
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
let window = window.window_handle();
scale_factor_property.set(window.scale_factor() as f32)
}
});
}
}
winit::event::Event::WindowEvent {
event:
winit::event::WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size: size,
},
..
} => {
if let Some(width_property) = window_properties.width {
width_property.set(size.width as f32)
}
if let Some(height_property) = window_properties.height {
height_property.set(size.height as f32)
}
if let Some(scale_factor_property) = window_properties.scale_factor {
scale_factor_property.set(scale_factor as f32)
}
}
winit::event::Event::WindowEvent {
ref window_id,
event: winit::event::WindowEvent::MouseInput { state, .. },
..
} => {
crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| {
driver.update_animations(instant::Instant::now());
});
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
let what = match state {
winit::event::ElementState::Pressed => MouseEventType::MousePressed,
winit::event::ElementState::Released => {
MouseEventType::MouseReleased
}
};
window.process_mouse_input(cursor_pos, what, component);
let window = window.window_handle();
// FIXME: remove this, it should be based on actual changes rather than this
window.request_redraw();
}
});
}
winit::event::Event::WindowEvent {
window_id,
event: winit::event::WindowEvent::CursorMoved { position, .. },
..
} => {
cursor_pos = position;
crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| {
driver.update_animations(instant::Instant::now());
});
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
window.process_mouse_input(
cursor_pos,
MouseEventType::MouseMoved,
component,
);
let window = window.window_handle();
// FIXME: remove this, it should be based on actual changes rather than this
window.request_redraw();
}
});
}
_ => (),
}
if *control_flow != winit::event_loop::ControlFlow::Exit {
crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| {
if !driver.has_active_animations() {
return;
}
*control_flow = ControlFlow::Poll;
//println!("Scheduling a redraw due to active animations");
ALL_WINDOWS.with(|windows| {
windows.borrow().values().for_each(|window| {
if let Some(window) = window.upgrade() {
window.request_redraw();
}
})
})
})
}
};
#[cfg(not(target_arch = "wasm32"))]
self.winit_loop.run_return(run_fn);
#[cfg(target_arch = "wasm32")]
{
// Since wasm does not have a run_return function that takes a non-static closure,
// we use this hack to work that around
scoped_tls_hkt::scoped_thread_local!(static mut RUN_FN_TLS: for <'a> &'a mut dyn FnMut(
Event<'_, ()>,
&EventLoopWindowTarget<()>,
&mut ControlFlow,
));
RUN_FN_TLS.set(&mut run_fn, move || {
self.winit_loop.run(|e, t, cf| RUN_FN_TLS.with(|mut run_fn| run_fn(e, t, cf)))
});
}
}
pub fn get_winit_event_loop(&self) -> &winit::event_loop::EventLoop<()> {
&self.winit_loop
}
}
pub mod ffi {
#![allow(unsafe_code)]
use super::*;
#[allow(non_camel_case_types)]
type c_void = ();
/// Same layout as ComponentWindow (fat pointer)
#[repr(C)]
pub struct ComponentWindowOpaque(*const c_void, *const c_void);
/// Releases the reference to the component window held by handle.
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_drop(handle: *mut ComponentWindowOpaque) {
assert_eq!(
core::mem::size_of::<ComponentWindow>(),
core::mem::size_of::<ComponentWindowOpaque>()
);
core::ptr::read(handle as *mut ComponentWindow);
}
/// Spins an event loop and renders the items of the provided component in this window.
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_run(
handle: *mut ComponentWindowOpaque,
component: Pin<VRef<ComponentVTable>>,
window_props: *mut WindowProperties,
) {
let window = &*(handle as *const ComponentWindow);
let window_props = &*(window_props as *const WindowProperties);
window.run(component, &window_props);
}
}