diff --git a/Cargo.lock b/Cargo.lock index 58f5510..bc512ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -825,7 +825,7 @@ dependencies = [ [[package]] name = "lines" -version = "0.1.0" +version = "0.1.16" dependencies = [ "base64", "chrono", diff --git a/Cargo.toml b/Cargo.toml index a4621a3..9ffe343 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lines" -version = "0.1.15" +version = "0.1.16" edition = "2024" authors = ["janvete"] description = "TUI for running frequently used commands" diff --git a/src/app.rs b/src/app.rs index bdcce14..b931a69 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,7 @@ use std::path::PathBuf; +use ratatui::widgets::ListState; + use crate::config::Terminal; use crate::parser::{load_groups, CommandFile, Group, Section}; @@ -23,6 +25,7 @@ pub struct SearchState { pub query: String, pub results: Vec, pub cursor: usize, + pub list_state: ListState, } #[derive(Debug, Clone)] @@ -52,6 +55,7 @@ pub struct CustomState { pub cursor: usize, pub focus: CustomFocus, pub command: String, + pub list_state: ListState, } impl CustomState { @@ -69,12 +73,15 @@ impl CustomState { } } let selected = vec![false; lines.len()]; + let mut list_state = ListState::default(); + list_state.select(Some(0)); CustomState { lines, selected, cursor: 0, focus: CustomFocus::Lines, command: String::new(), + list_state, } } @@ -148,11 +155,20 @@ pub struct App { pub search_state: Option, pub pending_command: Option, pub message: Option, + pub group_list_state: ListState, + pub file_list_state: ListState, + pub section_list_state: ListState, } impl App { pub fn new(data_dir: PathBuf, terminal: Terminal, shell: String) -> Self { let groups = load_groups(&data_dir); + let mut group_list_state = ListState::default(); + let mut file_list_state = ListState::default(); + let mut section_list_state = ListState::default(); + group_list_state.select(Some(0)); + file_list_state.select(Some(0)); + section_list_state.select(Some(0)); App { data_dir, terminal, @@ -167,6 +183,9 @@ impl App { search_state: None, pending_command: None, message: None, + group_list_state, + file_list_state, + section_list_state, } } @@ -191,10 +210,13 @@ impl App { pub fn enter_search(&mut self) { let results = build_search_index(&self.groups); + let mut list_state = ListState::default(); + list_state.select(Some(0)); self.search_state = Some(SearchState { query: String::new(), results, cursor: 0, + list_state, }); self.screen = Screen::Search; } diff --git a/src/custom.rs b/src/custom.rs index 82f02ef..3e57c19 100644 --- a/src/custom.rs +++ b/src/custom.rs @@ -30,6 +30,8 @@ mod tests { use crate::app::{CustomLine, CustomState}; fn make_state(lines: Vec<&str>, selected: Vec, command: &str) -> CustomState { + let mut list_state = ratatui::widgets::ListState::default(); + list_state.select(Some(0)); CustomState { lines: lines .into_iter() @@ -42,6 +44,7 @@ mod tests { cursor: 0, focus: crate::app::CustomFocus::Lines, command: command.to_string(), + list_state, } } diff --git a/src/main.rs b/src/main.rs index b8d1dff..a39152f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,7 +90,7 @@ where let tick_rate = Duration::from_millis(100); loop { - terminal.draw(|f| ui::draw(f, &app))?; + terminal.draw(|f| ui::draw(f, &mut app))?; if let Some(event) = poll_event(tick_rate)? && !handle_event(&mut app, event) diff --git a/src/ui.rs b/src/ui.rs index 598c122..ea83219 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -8,7 +8,7 @@ use ratatui::{ use crate::app::{App, CustomFocus, Focus, Screen}; -pub fn draw(f: &mut Frame, app: &App) { +pub fn draw(f: &mut Frame, app: &mut App) { match app.screen { Screen::Main => draw_main(f, app), Screen::Custom => draw_custom(f, app), @@ -16,7 +16,7 @@ pub fn draw(f: &mut Frame, app: &App) { } } -fn draw_main(f: &mut Frame, app: &App) { +fn draw_main(f: &mut Frame, app: &mut App) { let chunks = Layout::default() .direction(Direction::Vertical) .margin(1) @@ -43,7 +43,7 @@ fn draw_main(f: &mut Frame, app: &App) { draw_status(f, app, chunks[2]); } -fn draw_custom(f: &mut Frame, app: &App) { +fn draw_custom(f: &mut Frame, app: &mut App) { let chunks = Layout::default() .direction(Direction::Vertical) .margin(1) @@ -54,7 +54,7 @@ fn draw_custom(f: &mut Frame, app: &App) { ]) .split(f.area()); - let Some(state) = app.custom_state.as_ref() else { + let Some(state) = app.custom_state.as_mut() else { return; }; @@ -94,7 +94,8 @@ fn draw_custom(f: &mut Frame, app: &App) { Style::default().fg(Color::DarkGray) }); let list = List::new(items).block(list_block); - f.render_widget(list, chunks[0]); + state.list_state.select(Some(state.cursor)); + f.render_stateful_widget(list, chunks[0], &mut state.list_state); let input = Paragraph::new(state.command.as_str()) .block( @@ -119,7 +120,7 @@ fn draw_custom(f: &mut Frame, app: &App) { f.render_widget(paragraph, chunks[2]); } -fn draw_search(f: &mut Frame, app: &App) { +fn draw_search(f: &mut Frame, app: &mut App) { let chunks = Layout::default() .direction(Direction::Vertical) .margin(1) @@ -130,7 +131,7 @@ fn draw_search(f: &mut Frame, app: &App) { ]) .split(f.area()); - let Some(state) = app.search_state.as_ref() else { + let Some(state) = app.search_state.as_mut() else { return; }; @@ -170,7 +171,8 @@ fn draw_search(f: &mut Frame, app: &App) { .title(format!(" Results ({}) ", state.results.len())) .borders(Borders::ALL), ); - f.render_widget(list, chunks[1]); + state.list_state.select(Some(state.cursor)); + f.render_stateful_widget(list, chunks[1], &mut state.list_state); let preview_text = state .results @@ -183,7 +185,7 @@ fn draw_search(f: &mut Frame, app: &App) { f.render_widget(preview, chunks[2]); } -fn draw_groups(f: &mut Frame, app: &App, area: ratatui::layout::Rect) { +fn draw_groups(f: &mut Frame, app: &mut App, area: ratatui::layout::Rect) { let items: Vec = app .groups .iter() @@ -207,10 +209,11 @@ fn draw_groups(f: &mut Frame, app: &App, area: ratatui::layout::Rect) { .border_style(border_style(app.focus == Focus::Groups)); let list = List::new(items).block(block); - f.render_widget(list, area); + app.group_list_state.select(Some(app.group_index)); + f.render_stateful_widget(list, area, &mut app.group_list_state); } -fn draw_files(f: &mut Frame, app: &App, area: ratatui::layout::Rect) { +fn draw_files(f: &mut Frame, app: &mut App, area: ratatui::layout::Rect) { let items: Vec = app .current_files() .iter() @@ -234,10 +237,11 @@ fn draw_files(f: &mut Frame, app: &App, area: ratatui::layout::Rect) { .border_style(border_style(app.focus == Focus::Files)); let list = List::new(items).block(block); - f.render_widget(list, area); + app.file_list_state.select(Some(app.file_index)); + f.render_stateful_widget(list, area, &mut app.file_list_state); } -fn draw_sections(f: &mut Frame, app: &App, area: ratatui::layout::Rect) { +fn draw_sections(f: &mut Frame, app: &mut App, area: ratatui::layout::Rect) { let items: Vec = app .current_sections() .iter() @@ -266,7 +270,8 @@ fn draw_sections(f: &mut Frame, app: &App, area: ratatui::layout::Rect) { .border_style(border_style(app.focus == Focus::Sections)); let list = List::new(items).block(block); - f.render_widget(list, area); + app.section_list_state.select(Some(app.section_index)); + f.render_stateful_widget(list, area, &mut app.section_list_state); } fn draw_preview(f: &mut Frame, app: &App, area: ratatui::layout::Rect) {