use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use tachyonfx::{Duration, Effect, Interpolation, fx};
use crate::ui::style;
const QUEUE_PULSE_FRAME_COUNT: usize = 20;
const QUEUE_PULSE_HALF_PERIOD_MS: u32 = 1_000;
const QUEUE_PULSE_STEP_MS: u32 = 100;
pub(crate) struct QueuePulseEffect;
impl QueuePulseEffect {
pub(crate) fn apply_stateless(buffer: &mut Buffer, area: Rect, spinner_frame: usize) {
let mut effect = Self::build_effect();
let frame_offset = spinner_frame % QUEUE_PULSE_FRAME_COUNT;
let phase_ms = u32::try_from(frame_offset).unwrap_or_default() * QUEUE_PULSE_STEP_MS;
effect.process(Duration::from_millis(phase_ms), buffer, area);
}
fn build_effect() -> Effect {
fx::repeating(fx::ping_pong(fx::fade_to_fg(
style::palette::text(),
(QUEUE_PULSE_HALF_PERIOD_MS, Interpolation::SineInOut),
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apply_stateless_breathes_from_subtle_toward_normal_text() {
let area = Rect::new(0, 0, 1, 1);
let mut buffer = Buffer::empty(area);
buffer[(0, 0)]
.set_symbol("≡")
.set_fg(style::palette::text_subtle());
QueuePulseEffect::apply_stateless(&mut buffer, area, 5);
assert_ne!(buffer[(0, 0)].fg, style::palette::text_subtle());
assert_ne!(buffer[(0, 0)].fg, style::palette::warning());
}
#[test]
fn test_apply_stateless_wraps_after_full_period() {
let area = Rect::new(0, 0, 1, 1);
let mut first_cycle = Buffer::empty(area);
first_cycle[(0, 0)]
.set_symbol("≡")
.set_fg(style::palette::text_subtle());
let mut second_cycle = first_cycle.clone();
QueuePulseEffect::apply_stateless(&mut first_cycle, area, 5);
QueuePulseEffect::apply_stateless(&mut second_cycle, area, 25);
assert_eq!(first_cycle[(0, 0)].fg, second_cycle[(0, 0)].fg);
}
}