use ratatui::{
Frame,
layout::{Position, Rect},
style::{Color, Style},
text::{Line, Span},
};
use sigye_core::{AnimationSpeed, AnimationStyle, ColorTheme, apply_animation, is_colon_visible};
use sigye_fonts::Font;
pub struct AsciiTextParams {
pub color_theme: ColorTheme,
pub static_color: Color,
pub animation_style: AnimationStyle,
pub animation_speed: AnimationSpeed,
pub elapsed_ms: u64,
pub flash_intensity: f32,
pub colon_blink: bool,
}
pub fn render_ascii_text(
frame: &mut Frame,
area: Rect,
font: &Font,
text: &str,
params: &AsciiTextParams,
) -> (usize, usize) {
let time_lines = font.render_text(text);
let height = time_lines.len();
let width = time_lines.first().map(|s| s.chars().count()).unwrap_or(0);
let text_width = width as u16;
let start_x = area.x + (area.width.saturating_sub(text_width)) / 2;
let colon_positions: Vec<bool> = if params.colon_blink {
let mut mask = vec![false; width];
let mut x_pos = 0;
for ch in text.chars() {
let char_width = font.char_width(ch);
if ch == ':' {
for i in 0..char_width {
if x_pos + i < mask.len() {
mask[x_pos + i] = true;
}
}
}
x_pos += char_width;
}
mask
} else {
vec![]
};
let buf = frame.buffer_mut();
for (line_idx, line) in time_lines.iter().enumerate() {
let y_pos = area.y + line_idx as u16;
if y_pos >= area.y + area.height {
break;
}
for (char_idx, ch) in line.chars().enumerate() {
if ch == ' ' {
continue;
}
let x_pos = start_x + char_idx as u16;
if x_pos >= area.x + area.width {
continue;
}
if params.colon_blink {
let is_colon = colon_positions.get(char_idx).copied().unwrap_or(false);
if is_colon && !is_colon_visible(params.elapsed_ms) {
continue;
}
}
let base_color = if params.color_theme.is_dynamic() {
params
.color_theme
.color_at_position(char_idx, line_idx, width, height)
} else {
params.static_color
};
let animated_color = apply_animation(
base_color,
params.animation_style,
params.animation_speed,
params.elapsed_ms,
char_idx,
width,
params.flash_intensity,
);
if let Some(cell) = buf.cell_mut(Position::new(x_pos, y_pos)) {
cell.set_char(ch);
cell.set_fg(animated_color);
}
}
}
(width, height)
}
pub fn render_centered_text(frame: &mut Frame, area: Rect, text: &str, color: Color) {
let text_width = text.len() as u16;
let start_x = area.x + (area.width.saturating_sub(text_width)) / 2;
let y = area.y;
let buf = frame.buffer_mut();
for (char_idx, ch) in text.chars().enumerate() {
if ch == ' ' {
continue;
}
let x_pos = start_x + char_idx as u16;
if x_pos >= area.x + area.width {
continue;
}
if let Some(cell) = buf.cell_mut(Position::new(x_pos, y)) {
cell.set_char(ch);
cell.set_fg(color);
}
}
}
pub fn render_progress_bar(progress: f64, width: u16, accent: Color) -> Line<'static> {
let bar_width = width as usize;
if bar_width == 0 {
return Line::from("");
}
let filled = ((progress * bar_width as f64).round() as usize).min(bar_width);
let empty = bar_width - filled;
let filled_str: String = "\u{2501}".repeat(filled);
let empty_str: String = "\u{2500}".repeat(empty);
Line::from(vec![
Span::styled(filled_str, Style::default().fg(accent)),
Span::styled(empty_str, Style::default().dark_gray()),
])
}