use std::sync::Arc;
use crate::console::{Console, ConsoleOptions, Justify, Overflow};
use crate::measure::Measurement;
use crate::protocol::Renderable;
use crate::segment::Segment;
use crate::style::StyleType;
use crate::text::Text;
#[derive(Clone, Default)]
pub struct Renderables {
renderables: Vec<Arc<dyn Renderable + Send + Sync>>,
}
impl Renderables {
pub fn new(renderables: Vec<Arc<dyn Renderable + Send + Sync>>) -> Self {
Renderables { renderables }
}
pub fn append(&mut self, renderable: Arc<dyn Renderable + Send + Sync>) {
self.renderables.push(renderable);
}
pub fn renderables(&self) -> &[Arc<dyn Renderable + Send + Sync>] {
&self.renderables
}
}
impl Renderable for Renderables {
fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
let options = options.reset_height();
let mut segments: Vec<Segment> = Vec::new();
let mut first = true;
for renderable in &self.renderables {
let rendered = console.render(renderable.as_ref(), Some(&options));
if rendered.is_empty() {
continue;
}
if !first {
segments.push(Segment::line());
}
first = false;
segments.extend(rendered);
}
segments
}
fn measure(&self, console: &Console, options: &ConsoleOptions) -> Measurement {
let dimensions: Vec<Measurement> = self
.renderables
.iter()
.map(|renderable| Measurement::get(console, options, renderable.as_ref()))
.collect();
if dimensions.is_empty() {
return Measurement::new(1, 1);
}
Measurement::new(
dimensions.iter().map(|d| d.minimum).max().unwrap_or(0),
dimensions.iter().map(|d| d.maximum).max().unwrap_or(0),
)
}
}
#[derive(Clone, Debug, Default)]
pub struct Lines {
lines: Vec<Text>,
}
impl Lines {
pub fn new(lines: Vec<Text>) -> Self {
Lines { lines }
}
pub fn lines(&self) -> &[Text] {
&self.lines
}
pub fn lines_mut(&mut self) -> &mut Vec<Text> {
&mut self.lines
}
pub fn len(&self) -> usize {
self.lines.len()
}
pub fn is_empty(&self) -> bool {
self.lines.is_empty()
}
pub fn append(&mut self, line: Text) {
self.lines.push(line);
}
pub fn extend(&mut self, lines: impl IntoIterator<Item = Text>) {
self.lines.extend(lines);
}
pub fn pop(&mut self, index: Option<usize>) -> Option<Text> {
match index {
None => self.lines.pop(),
Some(index) if index < self.lines.len() => Some(self.lines.remove(index)),
Some(_) => None,
}
}
pub fn justify(
&mut self,
console: &Console,
width: usize,
justify: Justify,
overflow: Overflow,
) {
let cells = |line: &Text| crate::cells::cell_len(line.plain());
match justify {
Justify::Left => {
for line in &mut self.lines {
line.truncate(width, Some(overflow), true);
}
}
Justify::Center => {
for line in &mut self.lines {
line.rstrip();
line.truncate(width, Some(overflow), false);
line.pad_left(width.saturating_sub(cells(line)) / 2, ' ');
line.pad_right(width.saturating_sub(cells(line)), ' ');
}
}
Justify::Right => {
for line in &mut self.lines {
line.rstrip();
line.truncate(width, Some(overflow), false);
line.pad_left(width.saturating_sub(cells(line)), ' ');
}
}
Justify::Full => {
let last = self.lines.len().saturating_sub(1);
for line_index in 0..last {
let line = &self.lines[line_index];
let words = line.split(" ", false, false);
let words_size: usize = words.iter().map(cells).sum();
let mut num_spaces = words.len().saturating_sub(1);
let mut spaces = vec![1usize; num_spaces];
let mut index = 0;
if !spaces.is_empty() {
while words_size + num_spaces < width {
let slot = spaces.len() - index - 1;
spaces[slot] += 1;
num_spaces += 1;
index = (index + 1) % spaces.len();
}
}
let mut tokens: Vec<Text> = Vec::with_capacity(words.len() * 2);
for (index, word) in words.iter().enumerate() {
tokens.push(word.clone());
if index < spaces.len() {
let style = word.get_style_at_offset(console, -1);
let next_style = words[index + 1].get_style_at_offset(console, 0);
let space_style: StyleType = if style == next_style {
style.into()
} else {
line.base_style().clone()
};
tokens.push(Text::styled(" ".repeat(spaces[index]), space_style));
}
}
self.lines[line_index] = Text::new("").join(&tokens);
}
}
Justify::Default => {}
}
}
}
impl std::ops::Index<usize> for Lines {
type Output = Text;
fn index(&self, index: usize) -> &Text {
&self.lines[index]
}
}
impl std::ops::IndexMut<usize> for Lines {
fn index_mut(&mut self, index: usize) -> &mut Text {
&mut self.lines[index]
}
}
impl IntoIterator for Lines {
type Item = Text;
type IntoIter = std::vec::IntoIter<Text>;
fn into_iter(self) -> Self::IntoIter {
self.lines.into_iter()
}
}
impl<'a> IntoIterator for &'a Lines {
type Item = &'a Text;
type IntoIter = std::slice::Iter<'a, Text>;
fn into_iter(self) -> Self::IntoIter {
self.lines.iter()
}
}
impl Renderable for Lines {
fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
let renderables: Vec<Arc<dyn Renderable + Send + Sync>> = self
.lines
.iter()
.map(|line| Arc::new(line.clone()) as Arc<dyn Renderable + Send + Sync>)
.collect();
Renderables::new(renderables).rich_render(console, options)
}
}