use ahash::{HashMap, HashMapExt};
use astrelis_core::profiling::profile_function;
use std::sync::Arc;
use astrelis_winit::{
WindowId,
app::AppCtx,
event::{Event, EventBatch, HandleStatus},
window::WindowDescriptor,
};
use crate::{
context::GraphicsContext,
window::{RenderWindow, WindowContextDescriptor},
};
pub struct WindowManager {
graphics: Arc<GraphicsContext>,
windows: HashMap<WindowId, RenderWindow>,
}
impl WindowManager {
pub fn new(graphics: Arc<GraphicsContext>) -> Self {
Self {
graphics,
windows: HashMap::new(),
}
}
pub fn create_window(
&mut self,
ctx: &mut AppCtx,
descriptor: WindowDescriptor,
) -> Result<WindowId, crate::context::GraphicsError> {
self.create_window_with_descriptor(ctx, descriptor, WindowContextDescriptor::default())
}
pub fn create_window_with_descriptor(
&mut self,
ctx: &mut AppCtx,
descriptor: WindowDescriptor,
window_descriptor: WindowContextDescriptor,
) -> Result<WindowId, crate::context::GraphicsError> {
profile_function!();
let window = ctx
.create_window(descriptor)
.expect("Failed to create window");
let id = window.id();
let renderable =
RenderWindow::new_with_descriptor(window, self.graphics.clone(), window_descriptor)?;
self.windows.insert(id, renderable);
Ok(id)
}
pub fn get_window(&self, id: WindowId) -> Option<&RenderWindow> {
self.windows.get(&id)
}
pub fn get_window_mut(&mut self, id: WindowId) -> Option<&mut RenderWindow> {
self.windows.get_mut(&id)
}
pub fn remove_window(&mut self, id: WindowId) -> Option<RenderWindow> {
self.windows.remove(&id)
}
pub fn window_count(&self) -> usize {
self.windows.len()
}
pub fn window_ids(&self) -> impl Iterator<Item = WindowId> + '_ {
self.windows.keys().copied()
}
pub fn render_window<F>(&mut self, id: WindowId, events: &mut EventBatch, mut render_fn: F)
where
F: FnMut(&mut RenderWindow, &mut EventBatch),
{
profile_function!();
let Some(window) = self.windows.get_mut(&id) else {
return;
};
events.dispatch(|event| match event {
Event::WindowResized(size) => {
window.resized(*size);
HandleStatus::consumed()
}
_ => HandleStatus::ignored(),
});
render_fn(window, events);
}
pub fn render_window_result<F, E>(
&mut self,
id: WindowId,
events: &mut EventBatch,
mut render_fn: F,
) -> Result<(), E>
where
F: FnMut(&mut RenderWindow, &mut EventBatch) -> Result<(), E>,
{
let Some(window) = self.windows.get_mut(&id) else {
return Ok(());
};
events.dispatch(|event| match event {
Event::WindowResized(size) => {
window.resized(*size);
HandleStatus::consumed()
}
_ => HandleStatus::ignored(),
});
render_fn(window, events)
}
pub fn graphics(&self) -> &Arc<GraphicsContext> {
&self.graphics
}
pub fn iter(&self) -> impl Iterator<Item = (WindowId, &RenderWindow)> {
self.windows.iter().map(|(&id, window)| (id, window))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (WindowId, &mut RenderWindow)> {
self.windows.iter_mut().map(|(&id, window)| (id, window))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_window_manager_creation() {
let graphics =
GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
let manager = WindowManager::new(graphics.clone());
assert_eq!(manager.window_count(), 0);
assert_eq!(
manager.graphics().as_ref() as *const _,
graphics.as_ref() as *const _
);
}
#[test]
fn test_window_manager_window_count() {
let graphics =
GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
let manager = WindowManager::new(graphics);
assert_eq!(manager.window_count(), 0);
}
#[test]
fn test_window_manager_window_ids_empty() {
let graphics =
GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
let manager = WindowManager::new(graphics);
assert_eq!(manager.window_ids().count(), 0);
}
}