television_screen/
spinner.rs

1use ratatui::{
2    buffer::Buffer, layout::Rect, style::Style, widgets::StatefulWidget,
3};
4
5const FRAMES: &[&str] = &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
6
7/// A spinner widget.
8#[derive(Debug, Clone, Copy)]
9pub struct Spinner {
10    frames: &'static [&'static str],
11}
12
13impl Spinner {
14    pub fn new(frames: &'static [&str]) -> Spinner {
15        Spinner { frames }
16    }
17
18    pub fn frame(&self, index: usize) -> &str {
19        self.frames[index]
20    }
21}
22
23impl Default for Spinner {
24    fn default() -> Spinner {
25        Spinner::new(FRAMES)
26    }
27}
28
29#[derive(Debug)]
30pub struct SpinnerState {
31    pub current_frame: usize,
32    total_frames: usize,
33}
34
35impl SpinnerState {
36    pub fn new(total_frames: usize) -> SpinnerState {
37        SpinnerState {
38            current_frame: 0,
39            total_frames,
40        }
41    }
42
43    fn tick(&mut self) {
44        self.current_frame = (self.current_frame + 1) % self.total_frames;
45    }
46}
47
48impl From<&Spinner> for SpinnerState {
49    fn from(spinner: &Spinner) -> SpinnerState {
50        SpinnerState::new(spinner.frames.len())
51    }
52}
53
54impl StatefulWidget for Spinner {
55    type State = SpinnerState;
56
57    /// Renders the spinner in the given area.
58    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
59        buf.set_string(
60            area.left(),
61            area.top(),
62            self.frame(state.current_frame),
63            Style::default(),
64        );
65        state.tick();
66    }
67}
68impl StatefulWidget for &Spinner {
69    type State = SpinnerState;
70
71    /// Renders the spinner in the given area.
72    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
73        buf.set_string(
74            area.left(),
75            area.top(),
76            self.frame(state.current_frame),
77            Style::default(),
78        );
79        state.tick();
80    }
81}