use std::collections::HashMap; use ecs::extension::Collector as ExtensionCollector; use ecs::phase::{Phase, PRE_UPDATE as PRE_UPDATE_PHASE, START as START_PHASE}; use ecs::relationship::{ChildOf, Relationship}; use ecs::sole::Single; use ecs::{static_entity, Sole}; use crate::vector::Vec2; use crate::window::{Window, UPDATE_PHASE as WINDOW_UPDATE_PHASE}; mod reexports { pub use crate::window::{Key, KeyState}; } pub use reexports::*; static_entity!( SET_PREV_KEY_STATE_PHASE, ( Phase, >::new(*WINDOW_UPDATE_PHASE) ) ); #[derive(Debug, Sole)] pub struct Keys { map: HashMap, pending: Vec<(Key, KeyState)>, } impl Keys { #[must_use] pub fn new() -> Self { Self { map: Key::KEYS .iter() .map(|key| { ( *key, KeyData { state: KeyState::Released, prev_tick_state: KeyState::Released, }, ) }) .collect(), pending: Vec::with_capacity(Key::KEYS.len()), } } #[must_use] pub fn get_key_state(&self, key: Key) -> KeyState { let Some(key_data) = self.map.get(&key) else { unreachable!(); }; key_data.state } #[must_use] pub fn get_prev_key_state(&self, key: Key) -> KeyState { let Some(key_data) = self.map.get(&key) else { unreachable!(); }; key_data.prev_tick_state } pub fn set_key_state(&mut self, key: Key, new_key_state: KeyState) { let Some(key_data) = self.map.get_mut(&key) else { unreachable!(); }; key_data.state = new_key_state; } #[must_use] pub fn is_anything_pressed(&self) -> bool { self.map .values() .any(|key_data| matches!(key_data.state, KeyState::Pressed)) } } impl Default for Keys { fn default() -> Self { Self::new() } } #[derive(Debug, Default, Clone, Sole)] pub struct Cursor { pub position: Vec2, pub has_moved: bool, } #[derive(Debug, Clone, Sole)] pub struct CursorFlags { /// This flag is set in two situations: /// A: The window has just started /// B: The window has gained focus again after losing focus. /// /// This flag only lasts a single tick then it is cleared (at the beginning of the /// next tick). pub is_first_move: CursorFlag, } impl Default for CursorFlags { fn default() -> Self { Self { is_first_move: CursorFlag { flag: true, ..Default::default() }, } } } #[derive(Debug, Default, Clone)] pub struct CursorFlag { pub flag: bool, pub pending_clear: bool, } impl CursorFlag { pub fn clear(&mut self) { self.flag = false; self.pending_clear = false; } } /// Input extension. #[derive(Debug, Default)] pub struct Extension {} impl ecs::extension::Extension for Extension { fn collect(self, mut collector: ExtensionCollector<'_>) { collector.add_system(*START_PHASE, initialize); collector.add_system(*PRE_UPDATE_PHASE, maybe_clear_cursor_is_first_move); collector.add_system(*SET_PREV_KEY_STATE_PHASE, set_pending_key_states); collector.add_sole(Keys::default()).ok(); collector.add_sole(Cursor::default()).ok(); collector.add_sole(CursorFlags::default()).ok(); } } fn initialize( keys: Single, cursor: Single, cursor_flags: Single, window: Single, ) { let keys_weak_ref = keys.to_weak_ref(); window.set_key_callback(move |key, _scancode, key_state, _modifiers| { let keys_ref = keys_weak_ref.access().expect("No world"); let mut keys = keys_ref.to_single(); keys.pending.push((key, key_state)); }); let cursor_weak_ref = cursor.to_weak_ref(); window.set_cursor_pos_callback(move |cursor_position| { let cursor_ref = cursor_weak_ref.access().expect("No world"); let mut cursor = cursor_ref.to_single(); cursor.position = Vec2 { x: cursor_position.x, y: cursor_position.y, }; cursor.has_moved = true; }); let cursor_flags_weak_ref = cursor_flags.to_weak_ref(); window.set_focus_callback(move |is_focused| { tracing::trace!("Window is focused: {is_focused}"); let cursor_flags_ref = cursor_flags_weak_ref.access().expect("No world"); cursor_flags_ref.to_single().is_first_move.flag = is_focused; }); } fn maybe_clear_cursor_is_first_move( cursor: Single, mut cursor_flags: Single, ) { if cursor_flags.is_first_move.pending_clear { tracing::trace!("Clearing is_first_move"); // This flag was set for the whole previous tick so it can be cleared now cursor_flags.is_first_move.clear(); return; } if cursor.has_moved && cursor_flags.is_first_move.flag { tracing::trace!("Setting flag to clear is_first_move next tick"); // Make this system clear is_first_move the next time it runs cursor_flags.is_first_move.pending_clear = true; } } fn set_pending_key_states(mut keys: Single) { let Keys { map, pending } = &mut *keys; for key_data in map.values_mut() { key_data.prev_tick_state = key_data.state; } for (key, key_state) in pending { let Some(key_data) = map.get_mut(key) else { unreachable!(); }; key_data.state = *key_state; } } #[derive(Debug)] struct KeyData { state: KeyState, prev_tick_state: KeyState, }