mirror of
https://github.com/alexpasmantier/television.git
synced 2025-07-29 14:21:43 +00:00
fix(bindings): remove legacy binding, replace with newer Key
This commit is contained in:
parent
bf3a22a7cf
commit
75b6d48372
@ -1,20 +1,16 @@
|
|||||||
|
use crate::{
|
||||||
|
action::Action, channels::prototypes::ChannelPrototype,
|
||||||
|
config::KeyBindings, errors::unknown_channel_exit, event::Key,
|
||||||
|
};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use std::{
|
use std::{
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::{
|
|
||||||
action::Action,
|
|
||||||
channels::prototypes::ChannelPrototype,
|
|
||||||
config::{Binding, KeyBindings},
|
|
||||||
errors::unknown_channel_exit,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A neat `HashMap` of channel prototypes indexed by their name.
|
/// A neat `HashMap` of channel prototypes indexed by their name.
|
||||||
///
|
///
|
||||||
/// This is used to store cable channel prototypes throughout the application
|
/// This is used to store cable channel prototypes throughout the application
|
||||||
@ -62,28 +58,9 @@ impl Cable {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(name, prototype)| {
|
.filter_map(|(name, prototype)| {
|
||||||
if let Some(keybindings) = &prototype.keybindings {
|
if let Some(keybindings) = &prototype.keybindings {
|
||||||
if let Some(binding) = &keybindings.shortcut {
|
keybindings.shortcut.as_ref().map(|key| {
|
||||||
// Convert Binding to Key for new architecture
|
(*key, Action::SwitchToChannel(name.clone()).into())
|
||||||
match binding {
|
})
|
||||||
Binding::SingleKey(key) => Some((
|
|
||||||
*key,
|
|
||||||
Action::SwitchToChannel(name.clone()).into(),
|
|
||||||
)),
|
|
||||||
// For multiple keys, use the first one
|
|
||||||
Binding::MultipleKeys(keys)
|
|
||||||
if !keys.is_empty() =>
|
|
||||||
{
|
|
||||||
Some((
|
|
||||||
keys[0],
|
|
||||||
Action::SwitchToChannel(name.clone())
|
|
||||||
.into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Binding::MultipleKeys(_) => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -96,13 +73,12 @@ impl Cable {
|
|||||||
/// Get a channel prototype's shortcut binding.
|
/// Get a channel prototype's shortcut binding.
|
||||||
///
|
///
|
||||||
/// E.g. if the channel is "files" and the shortcut is "F1",
|
/// E.g. if the channel is "files" and the shortcut is "F1",
|
||||||
/// this will return `Some(Binding::SingleKey("F1"))`.
|
/// this will return `Some(Key::F(1))`.
|
||||||
pub fn get_channel_shortcut(&self, channel_name: &str) -> Option<Binding> {
|
pub fn get_channel_shortcut(&self, channel_name: &str) -> Option<Key> {
|
||||||
// Get only what we need, clone at the end
|
|
||||||
self.get(channel_name)
|
self.get(channel_name)
|
||||||
.and_then(|prototype| prototype.keybindings.as_ref())
|
.and_then(|prototype| prototype.keybindings.as_ref())
|
||||||
.and_then(|keybindings| keybindings.shortcut.as_ref())
|
.and_then(|keybindings| keybindings.shortcut.as_ref())
|
||||||
.cloned()
|
.copied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
channels::prototypes::Template, config::Binding,
|
channels::prototypes::Template,
|
||||||
|
event::Key,
|
||||||
screen::result_item::ResultItem,
|
screen::result_item::ResultItem,
|
||||||
};
|
};
|
||||||
use devicons::FileIcon;
|
use devicons::FileIcon;
|
||||||
@ -172,7 +173,7 @@ impl ResultItem for Entry {
|
|||||||
self.match_ranges.as_deref()
|
self.match_ranges.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shortcut(&self) -> Option<&Binding> {
|
fn shortcut(&self) -> Option<&Key> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::cli::parse_source_entry_delimiter;
|
use crate::cli::parse_source_entry_delimiter;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Binding, KeyBindings, ui},
|
config::{KeyBindings, ui},
|
||||||
|
event::Key,
|
||||||
features::Features,
|
features::Features,
|
||||||
screen::layout::{InputPosition, Orientation},
|
screen::layout::{InputPosition, Orientation},
|
||||||
};
|
};
|
||||||
@ -161,7 +162,7 @@ impl CommandSpec {
|
|||||||
pub struct ChannelKeyBindings {
|
pub struct ChannelKeyBindings {
|
||||||
/// Optional channel specific shortcut that, when pressed, switches directly to this channel.
|
/// Optional channel specific shortcut that, when pressed, switches directly to this channel.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub shortcut: Option<Binding>,
|
pub shortcut: Option<Key>,
|
||||||
/// Regular action -> binding mappings living at channel level.
|
/// Regular action -> binding mappings living at channel level.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -169,7 +170,7 @@ pub struct ChannelKeyBindings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ChannelKeyBindings {
|
impl ChannelKeyBindings {
|
||||||
pub fn channel_shortcut(&self) -> Option<&Binding> {
|
pub fn channel_shortcut(&self) -> Option<&Key> {
|
||||||
self.shortcut.as_ref()
|
self.shortcut.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,8 @@ use crate::{
|
|||||||
entry::into_ranges,
|
entry::into_ranges,
|
||||||
prototypes::{BinaryRequirement, ChannelPrototype},
|
prototypes::{BinaryRequirement, ChannelPrototype},
|
||||||
},
|
},
|
||||||
config::{Binding, ui::RemoteControlConfig},
|
config::ui::RemoteControlConfig,
|
||||||
|
event::Key,
|
||||||
matcher::{Matcher, config::Config},
|
matcher::{Matcher, config::Config},
|
||||||
screen::result_item::ResultItem,
|
screen::result_item::ResultItem,
|
||||||
};
|
};
|
||||||
@ -14,17 +15,17 @@ use devicons::FileIcon;
|
|||||||
pub struct CableEntry {
|
pub struct CableEntry {
|
||||||
pub channel_name: String,
|
pub channel_name: String,
|
||||||
pub match_ranges: Option<Vec<(u32, u32)>>,
|
pub match_ranges: Option<Vec<(u32, u32)>>,
|
||||||
pub shortcut: Option<Binding>,
|
pub shortcut: Option<Key>,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub requirements: Vec<BinaryRequirement>,
|
pub requirements: Vec<BinaryRequirement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CableEntry {
|
impl CableEntry {
|
||||||
pub fn new(name: String, shortcut: Option<&Binding>) -> Self {
|
pub fn new(name: String, shortcut: Option<&Key>) -> Self {
|
||||||
CableEntry {
|
CableEntry {
|
||||||
channel_name: name,
|
channel_name: name,
|
||||||
match_ranges: None,
|
match_ranges: None,
|
||||||
shortcut: shortcut.cloned(),
|
shortcut: shortcut.copied(),
|
||||||
description: None,
|
description: None,
|
||||||
requirements: Vec::new(),
|
requirements: Vec::new(),
|
||||||
}
|
}
|
||||||
@ -67,7 +68,7 @@ impl ResultItem for CableEntry {
|
|||||||
self.match_ranges.as_deref()
|
self.match_ranges.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shortcut(&self) -> Option<&crate::config::Binding> {
|
fn shortcut(&self) -> Option<&Key> {
|
||||||
self.shortcut.as_ref()
|
self.shortcut.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,28 +10,6 @@ use std::hash::Hash;
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
// Legacy binding structure for backward compatibility with shell integration
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum Binding {
|
|
||||||
SingleKey(Key),
|
|
||||||
MultipleKeys(Vec<Key>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Binding {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Binding::SingleKey(key) => write!(f, "{key}"),
|
|
||||||
Binding::MultipleKeys(keys) => {
|
|
||||||
let keys_str: Vec<String> = keys
|
|
||||||
.iter()
|
|
||||||
.map(std::string::ToString::to_string)
|
|
||||||
.collect();
|
|
||||||
write!(f, "{}", keys_str.join(", "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
/// Generic bindings structure that maps any key type to actions.
|
||||||
|
@ -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,
|
EventBindings, EventType, KeyBindings, merge_bindings,
|
||||||
};
|
};
|
||||||
pub use themes::Theme;
|
pub use themes::Theme;
|
||||||
pub use ui::UiConfig;
|
pub use ui::UiConfig;
|
||||||
@ -478,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(Key::from_str("ctrl-h").unwrap()),
|
Key::from_str("ctrl-h").unwrap(),
|
||||||
);
|
);
|
||||||
default_config.shell_integration.merge_triggers();
|
default_config.shell_integration.merge_triggers();
|
||||||
|
|
||||||
@ -570,14 +570,14 @@ mod tests {
|
|||||||
|
|
||||||
let config = Config::new(&config_env, None).unwrap();
|
let config = Config::new(&config_env, None).unwrap();
|
||||||
|
|
||||||
let expected: rustc_hash::FxHashMap<String, Binding> = [
|
let expected: rustc_hash::FxHashMap<String, Key> = [
|
||||||
(
|
(
|
||||||
"command_history".to_string(),
|
"command_history".to_string(),
|
||||||
Binding::SingleKey(Key::from_str("ctrl-[").unwrap()),
|
Key::from_str("ctrl-[").unwrap(),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"smart_autocomplete".to_string(),
|
"smart_autocomplete".to_string(),
|
||||||
Binding::SingleKey(Key::from_str("ctrl-t").unwrap()),
|
Key::from_str("ctrl-t").unwrap(),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{config::Binding, event::Key, utils::hashmaps};
|
use crate::{event::Key, utils::hashmaps};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ pub struct ShellIntegrationConfig {
|
|||||||
/// {channel: [commands]}
|
/// {channel: [commands]}
|
||||||
pub channel_triggers: FxHashMap<String, Vec<String>>,
|
pub channel_triggers: FxHashMap<String, Vec<String>>,
|
||||||
pub fallback_channel: String,
|
pub fallback_channel: String,
|
||||||
pub keybindings: FxHashMap<String, Binding>,
|
pub keybindings: FxHashMap<String, Key>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for ShellIntegrationConfig {
|
impl Hash for ShellIntegrationConfig {
|
||||||
@ -49,7 +49,7 @@ impl ShellIntegrationConfig {
|
|||||||
// (if any), extract the character triggers shell autocomplete
|
// (if any), extract the character triggers shell autocomplete
|
||||||
pub fn get_shell_autocomplete_keybinding_character(&self) -> char {
|
pub fn get_shell_autocomplete_keybinding_character(&self) -> char {
|
||||||
match self.keybindings.get(SMART_AUTOCOMPLETE_CONFIGURATION_KEY) {
|
match self.keybindings.get(SMART_AUTOCOMPLETE_CONFIGURATION_KEY) {
|
||||||
Some(binding) => extract_ctrl_char(binding)
|
Some(&key) => extract_ctrl_char(key)
|
||||||
.unwrap_or(DEFAULT_SHELL_AUTOCOMPLETE_KEY),
|
.unwrap_or(DEFAULT_SHELL_AUTOCOMPLETE_KEY),
|
||||||
None => DEFAULT_SHELL_AUTOCOMPLETE_KEY,
|
None => DEFAULT_SHELL_AUTOCOMPLETE_KEY,
|
||||||
}
|
}
|
||||||
@ -59,21 +59,17 @@ impl ShellIntegrationConfig {
|
|||||||
// through tv
|
// through tv
|
||||||
pub fn get_command_history_keybinding_character(&self) -> char {
|
pub fn get_command_history_keybinding_character(&self) -> char {
|
||||||
match self.keybindings.get(COMMAND_HISTORY_CONFIGURATION_KEY) {
|
match self.keybindings.get(COMMAND_HISTORY_CONFIGURATION_KEY) {
|
||||||
Some(binding) => extract_ctrl_char(binding)
|
Some(&key) => {
|
||||||
.unwrap_or(DEFAULT_COMMAND_HISTORY_KEY),
|
extract_ctrl_char(key).unwrap_or(DEFAULT_COMMAND_HISTORY_KEY)
|
||||||
|
}
|
||||||
None => DEFAULT_COMMAND_HISTORY_KEY,
|
None => DEFAULT_COMMAND_HISTORY_KEY,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract an upper-case character from a `Binding` if it is a single CTRL key
|
/// Extract an upper-case character from a `Key` if it is a single CTRL key
|
||||||
/// (or CTRL-Space). Returns `None` otherwise.
|
/// (or CTRL-Space). Returns `None` otherwise.
|
||||||
fn extract_ctrl_char(binding: &Binding) -> Option<char> {
|
fn extract_ctrl_char(key: Key) -> Option<char> {
|
||||||
let key = match binding {
|
|
||||||
Binding::SingleKey(k) => Some(k),
|
|
||||||
Binding::MultipleKeys(keys) => keys.first(),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
match key {
|
match key {
|
||||||
Key::Ctrl(c) => Some(c.to_ascii_uppercase()),
|
Key::Ctrl(c) => Some(c.to_ascii_uppercase()),
|
||||||
Key::CtrlSpace => Some(' '),
|
Key::CtrlSpace => Some(' '),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
action::{Action, Actions},
|
action::{Action, Actions},
|
||||||
config::{Binding, KeyBindings},
|
config::KeyBindings,
|
||||||
television::Mode,
|
television::Mode,
|
||||||
};
|
};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@ -151,18 +151,6 @@ impl ActionMapping {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unified key extraction function that works for both systems
|
|
||||||
pub fn extract_keys_from_binding(binding: &Binding) -> Vec<String> {
|
|
||||||
match binding {
|
|
||||||
Binding::SingleKey(key) => {
|
|
||||||
vec![key.to_string()]
|
|
||||||
}
|
|
||||||
Binding::MultipleKeys(keys) => {
|
|
||||||
keys.iter().map(ToString::to_string).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract keys for a single action from the new Key->Action keybindings format
|
/// Extract keys for a single action from the new Key->Action keybindings format
|
||||||
pub fn find_keys_for_action(
|
pub fn find_keys_for_action(
|
||||||
keybindings: &KeyBindings,
|
keybindings: &KeyBindings,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::Binding,
|
event::Key,
|
||||||
screen::{
|
screen::{
|
||||||
colors::ResultsColorscheme,
|
colors::ResultsColorscheme,
|
||||||
constants::{DESELECTED_SYMBOL, POINTER_SYMBOL, SELECTED_SYMBOL},
|
constants::{DESELECTED_SYMBOL, POINTER_SYMBOL, SELECTED_SYMBOL},
|
||||||
@ -40,7 +40,7 @@ pub trait ResultItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Optional shortcut binding shown after the name (remote-control entries).
|
/// Optional shortcut binding shown after the name (remote-control entries).
|
||||||
fn shortcut(&self) -> Option<&Binding> {
|
fn shortcut(&self) -> Option<&Key> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,13 +82,7 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>(
|
|||||||
|
|
||||||
let shortcut_extra: u16 = item
|
let shortcut_extra: u16 = item
|
||||||
.shortcut()
|
.shortcut()
|
||||||
.map(|b| match b {
|
.map(|k| 2 + k.to_string().len() as u16) // space + key
|
||||||
Binding::SingleKey(k) => 2 + k.to_string().len() as u16, // space + key
|
|
||||||
Binding::MultipleKeys(keys) => keys
|
|
||||||
.iter()
|
|
||||||
.map(|k| 1 + k.to_string().len() as u16) // space + key
|
|
||||||
.sum(),
|
|
||||||
})
|
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let item_max_width = area_width
|
let item_max_width = area_width
|
||||||
@ -126,26 +120,13 @@ pub fn build_result_line<'a, T: ResultItem + ?Sized>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show shortcut if present.
|
// Show shortcut if present.
|
||||||
if let Some(binding) = item.shortcut() {
|
if let Some(key) = item.shortcut() {
|
||||||
spans.push(Span::raw(" "));
|
spans.push(Span::raw(" "));
|
||||||
match binding {
|
|
||||||
Binding::SingleKey(k) => spans.push(Span::styled(
|
|
||||||
k.to_string(),
|
|
||||||
Style::default().fg(match_fg),
|
|
||||||
)),
|
|
||||||
Binding::MultipleKeys(keys) => {
|
|
||||||
for (i, k) in keys.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
spans.push(Span::raw(" "));
|
|
||||||
}
|
|
||||||
spans.push(Span::styled(
|
spans.push(Span::styled(
|
||||||
k.to_string(),
|
key.to_string(),
|
||||||
Style::default().fg(match_fg),
|
Style::default().fg(match_fg),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Line::from(spans)
|
Line::from(spans)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user