use std::{
str::FromStr,
time::Instant,
};
use lambda_platform::winit::{
winit_exports::{
ElementState,
Event as WinitEvent,
MouseButton,
WindowEvent as WinitWindowEvent,
},
Loop,
LoopBuilder,
};
use crate::{
component::Component,
events::{
Button,
ComponentEvent,
Events,
Key,
Mouse,
RuntimeEvent,
WindowEvent,
},
render::{
window::{
Window,
WindowBuilder,
},
RenderContext,
RenderContextBuilder,
},
runtime::Runtime,
};
#[derive(Clone, Debug)]
pub enum ComponentResult {
Success,
Failure,
}
pub struct ApplicationRuntimeBuilder {
app_name: String,
render_context_builder: RenderContextBuilder,
window_builder: WindowBuilder,
components: Vec<Box<dyn Component<ComponentResult, String>>>,
}
impl ApplicationRuntimeBuilder {
pub fn new(app_name: &str) -> Self {
return Self {
app_name: app_name.to_string(),
render_context_builder: RenderContextBuilder::new(app_name),
window_builder: WindowBuilder::new(),
components: Vec::new(),
};
}
pub fn with_app_name(mut self, name: &str) -> Self {
self.app_name = name.to_string();
return self;
}
pub fn with_renderer_configured_as(
mut self,
configuration: impl FnOnce(RenderContextBuilder) -> RenderContextBuilder,
) -> Self {
self.render_context_builder = configuration(self.render_context_builder);
return self;
}
pub fn with_window_configured_as(
mut self,
configuration: impl FnOnce(WindowBuilder) -> WindowBuilder,
) -> Self {
self.window_builder = configuration(self.window_builder);
return self;
}
pub fn with_component<
T: Default + Component<ComponentResult, String> + 'static,
>(
self,
configure_component: impl FnOnce(Self, T) -> (Self, T),
) -> Self {
let (mut kernel_builder, component) =
configure_component(self, T::default());
kernel_builder.components.push(Box::new(component));
return kernel_builder;
}
pub fn build(self) -> ApplicationRuntime {
let name = self.app_name;
let mut event_loop = LoopBuilder::new().build();
let window = self.window_builder.build(&mut event_loop);
let component_stack = self.components;
let render_context = self.render_context_builder.build(&window);
return ApplicationRuntime {
name,
event_loop,
window,
render_context,
component_stack,
};
}
}
pub struct ApplicationRuntime {
name: String,
event_loop: Loop<Events>,
window: Window,
component_stack: Vec<Box<dyn Component<ComponentResult, String>>>,
render_context: RenderContext,
}
impl ApplicationRuntime {}
impl Runtime<(), String> for ApplicationRuntime {
type Component = Box<dyn Component<ComponentResult, String>>;
fn run(self) -> Result<(), String> {
let ApplicationRuntime {
window,
mut event_loop,
mut component_stack,
name,
render_context,
} = self;
let mut active_render_context = Some(render_context);
let publisher = event_loop.create_event_publisher();
publisher.publish_event(Events::Runtime {
event: RuntimeEvent::Initialized,
issued_at: Instant::now(),
});
let mut current_frame = Instant::now();
let mut runtime_result: Box<Result<(), String>> = Box::new(Ok(()));
event_loop.run_forever(move |event, _, control_flow| {
let mapped_event: Option<Events> = match event {
WinitEvent::WindowEvent { event, .. } => match event {
WinitWindowEvent::CloseRequested => {
control_flow.set_exit();
Some(Events::Runtime {
event: RuntimeEvent::Shutdown,
issued_at: Instant::now(),
})
}
WinitWindowEvent::Resized(dims) => {
active_render_context
.as_mut()
.unwrap()
.resize(dims.width, dims.height);
Some(Events::Window {
event: WindowEvent::Resize {
width: dims.width,
height: dims.height,
},
issued_at: Instant::now(),
})
}
WinitWindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
active_render_context
.as_mut()
.unwrap()
.resize(new_inner_size.width, new_inner_size.height);
Some(Events::Window {
event: WindowEvent::Resize {
width: new_inner_size.width,
height: new_inner_size.height,
},
issued_at: Instant::now(),
})
}
WinitWindowEvent::Moved(_) => {None}
WinitWindowEvent::Destroyed => {None}
WinitWindowEvent::DroppedFile(_) => {None}
WinitWindowEvent::HoveredFile(_) => {None}
WinitWindowEvent::HoveredFileCancelled => {None}
WinitWindowEvent::ReceivedCharacter(_) => {None}
WinitWindowEvent::Focused(_) => {None}
WinitWindowEvent::KeyboardInput {
device_id: _,
input,
is_synthetic,
} => match (input.state, is_synthetic) {
(ElementState::Pressed, false) => {
Some(Events::Keyboard {
event: Key::Pressed {
scan_code: input.scancode,
virtual_key: input.virtual_keycode,
},
issued_at: Instant::now(),
})
}
(ElementState::Released, false) => {
Some(Events::Keyboard {
event: Key::Released {
scan_code: input.scancode,
virtual_key: input.virtual_keycode,
},
issued_at: Instant::now(),
})
}
_ => {
println!(
"[WARN] Unhandled synthetic keyboard event: {:?}",
input
);
None
}
},
WinitWindowEvent::ModifiersChanged(_) => {None}
WinitWindowEvent::CursorMoved {
device_id,
position,
modifiers,
} => {
Some(Events::Mouse {
event: Mouse::Moved {
x: position.x,
y: position.y,
dx: 0.0,
dy: 0.0,
device_id: 0
},
issued_at: Instant::now(),
})
}
WinitWindowEvent::CursorEntered { device_id } => {
Some(Events::Mouse {
event: Mouse::EnteredWindow { device_id: 0 },
issued_at: Instant::now(),
})
}
WinitWindowEvent::CursorLeft { device_id } => {
Some(Events::Mouse { event: Mouse::LeftWindow { device_id: 0 }, issued_at: Instant::now() })
}
WinitWindowEvent::MouseWheel {
device_id,
delta,
phase,
modifiers,
} => {
Some(Events::Mouse{
event: Mouse::Scrolled { device_id: 0 },
issued_at: Instant::now(),
})
}
WinitWindowEvent::MouseInput {
device_id,
state,
button,
modifiers,
} => {
let button = match button {
MouseButton::Left => Button::Left,
MouseButton::Right => Button::Right,
MouseButton::Middle => Button::Middle,
MouseButton::Other(other) => Button::Other(other),
};
let event = match state {
ElementState::Pressed => Mouse::Pressed {
button,
x: 0.0,
y: 0.0,
device_id: 0,
},
ElementState::Released => Mouse::Released {
button,
x: 0.0,
y: 0.0,
device_id: 0
},
};
Some(Events::Mouse {
event,
issued_at: Instant::now(),
})
}
WinitWindowEvent::TouchpadPressure {
device_id,
pressure,
stage,
} => {None}
WinitWindowEvent::AxisMotion {
device_id,
axis,
value,
} => {None}
WinitWindowEvent::Touch(_) => {None}
WinitWindowEvent::ThemeChanged(_) => {None}
_ => {None}
},
WinitEvent::MainEventsCleared => {
let last_frame = current_frame.clone();
current_frame = Instant::now();
let duration = ¤t_frame.duration_since(last_frame);
let active_render_context = active_render_context.as_mut().expect("Couldn't get the active render context. ");
for component in &mut component_stack {
component.on_update(duration);
let commands = component.on_render(active_render_context);
active_render_context.render(commands);
}
None
}
WinitEvent::RedrawRequested(_) => { None }
WinitEvent::NewEvents(_) => {None}
WinitEvent::DeviceEvent { device_id, event } => {None}
WinitEvent::UserEvent(lambda_event) => match lambda_event {
Events::Runtime { event, issued_at } => match event {
RuntimeEvent::Initialized => {
println!("[INFO] Initializing all of the components for the runtime: {}", name);
for component in &mut component_stack {
component.on_attach(active_render_context.as_mut().unwrap());
}
None
}
RuntimeEvent::Shutdown => {
for component in &mut component_stack {
component.on_detach(active_render_context.as_mut().unwrap());
}
*runtime_result = Ok(());
None
}
RuntimeEvent::ComponentPanic { message } => {
*runtime_result = Err(message);
None
}
},
_ => { None}
},
WinitEvent::Suspended => {None}
WinitEvent::Resumed => {None}
WinitEvent::RedrawEventsCleared => { None }
WinitEvent::LoopDestroyed => {
active_render_context
.take()
.expect("[ERROR] The render API has been already taken.")
.destroy();
println!("[INFO] All resources were successfully deleted.");
None
}
};
match mapped_event {
Some(event) => {
println!("Sending event: {:?} to all components", event);
for component in &mut component_stack {
let event_result = component.on_event(event.clone());
match event_result {
Ok(_) => {}
Err(e) => {
let error = format!("[ERROR] A component has panicked while handling an event. {:?}", e);
publisher.publish_event(Events::Runtime{event: RuntimeEvent::ComponentPanic{ message: error}, issued_at: Instant::now()});
}
}
}
}
None => { }
}
});
return Ok(());
}
fn on_start(&mut self) {
println!("[INFO] Starting the runtime {}", self.name);
}
fn on_stop(&mut self) {
println!("[INFO] Stopping {}", self.name)
}
}