use crate::app::{App, ViewMode};
use crate::ui::{Component, centered_rect, get_border_type};
use ratatui::style::{Color, Style, Stylize};
use ratatui::{
Frame,
layout::{Constraint, Direction, Layout, Rect},
text::Text,
widgets::{Block, Borders, Clear, List, ListItem, Paragraph},
};
use ratatui_image::picker::Picker;
pub struct BookmarkBrowser;
impl Component for BookmarkBrowser {
fn render(&self, f: &mut Frame, area: Rect, app: &mut App, _picker: &mut Picker) {
let area = centered_rect(80, 80, area);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(0), Constraint::Length(3)])
.split(area);
let main_area = chunks[0];
let help_area = chunks[1];
let inner_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(40), Constraint::Percentage(60)])
.split(main_area);
let list_area = inner_chunks[0];
let preview_area = inner_chunks[1];
self.render_bookmark_list(f, list_area, app);
self.render_bookmark_preview(f, preview_area, app);
self.render_help(f, help_area, app);
if app.mode == ViewMode::BookmarkRenaming {
self.render_renaming_popup(f, area, app);
}
}
}
impl BookmarkBrowser {
fn render_renaming_popup(&self, f: &mut Frame, area: Rect, app: &mut App) {
let area = centered_rect(60, 20, area);
f.render_widget(Clear, area);
let border_type = get_border_type(&app.config);
let block = Block::default()
.borders(Borders::ALL)
.border_type(border_type)
.title(" Rename Bookmark ");
let input = Paragraph::new(app.bookmark_rename_input.as_str())
.block(block)
.slow_blink()
.style(Style::default().fg(Color::Yellow));
f.render_widget(input, area);
f.set_cursor_position((
area.x + app.bookmark_rename_cursor_position as u16 + 1,
area.y + 1,
));
}
fn render_bookmark_list(&self, f: &mut Frame, area: Rect, app: &mut App) {
let mut list_items = Vec::new();
for (i, bookmark) in app.bookmarks.iter().enumerate() {
let content = format!("{}. {}", i + 1, bookmark.name);
let mut text = Text::from(content);
if app.bookmark_list_state.selected() == Some(i) {
text = text.style(Style::default().fg(Color::Yellow));
}
list_items.push(ListItem::new(text));
}
if list_items.is_empty() {
list_items.push(ListItem::new(Text::from("No bookmarks found.")));
}
let border_type = get_border_type(&app.config);
let list = List::new(list_items)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(border_type)
.title(" Bookmarks "),
)
.highlight_style(Style::default().fg(Color::Yellow))
.highlight_symbol("> ");
f.render_stateful_widget(list, area, &mut app.bookmark_list_state);
}
fn render_bookmark_preview(&self, f: &mut Frame, area: Rect, app: &mut App) {
let preview_content = if let Some(selected) = app.bookmark_list_state.selected() {
if let Some(bookmark) = app.bookmarks.get(selected) {
let mut text = format!("Chapter: {}\n", bookmark.chapter_title);
text.push_str(&format!("Position: {}\n\n", bookmark.scroll_position));
text.push_str(&bookmark.text_preview);
Text::from(text)
} else {
Text::from("No bookmark selected.")
}
} else {
Text::from("No bookmark selected.")
};
let border_type = get_border_type(&app.config);
let preview_paragraph = Paragraph::new(preview_content)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(border_type)
.title(" Preview "),
)
.style(Style::default().fg(Color::White))
.wrap(ratatui::widgets::Wrap { trim: true });
f.render_widget(preview_paragraph, area);
}
fn render_help(&self, f: &mut Frame, area: Rect, app: &mut App) {
let border_type = get_border_type(&app.config);
let help_text = Text::from("Enter: Jump | d: Delete | r: Rename | Esc/b: Back");
let help_paragraph = Paragraph::new(help_text)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(border_type),
)
.style(Style::default().fg(Color::Gray));
f.render_widget(help_paragraph, area);
}
}