Skip to content

Commit e2bc8fb

Browse files
authored
Merge pull request #32 from rgwood/key-guide
Key guide + shortcuts
2 parents 01b283d + d78fd1f commit e2bc8fb

3 files changed

Lines changed: 92 additions & 21 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "systemctl-tui"
33
description = "A simple TUI for interacting with systemd services and their logs"
44
homepage = "https://github.com/rgwood/systemctl-tui"
55
repository = "https://github.com/rgwood/systemctl-tui"
6-
version = "0.3.10"
6+
version = "0.4.0"
77
edition = "2021"
88
authors = ["Reilly Wood"]
99
license = "MIT"

src/components/home.rs

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,20 @@ pub struct Home {
6363
pub struct MenuItem {
6464
pub name: String,
6565
pub action: Action,
66+
pub key: Option<KeyCode>,
6667
}
6768

6869
impl MenuItem {
69-
pub fn new(name: &str, action: Action) -> Self {
70-
Self { name: name.to_owned(), action }
70+
pub fn new(name: &str, action: Action, key: Option<KeyCode>) -> Self {
71+
Self { name: name.to_owned(), action, key }
72+
}
73+
74+
pub fn key_string(&self) -> String {
75+
if let Some(key) = self.key {
76+
format!("{}", key)
77+
} else {
78+
String::new()
79+
}
7180
}
7281
}
7382

@@ -500,6 +509,14 @@ impl Component for Home {
500509
vec![Action::Render]
501510
},
502511
KeyCode::Char('/') => vec![Action::EnterMode(Mode::Search)],
512+
KeyCode::Char('e') => {
513+
if let Some(selected) = self.filtered_units.selected() {
514+
if let Some(Ok(file_path)) = &selected.file_path {
515+
return vec![Action::EditUnitFile { unit: selected.id(), path: file_path.clone() }];
516+
}
517+
}
518+
vec![]
519+
},
503520
KeyCode::Enter | KeyCode::Char(' ') => vec![Action::EnterMode(Mode::ActionMenu)],
504521
_ => vec![],
505522
}
@@ -548,7 +565,16 @@ impl Component for Home {
548565
Some(i) => vec![i.action.clone()],
549566
None => vec![Action::EnterMode(Mode::ServiceList)],
550567
},
551-
_ => vec![],
568+
_ => {
569+
for item in self.menu_items.items.iter() {
570+
if let Some(key_code) = item.key {
571+
if key_code == key.code {
572+
return vec![item.action.clone()];
573+
}
574+
}
575+
}
576+
vec![]
577+
},
552578
},
553579
Mode::Processing => match key.code {
554580
KeyCode::Esc => vec![Action::CancelTask],
@@ -567,20 +593,25 @@ impl Component for Home {
567593
if mode == Mode::ActionMenu {
568594
if let Some(selected) = self.filtered_units.selected() {
569595
let mut menu_items = vec![
570-
MenuItem::new("Start", Action::StartService(selected.id())),
571-
MenuItem::new("Stop", Action::StopService(selected.id())),
572-
MenuItem::new("Restart", Action::RestartService(selected.id())),
573-
MenuItem::new("Reload", Action::ReloadService(selected.id())),
596+
MenuItem::new("Start", Action::StartService(selected.id()), Some(KeyCode::Char('s'))),
597+
MenuItem::new("Stop", Action::StopService(selected.id()), Some(KeyCode::Char('t'))),
598+
MenuItem::new("Restart", Action::RestartService(selected.id()), Some(KeyCode::Char('r'))),
599+
MenuItem::new("Reload", Action::ReloadService(selected.id()), Some(KeyCode::Char('l'))),
574600
// TODO add these
575601
// MenuItem::new("Enable", Action::EnableService(selected.clone())),
576602
// MenuItem::new("Disable", Action::DisableService(selected.clone())),
577603
];
578604

579605
if let Some(Ok(file_path)) = &selected.file_path {
580-
menu_items.push(MenuItem::new("Copy unit file path to clipboard", Action::CopyUnitFilePath));
606+
menu_items.push(MenuItem::new(
607+
"Copy unit file path to clipboard",
608+
Action::CopyUnitFilePath,
609+
Some(KeyCode::Char('c')),
610+
));
581611
menu_items.push(MenuItem::new(
582612
"Edit unit file",
583613
Action::EditUnitFile { unit: selected.id(), path: file_path.clone() },
614+
Some(KeyCode::Char('e')),
584615
));
585616
}
586617

@@ -695,6 +726,18 @@ impl Component for Home {
695726
}
696727

697728
fn render(&mut self, f: &mut Frame<'_>, rect: Rect) {
729+
fn primary(s: &str) -> Span {
730+
Span::styled(s, Style::default().fg(Color::Cyan))
731+
}
732+
733+
fn span(s: &str, color: Color) -> Span {
734+
Span::styled(s, Style::default().fg(color))
735+
}
736+
737+
fn colored_line(value: &str, color: Color) -> Line {
738+
Line::from(vec![Span::styled(value, Style::default().fg(color))])
739+
}
740+
698741
let rect = if self.show_logger {
699742
let chunks = Layout::new(Direction::Vertical, Constraint::from_percentages([50, 50])).split(rect);
700743

@@ -704,13 +747,12 @@ impl Component for Home {
704747
rect
705748
};
706749

707-
let rects = Layout::new(Direction::Vertical, [Constraint::Min(3), Constraint::Percentage(100)]).split(rect);
750+
let rects =
751+
Layout::new(Direction::Vertical, [Constraint::Min(3), Constraint::Percentage(100), Constraint::Length(1)])
752+
.split(rect);
708753
let search_panel = rects[0];
709754
let main_panel = rects[1];
710-
711-
fn colored_line(value: &str, color: Color) -> Line {
712-
Line::from(vec![Span::styled(value, Style::default().fg(color))])
713-
}
755+
let help_line_rect = rects[2];
714756

715757
// Helper for colouring based on the same logic as sysz
716758
// https://github.com/joehillen/sysz/blob/8da8e0dcbfde8d68fbdb22382671e395bd370d69/sysz#L69C1-L72C24
@@ -840,7 +882,9 @@ impl Component for Home {
840882
.rev()
841883
.map(|l| {
842884
if let Some((date, rest)) = l.splitn(2, ' ').collect_tuple() {
843-
if date.len() != 24 {
885+
// This is not a good way to identify dates; the length can vary by system.
886+
// TODO: find a better way to identify dates
887+
if date.len() != 25 {
844888
return Line::from(l.as_str());
845889
}
846890
Line::from(vec![Span::styled(date, Style::default().fg(Color::DarkGray)), Span::raw(" "), Span::raw(rest)])
@@ -898,10 +942,6 @@ impl Component for Home {
898942
if self.mode == Mode::Help {
899943
let popup = centered_rect_abs(50, 18, f.area());
900944

901-
fn primary(s: &str) -> Span {
902-
Span::styled(s, Style::default().fg(Color::Cyan))
903-
}
904-
905945
let help_lines = vec![
906946
Line::from(""),
907947
Line::from(Span::styled("Shortcuts", Style::default().add_modifier(Modifier::UNDERLINED))),
@@ -955,6 +995,28 @@ impl Component for Home {
955995
None => return,
956996
};
957997

998+
// Help line at the bottom
999+
1000+
let version = format!("v{}", env!("CARGO_PKG_VERSION"));
1001+
1002+
let help_line_rects =
1003+
Layout::new(Direction::Horizontal, [Constraint::Fill(1), Constraint::Length(version.len() as u16)])
1004+
.split(help_line_rect);
1005+
let help_rect = help_line_rects[0];
1006+
let version_rect = help_line_rects[1];
1007+
1008+
let help_line = match self.mode {
1009+
Mode::Search => Line::from(span("Show actions: <enter>", Color::Blue)),
1010+
Mode::ServiceList => Line::from(span("Show actions: <enter> | Open unit file: e | Quit: q", Color::Blue)),
1011+
Mode::Help => Line::from(span("Close menu: <esc>", Color::Blue)),
1012+
Mode::ActionMenu => Line::from(span("Execute action: <enter> | Close menu: <esc>", Color::Blue)),
1013+
Mode::Processing => Line::from(span("Cancel task: <esc>", Color::Blue)),
1014+
Mode::Error => Line::from(span("Close menu: <esc>", Color::Blue)),
1015+
};
1016+
1017+
f.render_widget(help_line, help_rect);
1018+
f.render_widget(Line::from(version), version_rect);
1019+
9581020
let min_width = selected_item.name.len() as u16 + 14;
9591021
let desired_width = min_width + 4; // idk, looks alright
9601022
let popup_width = desired_width.min(f.area().width);
@@ -963,7 +1025,16 @@ impl Component for Home {
9631025
let height = self.menu_items.items.len() as u16 + 2;
9641026
let popup = centered_rect_abs(popup_width, height, f.area());
9651027

966-
let items: Vec<ListItem> = self.menu_items.items.iter().map(|i| ListItem::new(i.name.as_str())).collect();
1028+
let items: Vec<ListItem> = self
1029+
.menu_items
1030+
.items
1031+
.iter()
1032+
.map(|i| {
1033+
let key_string = Span::styled(format!(" {:1} ", i.key_string()), Style::default().fg(Color::Blue));
1034+
let line = Line::from(vec![key_string, Span::raw(&i.name)]);
1035+
ListItem::new(line)
1036+
})
1037+
.collect();
9671038
let items = List::new(items)
9681039
.block(
9691040
Block::default()

0 commit comments

Comments
 (0)