diff --git a/src/app.rs b/src/app.rs index 42733f3..d67b1c9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,6 +10,13 @@ pub enum ActivePane { PendingPublications, } +#[derive(Serialize, Deserialize, Default, PartialEq)] +pub enum ActiveTab { + #[default] + Seeding, + Snowballing, +} + #[derive(Serialize, Deserialize, Default)] pub enum SnowballingStep { #[default] @@ -84,6 +91,11 @@ pub struct App { /// UI state: active pane pub active_pane: ActivePane, + /// UI state: active window + pub active_tab: ActiveTab, + + pub seeding_input: String, + pub snowballing_iteration: usize, pub snowballing_step: SnowballingStep, @@ -100,94 +112,131 @@ pub struct App { // TODO: Implement export of included papers into zotero (Use RIS format somehow) impl App { pub fn handle_key(&mut self, key: KeyCode) { - match key { - KeyCode::Char('q') => { - self.should_quit = true; - } - KeyCode::Enter => match self.active_pane { - ActivePane::IncludedPublications => { - if let Some(idx) = self.included_list_state.selected() { - open::that(&self.included_publications[idx].id) - .unwrap(); + if KeyCode::Esc == key { + self.should_quit = true; + return; + } + + match self.active_tab { + ActiveTab::Seeding => match key { + KeyCode::Tab => { + self.active_tab = ActiveTab::Snowballing; + } + KeyCode::Enter => { + // TODO: Actually add paper to included list + self.seeding_input.clear(); + } + KeyCode::Char(to_insert) => self.seeding_input.push(to_insert), + KeyCode::Backspace => { + if self.seeding_input.len() > 0 { + self.seeding_input + .truncate(self.seeding_input.len() - 1); } } - ActivePane::PendingPublications => { - if let Some(idx) = self.pending_list_state.selected() { - open::that(&self.pending_publications[idx].id).unwrap(); + _ => {} + }, + ActiveTab::Snowballing => match key { + KeyCode::Enter => match self.active_pane { + ActivePane::IncludedPublications => { + if let Some(idx) = self.included_list_state.selected() { + open::that(&self.included_publications[idx].id) + .unwrap(); + } + } + ActivePane::PendingPublications => { + if let Some(idx) = self.pending_list_state.selected() { + open::that(&self.pending_publications[idx].id) + .unwrap(); + } + } + }, + KeyCode::Tab => { + self.active_tab = ActiveTab::Seeding; + } + KeyCode::Char('j') => match self.active_pane { + ActivePane::IncludedPublications => { + let i = match self.included_list_state.selected() { + Some(i) => { + if i >= self + .included_publications + .len() + .wrapping_sub(1) + { + 0 + } else { + i + 1 + } + } + None => 0, + }; + self.included_list_state.select(Some(i)); + } + ActivePane::PendingPublications => { + let i = match self.pending_list_state.selected() { + Some(i) => { + if i >= self + .pending_publications + .len() + .wrapping_sub(1) + { + 0 + } else { + i + 1 + } + } + None => 0, + }; + self.pending_list_state.select(Some(i)); + } + }, + KeyCode::Char('k') => match self.active_pane { + ActivePane::IncludedPublications => { + let i = match self.included_list_state.selected() { + Some(i) => { + if i == 0 { + self.included_publications + .len() + .wrapping_sub(1) + } else { + i - 1 + } + } + None => 0, + }; + self.included_list_state.select(Some(i)); + } + ActivePane::PendingPublications => { + let i = match self.pending_list_state.selected() { + Some(i) => { + if i == 0 { + self.pending_publications + .len() + .wrapping_sub(1) + } else { + i - 1 + } + } + None => 0, + }; + self.pending_list_state.select(Some(i)); + } + }, + KeyCode::Char('h') => { + self.active_pane = ActivePane::IncludedPublications; + + if let None = self.included_list_state.selected() { + self.included_list_state.select(Some(0)); } } - }, - KeyCode::Char('j') => match self.active_pane { - ActivePane::IncludedPublications => { - let i = match self.included_list_state.selected() { - Some(i) => { - if i >= self.included_publications.len() - 1 { - 0 - } else { - i + 1 - } - } - None => 0, - }; - self.included_list_state.select(Some(i)); - } - ActivePane::PendingPublications => { - let i = match self.pending_list_state.selected() { - Some(i) => { - if i >= self.pending_publications.len() - 1 { - 0 - } else { - i + 1 - } - } - None => 0, - }; - self.pending_list_state.select(Some(i)); - } - }, - KeyCode::Char('k') => match self.active_pane { - ActivePane::IncludedPublications => { - let i = match self.included_list_state.selected() { - Some(i) => { - if i == 0 { - self.included_publications.len() - 1 - } else { - i - 1 - } - } - None => 0, - }; - self.included_list_state.select(Some(i)); - } - ActivePane::PendingPublications => { - let i = match self.pending_list_state.selected() { - Some(i) => { - if i == 0 { - self.pending_publications.len() - 1 - } else { - i - 1 - } - } - None => 0, - }; - self.pending_list_state.select(Some(i)); - } - }, - KeyCode::Char('h') => { - self.active_pane = ActivePane::IncludedPublications; + KeyCode::Char('l') => { + self.active_pane = ActivePane::PendingPublications; - if let None = self.included_list_state.selected() { - self.included_list_state.select(Some(0)); + if let None = self.pending_list_state.selected() { + self.pending_list_state.select(Some(0)); + } } - } - KeyCode::Char('l') => { - self.active_pane = ActivePane::PendingPublications; - - if let None = self.pending_list_state.selected() { - self.pending_list_state.select(Some(0)); - } - } - _ => {} + _ => {} + }, } } } diff --git a/src/ui.rs b/src/ui.rs index 62a198c..4956bdd 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,5 +1,5 @@ use crate::{ - app::{ActivePane, App}, + app::{ActivePane, ActiveTab, App}, snowballing::Publication, }; use ratatui::{ @@ -7,10 +7,60 @@ use ratatui::{ layout::{Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style, Stylize}, text::{Line, Span}, - widgets::{Block, Borders, List, ListItem}, + widgets::{Block, Borders, List, ListItem, ListState, Paragraph}, }; pub fn draw(f: &mut Frame, app: &mut App) { + match app.active_tab { + ActiveTab::Seeding => draw_seeding_tab(f, app), + ActiveTab::Snowballing => draw_snowballing_tab(f, app), + } +} + +fn draw_seeding_tab(f: &mut Frame, app: &mut App) { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(3), Constraint::Length(3)]) + .split(f.area()); + + // Included publication list + + let items = create_publication_item_list( + &app.included_publications, + None, + chunks[0].width.saturating_sub(4) as usize, + false, + ); + + let list = List::new(items).block( + Block::default() + .title_top(Line::from("Included Publications").centered()) + .title_top( + Line::from(Span::styled( + format!("{} entries", app.included_publications.len()), + Style::default().fg(Color::Yellow), + )) + .right_aligned(), + ) + .borders(Borders::ALL), + ); + + f.render_stateful_widget( + list, + chunks[0], + &mut ListState::default() + .with_selected(Some(app.included_publications.len() - 1)), + ); + + // Text entry + + let input = Paragraph::new(app.seeding_input.as_str()) + .block(Block::bordered().title("Input")); + + f.render_widget(input, chunks[1]); +} + +fn draw_snowballing_tab(f: &mut Frame, app: &mut App) { let chunks = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(25), Constraint::Percentage(75)])