use std::time::Duration;
use ratatui::style::Color;
use tachyonfx::{CellFilter, Effect, EffectTimer, Interpolation, fx};
use crate::tui::theme::{FOCUS_COLOR, SURFACE2};
const CHASE_LEN: f32 = 0.35;
pub fn border_chase() -> Effect {
let timer = EffectTimer::new(Duration::from_millis(1000), Interpolation::Linear);
fx::effect_fn_buf((), timer, |_state, ctx, buf| {
let area = ctx.area;
if area.width < 2 || area.height < 2 {
return;
}
let progress = ctx.alpha();
let w = area.width as usize;
let h = area.height as usize;
let perimeter = 2 * (w + h) - 4;
if perimeter == 0 {
return;
}
let head = (progress * perimeter as f32) as usize;
for i in 0..perimeter {
let (x, y) = perimeter_pos(area.x, area.y, w, h, i);
let cell = &mut buf[(x, y)];
let tail_len = CHASE_LEN * perimeter as f32;
let behind = (head as isize - i as isize).rem_euclid(perimeter as isize) as f32;
if behind < tail_len {
let t = behind / tail_len;
let color = lerp_color(Color::White, FOCUS_COLOR, t);
cell.set_fg(color);
} else {
let color = lerp_color(SURFACE2, FOCUS_COLOR, progress);
cell.set_fg(color);
}
}
})
.with_filter(CellFilter::Outer(ratatui::layout::Margin::new(1, 1)))
}
fn perimeter_pos(rx: u16, ry: u16, w: usize, h: usize, idx: usize) -> (u16, u16) {
let top = w;
let right = top + h - 1;
let bottom = right + w - 1;
if idx < top {
(rx + idx as u16, ry)
} else if idx < right {
let offset = idx - top + 1;
(rx + w as u16 - 1, ry + offset as u16)
} else if idx < bottom {
let offset = idx - right + 1;
(rx + w as u16 - 1 - offset as u16, ry + h as u16 - 1)
} else {
let offset = idx - bottom + 1;
(rx, ry + h as u16 - 1 - offset as u16)
}
}
fn lerp_color(from: Color, to: Color, t: f32) -> Color {
let t = t.clamp(0.0, 1.0);
match (from, to) {
(Color::Rgb(r1, g1, b1), Color::Rgb(r2, g2, b2)) => {
let r = (r1 as f32 + (r2 as f32 - r1 as f32) * t) as u8;
let g = (g1 as f32 + (g2 as f32 - g1 as f32) * t) as u8;
let b = (b1 as f32 + (b2 as f32 - b1 as f32) * t) as u8;
Color::Rgb(r, g, b)
}
_ => to,
}
}