use winit::{
application::ApplicationHandler,
event::{WindowEvent, ElementState},
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
window::{Window, WindowAttributes, WindowId, WindowButtons, Icon},
dpi::{Size, Position, LogicalSize, LogicalPosition}
};
use std::sync::Arc;
use super::{
present_mode,
super::{
render::manager::RenderState,
super::{
text::text::TextHolder,
asset_loader::AssetLoader,
camera::camera2d::Camera2d,
ecs::{world::World, resource::{ResourceRef, ResourceRefMut}},
super::{Color, ColorOption},
game_loop::GameLoop,
context::Context,
bindings::{keyboard_input::KeyboardInput, mouse_input::MouseInput}
}
}
};
#[derive(Clone)]
pub struct WindowConfiguration {
pub icon_path: String,
pub title: String,
pub background_color: Option<Color>,
pub background_image_path: Option<String>,
pub width: f64,
pub height: f64,
pub position_x: f64,
pub position_y: f64,
pub resizable: bool,
pub decorations: bool,
pub transparent: bool,
pub active: bool,
pub enabled_buttons: WindowButtons,
pub present_mode: present_mode::PresentMode
}
impl Default for WindowConfiguration {
fn default() -> Self {
return Self {
icon_path: "".to_string(),
title: "New Game!".to_string(),
background_color: Some(Color::by_option(ColorOption::White)),
background_image_path: None,
width: 800.0,
height: 600.0,
position_x: 100.0,
position_y: 100.0,
resizable: true,
decorations: true,
transparent: true,
active: true,
enabled_buttons: WindowButtons::all(),
present_mode: present_mode::PresentMode::AutoNoVsync
};
}
}
impl WindowConfiguration {
pub fn icon_path(self, icon_path: String) -> Self {
return Self {
icon_path,
..self
};
}
pub fn title(self, title: String) -> Self {
return Self {
title,
..self
};
}
pub fn background_color(self, background_color: Option<Color>) -> Self {
return Self {
background_color,
..self
};
}
pub fn background_image_path(self, background_image_path: Option<String>) -> Self {
return Self {
background_image_path,
..self
};
}
pub fn width(self, width: f64) -> Self {
return Self {
width,
..self
};
}
pub fn height(self, height: f64) -> Self {
return Self {
height,
..self
};
}
pub fn position_x(self, position_x: f64) -> Self {
return Self {
position_x,
..self
};
}
pub fn position_y(self, position_y: f64) -> Self {
return Self {
position_y,
..self
};
}
pub fn resizable(self, resizable: bool) -> Self {
return Self {
resizable,
..self
};
}
pub fn decorations(self, decorations: bool) -> Self {
return Self {
decorations,
..self
};
}
pub fn transparent(self, transparent: bool) -> Self {
return Self {
transparent,
..self
};
}
pub(crate) fn _visible(self, _visible: bool) -> Self {
return Self {
..self
};
}
pub fn active(self, active: bool) -> Self {
return Self {
active,
..self
};
}
pub fn enabled_buttons(self, enabled_buttons: WindowButtons) -> Self {
return Self {
enabled_buttons,
..self
};
}
pub fn present_mode(self, present_mode: present_mode::PresentMode) -> Self {
return Self {
present_mode,
..self
};
}
pub fn get_icon_by_bytes(icon_as_bytes: Vec<u8>) -> Option<Icon> {
if let Ok(image) = image::load_from_memory(&icon_as_bytes) {
let icon_image = image.into_rgba8();
let (icon_width, icon_height): (u32, u32) = icon_image.dimensions();
let icon_rgba: Vec<u8> = icon_image.into_raw();
let icon: Icon = Icon::from_rgba(icon_rgba, icon_width, icon_height).unwrap();
return Some(icon);
};
return None;
}
}
pub struct Application {
window: Option<Arc<Window>>,
window_configuration: Option<WindowConfiguration>,
context: Option<Context>,
game_loop: GameLoop
}
impl ApplicationHandler for Application {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let mut color: Option<Color> = None;
let mut background_image_path: Option<String> = None;
let mut present_mode: present_mode::PresentMode = present_mode::PresentMode::AutoNoVsync;
let window: Arc<Window> = if let Some(window_configuration) = &self.window_configuration {
let mut window_attributes: WindowAttributes = Window::default_attributes();
window_attributes.title = window_configuration.title.clone();
window_attributes.inner_size = Some(Size::Logical(LogicalSize::new(
window_configuration.width,
window_configuration.height,
)));
window_attributes.position = Some(Position::Logical(LogicalPosition::new(
window_configuration.position_x,
window_configuration.position_y
)));
window_attributes.resizable = window_configuration.resizable;
window_attributes.decorations = window_configuration.decorations;
window_attributes.transparent = window_configuration.transparent;
window_attributes.visible = false; window_attributes.active = window_configuration.active;
window_attributes.enabled_buttons = window_configuration.enabled_buttons;
present_mode = window_configuration.present_mode;
if window_configuration.icon_path.is_empty() {
window_attributes.window_icon = WindowConfiguration::get_icon_by_bytes(
include_bytes!("../../../../assets/textures/lotus_pink_256x256_aligned.png").to_vec()
);
} else {
window_attributes.window_icon = WindowConfiguration::get_icon_by_bytes(
AssetLoader::load_bytes(&window_configuration.icon_path).ok().unwrap()
);
}
if let Some(background_color) = window_configuration.background_color {
color = Some(background_color);
}
if let Some(background_image_path_unwrapped) = &window_configuration.background_image_path {
background_image_path = Some(background_image_path_unwrapped.to_string());
}
Arc::new(event_loop.create_window(window_attributes).unwrap())
} else {
Arc::new(event_loop.create_window(Window::default_attributes()).unwrap())
};
self.window = Some(window.clone());
self.window.as_ref().unwrap().focus_window();
let world: World = World::new();
let mut render_state: RenderState = pollster::block_on(RenderState::new(window, present_mode.to_wgpu(), event_loop));
render_state.color = color;
render_state.background_image_path = background_image_path;
self.context = Some(Context::new(
render_state,
world,
self.window_configuration.as_ref().unwrap().clone(),
0.0
));
(self.game_loop.setup)(self.context.as_mut().unwrap());
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, window_event: WindowEvent) {
let context: &mut Context = &mut self.context.as_mut().unwrap();
let render_state: &mut RenderState = &mut context.render_state;
if !render_state.input(&window_event) {
match window_event {
WindowEvent::CloseRequested => {
event_loop.exit();
},
WindowEvent::Resized(new_size) => {
let camera2d: ResourceRef<'_, Camera2d> = context.world.get_resource::<Camera2d>().unwrap();
let text_holder: ResourceRef<'_, TextHolder> = context.world.get_resource::<TextHolder>().unwrap();
render_state.resize(new_size, &camera2d, &text_holder.text_renderers);
},
WindowEvent::KeyboardInput { device_id: _, event, is_synthetic: _ } => {
let mut keyboard_input: ResourceRefMut<'_, KeyboardInput> = context.world.get_resource_mut::<KeyboardInput>().unwrap();
if ElementState::Pressed == event.state {
keyboard_input.pressed.insert(event.physical_key);
} else if ElementState::Released == event.state {
keyboard_input.pressed.remove(&event.physical_key);
}
},
WindowEvent::CursorMoved { device_id: _, position } => {
let mut mouse_input: ResourceRefMut<'_, MouseInput> = context.world.get_resource_mut::<MouseInput>().unwrap();
mouse_input.mouse_position.x = position.x as f32;
mouse_input.mouse_position.y = position.y as f32;
},
WindowEvent::MouseInput { device_id: _, state, button } => {
let mut mouse_input: ResourceRefMut<'_, MouseInput> = context.world.get_resource_mut::<MouseInput>().unwrap();
if ElementState::Pressed == state {
mouse_input.pressed.insert(button);
} else if ElementState::Released == state {
mouse_input.pressed.remove(&button);
}
},
WindowEvent::RedrawRequested => {
render_state.window().request_redraw();
self.game_loop.render(&mut context.render_state, &mut context.world, event_loop);
}
_ => ()
}
}
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
self.window.as_mut().unwrap().set_visible(true);
self.game_loop.run(self.context.as_mut().unwrap(), event_loop);
}
}
pub async fn initialize_application(
window_configuration: Option<WindowConfiguration>,
setup: fn(context: &mut Context),
update: fn(context: &mut Context)
) {
env_logger::init();
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
event_loop.set_control_flow(ControlFlow::Poll);
let mut application: Application = if let Some(window_configuration_unwrapped) = window_configuration {
Application {
window: None,
context: None,
window_configuration: Some(window_configuration_unwrapped),
game_loop: GameLoop::new(setup, update)
}
} else {
Application {
window: None,
context: None,
window_configuration: None,
game_loop: GameLoop::new(setup, update)
}
};
let _ = event_loop.run_app(&mut application);
}