mirror of
https://github.com/alexpasmantier/television.git
synced 2025-07-29 14:21:43 +00:00
docs(bindings): added docs
This commit is contained in:
parent
78cc303003
commit
bf3a22a7cf
@ -117,16 +117,78 @@ pub enum Action {
|
|||||||
MouseClickAt(u16, u16),
|
MouseClickAt(u16, u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Container for one or more actions that can be executed together.
|
||||||
|
///
|
||||||
|
/// This enum enables binding single keys to multiple actions, allowing for
|
||||||
|
/// complex behaviors triggered by a single key press. It supports both
|
||||||
|
/// single action bindings (for backward compatibility) and multiple action
|
||||||
|
/// sequences.
|
||||||
|
///
|
||||||
|
/// # Variants
|
||||||
|
///
|
||||||
|
/// - `Single(Action)` - A single action binding
|
||||||
|
/// - `Multiple(Vec<Action>)` - Multiple actions executed in sequence
|
||||||
|
///
|
||||||
|
/// # Configuration Examples
|
||||||
|
///
|
||||||
|
/// ```toml
|
||||||
|
/// # Single action (traditional)
|
||||||
|
/// esc = "quit"
|
||||||
|
///
|
||||||
|
/// # Multiple actions (new feature)
|
||||||
|
/// "ctrl-r" = ["reload_source", "copy_entry_to_clipboard"]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// // Single action
|
||||||
|
/// let single = Actions::Single(Action::Quit);
|
||||||
|
/// assert_eq!(single.as_slice(), &[Action::Quit]);
|
||||||
|
///
|
||||||
|
/// // Multiple actions
|
||||||
|
/// let multiple = Actions::Multiple(vec![Action::ReloadSource, Action::Quit]);
|
||||||
|
/// assert_eq!(multiple.as_slice(), &[Action::ReloadSource, Action::Quit]);
|
||||||
|
///
|
||||||
|
/// // Convert to vector for execution
|
||||||
|
/// let actions_vec = multiple.into_vec();
|
||||||
|
/// assert_eq!(actions_vec, vec![Action::ReloadSource, Action::Quit]);
|
||||||
|
/// ```
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, PartialOrd, Ord,
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, PartialOrd, Ord,
|
||||||
)]
|
)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum Actions {
|
pub enum Actions {
|
||||||
|
/// A single action binding
|
||||||
Single(Action),
|
Single(Action),
|
||||||
|
/// Multiple actions executed in sequence
|
||||||
Multiple(Vec<Action>),
|
Multiple(Vec<Action>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actions {
|
impl Actions {
|
||||||
|
/// Converts the `Actions` into a `Vec<Action>` for execution.
|
||||||
|
///
|
||||||
|
/// This method consumes the `Actions` and returns a vector containing
|
||||||
|
/// all actions to be executed. For `Single`, it returns a vector with
|
||||||
|
/// one element. For `Multiple`, it returns the contained vector.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A `Vec<Action>` containing all actions to execute.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let single = Actions::Single(Action::Quit);
|
||||||
|
/// assert_eq!(single.into_vec(), vec![Action::Quit]);
|
||||||
|
///
|
||||||
|
/// let multiple = Actions::Multiple(vec![Action::ReloadSource, Action::Quit]);
|
||||||
|
/// assert_eq!(multiple.into_vec(), vec![Action::ReloadSource, Action::Quit]);
|
||||||
|
/// ```
|
||||||
pub fn into_vec(self) -> Vec<Action> {
|
pub fn into_vec(self) -> Vec<Action> {
|
||||||
match self {
|
match self {
|
||||||
Actions::Single(action) => vec![action],
|
Actions::Single(action) => vec![action],
|
||||||
@ -134,6 +196,26 @@ impl Actions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a slice view of the actions without consuming the `Actions`.
|
||||||
|
///
|
||||||
|
/// This method provides efficient access to the contained actions as a slice,
|
||||||
|
/// useful for iteration and inspection without taking ownership.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A `&[Action]` slice containing all actions.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let single = Actions::Single(Action::Quit);
|
||||||
|
/// assert_eq!(single.as_slice(), &[Action::Quit]);
|
||||||
|
///
|
||||||
|
/// let multiple = Actions::Multiple(vec![Action::ReloadSource, Action::Quit]);
|
||||||
|
/// assert_eq!(multiple.as_slice(), &[Action::ReloadSource, Action::Quit]);
|
||||||
|
/// ```
|
||||||
pub fn as_slice(&self) -> &[Action] {
|
pub fn as_slice(&self) -> &[Action] {
|
||||||
match self {
|
match self {
|
||||||
Actions::Single(action) => std::slice::from_ref(action),
|
Actions::Single(action) => std::slice::from_ref(action),
|
||||||
@ -143,12 +225,54 @@ impl Actions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<Action> for Actions {
|
impl From<Action> for Actions {
|
||||||
|
/// Converts a single `Action` into `Actions::Single`.
|
||||||
|
///
|
||||||
|
/// This conversion allows seamless use of single actions where
|
||||||
|
/// `Actions` is expected, maintaining backward compatibility.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let actions: Actions = Action::Quit.into();
|
||||||
|
/// assert_eq!(actions, Actions::Single(Action::Quit));
|
||||||
|
/// ```
|
||||||
fn from(action: Action) -> Self {
|
fn from(action: Action) -> Self {
|
||||||
Actions::Single(action)
|
Actions::Single(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<Action>> for Actions {
|
impl From<Vec<Action>> for Actions {
|
||||||
|
/// Converts a `Vec<Action>` into `Actions`.
|
||||||
|
///
|
||||||
|
/// This conversion optimizes single-element vectors into `Actions::Single`
|
||||||
|
/// for efficiency, while multi-element vectors become `Actions::Multiple`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `actions` - Vector of actions to convert
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// - `Actions::Single` if the vector has exactly one element
|
||||||
|
/// - `Actions::Multiple` if the vector has zero or multiple elements
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// // Single element becomes Single
|
||||||
|
/// let single_vec = vec![Action::Quit];
|
||||||
|
/// let actions: Actions = single_vec.into();
|
||||||
|
/// assert_eq!(actions, Actions::Single(Action::Quit));
|
||||||
|
///
|
||||||
|
/// // Multiple elements become Multiple
|
||||||
|
/// let multi_vec = vec![Action::ReloadSource, Action::Quit];
|
||||||
|
/// let actions: Actions = multi_vec.into();
|
||||||
|
/// assert!(matches!(actions, Actions::Multiple(_)));
|
||||||
|
/// ```
|
||||||
fn from(actions: Vec<Action>) -> Self {
|
fn from(actions: Vec<Action>) -> Self {
|
||||||
if actions.len() == 1 {
|
if actions.len() == 1 {
|
||||||
Actions::Single(actions.into_iter().next().unwrap())
|
Actions::Single(actions.into_iter().next().unwrap())
|
||||||
@ -159,6 +283,25 @@ impl From<Vec<Action>> for Actions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Action {
|
impl Display for Action {
|
||||||
|
/// Formats the action as its string representation for configuration files.
|
||||||
|
///
|
||||||
|
/// This implementation provides the `snake_case` string representation of each
|
||||||
|
/// action as used in TOML configuration files. The output matches the
|
||||||
|
/// `#[serde(rename_all = "snake_case")]` serialization format.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The `snake_case` string representation of the action.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// assert_eq!(Action::Quit.to_string(), "quit");
|
||||||
|
/// assert_eq!(Action::SelectNextEntry.to_string(), "select_next_entry");
|
||||||
|
/// assert_eq!(Action::TogglePreview.to_string(), "toggle_preview");
|
||||||
|
/// ```
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Action::AddInputChar(_) => write!(f, "add_input_char"),
|
Action::AddInputChar(_) => write!(f, "add_input_char"),
|
||||||
|
@ -34,6 +34,30 @@ impl Display for Binding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generic bindings structure that can map any key type to actions
|
/// Generic bindings structure that can map any key type to actions
|
||||||
|
/// Generic bindings structure that maps any key type to actions.
|
||||||
|
///
|
||||||
|
/// This is the core structure for storing key/event bindings in Television.
|
||||||
|
/// It provides a flexible mapping system that can work with different key types
|
||||||
|
/// (keyboard keys, mouse events, etc.) and supports serialization to/from TOML.
|
||||||
|
///
|
||||||
|
/// # Type Parameters
|
||||||
|
///
|
||||||
|
/// * `K` - The key type (must implement `Display`, `FromStr`, `Eq`, `Hash`)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::config::keybindings::{Bindings, KeyBindings};
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// // Create new empty bindings
|
||||||
|
/// let mut bindings = KeyBindings::new();
|
||||||
|
///
|
||||||
|
/// // Add a binding
|
||||||
|
/// bindings.insert(Key::Enter, Action::ConfirmSelection.into());
|
||||||
|
/// assert_eq!(bindings.len(), 1);
|
||||||
|
/// ```
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Bindings<K>
|
pub struct Bindings<K>
|
||||||
where
|
where
|
||||||
@ -53,6 +77,20 @@ where
|
|||||||
K: Display + FromStr + Eq + Hash,
|
K: Display + FromStr + Eq + Hash,
|
||||||
K::Err: Display,
|
K::Err: Display,
|
||||||
{
|
{
|
||||||
|
/// Creates a new empty bindings collection.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A new `Bindings` instance with no key mappings.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::config::keybindings::KeyBindings;
|
||||||
|
///
|
||||||
|
/// let bindings = KeyBindings::new();
|
||||||
|
/// assert!(bindings.is_empty());
|
||||||
|
/// ```
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Bindings {
|
Bindings {
|
||||||
bindings: FxHashMap::default(),
|
bindings: FxHashMap::default(),
|
||||||
@ -60,16 +98,83 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of keybindings that maps keys directly to actions.
|
/// A set of keybindings that maps keyboard keys directly to actions.
|
||||||
///
|
///
|
||||||
/// This struct represents the new architecture where keybindings are structured as
|
/// This type alias represents the primary keybinding system in Television, where
|
||||||
/// Key -> Action mappings in the configuration file. This eliminates the need for
|
/// keyboard keys are mapped directly to actions.
|
||||||
/// runtime inversion and provides better discoverability.
|
///
|
||||||
|
/// # Features
|
||||||
|
///
|
||||||
|
/// - Direct key-to-action mapping
|
||||||
|
/// - Support for single and multiple actions per key
|
||||||
|
/// - Key unbinding support (setting to `false`)
|
||||||
|
/// - Modifier key combinations (Ctrl, Alt, Shift, Super/Cmd)
|
||||||
|
///
|
||||||
|
/// # Configuration Format
|
||||||
|
///
|
||||||
|
/// ```toml
|
||||||
|
/// # Single action
|
||||||
|
/// esc = "quit"
|
||||||
|
/// enter = "confirm_selection"
|
||||||
|
///
|
||||||
|
/// # Multiple actions
|
||||||
|
/// "ctrl-s" = ["reload_source", "copy_entry_to_clipboard"]
|
||||||
|
///
|
||||||
|
/// # Unbind a key
|
||||||
|
/// "ctrl-c" = false
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::config::keybindings::KeyBindings;
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// let mut bindings = KeyBindings::new();
|
||||||
|
/// bindings.insert(Key::Enter, Action::ConfirmSelection.into());
|
||||||
|
/// bindings.insert(Key::Esc, Action::Quit.into());
|
||||||
|
/// assert_eq!(bindings.len(), 2);
|
||||||
|
/// ```
|
||||||
pub type KeyBindings = Bindings<Key>;
|
pub type KeyBindings = Bindings<Key>;
|
||||||
|
|
||||||
|
/// Types of events that can be bound to actions.
|
||||||
|
///
|
||||||
|
/// This enum defines the various non-keyboard events that can trigger actions
|
||||||
|
/// in Television, such as mouse events and terminal resize events.
|
||||||
|
///
|
||||||
|
/// # Variants
|
||||||
|
///
|
||||||
|
/// - `MouseClick` - Mouse button click events
|
||||||
|
/// - `MouseScrollUp` - Mouse wheel scroll up
|
||||||
|
/// - `MouseScrollDown` - Mouse wheel scroll down
|
||||||
|
/// - `Resize` - Terminal window resize events
|
||||||
|
/// - `Custom(String)` - Custom event types for extensibility
|
||||||
|
///
|
||||||
|
/// # Configuration
|
||||||
|
///
|
||||||
|
/// Events use kebab-case naming in TOML configuration:
|
||||||
|
///
|
||||||
|
/// ```toml
|
||||||
|
/// mouse-click = "confirm_selection"
|
||||||
|
/// mouse-scroll-up = "scroll_preview_up"
|
||||||
|
/// resize = "refresh_layout"
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::config::keybindings::EventType;
|
||||||
|
/// use std::str::FromStr;
|
||||||
|
///
|
||||||
|
/// let event = EventType::from_str("mouse-click").unwrap();
|
||||||
|
/// assert_eq!(event, EventType::MouseClick);
|
||||||
|
///
|
||||||
|
/// let custom = EventType::Custom("my-event".to_string());
|
||||||
|
/// assert_eq!(custom.to_string(), "my-event");
|
||||||
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
/// Types of events that can be bound to actions
|
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
MouseClick,
|
MouseClick,
|
||||||
MouseScrollUp,
|
MouseScrollUp,
|
||||||
@ -114,7 +219,36 @@ impl Display for EventType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of event bindings that maps events to actions.
|
/// A set of event bindings that maps non-keyboard events to actions.
|
||||||
|
///
|
||||||
|
/// This type alias provides bindings for mouse events, resize events,
|
||||||
|
/// and other non-keyboard interactions. It uses the same underlying
|
||||||
|
/// `Bindings` structure as `KeyBindings` but for `EventType` instead of `Key`.
|
||||||
|
///
|
||||||
|
/// # Default Bindings
|
||||||
|
///
|
||||||
|
/// - `mouse-scroll-up` → `scroll_preview_up`
|
||||||
|
/// - `mouse-scroll-down` → `scroll_preview_down`
|
||||||
|
///
|
||||||
|
/// # Configuration Example
|
||||||
|
///
|
||||||
|
/// ```toml
|
||||||
|
/// [event-bindings]
|
||||||
|
/// mouse-click = "confirm_selection"
|
||||||
|
/// mouse-scroll-up = "scroll_preview_up"
|
||||||
|
/// resize = "refresh_layout"
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::config::keybindings::{EventBindings, EventType};
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// let mut bindings = EventBindings::new();
|
||||||
|
/// bindings.insert(EventType::MouseClick, Action::ConfirmSelection.into());
|
||||||
|
/// assert_eq!(bindings.len(), 1);
|
||||||
|
/// ```
|
||||||
pub type EventBindings = Bindings<EventType>;
|
pub type EventBindings = Bindings<EventType>;
|
||||||
|
|
||||||
impl<K, I> From<I> for Bindings<K>
|
impl<K, I> From<I> for Bindings<K>
|
||||||
@ -168,7 +302,39 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic merge function for bindings
|
/// Merges two binding collections, with new bindings taking precedence.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `bindings` - The base bindings collection (will be consumed)
|
||||||
|
/// * `new_bindings` - The new bindings to merge in (higher precedence)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A new `Bindings` collection with merged key mappings.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::config::keybindings::{KeyBindings, merge_bindings};
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// let base = KeyBindings::from(vec![
|
||||||
|
/// (Key::Enter, Action::ConfirmSelection),
|
||||||
|
/// (Key::Esc, Action::Quit),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// let custom = KeyBindings::from(vec![
|
||||||
|
/// (Key::Esc, Action::NoOp), // Override quit with no-op
|
||||||
|
/// (Key::Tab, Action::ToggleSelectionDown), // Add new binding
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// let merged = merge_bindings(base, &custom);
|
||||||
|
/// assert_eq!(merged.get(&Key::Enter), Some(&Action::ConfirmSelection.into()));
|
||||||
|
/// assert_eq!(merged.get(&Key::Esc), Some(&Action::NoOp.into()));
|
||||||
|
/// assert_eq!(merged.get(&Key::Tab), Some(&Action::ToggleSelectionDown.into()));
|
||||||
|
/// ```
|
||||||
pub fn merge_bindings<K>(
|
pub fn merge_bindings<K>(
|
||||||
mut bindings: Bindings<K>,
|
mut bindings: Bindings<K>,
|
||||||
new_bindings: &Bindings<K>,
|
new_bindings: &Bindings<K>,
|
||||||
@ -256,12 +422,69 @@ impl Default for Bindings<EventType> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a string representation of a key event into a `KeyEvent`.
|
||||||
|
///
|
||||||
|
/// This function converts human-readable key descriptions (like "ctrl-a", "alt-enter")
|
||||||
|
/// into crossterm `KeyEvent` structures.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `raw` - String representation of the key (e.g., "ctrl-a", "shift-f1", "esc")
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(KeyEvent)` - Successfully parsed key event
|
||||||
|
/// * `Err(String)` - Parse error with description
|
||||||
|
///
|
||||||
|
/// # Supported Modifiers
|
||||||
|
///
|
||||||
|
/// - `ctrl-` - Control key
|
||||||
|
/// - `alt-` - Alt key
|
||||||
|
/// - `shift-` - Shift key
|
||||||
|
/// - `cmd-` - Command key (macOS)
|
||||||
|
/// - `super-` - Super key (Linux/Windows)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::config::keybindings::parse_key_event;
|
||||||
|
/// use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
|
///
|
||||||
|
/// let event = parse_key_event("ctrl-a").unwrap();
|
||||||
|
/// assert_eq!(event.code, KeyCode::Char('a'));
|
||||||
|
/// assert_eq!(event.modifiers, KeyModifiers::CONTROL);
|
||||||
|
///
|
||||||
|
/// let event = parse_key_event("alt-enter").unwrap();
|
||||||
|
/// assert_eq!(event.code, KeyCode::Enter);
|
||||||
|
/// assert_eq!(event.modifiers, KeyModifiers::ALT);
|
||||||
|
/// ```
|
||||||
pub fn parse_key_event(raw: &str) -> anyhow::Result<KeyEvent, String> {
|
pub fn parse_key_event(raw: &str) -> anyhow::Result<KeyEvent, String> {
|
||||||
let raw_lower = raw.to_ascii_lowercase();
|
let raw_lower = raw.to_ascii_lowercase();
|
||||||
let (remaining, modifiers) = extract_modifiers(&raw_lower);
|
let (remaining, modifiers) = extract_modifiers(&raw_lower);
|
||||||
parse_key_code_with_modifiers(remaining, modifiers)
|
parse_key_code_with_modifiers(remaining, modifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts modifier keys from a raw key string.
|
||||||
|
///
|
||||||
|
/// This helper function parses modifier prefixes (ctrl-, alt-, shift-, etc.)
|
||||||
|
/// from a key string and returns the remaining key part along with the
|
||||||
|
/// extracted modifiers as a `KeyModifiers` bitfield.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `raw` - The raw key string (already lowercased)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A tuple of (`remaining_key_string`, `extracted_modifiers`)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// let (key, mods) = extract_modifiers("ctrl-alt-a");
|
||||||
|
/// assert_eq!(key, "a");
|
||||||
|
/// assert!(mods.contains(KeyModifiers::CONTROL | KeyModifiers::ALT));
|
||||||
|
/// ```
|
||||||
fn extract_modifiers(raw: &str) -> (&str, KeyModifiers) {
|
fn extract_modifiers(raw: &str) -> (&str, KeyModifiers) {
|
||||||
let mut modifiers = KeyModifiers::empty();
|
let mut modifiers = KeyModifiers::empty();
|
||||||
let mut current = raw;
|
let mut current = raw;
|
||||||
@ -298,6 +521,28 @@ fn extract_modifiers(raw: &str) -> (&str, KeyModifiers) {
|
|||||||
(current, modifiers)
|
(current, modifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a key code string with pre-extracted modifiers into a `KeyEvent`.
|
||||||
|
///
|
||||||
|
/// This function handles the actual key code parsing after modifiers have
|
||||||
|
/// been extracted. It supports named keys (like "esc", "enter") and
|
||||||
|
/// single character keys.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `raw` - The key string with modifiers already removed
|
||||||
|
/// * `modifiers` - Pre-extracted modifier keys
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(KeyEvent)` - Successfully parsed key event
|
||||||
|
/// * `Err(String)` - Parse error for unrecognized keys
|
||||||
|
///
|
||||||
|
/// # Supported Keys
|
||||||
|
///
|
||||||
|
/// - Named keys: esc, enter, left, right, up, down, home, end, etc.
|
||||||
|
/// - Function keys: f1-f12
|
||||||
|
/// - Special keys: space, tab, backspace, delete, etc.
|
||||||
|
/// - Single characters: a-z, 0-9, punctuation
|
||||||
fn parse_key_code_with_modifiers(
|
fn parse_key_code_with_modifiers(
|
||||||
raw: &str,
|
raw: &str,
|
||||||
mut modifiers: KeyModifiers,
|
mut modifiers: KeyModifiers,
|
||||||
@ -361,6 +606,32 @@ fn parse_key_code_with_modifiers(
|
|||||||
Ok(KeyEvent::new(c, modifiers))
|
Ok(KeyEvent::new(c, modifiers))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a `KeyEvent` back to its string representation.
|
||||||
|
///
|
||||||
|
/// This function performs the reverse operation of `parse_key_event`,
|
||||||
|
/// converting a crossterm `KeyEvent` back into a human-readable string
|
||||||
|
/// format that can be used in configuration files.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key_event` - The key event to convert
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// String representation of the key event (e.g., "ctrl-a", "alt-enter")
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::config::keybindings::key_event_to_string;
|
||||||
|
/// use crossterm::event::{KeyEvent, KeyCode, KeyModifiers};
|
||||||
|
///
|
||||||
|
/// let event = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
|
||||||
|
/// assert_eq!(key_event_to_string(&event), "ctrl-a");
|
||||||
|
///
|
||||||
|
/// let event = KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT);
|
||||||
|
/// assert_eq!(key_event_to_string(&event), "alt-enter");
|
||||||
|
/// ```
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn key_event_to_string(key_event: &KeyEvent) -> String {
|
pub fn key_event_to_string(key_event: &KeyEvent) -> String {
|
||||||
let char;
|
let char;
|
||||||
@ -456,11 +727,25 @@ impl FromStr for Key {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_key(raw: &str) -> anyhow::Result<Key, String> {
|
/// Generic serializer that converts any key type to string for TOML compatibility.
|
||||||
Key::from_str(raw)
|
///
|
||||||
}
|
/// This function enables serialization of the bindings `HashMap` to TOML format
|
||||||
|
/// by converting keys to their string representation using the `Display` trait.
|
||||||
/// Generic serializer that converts any key type to string for TOML compatibility
|
/// This is used internally by serde when serializing `Bindings` structs.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `bindings` - The bindings `HashMap` to serialize
|
||||||
|
/// * `serializer` - The serde serializer instance
|
||||||
|
///
|
||||||
|
/// # Type Parameters
|
||||||
|
///
|
||||||
|
/// * `K` - Key type that implements `Display`
|
||||||
|
/// * `S` - Serializer type
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Result of the serialization operation
|
||||||
fn serialize_bindings<K, S>(
|
fn serialize_bindings<K, S>(
|
||||||
bindings: &FxHashMap<K, Actions>,
|
bindings: &FxHashMap<K, Actions>,
|
||||||
serializer: S,
|
serializer: S,
|
||||||
@ -477,7 +762,30 @@ where
|
|||||||
map.end()
|
map.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic deserializer that parses string keys back to key enum
|
/// Generic deserializer that parses string keys back to key enum.
|
||||||
|
///
|
||||||
|
/// This function enables deserialization from TOML format by parsing string keys
|
||||||
|
/// back into their appropriate key types using the `FromStr` trait. It also handles
|
||||||
|
/// special cases like boolean values for key unbinding.
|
||||||
|
///
|
||||||
|
/// # Special Value Handling
|
||||||
|
///
|
||||||
|
/// - `false` - Binds the key to `Action::NoOp` (effectively unbinding)
|
||||||
|
/// - `true` - Ignores the binding (preserves existing or default binding)
|
||||||
|
/// - String/Array - Normal action binding
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `deserializer` - The serde deserializer instance
|
||||||
|
///
|
||||||
|
/// # Type Parameters
|
||||||
|
///
|
||||||
|
/// * `K` - Key type that implements `FromStr`, `Eq`, and `Hash`
|
||||||
|
/// * `D` - Deserializer type
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Result containing the parsed bindings `HashMap` or deserialization error
|
||||||
fn deserialize_bindings<'de, K, D>(
|
fn deserialize_bindings<'de, K, D>(
|
||||||
deserializer: D,
|
deserializer: D,
|
||||||
) -> Result<FxHashMap<K, Actions>, D::Error>
|
) -> Result<FxHashMap<K, Actions>, D::Error>
|
||||||
|
@ -15,7 +15,7 @@ use std::{
|
|||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
pub use keybindings::{
|
pub use keybindings::{
|
||||||
Binding, EventBindings, EventType, KeyBindings, merge_bindings, parse_key,
|
Binding, EventBindings, EventType, KeyBindings, merge_bindings,
|
||||||
};
|
};
|
||||||
pub use themes::Theme;
|
pub use themes::Theme;
|
||||||
pub use ui::UiConfig;
|
pub use ui::UiConfig;
|
||||||
@ -371,6 +371,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::str::FromStr;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -477,7 +478,7 @@ mod tests {
|
|||||||
|
|
||||||
default_config.shell_integration.keybindings.insert(
|
default_config.shell_integration.keybindings.insert(
|
||||||
"command_history".to_string(),
|
"command_history".to_string(),
|
||||||
Binding::SingleKey(parse_key("ctrl-h").unwrap()),
|
Binding::SingleKey(Key::from_str("ctrl-h").unwrap()),
|
||||||
);
|
);
|
||||||
default_config.shell_integration.merge_triggers();
|
default_config.shell_integration.merge_triggers();
|
||||||
|
|
||||||
@ -550,8 +551,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_shell_integration_keybindings_are_overwritten_by_user() {
|
fn test_shell_integration_keybindings_are_overwritten_by_user() {
|
||||||
use crate::config::parse_key;
|
|
||||||
|
|
||||||
let user_config = r#"
|
let user_config = r#"
|
||||||
[shell_integration.keybindings]
|
[shell_integration.keybindings]
|
||||||
"smart_autocomplete" = "ctrl-t"
|
"smart_autocomplete" = "ctrl-t"
|
||||||
@ -574,11 +573,11 @@ mod tests {
|
|||||||
let expected: rustc_hash::FxHashMap<String, Binding> = [
|
let expected: rustc_hash::FxHashMap<String, Binding> = [
|
||||||
(
|
(
|
||||||
"command_history".to_string(),
|
"command_history".to_string(),
|
||||||
Binding::SingleKey(parse_key("ctrl-[").unwrap()),
|
Binding::SingleKey(Key::from_str("ctrl-[").unwrap()),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"smart_autocomplete".to_string(),
|
"smart_autocomplete".to_string(),
|
||||||
Binding::SingleKey(parse_key("ctrl-t").unwrap()),
|
Binding::SingleKey(Key::from_str("ctrl-t").unwrap()),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
use std::{
|
|
||||||
fmt::Display,
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll as TaskPoll},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crossterm::event::{
|
use crossterm::event::{
|
||||||
KeyCode::{
|
KeyCode::{
|
||||||
BackTab, Backspace, Char, Delete, Down, End, Enter, Esc, F, Home,
|
BackTab, Backspace, Char, Delete, Down, End, Enter, Esc, F, Home,
|
||||||
@ -14,11 +6,17 @@ use crossterm::event::{
|
|||||||
KeyEvent, KeyEventKind, KeyModifiers, MouseEvent, MouseEventKind,
|
KeyEvent, KeyEventKind, KeyModifiers, MouseEvent, MouseEventKind,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
str::FromStr,
|
||||||
|
task::{Context, Poll as TaskPoll},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use tokio::{signal, sync::mpsc};
|
use tokio::{signal, sync::mpsc};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use crate::config::parse_key;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Event<I> {
|
pub enum Event<I> {
|
||||||
Closed,
|
Closed,
|
||||||
@ -70,19 +68,89 @@ pub enum Key {
|
|||||||
Tab,
|
Tab,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unified input event type that encompasses all possible inputs.
|
||||||
|
///
|
||||||
|
/// This enum provides a unified interface for handling different types of input
|
||||||
|
/// events in Television, including keyboard input, mouse events, terminal resize
|
||||||
|
/// events, and custom events. It enables the new binding system to map any
|
||||||
|
/// type of input to actions.
|
||||||
|
///
|
||||||
|
/// # Variants
|
||||||
|
///
|
||||||
|
/// - `Key(Key)` - Keyboard input events
|
||||||
|
/// - `Mouse(MouseInputEvent)` - Mouse events with position information
|
||||||
|
/// - `Resize(u16, u16)` - Terminal resize events with new dimensions
|
||||||
|
/// - `Custom(String)` - Custom events for extensibility
|
||||||
|
///
|
||||||
|
/// # Usage in Bindings
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::event::{InputEvent, Key, MouseInputEvent};
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
///
|
||||||
|
/// let input_map = InputMap::new();
|
||||||
|
///
|
||||||
|
/// // Handle keyboard input
|
||||||
|
/// let key_event = InputEvent::Key(Key::Enter);
|
||||||
|
/// let actions = input_map.get_actions_for_input(&key_event);
|
||||||
|
/// assert_eq!(actions, None); // No bindings in empty map
|
||||||
|
///
|
||||||
|
/// // Handle mouse input
|
||||||
|
/// let mouse_event = InputEvent::Mouse(MouseInputEvent {
|
||||||
|
/// kind: crossterm::event::MouseEventKind::Down(crossterm::event::MouseButton::Left),
|
||||||
|
/// position: (10, 5),
|
||||||
|
/// });
|
||||||
|
/// let actions = input_map.get_actions_for_input(&mouse_event);
|
||||||
|
/// assert_eq!(actions, None); // No bindings in empty map
|
||||||
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
/// Unified input event type that encompasses all possible inputs
|
|
||||||
pub enum InputEvent {
|
pub enum InputEvent {
|
||||||
|
/// Keyboard input event
|
||||||
Key(Key),
|
Key(Key),
|
||||||
|
/// Mouse event with position information
|
||||||
Mouse(MouseInputEvent),
|
Mouse(MouseInputEvent),
|
||||||
|
/// Terminal resize event with new dimensions (width, height)
|
||||||
Resize(u16, u16),
|
Resize(u16, u16),
|
||||||
|
/// Custom event for extensibility
|
||||||
Custom(String),
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mouse event with position information for input mapping.
|
||||||
|
///
|
||||||
|
/// This structure combines a mouse event type with its screen coordinates,
|
||||||
|
/// enabling position-aware mouse handling in the binding system. It provides
|
||||||
|
/// the information needed to map mouse events to appropriate actions.
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
///
|
||||||
|
/// - `kind` - The type of mouse event (click, scroll, etc.)
|
||||||
|
/// - `position` - Screen coordinates as (column, row) tuple
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::event::MouseInputEvent;
|
||||||
|
/// use crossterm::event::{MouseEventKind, MouseButton};
|
||||||
|
///
|
||||||
|
/// // Left mouse button click at position (10, 5)
|
||||||
|
/// let click_event = MouseInputEvent {
|
||||||
|
/// kind: MouseEventKind::Down(MouseButton::Left),
|
||||||
|
/// position: (10, 5),
|
||||||
|
/// };
|
||||||
|
/// assert_eq!(click_event.position, (10, 5));
|
||||||
|
///
|
||||||
|
/// // Mouse scroll up at position (20, 15)
|
||||||
|
/// let scroll_event = MouseInputEvent {
|
||||||
|
/// kind: MouseEventKind::ScrollUp,
|
||||||
|
/// position: (20, 15),
|
||||||
|
/// };
|
||||||
|
/// assert_eq!(scroll_event.position, (20, 15));
|
||||||
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
/// Mouse event with position information for input mapping
|
|
||||||
pub struct MouseInputEvent {
|
pub struct MouseInputEvent {
|
||||||
|
/// The type of mouse event (click, scroll, etc.)
|
||||||
pub kind: MouseEventKind,
|
pub kind: MouseEventKind,
|
||||||
|
/// Screen coordinates as (column, row)
|
||||||
pub position: (u16, u16),
|
pub position: (u16, u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +160,7 @@ impl<'de> Deserialize<'de> for Key {
|
|||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let s = String::deserialize(deserializer)?;
|
let s = String::deserialize(deserializer)?;
|
||||||
parse_key(&s).map_err(serde::de::Error::custom)
|
Key::from_str(&s).map_err(serde::de::Error::custom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,6 +338,39 @@ fn flush_resize_events(first_resize: (u16, u16)) -> ((u16, u16), (u16, u16)) {
|
|||||||
(first_resize, last_resize)
|
(first_resize, last_resize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a crossterm `KeyEvent` into Television's internal `Key` representation.
|
||||||
|
///
|
||||||
|
/// This function handles the conversion from crossterm's key event format into
|
||||||
|
/// Television's simplified key representation, applying modifier key combinations
|
||||||
|
/// and filtering out key release events.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `event` - The crossterm `KeyEvent` to convert
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The corresponding `Key` enum variant, or `Key::Null` for unsupported events
|
||||||
|
///
|
||||||
|
/// # Key Mapping
|
||||||
|
///
|
||||||
|
/// - Modifier combinations are mapped to specific variants (e.g., `Ctrl+a` → `Key::Ctrl('a')`)
|
||||||
|
/// - Key release events are ignored (return `Key::Null`)
|
||||||
|
/// - Special keys are mapped directly (e.g., `Enter` → `Key::Enter`)
|
||||||
|
/// - Function keys preserve their number (e.g., `F1` → `Key::F(1)`)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::event::{convert_raw_event_to_key, Key};
|
||||||
|
/// use crossterm::event::{KeyEvent, KeyCode, KeyModifiers, KeyEventKind};
|
||||||
|
///
|
||||||
|
/// let event = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
|
||||||
|
/// assert_eq!(convert_raw_event_to_key(event), Key::Ctrl('a'));
|
||||||
|
///
|
||||||
|
/// let event = KeyEvent::new(KeyCode::Enter, KeyModifiers::empty());
|
||||||
|
/// assert_eq!(convert_raw_event_to_key(event), Key::Enter);
|
||||||
|
/// ```
|
||||||
pub fn convert_raw_event_to_key(event: KeyEvent) -> Key {
|
pub fn convert_raw_event_to_key(event: KeyEvent) -> Key {
|
||||||
trace!("Raw event: {:?}", event);
|
trace!("Raw event: {:?}", event);
|
||||||
if event.kind == KeyEventKind::Release {
|
if event.kind == KeyEventKind::Release {
|
||||||
|
@ -6,27 +6,66 @@ use crate::{
|
|||||||
use crossterm::event::MouseEventKind;
|
use crossterm::event::MouseEventKind;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
|
||||||
/// An input map that handles both keyboard and non-keyboard input events.
|
/// An input map that handles both keyboard and non-keyboard input events.
|
||||||
///
|
///
|
||||||
/// This replaces the old Keymap structure and provides unified access to
|
/// This structure supports multiple-actions-per-key and handles various
|
||||||
/// both key bindings and event bindings through a single interface.
|
/// input types including keyboard keys, mouse events, resize events, and
|
||||||
|
/// custom events.
|
||||||
///
|
///
|
||||||
/// # Example:
|
/// # Fields
|
||||||
/// ```ignore
|
///
|
||||||
/// InputMap {
|
/// - `key_actions` - Maps keyboard keys to actions
|
||||||
/// Key::Char('j') => Action::MoveDown,
|
/// - `event_actions` - Maps non-keyboard events to actions
|
||||||
/// Key::Char('k') => Action::MoveUp,
|
///
|
||||||
/// EventType::MouseClick => Action::ConfirmSelection,
|
/// # Examples
|
||||||
/// }
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::event::{Key, InputEvent};
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
/// use television::config::{KeyBindings, EventType};
|
||||||
|
///
|
||||||
|
/// // Create from key bindings
|
||||||
|
/// let keybindings = KeyBindings::from(vec![
|
||||||
|
/// (Key::Char('j'), Action::SelectNextEntry),
|
||||||
|
/// (Key::Char('k'), Action::SelectPrevEntry),
|
||||||
|
/// ]);
|
||||||
|
/// let input_map: InputMap = (&keybindings).into();
|
||||||
|
///
|
||||||
|
/// // Query actions for input
|
||||||
|
/// let key_input = InputEvent::Key(Key::Char('j'));
|
||||||
|
/// let actions = input_map.get_actions_for_input(&key_input);
|
||||||
|
/// assert_eq!(actions, Some(vec![Action::SelectNextEntry]));
|
||||||
/// ```
|
/// ```
|
||||||
|
/// ```
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct InputMap {
|
pub struct InputMap {
|
||||||
|
/// Maps keyboard keys to their associated actions
|
||||||
pub key_actions: FxHashMap<Key, Actions>,
|
pub key_actions: FxHashMap<Key, Actions>,
|
||||||
|
/// Maps non-keyboard events to their associated actions
|
||||||
pub event_actions: FxHashMap<EventType, Actions>,
|
pub event_actions: FxHashMap<EventType, Actions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputMap {
|
impl InputMap {
|
||||||
/// Create a new empty `InputMap`
|
/// Creates a new empty `InputMap`.
|
||||||
|
///
|
||||||
|
/// Returns an empty input map with no key or event bindings.
|
||||||
|
/// Bindings can be added using the `merge` methods or by directly
|
||||||
|
/// manipulating the `key_actions` and `event_actions` fields.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A new empty `InputMap` instance.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
///
|
||||||
|
/// let input_map = InputMap::new();
|
||||||
|
/// assert!(input_map.key_actions.is_empty());
|
||||||
|
/// assert!(input_map.event_actions.is_empty());
|
||||||
|
/// ```
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
key_actions: FxHashMap::default(),
|
key_actions: FxHashMap::default(),
|
||||||
@ -34,12 +73,67 @@ impl InputMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the actions for a given key
|
/// Gets all actions bound to a specific key.
|
||||||
|
///
|
||||||
|
/// Returns a reference to the `Actions` (single or multiple) bound to
|
||||||
|
/// the given key, or `None` if no binding exists.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The key to look up
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// - `Some(&Actions)` - The actions bound to the key
|
||||||
|
/// - `None` - No binding exists for this key
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let mut input_map = InputMap::new();
|
||||||
|
/// input_map.key_actions.insert(Key::Enter, Actions::Single(Action::ConfirmSelection));
|
||||||
|
///
|
||||||
|
/// let actions = input_map.get_actions_for_key(&Key::Enter).unwrap();
|
||||||
|
/// assert_eq!(actions.as_slice(), &[Action::ConfirmSelection]);
|
||||||
|
/// ```
|
||||||
pub fn get_actions_for_key(&self, key: &Key) -> Option<&Actions> {
|
pub fn get_actions_for_key(&self, key: &Key) -> Option<&Actions> {
|
||||||
self.key_actions.get(key)
|
self.key_actions.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the actions for a given event type
|
/// Gets all actions bound to a specific event type.
|
||||||
|
///
|
||||||
|
/// Returns a reference to the `Actions` (single or multiple) bound to
|
||||||
|
/// the given event type, or `None` if no binding exists.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `event` - The event type to look up
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// - `Some(&Actions)` - The actions bound to the event
|
||||||
|
/// - `None` - No binding exists for this event type
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::config::EventType;
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let mut input_map = InputMap::new();
|
||||||
|
/// input_map.event_actions.insert(
|
||||||
|
/// EventType::MouseClick,
|
||||||
|
/// Actions::Single(Action::ConfirmSelection)
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// let actions = input_map.get_actions_for_event(&EventType::MouseClick).unwrap();
|
||||||
|
/// assert_eq!(actions.as_slice(), &[Action::ConfirmSelection]);
|
||||||
|
/// ```
|
||||||
pub fn get_actions_for_event(
|
pub fn get_actions_for_event(
|
||||||
&self,
|
&self,
|
||||||
event: &EventType,
|
event: &EventType,
|
||||||
@ -47,7 +141,37 @@ impl InputMap {
|
|||||||
self.event_actions.get(event)
|
self.event_actions.get(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the action for a given key (backward compatibility)
|
/// Gets the first action bound to a specific key (backward compatibility).
|
||||||
|
///
|
||||||
|
/// This method provides backward compatibility with the old single-action
|
||||||
|
/// binding system. For keys with multiple actions, it returns only the
|
||||||
|
/// first action. Use `get_actions_for_key()` to get all actions.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `key` - The key to look up
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// - `Some(Action)` - The first action bound to the key
|
||||||
|
/// - `None` - No binding exists for this key
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let mut input_map = InputMap::new();
|
||||||
|
/// input_map.key_actions.insert(
|
||||||
|
/// Key::Ctrl('r'),
|
||||||
|
/// Actions::Multiple(vec![Action::ReloadSource, Action::ClearScreen])
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // Returns only the first action
|
||||||
|
/// assert_eq!(input_map.get_action_for_key(&Key::Ctrl('r')), Some(Action::ReloadSource));
|
||||||
|
/// ```
|
||||||
pub fn get_action_for_key(&self, key: &Key) -> Option<Action> {
|
pub fn get_action_for_key(&self, key: &Key) -> Option<Action> {
|
||||||
self.key_actions.get(key).and_then(|actions| match actions {
|
self.key_actions.get(key).and_then(|actions| match actions {
|
||||||
Actions::Single(action) => Some(action.clone()),
|
Actions::Single(action) => Some(action.clone()),
|
||||||
@ -55,7 +179,40 @@ impl InputMap {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the action for a given event type (backward compatibility)
|
/// Gets the first action bound to a specific event type (backward compatibility).
|
||||||
|
///
|
||||||
|
/// This method provides backward compatibility with the old single-action
|
||||||
|
/// binding system. For events with multiple actions, it returns only the
|
||||||
|
/// first action. Use `get_actions_for_event()` to get all actions.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `event` - The event type to look up
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// - `Some(Action)` - The first action bound to the event
|
||||||
|
/// - `None` - No binding exists for this event type
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::config::EventType;
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let mut input_map = InputMap::new();
|
||||||
|
/// input_map.event_actions.insert(
|
||||||
|
/// EventType::MouseClick,
|
||||||
|
/// Actions::Multiple(vec![Action::ConfirmSelection, Action::TogglePreview])
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // Returns only the first action
|
||||||
|
/// assert_eq!(
|
||||||
|
/// input_map.get_action_for_event(&EventType::MouseClick),
|
||||||
|
/// Some(Action::ConfirmSelection)
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
pub fn get_action_for_event(&self, event: &EventType) -> Option<Action> {
|
pub fn get_action_for_event(&self, event: &EventType) -> Option<Action> {
|
||||||
self.event_actions
|
self.event_actions
|
||||||
.get(event)
|
.get(event)
|
||||||
@ -65,7 +222,47 @@ impl InputMap {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all actions for any input event
|
/// Gets all actions for any input event.
|
||||||
|
///
|
||||||
|
/// This is the primary method for querying actions in the new input system.
|
||||||
|
/// It handles all types of input events and returns a vector of all actions
|
||||||
|
/// that should be executed in response to the input.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `input` - The input event to look up
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// - `Some(Vec<Action>)` - All actions bound to the input event
|
||||||
|
/// - `None` - No binding exists for this input
|
||||||
|
///
|
||||||
|
/// # Supported Input Types
|
||||||
|
///
|
||||||
|
/// - `InputEvent::Key` - Keyboard input
|
||||||
|
/// - `InputEvent::Mouse` - Mouse clicks and scrolling
|
||||||
|
/// - `InputEvent::Resize` - Terminal resize events
|
||||||
|
/// - `InputEvent::Custom` - Custom event types
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::event::{Key, InputEvent, MouseInputEvent};
|
||||||
|
/// use television::config::EventType;
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
/// use crossterm::event::MouseEventKind;
|
||||||
|
///
|
||||||
|
/// let mut input_map = InputMap::new();
|
||||||
|
/// input_map.key_actions.insert(
|
||||||
|
/// Key::Ctrl('s'),
|
||||||
|
/// Actions::Multiple(vec![Action::ReloadSource, Action::CopyEntryToClipboard])
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// let key_input = InputEvent::Key(Key::Ctrl('s'));
|
||||||
|
/// let actions = input_map.get_actions_for_input(&key_input).unwrap();
|
||||||
|
/// assert_eq!(actions, vec![Action::ReloadSource, Action::CopyEntryToClipboard]);
|
||||||
|
/// ```
|
||||||
pub fn get_actions_for_input(
|
pub fn get_actions_for_input(
|
||||||
&self,
|
&self,
|
||||||
input: &InputEvent,
|
input: &InputEvent,
|
||||||
@ -88,7 +285,47 @@ impl InputMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an action for any input event (backward compatibility)
|
/// Gets the first action for any input event (backward compatibility).
|
||||||
|
///
|
||||||
|
/// This method provides backward compatibility with the old single-action
|
||||||
|
/// system. It handles all input types and returns only the first action
|
||||||
|
/// bound to the input. Use `get_actions_for_input()` to get all actions.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `input` - The input event to look up
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// - `Some(Action)` - The first action bound to the input
|
||||||
|
/// - `None` - No binding exists for this input
|
||||||
|
///
|
||||||
|
/// # Supported Input Types
|
||||||
|
///
|
||||||
|
/// - `InputEvent::Key` - Returns first action for the key
|
||||||
|
/// - `InputEvent::Mouse` - Maps to appropriate event type and returns first action
|
||||||
|
/// - `InputEvent::Resize` - Returns first action for resize events
|
||||||
|
/// - `InputEvent::Custom` - Returns first action for the custom event
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::event::{Key, InputEvent};
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let mut input_map = InputMap::new();
|
||||||
|
/// input_map.key_actions.insert(
|
||||||
|
/// Key::Enter,
|
||||||
|
/// Actions::Multiple(vec![Action::ConfirmSelection, Action::Quit])
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// let key_input = InputEvent::Key(Key::Enter);
|
||||||
|
/// assert_eq!(
|
||||||
|
/// input_map.get_action_for_input(&key_input),
|
||||||
|
/// Some(Action::ConfirmSelection) // Only first action
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
pub fn get_action_for_input(&self, input: &InputEvent) -> Option<Action> {
|
pub fn get_action_for_input(&self, input: &InputEvent) -> Option<Action> {
|
||||||
match input {
|
match input {
|
||||||
InputEvent::Key(key) => self.get_action_for_key(key),
|
InputEvent::Key(key) => self.get_action_for_key(key),
|
||||||
@ -112,10 +349,38 @@ impl InputMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<&KeyBindings> for InputMap {
|
impl From<&KeyBindings> for InputMap {
|
||||||
/// Convert a `KeyBindings` into an `InputMap`.
|
/// Converts `KeyBindings` into an `InputMap`.
|
||||||
///
|
///
|
||||||
/// Since the new `KeyBindings` already store Key -> Action mappings,
|
/// This conversion creates an input map containing only keyboard bindings.
|
||||||
/// we can directly copy the bindings without inversion.
|
/// The event bindings will be empty. Since the new `KeyBindings` structure
|
||||||
|
/// already stores Key → Actions mappings, we can directly copy the bindings
|
||||||
|
/// without the inversion that was needed in the old system.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `keybindings` - The key bindings to convert
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An `InputMap` with the key bindings and empty event bindings.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::config::KeyBindings;
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// let keybindings = KeyBindings::from(vec![
|
||||||
|
/// (Key::Enter, Action::ConfirmSelection),
|
||||||
|
/// (Key::Esc, Action::Quit),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// let input_map: InputMap = (&keybindings).into();
|
||||||
|
/// assert_eq!(input_map.get_action_for_key(&Key::Enter), Some(Action::ConfirmSelection));
|
||||||
|
/// assert!(input_map.event_actions.is_empty());
|
||||||
|
/// ```
|
||||||
fn from(keybindings: &KeyBindings) -> Self {
|
fn from(keybindings: &KeyBindings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
key_actions: keybindings.bindings.clone(),
|
key_actions: keybindings.bindings.clone(),
|
||||||
@ -125,7 +390,39 @@ impl From<&KeyBindings> for InputMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<&EventBindings> for InputMap {
|
impl From<&EventBindings> for InputMap {
|
||||||
/// Convert `EventBindings` into an `InputMap`.
|
/// Converts `EventBindings` into an `InputMap`.
|
||||||
|
///
|
||||||
|
/// This conversion creates an input map containing only event bindings.
|
||||||
|
/// The key bindings will be empty. This is useful when you want to
|
||||||
|
/// handle only non-keyboard events.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `event_bindings` - The event bindings to convert
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An `InputMap` with the event bindings and empty key bindings.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::config::{EventBindings, EventType};
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// let event_bindings = EventBindings::from(vec![
|
||||||
|
/// (EventType::MouseClick, Action::ConfirmSelection),
|
||||||
|
/// (EventType::Resize, Action::ClearScreen),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// let input_map: InputMap = (&event_bindings).into();
|
||||||
|
/// assert_eq!(
|
||||||
|
/// input_map.get_action_for_event(&EventType::MouseClick),
|
||||||
|
/// Some(Action::ConfirmSelection)
|
||||||
|
/// );
|
||||||
|
/// assert!(input_map.key_actions.is_empty());
|
||||||
|
/// ```
|
||||||
fn from(event_bindings: &EventBindings) -> Self {
|
fn from(event_bindings: &EventBindings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
key_actions: FxHashMap::default(),
|
key_actions: FxHashMap::default(),
|
||||||
@ -135,7 +432,43 @@ impl From<&EventBindings> for InputMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<(&KeyBindings, &EventBindings)> for InputMap {
|
impl From<(&KeyBindings, &EventBindings)> for InputMap {
|
||||||
/// Convert both `KeyBindings` and `EventBindings` into an `InputMap`.
|
/// Converts both `KeyBindings` and `EventBindings` into an `InputMap`.
|
||||||
|
///
|
||||||
|
/// This conversion creates a complete input map with both keyboard and
|
||||||
|
/// event bindings. This is the most common way to create a fully
|
||||||
|
/// configured input map that handles all types of input events.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `keybindings` - The keyboard bindings to include
|
||||||
|
/// * `event_bindings` - The event bindings to include
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An `InputMap` containing both key and event bindings.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::config::{KeyBindings, EventBindings, EventType};
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// let keybindings = KeyBindings::from(vec![
|
||||||
|
/// (Key::Enter, Action::ConfirmSelection),
|
||||||
|
/// ]);
|
||||||
|
/// let event_bindings = EventBindings::from(vec![
|
||||||
|
/// (EventType::MouseClick, Action::ConfirmSelection),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// let input_map: InputMap = (&keybindings, &event_bindings).into();
|
||||||
|
/// assert_eq!(input_map.get_action_for_key(&Key::Enter), Some(Action::ConfirmSelection));
|
||||||
|
/// assert_eq!(
|
||||||
|
/// input_map.get_action_for_event(&EventType::MouseClick),
|
||||||
|
/// Some(Action::ConfirmSelection)
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
fn from(
|
fn from(
|
||||||
(keybindings, event_bindings): (&KeyBindings, &EventBindings),
|
(keybindings, event_bindings): (&KeyBindings, &EventBindings),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -147,9 +480,40 @@ impl From<(&KeyBindings, &EventBindings)> for InputMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InputMap {
|
impl InputMap {
|
||||||
/// Merge another `InputMap` into this one.
|
/// Merges another `InputMap` into this one.
|
||||||
///
|
///
|
||||||
/// This will overwrite any existing keys/events in `self` with the mappings from `other`.
|
/// This method combines the bindings from another `InputMap` into this one,
|
||||||
|
/// with bindings from `other` taking precedence over existing bindings.
|
||||||
|
/// This is useful for applying configuration hierarchies and user customizations.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `other` - The input map to merge into this one
|
||||||
|
///
|
||||||
|
/// # Behavior
|
||||||
|
///
|
||||||
|
/// - Existing keys/events in `self` are overwritten by those in `other`
|
||||||
|
/// - New keys/events from `other` are added to `self`
|
||||||
|
/// - Both key bindings and event bindings are merged
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::{Action, Actions};
|
||||||
|
///
|
||||||
|
/// let mut base_map = InputMap::new();
|
||||||
|
/// base_map.key_actions.insert(Key::Enter, Actions::Single(Action::ConfirmSelection));
|
||||||
|
///
|
||||||
|
/// let mut custom_map = InputMap::new();
|
||||||
|
/// custom_map.key_actions.insert(Key::Enter, Actions::Single(Action::Quit)); // Override
|
||||||
|
/// custom_map.key_actions.insert(Key::Esc, Actions::Single(Action::Quit)); // New binding
|
||||||
|
///
|
||||||
|
/// base_map.merge(&custom_map);
|
||||||
|
/// assert_eq!(base_map.get_action_for_key(&Key::Enter), Some(Action::Quit));
|
||||||
|
/// assert_eq!(base_map.get_action_for_key(&Key::Esc), Some(Action::Quit));
|
||||||
|
/// ```
|
||||||
pub fn merge(&mut self, other: &InputMap) {
|
pub fn merge(&mut self, other: &InputMap) {
|
||||||
for (key, action) in &other.key_actions {
|
for (key, action) in &other.key_actions {
|
||||||
self.key_actions.insert(*key, action.clone());
|
self.key_actions.insert(*key, action.clone());
|
||||||
@ -159,14 +523,66 @@ impl InputMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge key bindings into this `InputMap`
|
/// Merges key bindings into this `InputMap`.
|
||||||
|
///
|
||||||
|
/// This method adds all key bindings from a `KeyBindings` configuration
|
||||||
|
/// into this input map, overwriting any existing bindings for the same keys.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `keybindings` - The key bindings to merge
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::config::KeyBindings;
|
||||||
|
/// use television::event::Key;
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// let mut input_map = InputMap::new();
|
||||||
|
/// let keybindings = KeyBindings::from(vec![
|
||||||
|
/// (Key::Enter, Action::ConfirmSelection),
|
||||||
|
/// (Key::Esc, Action::Quit),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// input_map.merge_key_bindings(&keybindings);
|
||||||
|
/// assert_eq!(input_map.get_action_for_key(&Key::Enter), Some(Action::ConfirmSelection));
|
||||||
|
/// ```
|
||||||
pub fn merge_key_bindings(&mut self, keybindings: &KeyBindings) {
|
pub fn merge_key_bindings(&mut self, keybindings: &KeyBindings) {
|
||||||
for (key, action) in &keybindings.bindings {
|
for (key, action) in &keybindings.bindings {
|
||||||
self.key_actions.insert(*key, action.clone());
|
self.key_actions.insert(*key, action.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge event bindings into this `InputMap`
|
/// Merges event bindings into this `InputMap`.
|
||||||
|
///
|
||||||
|
/// This method adds all event bindings from an `EventBindings` configuration
|
||||||
|
/// into this input map, overwriting any existing bindings for the same events.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `event_bindings` - The event bindings to merge
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use television::keymap::InputMap;
|
||||||
|
/// use television::config::{EventBindings, EventType};
|
||||||
|
/// use television::action::Action;
|
||||||
|
///
|
||||||
|
/// let mut input_map = InputMap::new();
|
||||||
|
/// let event_bindings = EventBindings::from(vec![
|
||||||
|
/// (EventType::MouseClick, Action::ConfirmSelection),
|
||||||
|
/// (EventType::Resize, Action::ClearScreen),
|
||||||
|
/// ]);
|
||||||
|
///
|
||||||
|
/// input_map.merge_event_bindings(&event_bindings);
|
||||||
|
/// assert_eq!(
|
||||||
|
/// input_map.get_action_for_event(&EventType::MouseClick),
|
||||||
|
/// Some(Action::ConfirmSelection)
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
pub fn merge_event_bindings(&mut self, event_bindings: &EventBindings) {
|
pub fn merge_event_bindings(&mut self, event_bindings: &EventBindings) {
|
||||||
for (event, action) in &event_bindings.bindings {
|
for (event, action) in &event_bindings.bindings {
|
||||||
self.event_actions.insert(event.clone(), action.clone());
|
self.event_actions.insert(event.clone(), action.clone());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user