#![warn(missing_docs)]
use crate::component::ComponentRc;
use crate::graphics::Point;
use crate::items::{ItemRc, ItemRef};
use crate::slice::Slice;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
use std::{convert::TryInto, pin::Pin};
use crate::input::{KeyEvent, MouseEventType};
#[cfg(not(target_arch = "wasm32"))]
use winit::platform::desktop::EventLoopExtDesktop;
pub trait GenericWindow {
fn set_component(self: Rc<Self>, component: &ComponentRc);
fn draw(self: Rc<Self>);
fn process_mouse_input(
self: Rc<Self>,
pos: winit::dpi::PhysicalPosition<f64>,
what: MouseEventType,
);
fn process_key_input(self: Rc<Self>, event: &KeyEvent);
fn with_platform_window(&self, callback: &dyn Fn(&winit::window::Window));
fn map_window(self: Rc<Self>, event_loop: &EventLoop);
fn unmap_window(self: Rc<Self>);
fn request_redraw(&self);
fn scale_factor(&self) -> f32;
fn set_scale_factor(&self, factor: f32);
fn set_width(&self, width: f32);
fn set_height(&self, height: f32);
fn get_geometry(&self) -> crate::graphics::Rect;
fn free_graphics_resources<'a>(self: Rc<Self>, items: &Slice<'a, Pin<ItemRef<'a>>>);
fn set_cursor_blink_binding(&self, prop: &crate::properties::Property<bool>);
fn current_keyboard_modifiers(&self) -> crate::input::KeyboardModifiers;
fn set_current_keyboard_modifiers(&self, modifiers: crate::input::KeyboardModifiers);
fn set_focus_item(self: Rc<Self>, focus_item: &ItemRc);
fn set_focus(self: Rc<Self>, have_focus: bool);
fn show_popup(&self, popup: &ComponentRc, position: Point);
fn close_popup(&self);
}
#[repr(C)]
#[derive(Clone)]
pub struct ComponentWindow(pub std::rc::Rc<dyn crate::eventloop::GenericWindow>);
impl ComponentWindow {
pub fn new(window_impl: std::rc::Rc<dyn crate::eventloop::GenericWindow>) -> Self {
Self(window_impl)
}
pub fn run(&self) {
let event_loop = crate::eventloop::EventLoop::new();
self.0.clone().map_window(&event_loop);
event_loop.run();
self.0.clone().unmap_window();
}
pub fn scale_factor(&self) -> f32 {
self.0.scale_factor()
}
pub fn set_scale_factor(&self, factor: f32) {
self.0.set_scale_factor(factor)
}
pub fn free_graphics_resources<'a>(&self, items: &Slice<'a, Pin<ItemRef<'a>>>) {
self.0.clone().free_graphics_resources(items);
}
pub(crate) fn set_cursor_blink_binding(&self, prop: &crate::properties::Property<bool>) {
self.0.clone().set_cursor_blink_binding(prop)
}
pub(crate) fn set_current_keyboard_modifiers(
&self,
modifiers: crate::input::KeyboardModifiers,
) {
self.0.clone().set_current_keyboard_modifiers(modifiers)
}
pub(crate) fn current_keyboard_modifiers(&self) -> crate::input::KeyboardModifiers {
self.0.clone().current_keyboard_modifiers()
}
pub(crate) fn process_key_input(&self, event: &KeyEvent) {
self.0.clone().process_key_input(event)
}
pub fn set_focus_item(&self, focus_item: &ItemRc) {
self.0.clone().set_focus_item(focus_item)
}
pub fn set_component(&self, component: &ComponentRc) {
self.0.clone().set_component(component)
}
pub fn show_popup(&self, popup: &ComponentRc, position: Point) {
self.0.clone().show_popup(popup, position)
}
pub fn close_popup(&self) {
self.0.clone().close_popup()
}
}
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 enum CustomEvent {
WakeUpAndPoll,
}
pub struct EventLoop {
winit_loop: winit::event_loop::EventLoop<CustomEvent>,
}
impl EventLoop {
pub fn new() -> Self {
Self { winit_loop: winit::event_loop::EventLoop::with_user_event() }
}
#[allow(unused_mut)]
pub fn run(mut self) {
use winit::event::Event;
use winit::event_loop::{ControlFlow, EventLoopWindowTarget};
let mut cursor_pos = winit::dpi::PhysicalPosition::new(0., 0.);
let mut pressed = false;
let mut run_fn = move |event: Event<CustomEvent>,
_: &EventLoopWindowTarget<CustomEvent>,
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::update_animations();
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&id).map(|weakref| weakref.upgrade())
{
window.draw();
}
});
}
winit::event::Event::WindowEvent {
event: winit::event::WindowEvent::Resized(size),
window_id,
} => {
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
window.with_platform_window(&|platform_window| {
window.set_scale_factor(platform_window.scale_factor() as f32);
});
window.set_width(size.width as f32);
window.set_height(size.height as f32);
}
});
}
winit::event::Event::WindowEvent {
event:
winit::event::WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size: size,
},
window_id,
} => {
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
window.set_scale_factor(scale_factor as f32);
window.set_width(size.width as f32);
window.set_height(size.height as f32);
}
});
}
winit::event::Event::WindowEvent {
ref window_id,
event: winit::event::WindowEvent::MouseInput { state, .. },
..
} => {
crate::animations::update_animations();
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 => {
pressed = true;
MouseEventType::MousePressed
}
winit::event::ElementState::Released => {
pressed = false;
MouseEventType::MouseReleased
}
};
window.clone().process_mouse_input(cursor_pos, what);
window.request_redraw();
}
});
}
winit::event::Event::WindowEvent {
ref window_id,
event: winit::event::WindowEvent::Touch(touch),
..
} => {
crate::animations::update_animations();
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
let cursor_pos = touch.location;
let what = match touch.phase {
winit::event::TouchPhase::Started => {
pressed = true;
MouseEventType::MousePressed
}
winit::event::TouchPhase::Ended
| winit::event::TouchPhase::Cancelled => {
pressed = false;
MouseEventType::MouseReleased
}
winit::event::TouchPhase::Moved => MouseEventType::MouseMoved,
};
window.clone().process_mouse_input(cursor_pos, what);
window.request_redraw();
}
});
}
winit::event::Event::WindowEvent {
window_id,
event: winit::event::WindowEvent::CursorMoved { position, .. },
..
} => {
cursor_pos = position;
crate::animations::update_animations();
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
window
.clone()
.process_mouse_input(cursor_pos, MouseEventType::MouseMoved);
window.request_redraw();
}
});
}
#[cfg(target_arch = "wasm32")]
winit::event::Event::WindowEvent {
ref window_id,
event: winit::event::WindowEvent::CursorLeft { .. },
..
} => {
if pressed {
crate::animations::update_animations();
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
pressed = false;
window
.clone()
.process_mouse_input(cursor_pos, MouseEventType::MouseExit);
window.request_redraw();
}
});
}
}
winit::event::Event::WindowEvent {
ref window_id,
event: winit::event::WindowEvent::KeyboardInput { ref input, .. },
} => {
crate::animations::update_animations();
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
if let Some(ref key_event) =
(input, window.current_keyboard_modifiers()).try_into().ok()
{
window.clone().process_key_input(key_event);
window.request_redraw();
}
}
});
}
winit::event::Event::WindowEvent {
ref window_id,
event: winit::event::WindowEvent::ReceivedCharacter(ch),
} => {
if !ch.is_control() {
crate::animations::update_animations();
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
let modifiers = window.current_keyboard_modifiers();
if !modifiers.control() && !modifiers.alt() && !modifiers.logo() {
let key_event = KeyEvent::CharacterInput {
unicode_scalar: ch.into(),
modifiers,
};
window.clone().process_key_input(&key_event);
window.request_redraw();
}
}
});
}
}
winit::event::Event::WindowEvent {
ref window_id,
event: winit::event::WindowEvent::ModifiersChanged(state),
} => {
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
window.set_current_keyboard_modifiers(state.into());
}
});
}
winit::event::Event::WindowEvent {
ref window_id,
event: winit::event::WindowEvent::Focused(have_focus),
} => {
ALL_WINDOWS.with(|windows| {
if let Some(Some(window)) =
windows.borrow().get(&window_id).map(|weakref| weakref.upgrade())
{
window.clone().set_focus(have_focus);
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;
ALL_WINDOWS.with(|windows| {
windows.borrow().values().for_each(|window| {
if let Some(window) = window.upgrade() {
window.request_redraw();
}
})
})
})
}
if crate::timers::TimerList::maybe_activate_timers() {
ALL_WINDOWS.with(|windows| {
windows.borrow().values().for_each(|window| {
if let Some(window) = window.upgrade() {
window.request_redraw();
}
})
})
}
if *control_flow == winit::event_loop::ControlFlow::Wait {
if let Some(next_timer) = crate::timers::TimerList::next_timeout() {
*control_flow = winit::event_loop::ControlFlow::WaitUntil(next_timer);
}
}
};
#[cfg(not(target_arch = "wasm32"))]
self.winit_loop.run_return(run_fn);
#[cfg(target_arch = "wasm32")]
{
scoped_tls_hkt::scoped_thread_local!(static mut RUN_FN_TLS: for <'a> &'a mut dyn FnMut(
Event<'_, CustomEvent>,
&EventLoopWindowTarget<CustomEvent>,
&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<CustomEvent> {
&self.winit_loop
}
}
pub mod ffi {
#![allow(unsafe_code)]
use super::*;
#[allow(non_camel_case_types)]
type c_void = ();
#[repr(C)]
pub struct ComponentWindowOpaque(*const c_void, *const c_void);
#[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);
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_clone(
source: *const ComponentWindowOpaque,
target: *mut ComponentWindowOpaque,
) {
assert_eq!(
core::mem::size_of::<ComponentWindow>(),
core::mem::size_of::<ComponentWindowOpaque>()
);
let window = &*(source as *const ComponentWindow);
core::ptr::write(target as *mut ComponentWindow, window.clone());
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_run(handle: *const ComponentWindowOpaque) {
let window = &*(handle as *const ComponentWindow);
window.run();
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_get_scale_factor(
handle: *const ComponentWindowOpaque,
) -> f32 {
assert_eq!(
core::mem::size_of::<ComponentWindow>(),
core::mem::size_of::<ComponentWindowOpaque>()
);
let window = &*(handle as *const ComponentWindow);
window.scale_factor()
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_set_scale_factor(
handle: *const ComponentWindowOpaque,
value: f32,
) {
let window = &*(handle as *const ComponentWindow);
window.set_scale_factor(value)
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_free_graphics_resources<'a>(
handle: *const ComponentWindowOpaque,
items: &Slice<'a, Pin<ItemRef<'a>>>,
) {
let window = &*(handle as *const ComponentWindow);
window.free_graphics_resources(items)
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_set_focus_item(
handle: *const ComponentWindowOpaque,
focus_item: &ItemRc,
) {
let window = &*(handle as *const ComponentWindow);
window.set_focus_item(focus_item)
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_set_component(
handle: *const ComponentWindowOpaque,
component: &ComponentRc,
) {
let window = &*(handle as *const ComponentWindow);
window.set_component(component)
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_window_show_popup(
handle: *const ComponentWindowOpaque,
popup: &ComponentRc,
position: crate::graphics::Point,
) {
let window = &*(handle as *const ComponentWindow);
window.show_popup(popup, position);
}
pub unsafe extern "C" fn sixtyfps_component_window_close_popup(
handle: *const ComponentWindowOpaque,
) {
let window = &*(handle as *const ComponentWindow);
window.close_popup();
}
}