use crate::component::{Component, EventCx, MeasureCx};
use crate::event::Event;
use crate::geom::{Rect, Size};
use crate::layout::Constraint;
use crate::render::RenderCx;
use crate::style::Style;
use crate::text::Text;
const FRAMES: &[&str] = &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
pub struct Spinner {
message: Text,
frame: usize,
style: Style,
}
impl Spinner {
pub fn new(message: impl Into<Text>) -> Self {
Self { message: message.into(), frame: 0, style: Style::default() }
}
pub fn style(mut self, style: Style) -> Self { self.style = style; self }
}
impl Component for Spinner {
fn render(&self, cx: &mut RenderCx) {
let ch = FRAMES[self.frame % FRAMES.len()];
cx.set_style(self.style.clone());
cx.line(&format!("{} {}", ch, self.message.first_text()));
}
fn measure(&self, _c: Constraint, _cx: &mut MeasureCx) -> Size {
Size { width: self.message.max_width() + 2, height: 1 }
}
fn event(&mut self, event: &Event, cx: &mut EventCx) {
if matches!(event, Event::Tick) {
self.frame += 1;
cx.invalidate_paint();
}
}
fn layout(&mut self, _rect: Rect, _cx: &mut crate::component::LayoutCx) {}
fn focusable(&self) -> bool { false }
fn style(&self) -> Style { self.style.clone() }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testbuffer::TestBuffer;
#[test]
fn test_spinner_renders() {
let mut tb = TestBuffer::new(20, 1);
tb.render(&Spinner::new("loading"));
assert!(tb.buffer.cells[0].symbol.chars().next().unwrap() != ' '); }
}