Something went wrong. Try again.
Homebrew 4D Game Engine In Rust, inspired by Engine4D
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153//! Window management system//!//! Handles window creation, cursor capture/release, fullscreen toggle, and title updates.
use crate::config::WindowConfig;use std::sync::Arc;use winit::{ event_loop::ActiveEventLoop, window::{CursorGrabMode, Fullscreen, Window},};
/// Manages the application window and cursor statepub struct WindowSystem { window: Arc<Window>, cursor_captured: bool, base_title: String,}
impl WindowSystem { /// Create window from config pub fn create( event_loop: &ActiveEventLoop, config: &WindowConfig, ) -> Result<Self, WindowError> { let mut attrs = Window::default_attributes() .with_title(&config.title) .with_inner_size(winit::dpi::LogicalSize::new(config.width, config.height));
if config.fullscreen { attrs = attrs.with_fullscreen(Some(Fullscreen::Borderless(None))); }
let window = Arc::new( event_loop .create_window(attrs) .map_err(|e| WindowError::CreationFailed(e.to_string()))?, );
Ok(Self { window, cursor_captured: false, base_title: config.title.clone(), }) }
/// Get window reference (for RenderContext creation) pub fn window(&self) -> &Arc<Window> { &self.window }
/// Check if cursor is captured pub fn is_cursor_captured(&self) -> bool { self.cursor_captured }
/// Capture cursor for FPS-style controls pub fn capture_cursor(&mut self) -> bool { let grab_result = self .window .set_cursor_grab(CursorGrabMode::Locked) .or_else(|_| self.window.set_cursor_grab(CursorGrabMode::Confined));
if grab_result.is_ok() { self.window.set_cursor_visible(false); self.cursor_captured = true; log::info!("Cursor captured - Escape to release"); true } else { log::warn!("Failed to capture cursor"); false } }
/// Release cursor pub fn release_cursor(&mut self) { let _ = self.window.set_cursor_grab(CursorGrabMode::None); self.window.set_cursor_visible(true); self.cursor_captured = false; log::info!("Cursor released - click to capture"); }
/// Toggle fullscreen mode pub fn toggle_fullscreen(&self) { let new_fullscreen = if self.window.fullscreen().is_some() { None } else { Some(Fullscreen::Borderless(None)) }; self.window.set_fullscreen(new_fullscreen); }
/// Update window title with position/state info pub fn update_title(&self, pos: [f32; 4], slice_w: f32) { let title = if self.cursor_captured { format!( "{} - ({:.1}, {:.1}, {:.1}, {:.1}) W:{:.2} [Esc to release]", self.base_title, pos[0], pos[1], pos[2], pos[3], slice_w ) } else { format!( "{} - ({:.1}, {:.1}, {:.1}, {:.1}) W:{:.2} [Click to capture]", self.base_title, pos[0], pos[1], pos[2], pos[3], slice_w ) }; self.window.set_title(&title); }
/// Request a redraw pub fn request_redraw(&self) { self.window.request_redraw(); }}
#[derive(Debug)]pub enum WindowError { CreationFailed(String),}
impl std::fmt::Display for WindowError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { WindowError::CreationFailed(msg) => write!(f, "Window creation failed: {}", msg), } }}
impl std::error::Error for WindowError {}
#[cfg(test)]mod tests { #[test] fn test_title_formatting_captured() { // Test title format when cursor is captured // Note: Can't test actual window without event loop let pos = [1.0, 2.0, 3.0, 4.0]; let title = format!( "Test - ({:.1}, {:.1}, {:.1}, {:.1}) W:{:.2} [Esc to release]", pos[0], pos[1], pos[2], pos[3], 0.5 ); assert!(title.contains("Esc to release")); }
#[test] fn test_title_formatting_released() { let pos = [1.0, 2.0, 3.0, 4.0]; let title = format!( "Test - ({:.1}, {:.1}, {:.1}, {:.1}) W:{:.2} [Click to capture]", pos[0], pos[1], pos[2], pos[3], 0.5 ); assert!(title.contains("Click to capture")); }}