use ratatui::buffer::Buffer;
use ratatui::style::{Color, Modifier};
#[derive(Debug, Clone)]
pub struct CellPreview {
pub symbol: String,
pub fg: Color,
pub bg: Color,
pub modifier: Modifier,
}
impl CellPreview {
pub fn new(symbol: impl Into<String>, fg: Color, bg: Color, modifier: Modifier) -> Self {
Self {
symbol: symbol.into(),
fg,
bg,
modifier,
}
}
pub fn is_default_style(&self) -> bool {
self.fg == Color::Reset && self.bg == Color::Reset && self.modifier.is_empty()
}
}
pub fn inspect_cell(buffer: &Buffer, x: u16, y: u16) -> Option<CellPreview> {
let area = buffer.area;
if !point_in_rect(area.x, area.y, area.width, area.height, x, y) {
return None;
}
let cell = &buffer[(x, y)];
Some(CellPreview {
symbol: cell.symbol().to_string(),
fg: cell.fg,
bg: cell.bg,
modifier: cell.modifier,
})
}
#[inline]
pub fn point_in_rect(rect_x: u16, rect_y: u16, width: u16, height: u16, x: u16, y: u16) -> bool {
let within_x = x >= rect_x && x < rect_x.saturating_add(width);
let within_y = y >= rect_y && y < rect_y.saturating_add(height);
within_x && within_y
}
pub fn format_color_compact(color: Color) -> String {
match color {
Color::Rgb(r, g, b) => format!("({r},{g},{b})"),
Color::Indexed(i) => format!("#{i}"),
other => format!("{other:?}"),
}
}
pub fn format_modifier_compact(modifier: Modifier) -> String {
if modifier.is_empty() {
return String::new();
}
let mut parts = Vec::new();
if modifier.contains(Modifier::BOLD) {
parts.push("B");
}
if modifier.contains(Modifier::DIM) {
parts.push("D");
}
if modifier.contains(Modifier::ITALIC) {
parts.push("I");
}
if modifier.contains(Modifier::UNDERLINED) {
parts.push("U");
}
if modifier.contains(Modifier::SLOW_BLINK) || modifier.contains(Modifier::RAPID_BLINK) {
parts.push("*");
}
if modifier.contains(Modifier::REVERSED) {
parts.push("R");
}
if modifier.contains(Modifier::CROSSED_OUT) {
parts.push("X");
}
parts.join("")
}
#[cfg(test)]
mod tests {
use super::*;
use ratatui::layout::Rect;
#[test]
fn test_point_in_rect() {
assert!(point_in_rect(0, 0, 10, 10, 5, 5));
assert!(point_in_rect(0, 0, 10, 10, 0, 0));
assert!(point_in_rect(0, 0, 10, 10, 9, 9));
assert!(!point_in_rect(0, 0, 10, 10, 10, 10));
assert!(!point_in_rect(5, 5, 10, 10, 4, 5));
}
#[test]
fn test_format_color_compact() {
assert_eq!(format_color_compact(Color::Rgb(255, 0, 128)), "(255,0,128)");
assert_eq!(format_color_compact(Color::Red), "Red");
assert_eq!(format_color_compact(Color::Reset), "Reset");
assert_eq!(format_color_compact(Color::Indexed(42)), "#42");
}
#[test]
fn test_format_modifier_compact() {
assert_eq!(format_modifier_compact(Modifier::empty()), "");
assert_eq!(format_modifier_compact(Modifier::BOLD), "B");
assert_eq!(
format_modifier_compact(Modifier::BOLD | Modifier::ITALIC),
"BI"
);
}
#[test]
fn test_inspect_cell() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
buffer[(5, 5)].set_char('X');
buffer[(5, 5)].set_fg(Color::Red);
let preview = inspect_cell(&buffer, 5, 5).unwrap();
assert_eq!(preview.symbol, "X");
assert_eq!(preview.fg, Color::Red);
assert!(inspect_cell(&buffer, 20, 20).is_none());
}
#[test]
fn test_cell_preview_is_default() {
let default = CellPreview::new(" ", Color::Reset, Color::Reset, Modifier::empty());
assert!(default.is_default_style());
let styled = CellPreview::new("X", Color::Red, Color::Reset, Modifier::empty());
assert!(!styled.is_default_style());
}
}