Implement seeding tab layout
This commit is contained in:
parent
e30b22199f
commit
4e46184f37
213
src/app.rs
213
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));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
54
src/ui.rs
54
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)])
|
||||
|
||||
Loading…
Reference in New Issue
Block a user