use crate::rendering::{Cell, Charset, Theme, VideoBuffer};
pub struct ErrorDialog {
pub error_message: String,
pub width: u16,
pub height: u16,
pub x: u16,
pub y: u16,
button_x: u16,
button_y: u16,
button_width: u16,
}
impl ErrorDialog {
pub fn new(buffer_width: u16, buffer_height: u16, error_message: String) -> Self {
let max_message_width = 60u16;
let message_lines = Self::wrap_text(&error_message, max_message_width as usize);
let message_height = message_lines.len() as u16;
let width = (max_message_width + 4).min(buffer_width.saturating_sub(4));
let height = 7 + message_height;
let x = (buffer_width.saturating_sub(width)) / 2;
let y = (buffer_height.saturating_sub(height)) / 2;
let button_width = 8u16; let button_x = x + (width - button_width) / 2;
let button_y = y + height - 3;
ErrorDialog {
error_message,
width,
height,
x,
y,
button_x,
button_y,
button_width,
}
}
fn wrap_text(text: &str, max_width: usize) -> Vec<String> {
let mut lines = Vec::new();
let mut current_line = String::new();
for word in text.split_whitespace() {
if current_line.is_empty() {
current_line = word.to_string();
} else if current_line.len() + 1 + word.len() <= max_width {
current_line.push(' ');
current_line.push_str(word);
} else {
lines.push(current_line);
current_line = word.to_string();
}
}
if !current_line.is_empty() {
lines.push(current_line);
}
if lines.is_empty() {
lines.push(String::new());
}
lines
}
pub fn render(&self, buffer: &mut VideoBuffer, charset: &Charset, theme: &Theme) {
let content_width = self.width.saturating_sub(4);
for row in 0..self.height {
for col in 0..self.width {
let x = self.x + col;
let y = self.y + row;
buffer.set(
x,
y,
Cell::new(' ', theme.prompt_danger_fg, theme.prompt_danger_bg),
);
}
}
buffer.set(
self.x,
self.y,
Cell::new(
charset.border_top_left,
theme.prompt_danger_fg,
theme.prompt_danger_bg,
),
);
for col in 1..self.width - 1 {
buffer.set(
self.x + col,
self.y,
Cell::new(
charset.border_horizontal,
theme.prompt_danger_fg,
theme.prompt_danger_bg,
),
);
}
buffer.set(
self.x + self.width - 1,
self.y,
Cell::new(
charset.border_top_right,
theme.prompt_danger_fg,
theme.prompt_danger_bg,
),
);
for row in 1..self.height - 1 {
buffer.set(
self.x,
self.y + row,
Cell::new(
charset.border_vertical,
theme.prompt_danger_fg,
theme.prompt_danger_bg,
),
);
buffer.set(
self.x + self.width - 1,
self.y + row,
Cell::new(
charset.border_vertical,
theme.prompt_danger_fg,
theme.prompt_danger_bg,
),
);
}
buffer.set(
self.x,
self.y + self.height - 1,
Cell::new(
charset.border_bottom_left,
theme.prompt_danger_fg,
theme.prompt_danger_bg,
),
);
for col in 1..self.width - 1 {
buffer.set(
self.x + col,
self.y + self.height - 1,
Cell::new(
charset.border_horizontal,
theme.prompt_danger_fg,
theme.prompt_danger_bg,
),
);
}
buffer.set(
self.x + self.width - 1,
self.y + self.height - 1,
Cell::new(
charset.border_bottom_right,
theme.prompt_danger_fg,
theme.prompt_danger_bg,
),
);
let title = "Error";
let title_start = self.x + 2 + (content_width.saturating_sub(title.len() as u16)) / 2;
for (i, ch) in title.chars().enumerate() {
buffer.set(
title_start + i as u16,
self.y + 1,
Cell::new(ch, theme.prompt_danger_fg, theme.prompt_danger_bg),
);
}
let message_lines = Self::wrap_text(&self.error_message, content_width as usize);
let message_start_y = self.y + 3;
for (line_idx, line) in message_lines.iter().enumerate() {
let line_y = message_start_y + line_idx as u16;
let line_start = self.x + 2 + (content_width.saturating_sub(line.len() as u16)) / 2;
for (i, ch) in line.chars().enumerate() {
if i < content_width as usize {
buffer.set(
line_start + i as u16,
line_y,
Cell::new(ch, theme.prompt_danger_fg, theme.prompt_danger_bg),
);
}
}
}
let button_text = " [ OK ] ";
for (i, ch) in button_text.chars().enumerate() {
buffer.set(
self.button_x + i as u16,
self.button_y,
Cell::new(
ch,
theme.dialog_button_primary_danger_fg,
theme.dialog_button_primary_danger_bg,
),
);
}
self.draw_shadow(buffer, charset, theme);
}
fn draw_shadow(&self, buffer: &mut VideoBuffer, charset: &Charset, theme: &Theme) {
let shadow_char = charset.shadow;
let shadow_color = theme.window_shadow_color;
let (buffer_width, buffer_height) = buffer.dimensions();
for row in 1..=self.height {
for offset in 0..2 {
let x = self.x + self.width + offset;
let y = self.y + row;
if x < buffer_width && y < buffer_height {
if let Some(existing_cell) = buffer.get(x, y) {
let cell = Cell::new(shadow_char, existing_cell.fg_color, shadow_color);
buffer.set(x, y, cell);
}
}
}
}
for col in 2..=self.width + 1 {
let x = self.x + col;
let y = self.y + self.height;
if x < buffer_width && y < buffer_height {
if let Some(existing_cell) = buffer.get(x, y) {
let cell = Cell::new(shadow_char, existing_cell.fg_color, shadow_color);
buffer.set(x, y, cell);
}
}
}
}
pub fn is_ok_button_clicked(&self, mouse_x: u16, mouse_y: u16) -> bool {
mouse_y == self.button_y
&& mouse_x >= self.button_x
&& mouse_x < self.button_x + self.button_width
}
}