use ratatui_core::buffer::Buffer;
use ratatui_core::layout::Rect;
use ratatui_core::style::Style;
use ratatui_core::text::Text as RText;
use crate::element::Element;
pub struct Viewport {
lines: Vec<String>,
height: u16,
style: Style,
wrap: bool,
}
pub fn viewport(lines: impl IntoIterator<Item = impl Into<String>>) -> Viewport {
Viewport {
lines: lines.into_iter().map(Into::into).collect(),
height: 1,
style: Style::default(),
wrap: true,
}
}
impl Viewport {
pub fn height(mut self, rows: u16) -> Self {
self.height = rows.max(1);
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn wrap(mut self, wrap: bool) -> Self {
self.wrap = wrap;
self
}
}
impl Element for Viewport {
fn height(&self, width: u16) -> u16 {
if width == 0 {
return 0;
}
self.height
}
fn render(&self, area: Rect, buf: &mut Buffer) {
if area.width == 0 || area.height == 0 {
return;
}
if self.wrap {
let text = RText::from(self.lines.join("\n")).style(self.style);
let total = eye_declare_engine::wrap::wrapped_line_count(&text, area.width);
let scroll = total.saturating_sub(area.height);
eye_declare_engine::wrap::render_wrapped(
text,
ratatui_core::layout::Alignment::Left,
scroll,
area,
buf,
);
} else {
let skip = self.lines.len().saturating_sub(area.height as usize);
for (row, line) in self.lines.iter().skip(skip).enumerate() {
if row as u16 >= area.height {
break;
}
buf.set_stringn(
area.x,
area.y + row as u16,
line,
area.width as usize,
self.style,
);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn rendered(el: &impl Element, width: u16) -> Vec<String> {
let height = el.height(width);
let area = Rect::new(0, 0, width, height);
let mut buf = Buffer::empty(area);
el.render(area, &mut buf);
(0..height)
.map(|y| {
let mut line: String = (0..width).map(|x| buf[(x, y)].symbol()).collect();
while line.ends_with(' ') {
line.pop();
}
line
})
.collect()
}
#[test]
fn shows_the_tail_of_lines() {
let el = viewport(["one", "two", "three"]).height(2).wrap(false);
assert_eq!(rendered(&el, 10), vec!["two", "three"]);
}
#[test]
fn fixed_height_even_with_less_content() {
let el = viewport(["only"]).height(3).wrap(false);
assert_eq!(Element::height(&el, 10), 3);
assert_eq!(rendered(&el, 10), vec!["only", "", ""]);
}
#[test]
fn wrapping_shows_last_wrapped_rows() {
let el = viewport(["hello world", "tail"]).height(2);
assert_eq!(rendered(&el, 6), vec!["world", "tail"]);
}
#[test]
fn no_wrap_truncates_long_lines() {
let el = viewport(["abcdefghij"]).height(1).wrap(false);
assert_eq!(rendered(&el, 4), vec!["abcd"]);
}
}