Skip to main content

libsurfer/
toolbar.rs

1//! Toolbar handling.
2use egui::{Button, Layout, Panel, RichText, Ui};
3use egui_remixicon::icons;
4use emath::{Align, Vec2};
5
6use crate::message::MessageTarget;
7use crate::time::time_input_widget;
8use crate::wave_container::SimulationStatus;
9use crate::wave_source::LoadOptions;
10use crate::{
11    SystemState,
12    file_dialog::OpenMode,
13    message::Message,
14    wave_data::{PER_SCROLL_EVENT, SCROLL_EVENTS_PER_PAGE},
15};
16
17/// Helper function to add a new toolbar button, setting up icon, hover text etc.
18fn add_toolbar_button(
19    ui: &mut Ui,
20    msgs: &mut Vec<Message>,
21    icon_string: &str,
22    hover_text: &str,
23    message: Message,
24    enabled: bool,
25) {
26    let button = Button::new(RichText::new(icon_string).heading()).frame(false);
27    if ui
28        .add_enabled(enabled, button)
29        .on_hover_text(hover_text)
30        .clicked()
31    {
32        msgs.push(message);
33    }
34}
35
36impl SystemState {
37    /// Add panel and draw toolbar.
38    pub fn add_toolbar_panel(&self, ui: &mut Ui, msgs: &mut Vec<Message>) {
39        Panel::top("toolbar").show_inside(ui, |ui| {
40            self.draw_toolbar(ui, msgs);
41        });
42    }
43
44    fn simulation_status_toolbar(&self, ui: &mut Ui, msgs: &mut Vec<Message>) {
45        let Some(waves) = &self.user.waves else {
46            return;
47        };
48        let Some(status) = waves.inner.simulation_status() else {
49            return;
50        };
51
52        ui.separator();
53
54        ui.label("Simulation ");
55        match status {
56            SimulationStatus::Paused => add_toolbar_button(
57                ui,
58                msgs,
59                icons::PLAY_CIRCLE_FILL,
60                "Run simulation",
61                Message::UnpauseSimulation,
62                true,
63            ),
64            SimulationStatus::Running => add_toolbar_button(
65                ui,
66                msgs,
67                icons::PAUSE_CIRCLE_FILL,
68                "Pause simulation",
69                Message::PauseSimulation,
70                true,
71            ),
72            SimulationStatus::Finished => {
73                ui.label("Finished");
74            }
75        }
76    }
77
78    fn draw_toolbar(&self, ui: &mut Ui, msgs: &mut Vec<Message>) {
79        let wave_loaded = self.user.waves.is_some();
80        let undo_available = !self.undo_stack.is_empty();
81        let redo_available = !self.redo_stack.is_empty();
82
83        let (item_selected, cursor_set, multiple_viewports) = if let Some(waves) = &self.user.waves
84        {
85            (
86                waves.focused_item.is_some(),
87                waves.cursor.is_some(),
88                waves.viewports.len() > 1,
89            )
90        } else {
91            (false, false, false)
92        };
93
94        ui.with_layout(Layout::left_to_right(Align::LEFT), |ui| {
95            if !self.show_menu() {
96                // Menu
97                ui.menu_button(RichText::new(icons::MENU_FILL).heading(), |ui| {
98                    self.menu_contents(ui, msgs);
99                });
100                ui.separator();
101            }
102            // Files
103            add_toolbar_button(
104                ui,
105                msgs,
106                icons::FOLDER_OPEN_FILL,
107                "Open file...",
108                Message::OpenFileDialog(OpenMode::Open),
109                true,
110            );
111            add_toolbar_button(
112                ui,
113                msgs,
114                icons::DOWNLOAD_CLOUD_FILL,
115                "Open URL...",
116                Message::SetUrlEntryVisible(
117                    true,
118                    Some(Box::new(|url: String| {
119                        Message::LoadWaveformFileFromUrl(url.clone(), LoadOptions::Clear)
120                    })),
121                ),
122                true,
123            );
124            add_toolbar_button(
125                ui,
126                msgs,
127                icons::REFRESH_LINE,
128                "Reload",
129                Message::ReloadWaveform(self.user.config.behavior.keep_during_reload),
130                wave_loaded,
131            );
132            add_toolbar_button(
133                ui,
134                msgs,
135                icons::RUN_LINE,
136                "Run command file...",
137                Message::OpenCommandFileDialog,
138                true,
139            );
140            if self.user.surver_url.is_some() {
141                ui.separator();
142                add_toolbar_button(
143                    ui,
144                    msgs,
145                    icons::FILE_LIST_FILL,
146                    "Select Surver file",
147                    Message::SetSurverFileWindowVisible(true),
148                    true,
149                );
150            }
151            ui.separator();
152            add_toolbar_button(
153                ui,
154                msgs,
155                icons::FILE_COPY_FILL,
156                "Copy variable value",
157                Message::VariableValueToClipbord(MessageTarget::CurrentSelection),
158                item_selected && cursor_set,
159            );
160
161            ui.separator();
162            // Zoom
163            add_toolbar_button(
164                ui,
165                msgs,
166                icons::ZOOM_IN_FILL,
167                "Zoom in",
168                Message::CanvasZoom {
169                    mouse_ptr: None,
170                    delta: 0.5,
171                    viewport_idx: 0,
172                },
173                wave_loaded,
174            );
175            add_toolbar_button(
176                ui,
177                msgs,
178                icons::ZOOM_OUT_FILL,
179                "Zoom out",
180                Message::CanvasZoom {
181                    mouse_ptr: None,
182                    delta: 2.0,
183                    viewport_idx: 0,
184                },
185                wave_loaded,
186            );
187            add_toolbar_button(
188                ui,
189                msgs,
190                icons::ASPECT_RATIO_FILL,
191                "Zoom to fit",
192                Message::ZoomToFit { viewport_idx: 0 },
193                wave_loaded,
194            );
195            ui.separator();
196
197            // Navigation
198            add_toolbar_button(
199                ui,
200                msgs,
201                icons::REWIND_START_FILL,
202                "Go to start",
203                Message::GoToStart { viewport_idx: 0 },
204                wave_loaded,
205            );
206            add_toolbar_button(
207                ui,
208                msgs,
209                icons::REWIND_FILL,
210                "Go one page left",
211                Message::CanvasScroll {
212                    delta: Vec2 {
213                        y: PER_SCROLL_EVENT * SCROLL_EVENTS_PER_PAGE,
214                        x: 0.,
215                    },
216                    viewport_idx: 0,
217                },
218                wave_loaded,
219            );
220            add_toolbar_button(
221                ui,
222                msgs,
223                icons::PLAY_REVERSE_FILL,
224                "Go left",
225                Message::CanvasScroll {
226                    delta: Vec2 {
227                        y: PER_SCROLL_EVENT,
228                        x: 0.,
229                    },
230                    viewport_idx: 0,
231                },
232                wave_loaded,
233            );
234            add_toolbar_button(
235                ui,
236                msgs,
237                icons::PLAY_FILL,
238                "Go right",
239                Message::CanvasScroll {
240                    delta: Vec2 {
241                        y: -PER_SCROLL_EVENT,
242                        x: 0.,
243                    },
244                    viewport_idx: 0,
245                },
246                wave_loaded,
247            );
248            add_toolbar_button(
249                ui,
250                msgs,
251                icons::SPEED_FILL,
252                "Go one page right",
253                Message::CanvasScroll {
254                    delta: Vec2 {
255                        y: -PER_SCROLL_EVENT * SCROLL_EVENTS_PER_PAGE,
256                        x: 0.,
257                    },
258                    viewport_idx: 0,
259                },
260                wave_loaded,
261            );
262            add_toolbar_button(
263                ui,
264                msgs,
265                icons::FORWARD_END_FILL,
266                "Go to end",
267                Message::GoToEnd { viewport_idx: 0 },
268                wave_loaded,
269            );
270            ui.separator();
271
272            // Next transition
273            add_toolbar_button(
274                ui,
275                msgs,
276                icons::CONTRACT_LEFT_FILL,
277                "Set cursor on previous transition of focused variable",
278                Message::MoveCursorToTransition {
279                    next: false,
280                    variable: None,
281                    skip_zero: false,
282                },
283                item_selected && cursor_set,
284            );
285            add_toolbar_button(
286                ui,
287                msgs,
288                icons::CONTRACT_RIGHT_FILL,
289                "Set cursor on next transition of focused variable",
290                Message::MoveCursorToTransition {
291                    next: true,
292                    variable: None,
293                    skip_zero: false,
294                },
295                item_selected && cursor_set,
296            );
297            ui.separator();
298
299            // Add items
300            add_toolbar_button(
301                ui,
302                msgs,
303                icons::SPACE,
304                "Add divider",
305                Message::AddDivider(None, None),
306                wave_loaded,
307            );
308            add_toolbar_button(
309                ui,
310                msgs,
311                icons::TIME_FILL,
312                "Add timeline",
313                Message::AddTimeLine(None),
314                wave_loaded,
315            );
316            ui.separator();
317
318            // Add/remove viewport
319            add_toolbar_button(
320                ui,
321                msgs,
322                icons::ADD_BOX_FILL,
323                "Add viewport",
324                Message::AddViewport,
325                wave_loaded,
326            );
327            add_toolbar_button(
328                ui,
329                msgs,
330                icons::CHECKBOX_INDETERMINATE_FILL,
331                "Remove viewport",
332                Message::RemoveViewport,
333                wave_loaded && multiple_viewports,
334            );
335
336            let undo_tooltip = if let Some(undo_op) = self.undo_stack.last() {
337                format!("Undo: {}", undo_op.message)
338            } else {
339                "Undo".into()
340            };
341            let redo_tooltip = if let Some(redo_op) = self.redo_stack.last() {
342                format!("Redo: {}", redo_op.message)
343            } else {
344                "Redo".into()
345            };
346            add_toolbar_button(
347                ui,
348                msgs,
349                icons::ARROW_GO_BACK_FILL,
350                &undo_tooltip,
351                Message::Undo(1),
352                undo_available,
353            );
354            add_toolbar_button(
355                ui,
356                msgs,
357                icons::ARROW_GO_FORWARD_FILL,
358                &redo_tooltip,
359                Message::Redo(1),
360                redo_available,
361            );
362
363            self.simulation_status_toolbar(ui, msgs);
364            if let Some(waves) = &self.user.waves {
365                ui.separator();
366
367                time_input_widget(
368                    ui,
369                    waves,
370                    msgs,
371                    &mut self.time_widget.borrow_mut(),
372                    self.request_time_edit_focus,
373                );
374            }
375        });
376    }
377}