use super::color::{RainColumn, pseudo_rand};
use crate::widgets::spinner::SPINNER_FRAMES;
#[derive(Debug, Clone)]
pub struct WelcomePanelState {
pub(super) gradient_offset: u16,
pub(super) braille_offset: usize,
pub(super) braille_tick: u8,
pub(super) breathe_phase: f64,
pub(super) rain_columns: Vec<RainColumn>,
pub(super) rain_width: usize,
pub(super) rain_height: usize,
pub(super) fade_progress: f32,
pub is_fading: bool,
pub fade_complete: bool,
pub(super) rng_seed: u64,
}
impl WelcomePanelState {
pub fn new() -> Self {
let seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(42);
Self {
gradient_offset: 0,
braille_offset: 0,
braille_tick: 0,
breathe_phase: 0.0,
rain_columns: Vec::new(),
rain_width: 0,
rain_height: 0,
fade_progress: 1.0,
is_fading: false,
fade_complete: false,
rng_seed: seed,
}
}
pub fn tick(&mut self, _terminal_width: u16, _terminal_height: u16) {
if self.is_fading {
self.fade_progress -= 0.1;
if self.fade_progress <= 0.0 {
self.fade_progress = 0.0;
self.fade_complete = true;
}
return;
}
self.gradient_offset = (self.gradient_offset + 3) % 360;
self.braille_tick += 1;
if self.braille_tick >= 2 {
self.braille_tick = 0;
self.braille_offset = (self.braille_offset + 1) % SPINNER_FRAMES.len();
}
self.breathe_phase += std::f64::consts::TAU / 67.0;
if self.breathe_phase >= std::f64::consts::TAU {
self.breathe_phase -= std::f64::consts::TAU;
}
self.step_rain();
}
pub fn start_fade(&mut self) {
self.is_fading = true;
}
pub fn ensure_rain_field(&mut self, w: usize, h: usize) {
if self.rain_width == w && self.rain_height == h && !self.rain_columns.is_empty() {
return;
}
self.rain_width = w;
self.rain_height = h;
self.rain_columns.clear();
self.rain_columns.reserve(w);
for _ in 0..w {
let y = pseudo_rand(&mut self.rng_seed) * h as f32;
let speed = 0.10 + pseudo_rand(&mut self.rng_seed) * 0.40;
let trail_len = 4 + (pseudo_rand(&mut self.rng_seed) * 6.0) as u8;
let char_offset = (pseudo_rand(&mut self.rng_seed) * SPINNER_FRAMES.len() as f32) as u8;
let hue_offset = pseudo_rand(&mut self.rng_seed) * 30.0 - 15.0; self.rain_columns.push(RainColumn {
y,
speed,
trail_len,
char_offset,
hue_offset,
});
}
}
pub(super) fn step_rain(&mut self) {
let h = self.rain_height as f32;
if h <= 0.0 {
return;
}
for col in &mut self.rain_columns {
col.y += col.speed;
if col.y > h + col.trail_len as f32 {
col.y = -(pseudo_rand(&mut (col.char_offset as u64 ^ 0xDEAD_BEEF)) * 6.0);
col.speed = 0.10 + (col.char_offset as f32 % 9.0) * 0.04;
}
}
}
}
impl Default for WelcomePanelState {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "state_tests.rs"]
mod tests;