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