use super::core::Streamline;
use crate::render::Cell;
use crate::utils::{char_width, display_width};
use crate::widget::traits::{RenderContext, View};
impl View for Streamline {
fn render(&self, ctx: &mut RenderContext) {
let area = ctx.area;
let height = self.height.unwrap_or(area.height);
if area.width < 5 || height < 3 {
return;
}
if let Some(bg) = self.bg_color {
for y in 0..height.min(area.height) {
for x in 0..area.width {
let mut cell = Cell::new(' ');
cell.bg = Some(bg);
ctx.set(x, y, cell);
}
}
}
let mut chart_y: u16 = 0;
let mut chart_height = height.min(area.height);
if let Some(ref title) = self.title {
let title_x = (area.width.saturating_sub(display_width(title) as u16)) / 2;
ctx.draw_text(title_x, chart_y, title, crate::style::Color::WHITE);
chart_y += 1;
chart_height = chart_height.saturating_sub(1);
}
if self.show_legend && !self.layers.is_empty() {
let mut x: u16 = 1;
for (i, layer) in self.layers.iter().enumerate() {
let color = self.get_layer_color(i);
let mut cell = Cell::new('█');
cell.fg = Some(color);
ctx.set(x, chart_y, cell);
x += 2;
let mut dx: u16 = 0;
for ch in layer.name.chars() {
let cw = char_width(ch) as u16;
if x + dx >= area.width {
break;
}
let mut c = Cell::new(ch);
c.fg = Some(crate::style::Color::WHITE);
c.bg = self.bg_color;
ctx.set(x + dx, chart_y, c);
dx += cw;
}
x += display_width(&layer.name) as u16 + 2;
if x > area.width - 10 {
break;
}
}
chart_y += 1;
chart_height = chart_height.saturating_sub(1);
}
let plot_height = if self.x_labels.is_empty() {
chart_height
} else {
chart_height.saturating_sub(1)
};
if plot_height < 2 {
return;
}
let stacks = self.compute_stacks();
if stacks.is_empty() || stacks[0].is_empty() {
return;
}
let num_points = stacks[0].len();
let mut min_y = f64::MAX;
let mut max_y = f64::MIN;
for layer_stack in &stacks {
for &(y0, y1) in layer_stack {
min_y = min_y.min(y0);
max_y = max_y.max(y1);
}
}
if min_y == max_y {
max_y = min_y + 1.0;
}
let y_range = max_y - min_y;
for (layer_idx, layer_stack) in stacks.iter().enumerate() {
let color = self.get_layer_color(layer_idx);
let is_highlighted = self.highlighted.is_none() || self.highlighted == Some(layer_idx);
let display_color = if is_highlighted {
color
} else {
crate::style::Color::rgb(
(color.r as u16 / 3) as u8,
(color.g as u16 / 3) as u8,
(color.b as u16 / 3) as u8,
)
};
for x_idx in 0..num_points {
let (y0, y1) = layer_stack[x_idx];
let screen_x = (x_idx as f64 / (num_points - 1).max(1) as f64
* (area.width - 1) as f64) as u16;
let screen_y0 = chart_y + plot_height
- 1
- ((y0 - min_y) / y_range * (plot_height - 1) as f64) as u16;
let screen_y1 = chart_y + plot_height
- 1
- ((y1 - min_y) / y_range * (plot_height - 1) as f64) as u16;
let (top_y, bottom_y) = if screen_y0 <= screen_y1 {
(screen_y0, screen_y1)
} else {
(screen_y1, screen_y0)
};
for y in top_y..=bottom_y {
if y >= chart_y && y < chart_y + plot_height {
let mut cell = Cell::new('█');
cell.fg = Some(display_color);
ctx.set(screen_x, y, cell);
}
}
}
if self.show_labels && !self.layers[layer_idx].name.is_empty() {
let mut max_width_x = 0;
let mut max_width = 0.0f64;
for (x_idx, &(y0, y1)) in layer_stack.iter().enumerate() {
let width = (y1 - y0).abs();
if width > max_width {
max_width = width;
max_width_x = x_idx;
}
}
let (y0, y1) = layer_stack[max_width_x];
let mid_y = (y0 + y1) / 2.0;
let screen_x = (max_width_x as f64 / (num_points - 1).max(1) as f64
* (area.width - 1) as f64) as u16;
let screen_y = chart_y + plot_height
- 1
- ((mid_y - min_y) / y_range * (plot_height - 1) as f64) as u16;
let label = &self.layers[layer_idx].name;
let label_x = screen_x.saturating_sub(display_width(label) as u16 / 2);
if screen_y >= chart_y && screen_y < chart_y + plot_height {
let mut dx: u16 = 0;
for ch in label.chars() {
let cw = char_width(ch) as u16;
if label_x + dx >= area.width {
break;
}
let mut c = Cell::new(ch);
c.fg = Some(crate::style::Color::WHITE);
c.bg = Some(display_color);
ctx.set(label_x + dx, screen_y, c);
dx += cw;
}
}
}
}
if !self.x_labels.is_empty() {
let label_y = chart_y + plot_height;
let num_labels = self.x_labels.len().min(area.width as usize / 8);
for (i, label) in self.x_labels.iter().take(num_labels).enumerate() {
let x =
(i as f64 / (num_labels - 1).max(1) as f64 * (area.width - 1) as f64) as u16;
let label_x = x.saturating_sub(display_width(label) as u16 / 2);
let mut dx: u16 = 0;
for ch in label.chars() {
let cw = char_width(ch) as u16;
if label_x + dx >= area.width {
break;
}
let mut c = Cell::new(ch);
c.fg = Some(crate::style::Color::WHITE);
c.bg = self.bg_color;
ctx.set(label_x + dx, label_y, c);
dx += cw;
}
}
}
}
crate::impl_view_meta!("Streamline");
}