diff options
| -rw-r--r-- | src/app.rs | 2 | ||||
| -rw-r--r-- | src/tui/ui.rs | 574 |
2 files changed, 5 insertions, 571 deletions
@@ -137,7 +137,7 @@ impl App { self.bibiman.select_previous_entry(amount); } CurrentArea::TagArea => { - self.bibiman.select_next_tag(amount); + self.bibiman.select_previous_tag(amount); } _ => {} }, diff --git a/src/tui/ui.rs b/src/tui/ui.rs index da9a7d5..9e561c3 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -195,6 +195,8 @@ pub fn render_file_info(app: &mut App, frame: &mut Frame, rect: Rect) { BOX_UNSELECTED_BORDER_STYLE }); + frame.render_widget(block, rect); + let [file_area, keyword_area, count_area] = Layout::horizontal([ Constraint::Fill(3), Constraint::Fill(4), @@ -709,8 +711,8 @@ fn render_cursor(app: &mut App, frame: &mut Frame, area: Rect) { + Input::default() .with_value(app.input.value().to_string()) .visual_cursor() as u16 - + 2, - area.bottom().saturating_sub(1), + + 1, + area.bottom().saturating_sub(2), ); frame.render_widget( Clear, @@ -724,571 +726,3 @@ fn render_cursor(app: &mut App, frame: &mut Frame, area: Rect) { frame.set_cursor_position(Position::new(x, y)); } } - -// impl Widget for &mut App { -// fn render(self, area: Rect, buf: &mut Buffer) { -// let [header_area, main_area, footer_area] = Layout::vertical([ -// Constraint::Length(1), -// Constraint::Fill(1), -// Constraint::Length(3), -// ]) -// .areas(area); - -// let [list_area, item_area] = -// Layout::vertical([Constraint::Fill(1), Constraint::Fill(1)]).areas(main_area); - -// let [entry_area, entry_info_area] = -// Layout::vertical([Constraint::Fill(1), Constraint::Length(2)]).areas(list_area); - -// let [tag_area, info_area] = -// Layout::horizontal([Constraint::Max(25), Constraint::Min(35)]).areas(item_area); - -// // Render header and footer -// Bibiman::render_header(header_area, buf); -// self.bibiman.render_footer(footer_area, buf); -// // Render list area where entry gets selected -// self.bibiman.render_entrytable(entry_area, buf); -// self.bibiman.render_file_info(entry_info_area, buf); -// // Render infos related to selected entry -// self.bibiman.render_taglist(tag_area, buf); -// self.bibiman.render_selected_item(info_area, buf); -// } -// } - -// impl Bibiman { -// pub fn render_header(area: Rect, buf: &mut Buffer) { -// Paragraph::new("BIBIMAN – BibLaTeX manager TUI") -// .bold() -// .fg(MAIN_BLUE_COLOR) -// .centered() -// .render(area, buf); -// } - -// pub fn render_footer(&mut self, area: Rect, buf: &mut Buffer) { -// match &self.current_area { -// CurrentArea::SearchArea => { -// let search_title = { -// match self.former_area { -// Some(FormerArea::EntryArea) => { -// let search_title = " Search Entries ".to_string(); -// search_title -// } -// Some(FormerArea::TagArea) => { -// let search_title = " Search Keywords ".to_string(); -// search_title -// } -// _ => { -// let search_title = " Search ".to_string(); -// search_title -// } -// } -// }; - -// let block = Block::bordered() -// .title(Line::styled(search_title, BOX_SELECTED_TITLE_STYLE)) -// .border_style(BOX_SELECTED_BOX_STYLE) -// .border_set(symbols::border::THICK); -// Paragraph::new(self.search_struct.search_string.clone()) -// .block(block) -// .render(area, buf); -// } -// _ => { -// let style_emph = Style::new().bold().fg(TEXT_FG_COLOR); -// let block = Block::bordered() -// .title(Line::raw(" Basic Commands ").centered()) -// .border_style(BOX_UNSELECTED_BORDER_STYLE) -// .border_set(symbols::border::PLAIN); -// Paragraph::new(Line::from(vec![ -// Span::styled("j/k: ", style_emph), -// Span::raw("move | "), -// Span::styled("g/G: ", style_emph), -// Span::raw("top/bottom | "), -// Span::styled("TAB: ", style_emph), -// Span::raw("switch tab | "), -// Span::styled("y: ", style_emph), -// Span::raw("yank citekey | "), -// Span::styled("e: ", style_emph), -// Span::raw("edit | "), -// Span::styled("/: ", style_emph), -// Span::raw("search | "), -// Span::styled("o/u: ", style_emph), -// Span::raw("open PDF/DOI"), -// ])) -// .block(block) -// .centered() -// .render(area, buf); -// } -// } -// } - -// // Render info of the current file and process -// // 1. Basename of the currently loaded file -// // 2. Keyword by which the entries are filtered at the moment -// // 3. Currently selected entry and total count of entries -// pub fn render_file_info(&mut self, area: Rect, buf: &mut Buffer) { -// let block = Block::new() // can also be Block::new -// // Leave Top empty to simulate one large box with borders of entry list -// .borders(Borders::LEFT | Borders::RIGHT | Borders::BOTTOM) -// .border_set(if let CurrentArea::EntryArea = self.current_area { -// symbols::border::THICK -// } else { -// symbols::border::PLAIN -// }) -// .border_style(if let CurrentArea::EntryArea = self.current_area { -// BOX_SELECTED_BOX_STYLE -// } else { -// BOX_UNSELECTED_BORDER_STYLE -// }); - -// let [file_area, keyword_area, count_area] = Layout::horizontal([ -// Constraint::Fill(3), -// Constraint::Fill(4), -// Constraint::Fill(1), -// ]) -// .horizontal_margin(1) -// .areas(area); - -// Line::from(vec![ -// Span::raw("File: ").bold(), -// Span::raw(self.main_bibfile.file_name().unwrap().to_string_lossy()).bold(), -// ]) -// .bg(HEADER_FOOTER_BG) -// .render(file_area, buf); - -// Line::from(if !self.tag_list.selected_keywords.is_empty() { -// vec![ -// Span::raw("Selected keywords: "), -// // Show all keywords in correct order if list is filtered -// // successively by multiple keywords -// Span::raw(self.tag_list.selected_keywords.join(" → ")) -// .bold() -// .green(), -// ] -// } else { -// vec![Span::raw(" ")] -// }) -// .bg(HEADER_FOOTER_BG) -// .render(keyword_area, buf); - -// Line::from(if self.entry_table.entry_table_state.selected().is_some() { -// vec![ -// Span::raw((self.entry_table.entry_table_state.selected().unwrap() + 1).to_string()) -// .bold(), -// Span::raw("/"), -// Span::raw(self.entry_table.entry_table_items.len().to_string()), -// ] -// } else { -// vec![Span::raw("No entries")] -// }) -// .right_aligned() -// .bg(HEADER_FOOTER_BG) -// .render(count_area, buf); - -// // Render that stuff -// Widget::render(block, area, buf) -// } - -// pub fn render_entrytable(&mut self, area: Rect, buf: &mut Buffer) { -// let block = Block::new() // can also be Block::new -// .title( -// Line::styled( -// " Bibliographic Entries ", -// if let CurrentArea::EntryArea = self.current_area { -// BOX_SELECTED_TITLE_STYLE -// } else { -// BOX_UNSELECTED_TITLE_STYLE -// }, -// ) -// .centered(), -// ) -// .borders(Borders::LEFT | Borders::RIGHT | Borders::TOP) -// .border_set(if let CurrentArea::EntryArea = self.current_area { -// symbols::border::THICK -// } else { -// symbols::border::PLAIN -// }) -// .border_style(if let CurrentArea::EntryArea = self.current_area { -// BOX_SELECTED_BOX_STYLE -// } else { -// BOX_UNSELECTED_BORDER_STYLE -// }); - -// let header_style = Style::default() -// .bold() -// .fg(TEXT_FG_COLOR) -// .bg(HEADER_FOOTER_BG); - -// let header_selected_col = Style::default().underlined(); - -// let header = Row::new(vec![ -// Cell::from(Line::from(vec![ -// { -// if let EntryTableColumn::Authors = self.entry_table.entry_table_selected_column -// { -// Span::styled("Author", header_selected_col) -// } else { -// Span::raw("Author") -// } -// }, -// { -// if let EntryTableColumn::Authors = self.entry_table.entry_table_sorted_by_col { -// Span::raw(format!( -// " {}", -// if self.entry_table.entry_table_reversed_sort { -// SORTED_ENTRIES_REVERSED -// } else { -// SORTED_ENTRIES -// } -// )) -// } else { -// Span::raw("") -// } -// }, -// ])), -// Cell::from(Line::from(vec![ -// { -// if let EntryTableColumn::Title = self.entry_table.entry_table_selected_column { -// Span::styled("Title", header_selected_col) -// } else { -// Span::raw("Title") -// } -// }, -// { -// if let EntryTableColumn::Title = self.entry_table.entry_table_sorted_by_col { -// Span::raw(format!( -// " {}", -// if self.entry_table.entry_table_reversed_sort { -// SORTED_ENTRIES_REVERSED -// } else { -// SORTED_ENTRIES -// } -// )) -// } else { -// Span::raw("") -// } -// }, -// ])), -// Cell::from(Line::from(vec![ -// { -// if let EntryTableColumn::Year = self.entry_table.entry_table_selected_column { -// Span::styled("Year", header_selected_col) -// } else { -// Span::raw("Year") -// } -// }, -// { -// if let EntryTableColumn::Year = self.entry_table.entry_table_sorted_by_col { -// Span::raw(format!( -// " {}", -// if self.entry_table.entry_table_reversed_sort { -// SORTED_ENTRIES_REVERSED -// } else { -// SORTED_ENTRIES -// } -// )) -// } else { -// Span::raw("") -// } -// }, -// ])), -// Cell::from(Line::from(vec![ -// { -// if let EntryTableColumn::Pubtype = self.entry_table.entry_table_selected_column -// { -// Span::styled("Pubtype", header_selected_col) -// } else { -// Span::raw("Pubtype") -// } -// }, -// { -// if let EntryTableColumn::Pubtype = self.entry_table.entry_table_sorted_by_col { -// Span::raw(format!( -// " {}", -// if self.entry_table.entry_table_reversed_sort { -// SORTED_ENTRIES_REVERSED -// } else { -// SORTED_ENTRIES -// } -// )) -// } else { -// Span::raw("") -// } -// }, -// ])), -// ]) -// .style(header_style) -// .height(1); - -// // Iterate over vector storing each entries data fields -// let rows = self -// .entry_table -// .entry_table_items -// .iter_mut() -// .enumerate() -// .map(|(_i, data)| { -// let item = data.ref_vec(); -// item.into_iter() -// .map(|content| Cell::from(Text::from(format!("{content}")))) -// .collect::<Row>() -// .style(Style::new().fg(TEXT_FG_COLOR)) //.bg(alternate_colors(i))) -// .height(1) -// }); -// let entry_table = Table::new( -// rows, -// [ -// Constraint::Percentage(20), -// Constraint::Fill(1), -// Constraint::Length( -// if let EntryTableColumn::Year = self.entry_table.entry_table_sorted_by_col { -// 6 -// } else { -// 4 -// }, -// ), -// Constraint::Percentage(10), -// ], -// ) -// .block(block) -// .header(header) -// .column_spacing(2) -// .row_highlight_style(SELECTED_STYLE) -// // .bg(Color::Black) -// .highlight_spacing(HighlightSpacing::Always); -// StatefulWidget::render( -// entry_table, -// area, -// buf, -// &mut self.entry_table.entry_table_state, -// ); - -// // Scrollbar for entry table -// let scrollbar = Scrollbar::default() -// .orientation(ScrollbarOrientation::VerticalRight) -// .track_symbol(None) -// .begin_symbol(SCROLLBAR_UPPER_CORNER) -// .end_symbol(None) -// .thumb_style(Style::new().fg(Color::DarkGray)); - -// if let CurrentArea::EntryArea = self.current_area { -// // render the scrollbar -// StatefulWidget::render( -// scrollbar, -// area, -// buf, -// &mut self.entry_table.entry_scroll_state, -// ); -// } -// } - -// pub fn render_selected_item(&mut self, area: Rect, buf: &mut Buffer) { -// // We get the info depending on the item's state. -// let style_value = Style::new().bold().fg(TEXT_FG_COLOR); -// let style_value_sec = Style::new() -// .add_modifier(Modifier::ITALIC) -// .fg(TEXT_FG_COLOR); -// let lines = { -// // if self.entry_table.entry_table_items.len() > 0 { -// if self.entry_table.entry_table_state.selected().is_some() { -// let idx = self.entry_table.entry_table_state.selected().unwrap(); -// let cur_entry = &self.entry_table.entry_table_items[idx]; -// let mut lines = vec![]; -// lines.push(Line::from(vec![ -// Span::styled("Authors: ", style_value), -// // Span::styled(cur_entry.authors.clone(), Style::new().green()), -// Span::styled(cur_entry.authors(), Style::new().green()), -// ])); -// lines.push(Line::from(vec![ -// Span::styled("Title: ", style_value), -// Span::styled(cur_entry.title(), Style::new().magenta()), -// ])); -// lines.push(Line::from(vec![ -// Span::styled("Year: ", style_value), -// Span::styled(cur_entry.year(), Style::new().light_magenta()), -// ])); -// // Render keywords in info box in Markdown code style -// if !cur_entry.keywords.is_empty() { -// let kw: Vec<&str> = cur_entry -// .keywords -// .split(",") -// .map(|k| k.trim()) -// .filter(|k| !k.is_empty()) -// .collect(); -// let mut content = vec![Span::styled("Keywords: ", style_value)]; -// for k in kw { -// // Add half block highlighted in bg color to enlarge block -// content.push(Span::raw("▐").fg(HEADER_FOOTER_BG)); -// content.push(Span::styled( -// k, -// Style::default().bg(HEADER_FOOTER_BG).fg( -// // Highlight selected keyword green -// if self.tag_list.selected_keywords.iter().any(|e| e == k) { -// Color::Green -// } else { -// TEXT_FG_COLOR -// }, -// ), -// )); -// content.push(Span::raw("▌").fg(HEADER_FOOTER_BG)); -// } -// lines.push(Line::from(content)) -// } -// if !cur_entry.doi_url.is_empty() || !cur_entry.filepath.is_empty() { -// lines.push(Line::raw("")); -// } -// if !cur_entry.doi_url.is_empty() { -// lines.push(Line::from(vec![ -// Span::styled("DOI/URL: ", style_value_sec), -// Span::styled( -// cur_entry.doi_url(), -// Style::default().fg(TEXT_FG_COLOR).underlined(), -// ), -// ])); -// } -// if !cur_entry.filepath.is_empty() { -// lines.push(Line::from(vec![ -// Span::styled("File: ", style_value_sec), -// Span::styled(cur_entry.filepath(), Style::default().fg(TEXT_FG_COLOR)), -// ])); -// } -// lines.push(Line::from("")); -// lines.push(Line::from(vec![Span::styled( -// cur_entry.abstract_text.clone(), -// Style::default().fg(TEXT_FG_COLOR), -// )])); -// lines -// } else { -// let lines = vec![ -// Line::from(" "), -// Line::from("No entry selected".bold().into_centered_line().red()), -// ]; -// lines -// } -// }; -// let info = Text::from(lines); - -// // We show the list item's info under the list in this paragraph -// let block = Block::bordered() -// .title(Line::raw(" Entry Information ").centered().bold()) -// // .borders(Borders::TOP) -// .border_set(symbols::border::PLAIN) -// .border_style(BOX_UNSELECTED_BORDER_STYLE) -// // .bg(Color::Black) -// .padding(Padding::horizontal(1)); - -// // INFO: '.line_count' method only possible with unstable-rendered-line-info feature -> API might change: https://github.com/ratatui/ratatui/issues/293#ref-pullrequest-2027056434 -// let box_height = Paragraph::new(info.clone()) -// .block(block.clone()) -// .wrap(Wrap { trim: false }) -// .line_count(area.width); -// // Make sure to allow scroll only if text is larger than the rendered area and stop scrolling when last line is reached -// let scroll_height = { -// if self.entry_table.entry_info_scroll == 0 { -// self.entry_table.entry_info_scroll -// } else if area.height > box_height as u16 { -// self.entry_table.entry_info_scroll = 0; -// self.entry_table.entry_info_scroll -// } else if self.entry_table.entry_info_scroll > (box_height as u16 + 2 - area.height) { -// self.entry_table.entry_info_scroll = box_height as u16 + 2 - area.height; -// self.entry_table.entry_info_scroll -// } else { -// self.entry_table.entry_info_scroll -// } -// }; - -// // We can now render the item info -// Paragraph::new(info) -// .block( -// block -// // Render arrows to show that info box has content outside the block -// .title_bottom( -// Line::from( -// if box_height > area.height.into() -// && self.entry_table.entry_info_scroll -// < box_height as u16 + 2 - area.height -// { -// " ▼ " -// } else { -// "" -// }, -// ) -// .alignment(Alignment::Right), -// ) -// .title_top( -// Line::from(if scroll_height > 0 { " ▲ " } else { "" }) -// .alignment(Alignment::Right), -// ), -// ) -// // .fg(TEXT_FG_COLOR) -// .wrap(Wrap { trim: false }) -// .scroll((scroll_height, 0)) -// .render(area, buf); -// } - -// pub fn render_taglist(&mut self, area: Rect, buf: &mut Buffer) { -// let block = Block::bordered() -// .title( -// Line::styled( -// " Keywords ", -// if let CurrentArea::TagArea = self.current_area { -// BOX_SELECTED_TITLE_STYLE -// } else { -// BOX_UNSELECTED_TITLE_STYLE -// }, -// ) -// .centered(), -// ) -// .border_set(if let CurrentArea::TagArea = self.current_area { -// symbols::border::THICK -// } else { -// symbols::border::PLAIN -// }) -// .border_style(if let CurrentArea::TagArea = self.current_area { -// BOX_SELECTED_BOX_STYLE -// } else { -// BOX_UNSELECTED_BORDER_STYLE -// }); -// // .bg(Color::Black); - -// // Iterate through all elements in the `items` and stylize them. -// let items: Vec<ListItem> = self -// .tag_list -// .tag_list_items -// .iter() -// .enumerate() -// .map(|(_i, todo_item)| { -// // let color = alternate_colors(i); -// ListItem::from(todo_item.to_owned()) //.bg(color) -// }) -// .collect(); - -// // Create a List from all list items and highlight the currently selected one -// let list = List::new(items) -// .block(block) -// .highlight_style(SELECTED_STYLE) -// // .highlight_symbol("> ") -// .highlight_spacing(HighlightSpacing::Always); - -// // Save list length for calculating scrollbar need -// // Add 2 to compmensate lines of the block border -// let list_length = list.len() + 2; - -// // We need to disambiguate this trait method as both `Widget` and `StatefulWidget` share the -// // same method name `render`. -// StatefulWidget::render(list, area, buf, &mut self.tag_list.tag_list_state); - -// // Scrollbar for keyword list -// let scrollbar = Scrollbar::default() -// .orientation(ScrollbarOrientation::VerticalRight) -// .track_symbol(None) -// .begin_symbol(SCROLLBAR_UPPER_CORNER) -// .end_symbol(SCROLLBAR_LOWER_CORNER) -// .thumb_style(Style::new().fg(Color::DarkGray)); - -// if list_length > area.height.into() { -// if let CurrentArea::TagArea = self.current_area { -// // render the scrollbar -// StatefulWidget::render(scrollbar, area, buf, &mut self.tag_list.tag_scroll_state); -// } -// } -// } -// } |
