use sdl2::event::Event;
use sdl2::video::Window;
use sdl2::Sdl;
use crate::render::layout::Layout;
use crate::render::layout_cache::LayoutCache;
use crate::render::widget::{BaseWidget, Widget};
use crate::render::widget_cache::WidgetCache;
use crate::render::{make_points_origin, make_size};
use sdl2::pixels::Color;
use std::thread::sleep;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub type OnExitCallbackType = Option<Box<dyn FnMut(&mut Engine) -> bool>>;
pub struct Engine {
widget_cache: WidgetCache,
layout_cache: LayoutCache,
current_widget_id: i32,
frame_rate: u8,
running: bool,
on_exit: OnExitCallbackType,
}
impl Engine {
pub fn new(w: u32, h: u32, frame_rate: u8) -> Self {
let base_widget = BaseWidget::new(make_points_origin(), make_size(w, h));
let mut cache = WidgetCache::new();
cache.add_widget(Box::new(base_widget), "base".to_string());
Self {
widget_cache: cache,
layout_cache: LayoutCache::new(),
current_widget_id: 0,
frame_rate,
running: true,
on_exit: None,
}
}
pub fn add_widget(&mut self, widget: Box<dyn Widget>, widget_name: String) -> i32 {
self.widget_cache.add_widget(widget, widget_name)
}
pub fn add_layout(&mut self, layout: Box<dyn Layout>) -> i32 {
self.layout_cache.add_layout(layout)
}
pub fn set_running(&mut self, state: bool) {
self.running = state;
}
pub fn on_exit<F>(&mut self, callback: F)
where
F: FnMut(&mut Engine) -> bool + 'static,
{
self.on_exit = Some(Box::new(callback));
}
fn call_exit_callback(&mut self) -> bool {
if let Some(mut cb) = self.on_exit.take() {
let return_value = cb(self);
self.on_exit = Some(cb);
return_value
} else {
eprintln!("No exit callback defined: returning true, application exiting.");
true
}
}
pub fn run(&mut self, sdl: Sdl, window: Window) {
let mut canvas = window
.into_canvas()
.target_texture()
.accelerated()
.build()
.unwrap();
canvas.set_draw_color(Color::RGB(255, 255, 255));
canvas.clear();
canvas.present();
let mut event_pump = sdl.event_pump().unwrap();
let fps_as_ms = (1000.0 / self.frame_rate as f64) as u128;
'running: loop {
let start = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
for event in event_pump.poll_iter() {
match event {
Event::MouseButtonDown {
mouse_btn, clicks, ..
} => {
self.widget_cache.button_clicked(
self.current_widget_id,
mouse_btn as u8,
clicks,
true,
self.layout_cache.get_layout_cache(),
);
}
Event::MouseButtonUp {
mouse_btn, clicks, ..
} => {
self.widget_cache.button_clicked(
-1,
mouse_btn as u8,
clicks,
false,
self.layout_cache.get_layout_cache(),
);
}
Event::MouseMotion { x, y, .. } => {
let cur_widget_id = self.current_widget_id;
self.current_widget_id = self.widget_cache.find_widget(x, y);
if cur_widget_id != self.current_widget_id {
self.widget_cache
.mouse_exited(cur_widget_id, self.layout_cache.get_layout_cache());
self.widget_cache.mouse_entered(
self.current_widget_id,
self.layout_cache.get_layout_cache(),
);
}
self.widget_cache.mouse_moved(
self.current_widget_id,
vec![x, y],
self.layout_cache.get_layout_cache(),
);
}
Event::MouseWheel { x, y, .. } => {
self.widget_cache.mouse_scrolled(
self.current_widget_id,
vec![x, y],
self.layout_cache.get_layout_cache(),
);
}
Event::Quit { .. } => {
if self.call_exit_callback() {
break 'running;
}
}
remaining_event => {
self.widget_cache.other_event(
self.current_widget_id,
remaining_event,
self.layout_cache.get_layout_cache(),
);
}
}
}
self.widget_cache.tick(self.layout_cache.get_layout_cache());
self.layout_cache
.do_layout(self.widget_cache.borrow_cache());
self.widget_cache.draw_loop(&mut canvas);
canvas.present();
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
if now - start < fps_as_ms {
let diff = fps_as_ms - (now - start);
sleep(Duration::from_millis(diff as u64));
}
if !self.running {
break 'running;
}
}
}
}