textual_rs/widget/
loading_indicator.rs1use ratatui::buffer::Buffer;
3use ratatui::layout::Rect;
4use ratatui::style::{Color, Style};
5
6use super::context::AppContext;
7use super::Widget;
8
9const SPINNER_FRAMES: [char; 8] = [
13 '\u{28FE}',
14 '\u{28FD}',
15 '\u{28FB}',
16 '\u{283F}',
17 '\u{285F}',
18 '\u{289F}',
19 '\u{28AF}',
20 '\u{28B7}',
21];
22
23pub struct LoadingIndicator;
43
44impl LoadingIndicator {
45 pub fn new() -> Self {
47 Self
48 }
49}
50
51impl Default for LoadingIndicator {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl Widget for LoadingIndicator {
58 fn widget_type_name(&self) -> &'static str {
59 "LoadingIndicator"
60 }
61
62 fn default_css() -> &'static str
63 where
64 Self: Sized,
65 {
66 "LoadingIndicator { width: 100%; height: 100%; min-height: 1; }"
67 }
68
69 fn render(&self, ctx: &AppContext, area: Rect, buf: &mut Buffer) {
70 if area.height == 0 || area.width == 0 {
71 return;
72 }
73
74 let style = Style::default().fg(Color::Rgb(0, 255, 163)); if ctx.skip_animations {
77 let text = "Loading...";
79 let x = area.x + area.width.saturating_sub(text.len() as u16) / 2;
80 let y = area.y + area.height / 2;
81 buf.set_string(x, y, text, style);
82 return;
83 }
84
85 let frame_idx = (ctx.spinner_tick.get() / 2) as usize % SPINNER_FRAMES.len();
87 let ch = SPINNER_FRAMES[frame_idx];
88 let x = area.x + area.width / 2;
89 let y = area.y + area.height / 2;
90 buf.set_string(x, y, ch.to_string(), style);
91 }
92}
93
94pub fn draw_loading_spinner_overlay(area: Rect, buf: &mut Buffer, tick: u8, skip_animations: bool) {
101 if area.height == 0 || area.width == 0 {
102 return;
103 }
104
105 let bg_style = Style::default().bg(Color::Rgb(20, 20, 28));
107 for y in area.y..area.y + area.height {
108 for x in area.x..area.x + area.width {
109 if let Some(cell) = buf.cell_mut((x, y)) {
110 cell.set_char(' ');
111 cell.set_style(bg_style);
112 }
113 }
114 }
115
116 let fg_style = Style::default()
118 .fg(Color::Rgb(0, 255, 163))
119 .bg(Color::Rgb(20, 20, 28));
120
121 if skip_animations {
122 let text = "Loading...";
123 let x = area.x + area.width.saturating_sub(text.len() as u16) / 2;
124 let y = area.y + area.height / 2;
125 buf.set_string(x, y, text, fg_style);
126 } else {
127 let frame_idx = (tick / 2) as usize % SPINNER_FRAMES.len();
128 let ch = SPINNER_FRAMES[frame_idx];
129 let x = area.x + area.width / 2;
130 let y = area.y + area.height / 2;
131 buf.set_string(x, y, ch.to_string(), fg_style);
132 }
133}