use crate::core::command::CommandId;
use crate::core::event::KeyCode;
#[derive(Clone, Debug)]
pub struct StatusItem {
pub text: String,
pub key_code: KeyCode,
pub command: CommandId,
}
impl StatusItem {
pub fn new(text: &str, key_code: KeyCode, command: CommandId) -> Self {
Self {
text: text.to_string(),
key_code,
command,
}
}
pub fn get_accelerator(&self) -> Option<char> {
let mut chars = self.text.chars();
while let Some(ch) = chars.next() {
if ch == '~' {
if let Some(accel) = chars.next() {
return Some(accel.to_ascii_lowercase());
}
}
}
None
}
}
#[derive(Clone, Debug)]
pub struct StatusDef {
pub min: u16,
pub max: u16,
pub items: Vec<StatusItem>,
}
impl StatusDef {
pub fn new(min: u16, max: u16, items: Vec<StatusItem>) -> Self {
Self { min, max, items }
}
pub fn default_range(items: Vec<StatusItem>) -> Self {
Self {
min: 0,
max: 0xFFFF,
items,
}
}
pub fn applies_to(&self, command_set: u16) -> bool {
command_set >= self.min && command_set <= self.max
}
pub fn add(&mut self, item: StatusItem) {
self.items.push(item);
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
}
#[derive(Clone, Debug)]
pub struct StatusLine {
pub defs: Vec<StatusDef>,
}
impl StatusLine {
pub fn new(defs: Vec<StatusDef>) -> Self {
Self { defs }
}
pub fn single(items: Vec<StatusItem>) -> Self {
Self {
defs: vec![StatusDef::default_range(items)],
}
}
pub fn get_def_for(&self, command_set: u16) -> Option<&StatusDef> {
self.defs.iter().find(|def| def.applies_to(command_set))
}
pub fn add_def(&mut self, def: StatusDef) {
self.defs.push(def);
}
}
pub struct StatusLineBuilder {
defs: Vec<StatusDef>,
}
impl StatusLineBuilder {
pub fn new() -> Self {
Self {
defs: Vec::new(),
}
}
pub fn add_def(mut self, min: u16, max: u16, items: Vec<StatusItem>) -> Self {
self.defs.push(StatusDef::new(min, max, items));
self
}
pub fn add_default_def(mut self, items: Vec<StatusItem>) -> Self {
self.defs.push(StatusDef::default_range(items));
self
}
pub fn build(self) -> StatusLine {
StatusLine::new(self.defs)
}
}
impl Default for StatusLineBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_status_def() {
let def = StatusDef::new(0, 100, vec![
StatusItem::new("~F1~ Help", 0x3B00, 100),
StatusItem::new("~Alt+X~ Exit", 0x2D00, 101),
]);
assert!(def.applies_to(50));
assert!(def.applies_to(0));
assert!(def.applies_to(100));
assert!(!def.applies_to(101));
assert_eq!(def.len(), 2);
}
#[test]
fn test_status_line_builder() {
let status = StatusLineBuilder::new()
.add_def(0, 100, vec![
StatusItem::new("~F1~ Help", 0x3B00, 100),
StatusItem::new("~F2~ Save", 0x3C00, 101),
])
.add_def(101, 200, vec![
StatusItem::new("~F1~ Help", 0x3B00, 100),
StatusItem::new("~Esc~ Cancel", 0x011B, 102),
])
.build();
assert_eq!(status.defs.len(), 2);
let def1 = status.get_def_for(50);
assert!(def1.is_some());
assert_eq!(def1.unwrap().len(), 2);
let def2 = status.get_def_for(150);
assert!(def2.is_some());
assert_eq!(def2.unwrap().len(), 2);
}
#[test]
fn test_accelerator() {
let item = StatusItem::new("~F1~ Help", 0x3B00, 100);
assert_eq!(item.get_accelerator(), Some('f'));
}
}