use crate::render::Cell;
use crate::style::Color;
use crate::utils::{char_width, truncate_to_width};
use crate::widget::traits::RenderContext;
pub fn row_number_width(show_row_numbers: bool, row_count: usize) -> u16 {
if show_row_numbers {
let digits = (row_count.max(1) as f64).log10().floor() as u16 + 1;
digits.max(2) + 1 } else {
0
}
}
pub fn render_csv_viewer(
ctx: &mut RenderContext,
data: &[Vec<String>],
column_widths: &[u16],
selected_row: usize,
selected_col: usize,
scroll_row: usize,
sort_column: Option<usize>,
sort_order: super::types::SortOrder,
search_matches: &[(usize, usize)],
sorted_indices: &[usize],
has_header: bool,
show_row_numbers: bool,
show_separators: bool,
header_fg: Option<Color>,
header_bg: Option<Color>,
selected_fg: Option<Color>,
selected_bg: Option<Color>,
match_fg: Option<Color>,
match_bg: Option<Color>,
separator_fg: Option<Color>,
row_number_fg: Option<Color>,
fg: Option<Color>,
bg: Option<Color>,
) {
let area = ctx.area;
if area.width < 5 || area.height < 2 {
return;
}
let row_num_width = row_number_width(show_row_numbers, sorted_indices.len());
let content_start_x = row_num_width;
let _content_width = area.width.saturating_sub(row_num_width);
let header_rows = if has_header { 1 } else { 0 };
let visible_data_rows = (area.height as usize).saturating_sub(header_rows);
let mut scroll_row = scroll_row;
if selected_row < scroll_row {
scroll_row = selected_row;
} else if selected_row >= scroll_row + visible_data_rows {
scroll_row = selected_row.saturating_sub(visible_data_rows - 1);
}
let mut y = 0u16;
if has_header {
if let Some(header_row) = data.first() {
if show_row_numbers {
for x in 0..content_start_x {
let mut cell = Cell::new(' ');
cell.fg = header_fg;
cell.bg = header_bg;
ctx.set(x, y, cell);
}
}
let mut x = content_start_x;
for (col_idx, cell_value) in header_row.iter().enumerate() {
let width = column_widths.get(col_idx).copied().unwrap_or(10) as usize;
let sort_indicator = if sort_column == Some(col_idx) {
match sort_order {
super::types::SortOrder::Ascending => " ▲",
super::types::SortOrder::Descending => " ▼",
super::types::SortOrder::None => "",
}
} else {
""
};
let combined = format!("{}{}", cell_value, sort_indicator);
let display = truncate_to_width(&combined, width);
let mut dx: u16 = 0;
for ch in display.chars() {
let cw = char_width(ch) as u16;
if x + dx + cw > area.width {
break;
}
let mut cell = Cell::new(ch).bold();
cell.fg = header_fg;
cell.bg = header_bg;
ctx.set(x + dx, y, cell);
dx += cw;
}
for i in dx..(width as u16) {
if x + i >= area.width {
break;
}
let mut cell = Cell::new(' ');
cell.fg = header_fg;
cell.bg = header_bg;
ctx.set(x + i, y, cell);
}
x += width as u16;
if show_separators && x < area.width {
let mut cell = Cell::new('│');
cell.fg = separator_fg;
cell.bg = header_bg;
ctx.set(x, y, cell);
x += 1;
}
}
y += 1;
}
}
for &data_idx in sorted_indices
.iter()
.skip(scroll_row)
.take(visible_data_rows)
{
if y >= area.height {
break;
}
let row_idx = data_idx - if has_header { 1 } else { 0 };
let is_selected_row = row_idx == selected_row;
if show_row_numbers {
let num_str = format!(
"{:>width$}",
row_idx + 1,
width = (row_num_width - 1) as usize
);
for (i, ch) in num_str.chars().enumerate() {
let mut cell = Cell::new(ch);
cell.fg = row_number_fg;
cell.bg = if is_selected_row { selected_bg } else { bg };
ctx.set(i as u16, y, cell);
}
}
let mut x = content_start_x;
if let Some(row_data) = data.get(data_idx) {
for (col_idx, cell_value) in row_data.iter().enumerate() {
let width = column_widths.get(col_idx).copied().unwrap_or(10) as usize;
let is_selected = is_selected_row && col_idx == selected_col;
let is_match = search_matches.contains(&(row_idx, col_idx));
let (fg, bg) = if is_selected {
(selected_fg, selected_bg)
} else if is_match {
(match_fg, match_bg)
} else {
(fg, bg)
};
let display = truncate_to_width(cell_value, width);
let mut dx: u16 = 0;
for ch in display.chars() {
let cw = char_width(ch) as u16;
if x + dx + cw > area.width {
break;
}
let mut cell = Cell::new(ch);
cell.fg = fg;
cell.bg = bg;
ctx.set(x + dx, y, cell);
dx += cw;
}
for i in dx..(width as u16) {
if x + i >= area.width {
break;
}
let mut cell = Cell::new(' ');
cell.fg = fg;
cell.bg = bg;
ctx.set(x + i, y, cell);
}
x += width as u16;
if show_separators && x < area.width {
let mut cell = Cell::new('│');
cell.fg = separator_fg;
cell.bg = if is_selected_row { selected_bg } else { bg };
ctx.set(x, y, cell);
x += 1;
}
}
}
y += 1;
}
}