cotis-interactivity 0.1.0-alpha

Higher-level input managers for Cotis applications
Documentation
//! Keyboard edge detection and text input buffering.
//!
//! [`KeyboardManager`] turns per-frame key state from
//! [`cotis_utils::interactivity::keyboard::SimpleKeyboardProvider`] into edge
//! signals (press, release, hold). [`TextManager`] accumulates typed characters
//! from [`cotis_utils::interactivity::keyboard::SimpleTextProvider`].

use crate::utils::UpdatableSignal;
use cotis_utils::interactivity::keyboard::{
    KeyboardKey, SimpleKeyboardProvider, SimpleTextProvider,
};
use std::collections::{HashMap, HashSet, VecDeque};

/// Tracks keyboard key state and edge transitions across frames.
///
/// Call [`update`](Self::update) each frame with a keyboard provider, then query
/// edge methods such as [`is_key_pressed`](Self::is_key_pressed).
///
/// # Examples
///
/// ```rust,ignore
/// use cotis_interactivity::keyboard::KeyboardManager;
/// use cotis_utils::interactivity::keyboard::KeyboardKey;
///
/// // let mut keyboard = KeyboardManager::new();
/// // keyboard.update(app.render_borrow());
/// // if keyboard.is_key_pressed(KeyboardKey::KeyEscape) { ... }
/// ```
pub struct KeyboardManager {
    keys: HashMap<KeyboardKey, UpdatableSignal>,
    latest_key: Option<KeyboardKey>,
}

impl Default for KeyboardManager {
    fn default() -> Self {
        Self::new()
    }
}

impl KeyboardManager {
    /// Creates an empty keyboard manager.
    pub fn new() -> KeyboardManager {
        Self {
            keys: Default::default(),
            latest_key: None,
        }
    }

    /// Syncs tracked keys from `provider`'s current pressed-key set.
    ///
    /// Keys that appear in the provider for the first time produce a rising edge
    /// on the frame they are first seen.
    pub fn update(&mut self, provider: &dyn SimpleKeyboardProvider) {
        let keys = provider.get_pressed_keys();
        self.latest_key = keys.last().cloned();
        let keys: HashSet<_> = keys.into_iter().collect();
        for (key, signal) in self.keys.iter_mut() {
            if !keys.contains(key) {
                signal.update(false);
            }
        }
        for key in keys {
            if let Some(signal) = self.keys.get_mut(&key) {
                signal.update(true);
            } else {
                let mut signal = UpdatableSignal::new(false);
                signal.update(true);
                self.keys.insert(key, signal);
            }
        }
    }

    /// Returns `true` on the frame a key is first pressed (rising edge).
    pub fn is_key_pressed(&self, key: KeyboardKey) -> bool {
        if let Some(signal) = self.keys.get(&key) {
            signal.rising_edge()
        } else {
            false
        }
    }

    /// Returns `true` when a key has been held for two or more consecutive frames.
    ///
    /// # Note
    ///
    /// This is **not** operating-system key-repeat timing. It detects sustained
    /// holds via an internal two-frame threshold (`is_on_long`).
    pub fn is_key_pressed_repeat(&self, key: KeyboardKey) -> bool {
        if let Some(signal) = self.keys.get(&key) {
            signal.is_on_long()
        } else {
            false
        }
    }

    /// Returns `true` while a key is currently held down.
    pub fn is_key_down(&self, key: KeyboardKey) -> bool {
        if let Some(signal) = self.keys.get(&key) {
            signal.is_on()
        } else {
            false
        }
    }

    /// Returns `true` while a key is not held down.
    pub fn is_key_up(&self, key: KeyboardKey) -> bool {
        !self.is_key_down(key)
    }

    /// Returns `true` on the frame a key is released (falling edge).
    pub fn is_key_released(&self, key: KeyboardKey) -> bool {
        if let Some(signal) = self.keys.get(&key) {
            signal.falling_edge()
        } else {
            false
        }
    }

    /// Returns the most recently pressed key according to provider iteration order.
    ///
    /// The return value reflects the last key in `get_pressed_keys()` from the
    /// most recent [`update`](Self::update) call.
    pub fn get_key_pressed(&mut self) -> Option<KeyboardKey> {
        self.latest_key
    }
}

/// FIFO buffer for typed characters from a text provider.
///
/// Call [`update`](Self::update) each frame to append new characters, then
/// consume them with [`pop`](Self::pop) or inspect with [`peek`](Self::peek).
pub struct TextManager {
    buffer: VecDeque<char>,
}

impl Default for TextManager {
    fn default() -> Self {
        Self::new()
    }
}

impl TextManager {
    /// Creates an empty text buffer.
    pub fn new() -> TextManager {
        Self {
            buffer: Default::default(),
        }
    }

    /// Appends characters from `provider` to the end of the buffer.
    pub fn update(&mut self, provider: &dyn SimpleTextProvider) {
        for c in provider.get_pressed_chars() {
            self.buffer.push_back(c);
        }
    }

    /// Discards all buffered characters.
    pub fn clear(&mut self) {
        self.buffer.clear();
    }

    /// Removes and returns the oldest buffered character.
    pub fn pop(&mut self) -> Option<char> {
        self.buffer.pop_front()
    }

    /// Returns a reference to the oldest buffered character without removing it.
    pub fn peek(&mut self) -> Option<&char> {
        self.buffer.front()
    }
}