use cursive::Vec2;
use std::collections::HashMap;
use std::rc::Rc;
use crate::ui::article::lines::{Line, LinesWrapper};
use crate::wiki::article::{Article, Element, LanguageLink};
pub struct ArticleContent {
article: Article,
rendered_lines: Vec<Line>,
anchors: HashMap<String, usize>,
links: Vec<(usize, Vec2)>,
current_link: usize,
}
impl ArticleContent {
pub fn new(article: Article) -> Self {
ArticleContent {
article,
rendered_lines: Vec::new(),
anchors: HashMap::new(),
links: Vec::new(),
current_link: 0,
}
}
pub fn required_size(&mut self, size: Vec2) -> Vec2 {
let content = self
.article
.content()
.map(|x| x.cloned())
.map(|x| x.collect::<Vec<Element>>());
if content.is_none() {
return Vec2::zero();
}
let required_width = LinesWrapper::new(
size.x,
Rc::new(content.unwrap()),
)
.required_width();
if self.rendered_lines.is_empty() {
self.compute_lines(size);
}
if required_width == 0 {
return Vec2::new(size.x, self.rendered_lines.len());
}
debug!(
"required size is ({},{})",
required_width,
self.rendered_lines.len()
);
Vec2::new(required_width, self.rendered_lines.len())
}
pub fn compute_lines(&mut self, size: Vec2) {
debug!("rendering the article with a size constraint of {:?}", size);
let content = self
.article
.content()
.map(|x| x.cloned())
.map(|x| x.collect::<Vec<Element>>());
if content.is_none() {
return;
}
let lines_wrapper = LinesWrapper::new(
size.x.saturating_sub(1),
Rc::new(content.unwrap()),
)
.wrap_lines();
self.rendered_lines = lines_wrapper.rendered_lines;
self.anchors = lines_wrapper.anchors;
let link_id = self.current_link_element_id();
self.links = lines_wrapper.links;
self.select_link_by_id(link_id);
debug!("sucessfully rendered '{}' lines", self.rendered_lines.len());
}
pub fn get_rendered_lines(&self) -> impl Iterator<Item = &Line> {
self.rendered_lines.iter()
}
pub fn anchor(&self, anchor: &str) -> Option<usize> {
self.anchors.get(anchor).copied()
}
pub fn current_link_element_id(&self) -> usize {
if self.links.len() <= self.current_link {
return 0;
}
self.links[self.current_link].0
}
pub fn current_link_coords(&self) -> Vec2 {
if self.links.len() <= self.current_link {
return Vec2::zero();
}
self.links[self.current_link].1
}
pub fn select_link_by_id(&mut self, id: usize) {
if let Some(new_link) = self.links.iter().position(|x| x.0 == id) {
self.current_link = new_link;
}
}
pub fn links(&self) -> impl Iterator<Item = &(usize, Vec2)> + DoubleEndedIterator {
self.links.iter()
}
pub fn current_link_idx(&self) -> usize {
self.current_link
}
pub fn has_links(&self) -> bool {
!self.links.is_empty()
}
pub fn select_next_link(&mut self) {
if self.current_link + 1 >= self.links.len() {
return;
}
self.current_link = self
.links
.iter()
.position(|(id, _)| id > &self.current_link_element_id())
.unwrap_or(self.current_link);
}
pub fn select_prev_link(&mut self) {
if let Some(link) = self
.links
.iter()
.rev()
.skip(self.links.len().saturating_sub(self.current_link))
.find(|(id, _)| id < &self.current_link_element_id())
{
let position = self.links.iter().position(|x| x == link).unwrap();
self.current_link = position;
}
}
pub fn element_by_id(&self, id: usize) -> Option<Element> {
self.article
.content()
.and_then(|mut x| x.find(|e| e.id() == id))
.cloned()
}
pub fn element_by_pos(&self, pos: Vec2) -> Option<Element> {
if pos.y >= self.rendered_lines.len() || self.rendered_lines.is_empty() {
return None;
}
let line = &self.rendered_lines[pos.y];
let mut x_offset = 0;
for element in line {
if pos.x >= x_offset && pos.x <= x_offset.saturating_add(element.width) {
return self.element_by_id(element.id);
}
x_offset += element.width;
}
None
}
pub fn language_links(&self) -> Option<Vec<LanguageLink>> {
self.article.language_links.clone()
}
}