use crate::app::{App, ViewMode};
use crate::core::Reader;
use crate::ui::components::reading::render_chapter_content;
use crate::ui::components::{
BookmarkBrowser, ChapterBrowser, CoverView, Footer, Header, LibraryView, SearchView,
};
use crate::ui::{Component, View, count_lines};
use ratatui::{
Frame,
layout::{Constraint, Direction, Layout, Margin},
widgets::{Scrollbar, ScrollbarOrientation, ScrollbarState},
};
use ratatui_image::picker::Picker;
pub struct BookView {
header: Header,
footer: Footer,
cover_view: CoverView,
chapter_browser: ChapterBrowser,
bookmark_browser: BookmarkBrowser,
library_view: LibraryView,
search_view: SearchView,
}
impl BookView {
pub fn new() -> Self {
Self {
header: Header,
footer: Footer,
cover_view: CoverView,
chapter_browser: ChapterBrowser,
bookmark_browser: BookmarkBrowser,
library_view: LibraryView,
search_view: SearchView,
}
}
}
impl View for BookView {
fn render(&self, f: &mut Frame, app: &mut App, picker: &mut Picker) {
let size = f.area();
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3),
Constraint::Min(0),
Constraint::Length(3),
])
.split(size);
self.header.render(f, chunks[0], app, picker);
match app.mode {
ViewMode::Reading | ViewMode::Visual => {
if let Ok(chapter) = app.reader.get_chapter(app.current_chapter) {
let panes = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(50),
Constraint::Length(1),
Constraint::Percentage(50),
])
.split(chunks[1]);
let width = panes[0].width.saturating_sub(2) as usize;
let viewport_height = panes[0].height.saturating_sub(2) as usize;
let mut heights = Vec::new();
for element in &chapter.elements {
heights.push(count_lines(element, width));
}
let total_height: usize = heights.iter().sum();
app.total_height = total_height;
app.viewport_height = viewport_height;
if app.scroll_to_end && total_height > viewport_height {
app.scroll = total_height.saturating_sub(2 * viewport_height);
app.scroll_to_end = false;
}
if app.scroll_to_start {
app.scroll = 0;
app.scroll_to_start = false;
}
let chapter_title = format!(" {} ", chapter.title);
render_chapter_content(
f,
panes[0],
app,
picker,
&chapter,
app.scroll,
&heights,
viewport_height,
Some(chapter_title),
true,
);
let right_scroll = app.scroll + viewport_height;
if right_scroll < total_height {
render_chapter_content(
f,
panes[2],
app,
picker,
&chapter,
right_scroll,
&heights,
viewport_height,
None,
true,
);
}
let max_scroll = total_height.saturating_sub(viewport_height);
let scroll_position = app.scroll.min(max_scroll);
let mut scrollbar_state = ScrollbarState::new(total_height)
.position(scroll_position)
.viewport_content_length(viewport_height);
f.render_stateful_widget(
Scrollbar::new(ScrollbarOrientation::VerticalRight)
.begin_symbol(Some("↑"))
.end_symbol(Some("↓")),
chunks[1].inner(Margin {
vertical: 1,
horizontal: 0,
}),
&mut scrollbar_state,
);
}
}
ViewMode::Cover => self.cover_view.render(f, chunks[1], app, picker),
ViewMode::ChapterBrowser => self.chapter_browser.render(f, chunks[1], app, picker),
ViewMode::BookmarkBrowser | ViewMode::BookmarkRenaming => {
self.bookmark_browser.render(f, chunks[1], app, picker)
}
ViewMode::Library => self.library_view.render(f, chunks[1], app, picker),
ViewMode::Search => self.search_view.render(f, chunks[1], app, picker),
}
self.footer.render(f, chunks[2], app, picker);
}
}