use std::{cell::RefCell, sync::Mutex};
use druid_shell::{
kurbo::Rect,
piet::{Color, RenderContext},
Cursor, IdleToken, KeyEvent, MouseEvent,
};
use crate::{
api::{events::Event, WidgetPtr},
app::{
listeners::keyboard::KEYBOARD_EVENT_LISTENERS,
tree::{WidgetNodeRef, WidgetTree},
TEXT_FACTORY,
},
prelude::{Constraints, Size, Widget},
};
use super::{Application, FruiWindowHandler, IdleHandle, PaintContext, WindowHandle};
thread_local! {
pub(crate) static APP_HANDLE: std::cell::RefCell<Option<IdleHandle>> = RefCell::new(None);
}
thread_local! {
pub(crate) static NEED_REBUILD: Mutex<Vec<WidgetNodeRef>> = Mutex::new(Vec::with_capacity(100));
}
pub struct WindowHandler {
window_size: Size,
window_handle: WindowHandle,
pending_update: bool,
widget_tree: WidgetTree,
root_temp: Option<WidgetPtr<'static>>,
}
impl WindowHandler {
pub fn new<W: Widget + 'static>(widget: W) -> Self {
Self {
window_size: Size::default(),
window_handle: WindowHandle::default(),
pending_update: true,
widget_tree: WidgetTree::default(),
root_temp: Some(WidgetPtr::from_owned(Box::new(widget))),
}
}
pub fn schedule_update(&mut self) {
if !self.pending_update {
self.pending_update = true;
self.window_handle.invalidate();
self.window_handle.request_anim_frame();
}
}
fn rebuild_dirty(&mut self) {
NEED_REBUILD.with(|need_rebuild| {
let mut idx = 0;
loop {
let need_rebuild = need_rebuild.lock().unwrap();
if idx == need_rebuild.len() {
break;
}
let node = need_rebuild[idx].clone();
drop(need_rebuild);
if node.is_alive() {
if node.borrow().dirty {
node.update_subtree();
}
}
idx += 1;
}
need_rebuild.lock().unwrap().clear();
});
}
}
impl FruiWindowHandler for WindowHandler {
fn connect(&mut self, handle: &WindowHandle) {
APP_HANDLE.with(|r| *r.borrow_mut() = Some(handle.get_idle_handle().unwrap()));
#[cfg(not(feature = "miri"))]
TEXT_FACTORY.with(|f| f.set(self.window_handle.text()));
let root_widget = std::mem::take(&mut self.root_temp);
self.widget_tree = WidgetTree::new(root_widget.unwrap());
self.window_handle = handle.clone();
self.window_handle.set_cursor(&Cursor::Arrow);
}
fn prepare_paint(&mut self) {}
fn paint(&mut self, piet: &mut PaintContext, _invalid: &druid_shell::Region) {
let size = self.window_size;
let rect = Rect::new(0., 0., size.width, size.height);
let brush = &piet.solid_brush(Color::from_hex_str("#202324").unwrap());
druid_shell::piet::RenderContext::fill(piet, rect, brush);
self.pending_update = false;
self.rebuild_dirty();
self.widget_tree.layout(Constraints {
min_width: 0.,
max_width: self.window_size.width,
min_height: 0.,
max_height: self.window_size.height,
});
self.widget_tree.paint(piet);
}
fn size(&mut self, size: druid_shell::kurbo::Size) {
self.window_size = size.into();
}
fn idle(&mut self, _token: IdleToken) {
self.schedule_update();
}
fn destroy(&mut self) {
Application::global().quit()
}
fn as_any(&mut self) -> &mut dyn std::any::Any {
self
}
fn mouse_down(&mut self, event: &MouseEvent) {
self.widget_tree
.handle_event(Event::MouseDown(event.clone()));
}
fn mouse_move(&mut self, event: &MouseEvent) {
self.widget_tree
.handle_event(Event::MouseMove(event.clone()));
self.window_handle.set_cursor(&Cursor::Arrow);
}
fn mouse_up(&mut self, event: &MouseEvent) {
self.widget_tree.handle_event(Event::MouseUp(event.clone()));
}
fn wheel(&mut self, event: &MouseEvent) {
self.widget_tree
.handle_event(Event::MouseWheel(event.clone()));
}
fn key_down(&mut self, event: KeyEvent) -> bool {
KEYBOARD_EVENT_LISTENERS.with(|listeners| {
for listener in listeners.borrow_mut().iter() {
listener(event.clone());
}
});
true
}
}