use ratatui::{buffer::Buffer, layout::Rect, style::Style};
use super::theme;
pub enum Direction {
Right,
Left,
}
#[inline]
pub fn set_cell_if(buf: &mut Buffer, bounds: &Rect, x: u16, y: u16, ch: char, style: Style) {
if x >= bounds.x && x < bounds.x + bounds.width && y >= bounds.y && y < bounds.y + bounds.height
{
buf[(x, y)].set_char(ch);
buf[(x, y)].set_style(style);
}
}
pub fn write_str_clipped(
buf: &mut Buffer,
x: u16,
y: u16,
text: &str,
style: Style,
clip: &Rect,
ellipsis: bool,
) -> bool {
let bounds = *buf.area();
let clip_end = clip.x + clip.width;
let char_count = text.chars().count() as u16;
let truncated = x + char_count > clip_end;
for (i, ch) in text.chars().enumerate() {
let cx = x + i as u16;
if cx >= clip_end {
break;
}
if cx >= bounds.x
&& cx < bounds.x + bounds.width
&& y >= bounds.y
&& y < bounds.y + bounds.height
{
buf[(cx, y)].set_char(ch);
buf[(cx, y)].set_style(style);
}
}
if truncated && ellipsis && clip_end > clip.x {
let last = clip_end - 1;
if last >= bounds.x
&& last < bounds.x + bounds.width
&& y >= bounds.y
&& y < bounds.y + bounds.height
{
buf[(last, y)].set_char('\u{2026}');
buf[(last, y)].set_style(style);
}
}
truncated
}
pub fn render_tooltip_box(
buf: &mut Buffer,
term_area: Rect,
label: &str,
anchor_x: u16,
anchor_y: u16,
direction: Direction,
) {
let bounds = *buf.area();
let content_width = label.chars().count() as u16 + 2; let box_height: u16 = 3;
let (tooltip_x, box_width) = match direction {
Direction::Right => {
let max_w = term_area.width.saturating_sub(anchor_x);
if max_w < 5 {
return;
}
(anchor_x, (content_width + 2).min(max_w))
}
Direction::Left => {
let total = (content_width + 2).min(anchor_x.saturating_sub(term_area.x));
if total < 5 {
return;
}
(anchor_x.saturating_sub(total), total)
}
};
let tooltip_y = if anchor_y + box_height > term_area.y + term_area.height {
(term_area.y + term_area.height).saturating_sub(box_height)
} else {
anchor_y
};
if tooltip_y + box_height > term_area.y + term_area.height {
return;
}
let border_style = Style::default().fg(theme::CYAN).bg(theme::BG);
let text_style = Style::default().fg(theme::TEXT).bg(theme::BG);
for dy in 0..box_height {
for dx in 0..box_width {
set_cell_if(
buf,
&bounds,
tooltip_x + dx,
tooltip_y + dy,
' ',
Style::default().bg(theme::BG),
);
}
}
set_cell_if(buf, &bounds, tooltip_x, tooltip_y, '╭', border_style);
for dx in 1..box_width - 1 {
set_cell_if(buf, &bounds, tooltip_x + dx, tooltip_y, '─', border_style);
}
set_cell_if(
buf,
&bounds,
tooltip_x + box_width - 1,
tooltip_y,
'╮',
border_style,
);
let mid_y = tooltip_y + 1;
set_cell_if(buf, &bounds, tooltip_x, mid_y, '│', border_style);
set_cell_if(
buf,
&bounds,
tooltip_x + box_width - 1,
mid_y,
'│',
border_style,
);
for dx in 1..box_width - 1 {
set_cell_if(buf, &bounds, tooltip_x + dx, mid_y, ' ', text_style);
}
let text_clip = Rect::new(tooltip_x + 1, mid_y, box_width.saturating_sub(2), 1);
write_str_clipped(
buf,
tooltip_x + 2,
mid_y,
label,
text_style,
&text_clip,
false,
);
let bot_y = tooltip_y + 2;
set_cell_if(buf, &bounds, tooltip_x, bot_y, '╰', border_style);
for dx in 1..box_width - 1 {
set_cell_if(buf, &bounds, tooltip_x + dx, bot_y, '─', border_style);
}
set_cell_if(
buf,
&bounds,
tooltip_x + box_width - 1,
bot_y,
'╯',
border_style,
);
}