Skip to main content

libsurfer/
statusbar.rs

1use egui::{Context, Frame, Layout, Margin, TopBottomPanel, Ui};
2use emath::Align;
3use web_time::{Duration, Instant};
4
5use crate::time::{time_string, timeunit_menu};
6use crate::wave_source::draw_progress_information;
7use crate::{SystemState, message::Message, wave_data::WaveData};
8
9/// Debounce duration for progress information display (in milliseconds)
10/// Progress is only shown after this duration to avoid flicker on fast operations
11const PROGRESS_DEBOUNCE_MS: u64 = 100;
12
13impl SystemState {
14    pub fn add_statusbar_panel(
15        &self,
16        ctx: &Context,
17        waves: Option<&WaveData>,
18        msgs: &mut Vec<Message>,
19    ) {
20        TopBottomPanel::bottom("statusbar")
21            .frame(Frame {
22                fill: self.user.config.theme.primary_ui_color.background,
23                inner_margin: Margin {
24                    left: 5,
25                    right: 5,
26                    top: 0,
27                    bottom: 5,
28                },
29                ..Default::default()
30            })
31            .show(ctx, |ui| {
32                self.draw_statusbar(ui, waves, msgs);
33            });
34    }
35
36    fn draw_statusbar(&self, ui: &mut Ui, waves: Option<&WaveData>, msgs: &mut Vec<Message>) {
37        ui.visuals_mut().override_text_color =
38            Some(self.user.config.theme.primary_ui_color.foreground);
39        ui.with_layout(Layout::left_to_right(Align::RIGHT), |ui| {
40            self.draw_statusbar_left(ui, waves);
41            self.draw_statusbar_right(ui, waves, msgs);
42        });
43    }
44
45    /// Draw left-aligned status bar elements: wave source and generation date
46    fn draw_statusbar_left(&self, ui: &mut Ui, waves: Option<&WaveData>) {
47        if let Some(waves) = waves {
48            ui.label(waves.source.to_string());
49            if let Some(idx) = self.user.selected_server_file_index
50                && let Some(infos) = self.user.surver_file_infos.as_ref()
51                && let Some(file) = infos.get(idx)
52            {
53                ui.separator();
54                ui.label(&file.filename);
55            }
56            if let Some(datetime) = waves.inner.metadata().date {
57                ui.separator();
58                ui.label(format!("Generated: {datetime}"));
59            }
60        }
61
62        if let Some(state_file) = &self.user.state_file {
63            ui.separator();
64            ui.label(state_file.to_string_lossy());
65        }
66
67        if let Some(progress_data) = &self.progress_tracker
68            && Instant::now().duration_since(progress_data.started)
69                > Duration::from_millis(PROGRESS_DEBOUNCE_MS)
70        {
71            ui.separator();
72            draw_progress_information(ui, progress_data);
73        }
74
75        // Show analog cache building status
76        if let Some(waves) = waves {
77            let in_progress_count = waves.inflight_caches.len();
78            if in_progress_count > 0 {
79                ui.separator();
80                ui.spinner();
81                if in_progress_count == 1 {
82                    ui.label("Building analog cache…");
83                } else {
84                    ui.label(format!("Building {in_progress_count} analog caches…"));
85                }
86            }
87        }
88    }
89
90    /// Draw right-aligned status bar elements: cursor time, undo info, and count
91    fn draw_statusbar_right(&self, ui: &mut Ui, waves: Option<&WaveData>, msgs: &mut Vec<Message>) {
92        if let Some(waves) = waves {
93            ui.with_layout(Layout::right_to_left(Align::RIGHT), |ui| {
94                if let Some(time) = &waves.cursor {
95                    ui.label(time_string(
96                        time,
97                        &waves.inner.metadata().timescale,
98                        &self.user.wanted_timeunit,
99                        &self.get_time_format(),
100                    ))
101                    .context_menu(|ui| timeunit_menu(ui, msgs, &self.user.wanted_timeunit));
102                }
103                if let Some(undo_op) = &self.undo_stack.last() {
104                    ui.separator();
105                    ui.label(format!("Undo: {}", undo_op.message));
106                }
107                if let Some(count) = &self.user.count {
108                    ui.separator();
109                    ui.label(format!("Count: {count}"));
110                }
111            });
112        }
113    }
114}