pub trait HotkeyCategory: Clone + Copy + PartialEq + Default + 'static {
fn all() -> &'static [Self];
fn display_name(&self) -> &str;
fn icon(&self) -> &str {
""
}
fn next(&self) -> Self;
fn prev(&self) -> Self;
}
#[derive(Debug, Clone)]
pub struct HotkeyEntryData {
pub key_combination: String,
pub action: String,
pub context: String,
pub is_global: bool,
pub is_customizable: bool,
}
impl HotkeyEntryData {
pub fn new(
key_combination: impl Into<String>,
action: impl Into<String>,
context: impl Into<String>,
) -> Self {
Self {
key_combination: key_combination.into(),
action: action.into(),
context: context.into(),
is_global: false,
is_customizable: true,
}
}
pub fn global(key_combination: impl Into<String>, action: impl Into<String>) -> Self {
Self {
key_combination: key_combination.into(),
action: action.into(),
context: "Global".to_string(),
is_global: true,
is_customizable: true,
}
}
pub fn fixed(mut self) -> Self {
self.is_customizable = false;
self
}
pub fn with_global(mut self, is_global: bool) -> Self {
self.is_global = is_global;
self
}
}
pub trait HotkeyProvider {
type Category: HotkeyCategory;
fn entries_for_category(&self, category: Self::Category) -> Vec<HotkeyEntryData>;
fn search(&self, query: &str) -> Vec<(Self::Category, HotkeyEntryData)>;
fn total_count(&self) -> usize {
Self::Category::all()
.iter()
.map(|c| self.entries_for_category(*c).len())
.sum()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
enum TestCategory {
#[default]
General,
Advanced,
}
impl HotkeyCategory for TestCategory {
fn all() -> &'static [Self] {
&[Self::General, Self::Advanced]
}
fn display_name(&self) -> &str {
match self {
Self::General => "General",
Self::Advanced => "Advanced",
}
}
fn next(&self) -> Self {
match self {
Self::General => Self::Advanced,
Self::Advanced => Self::General,
}
}
fn prev(&self) -> Self {
match self {
Self::General => Self::Advanced,
Self::Advanced => Self::General,
}
}
}
#[test]
fn test_category_navigation() {
let cat = TestCategory::General;
assert_eq!(cat.next(), TestCategory::Advanced);
assert_eq!(cat.next().prev(), TestCategory::General);
}
#[test]
fn test_entry_creation() {
let entry = HotkeyEntryData::new("Ctrl+S", "Save file", "Normal");
assert_eq!(entry.key_combination, "Ctrl+S");
assert_eq!(entry.action, "Save file");
assert!(!entry.is_global);
assert!(entry.is_customizable);
let global = HotkeyEntryData::global("Ctrl+C", "Quit");
assert!(global.is_global);
let fixed = HotkeyEntryData::new("Ctrl+C", "Quit", "Global").fixed();
assert!(!fixed.is_customizable);
}
}