libsurfer/
help.rs

1//! Help texts and dialogs.
2use egui::{Context, Grid, OpenUrl, RichText, ScrollArea, Ui, Window};
3use egui_remixicon::icons;
4use emath::{Align2, Pos2};
5
6use crate::wave_source::LoadOptions;
7use crate::{message::Message, SystemState};
8
9impl SystemState {
10    pub fn help_message(&self, ui: &mut Ui) {
11        if self.user.waves.is_none() {
12            ui.label(RichText::new(
13                "Drag and drop a VCD, FST, or GHW file here to open it",
14            ));
15
16            #[cfg(target_arch = "wasm32")]
17            ui.label(RichText::new("Or press space and type load_url"));
18            #[cfg(not(target_arch = "wasm32"))]
19            ui.label(RichText::new(
20                "Or press space and type load_file or load_url",
21            ));
22            #[cfg(target_arch = "wasm32")]
23            ui.label(RichText::new(
24                "Or use the file menu or toolbar to open a URL",
25            ));
26            #[cfg(not(target_arch = "wasm32"))]
27            ui.label(RichText::new(
28                "Or use the file menu or toolbar to open a file or a URL",
29            ));
30            ui.horizontal(|ui| {
31                ui.label(RichText::new("Or click"));
32                if ui.link("here").clicked() {
33                    self.channels
34                        .msg_sender
35                        .send(Message::LoadWaveformFileFromUrl(
36                            "https://app.surfer-project.org/picorv32.vcd".to_string(),
37                            LoadOptions::clean(),
38                        ))
39                        .ok();
40                }
41                ui.label("to open an example waveform");
42            });
43
44            ui.add_space(20.0);
45            ui.separator();
46            ui.add_space(20.0);
47        }
48
49        controls_listing(ui);
50
51        ui.add_space(20.0);
52        ui.separator();
53        ui.add_space(20.0);
54
55        #[cfg(target_arch = "wasm32")]
56        {
57            ui.label(RichText::new(
58            "Note that this web based version is a bit slower than a natively installed version. There may also be a long delay with unresponsiveness when loading large waveforms because the web assembly version does not currently support multi threading.",
59        ));
60
61            ui.hyperlink_to(
62                "See https://gitlab.com/surfer-project/surfer for install instructions",
63                "https://gitlab.com/surfer-project/surfer",
64            );
65        }
66    }
67}
68
69pub fn draw_about_window(ctx: &Context, msgs: &mut Vec<Message>) {
70    let mut open = true;
71    Window::new("About Surfer")
72        .open(&mut open)
73        .collapsible(false)
74        .resizable(true)
75        .show(ctx, |ui| {
76            ui.vertical_centered(|ui| {
77                ui.label(RichText::new("🏄 Surfer").monospace().size(24.));
78                ui.add_space(20.);
79                ui.label(format!(
80                    "Cargo version: {ver}",
81                    ver = env!("CARGO_PKG_VERSION")
82                ));
83                if ui
84                    .small_button(format!(
85                        "Git version: {ver}",
86                        ver = env!("VERGEN_GIT_DESCRIBE")
87                    ))
88                    .on_hover_text("Click to copy git version")
89                    .clicked()
90                {
91                    ctx.copy_text(env!("VERGEN_GIT_DESCRIBE").to_string());
92                }
93                ui.label(format!(
94                    "Build date: {date}",
95                    date = env!("VERGEN_BUILD_DATE")
96                ));
97                ui.hyperlink_to(
98                    (icons::GITLAB_FILL).to_string() + " repository",
99                    "https://gitlab.com/surfer-project/surfer",
100                );
101                ui.hyperlink_to("Homepage", "https://surfer-project.org/");
102                ui.add_space(10.);
103                if ui.button("Close").clicked() {
104                    msgs.push(Message::SetAboutVisible(false));
105                }
106            })
107        });
108    if !open {
109        msgs.push(Message::SetAboutVisible(false));
110    }
111}
112
113pub fn draw_quickstart_help_window(ctx: &Context, msgs: &mut Vec<Message>) {
114    let mut open = true;
115    Window::new("🏄 Surfer quick start")
116        .collapsible(true)
117        .resizable(true)
118        .pivot(Align2::CENTER_CENTER)
119        .open(&mut open)
120        .default_pos(Pos2::new(
121            ctx.available_rect().size().x / 2.,
122            ctx.available_rect().size().y / 2.,
123        ))
124        .show(ctx, |ui| {
125            ui.vertical(|ui| {
126                ui.add_space(5.);
127
128                ui.label(RichText::new("Controls").size(20.));
129                ui.add_space(5.);
130                ui.label("↔ Use scroll and ctrl+scroll to navigate the waveform");
131                ui.label("🚀 Press space to open the command palette");
132                ui.label("✋ Click the middle mouse button for gestures");
133                ui.label("❓ See the help menu for more controls");
134                ui.add_space(10.);
135                ui.label(RichText::new("Adding traces").size(20.));
136                ui.add_space(5.);
137                ui.label("Add more traces using the command palette or using the sidebar");
138                ui.add_space(10.);
139                ui.label(RichText::new("Opening files").size(20.));
140                ui.add_space(5.);
141                ui.label("Open a new file by");
142                ui.label("- dragging a VCD, FST, or GHW file");
143                #[cfg(target_arch = "wasm32")]
144                ui.label("- typing load_url in the command palette");
145                #[cfg(not(target_arch = "wasm32"))]
146                ui.label("- typing load_url or load_file in the command palette");
147                ui.label("- using the file menu");
148                ui.label("- using the toolbar");
149                ui.add_space(10.);
150            });
151            ui.vertical_centered(|ui| {
152                if ui.button("Close").clicked() {
153                    msgs.push(Message::SetQuickStartVisible(false));
154                }
155            })
156        });
157    if !open {
158        msgs.push(Message::SetQuickStartVisible(false));
159    }
160}
161
162pub fn draw_control_help_window(ctx: &Context, msgs: &mut Vec<Message>) {
163    let mut open = true;
164    Window::new("🖮 Surfer controls")
165        .collapsible(true)
166        .resizable(true)
167        .open(&mut open)
168        .show(ctx, |ui| {
169            ui.vertical_centered(|ui| {
170                key_listing(ui);
171                ui.add_space(10.);
172                if ui.button("Close").clicked() {
173                    msgs.push(Message::SetKeyHelpVisible(false));
174                }
175            });
176        });
177    if !open {
178        msgs.push(Message::SetKeyHelpVisible(false));
179    }
180}
181
182/// Long list of key binding for the dialog.
183fn key_listing(ui: &mut Ui) {
184    let keys = vec![
185        ("🚀", "Space", "Show command prompt"),
186        ("↔", "Scroll", "Pan"),
187        ("🔎", "Ctrl+Scroll", "Zoom"),
188        (icons::SAVE_FILL, "Ctrl+s", "Save the state"),
189        (
190            icons::LAYOUT_LEFT_FILL,
191            "b",
192            "Show or hide the design hierarchy",
193        ),
194        (icons::MENU_FILL, "Alt+m", "Show or hide menu"),
195        (icons::TOOLS_FILL, "t", "Show or hide toolbar"),
196        (icons::ZOOM_IN_FILL, "+", "Zoom in"),
197        (icons::ZOOM_OUT_FILL, "-", "Zoom out"),
198        ("", "k/⬆", "Scroll up"),
199        ("", "j/⬇", "Scroll down"),
200        ("", "Ctrl+k/⬆", "Move focused item up"),
201        ("", "Ctrl+j/⬇", "Move focused item down"),
202        ("", "Alt+k/⬆", "Move focus up"),
203        ("", "Alt+j/⬇", "Move focus down"),
204        ("", "a", "Add focused item to selection"),
205        ("", "Ctrl+Alt+k/⬆", "Extend selection up"),
206        ("", "Ctrl+Alt+j/⬇", "Extend selection down"),
207        ("", "u/Shift+u", "Undo/redo last change"),
208        ("", "Ctrl+z/Ctrl+y", "Undo/redo last change"),
209        ("", "f", "Fast focus a signal"),
210        ("", "m", "Add marker at current cursor"),
211        ("", "Ctrl+0-9", "Add numbered marker"),
212        ("", "0-9", "Center view at numbered marker"),
213        (icons::REWIND_START_FILL, "s", "Go to start"),
214        (icons::FORWARD_END_FILL, "e", "Go to end"),
215        (icons::REFRESH_LINE, "r", "Reload waveform"),
216        (icons::SPEED_FILL, "Page up", "Go one page/screen right"),
217        (icons::REWIND_FILL, "Page down", "Go one page/screen left"),
218        (
219            icons::PLAY_FILL,
220            "➡/l",
221            "Go to next transition of focused variable (changeable in config)",
222        ),
223        (
224            icons::PLAY_REVERSE_FILL,
225            "⬅/h",
226            "Go to previous transition of focused variable (changeable in config)",
227        ),
228        (
229            "",
230            "Ctrl+➡/l",
231            "Go to next non-zero transition of focused variable",
232        ),
233        (
234            "",
235            "Ctrl+⬅/h",
236            "Go to previous non-zero transition of focused variable",
237        ),
238        (icons::DELETE_BIN_2_FILL, "x/Delete", "Delete focused item"),
239        #[cfg(not(target_arch = "wasm32"))]
240        (icons::FULLSCREEN_LINE, "F11", "Toggle full screen"),
241    ];
242
243    Grid::new("keys")
244        .num_columns(3)
245        .spacing([5., 5.])
246        .show(ui, |ui| {
247            for (symbol, control, description) in keys {
248                let control = ctrl_to_cmd(control);
249                ui.label(symbol);
250                ui.label(control);
251                ui.label(description);
252                ui.end_row();
253            }
254        });
255
256    add_hint_text(ui);
257}
258
259/// Shorter list displayed at startup screen.
260fn controls_listing(ui: &mut Ui) {
261    let controls = vec![
262        ("🚀", "Space", "Show command prompt"),
263        ("↔", "Horizontal Scroll", "Pan"),
264        ("↕", "j, k, Up, Down", "Scroll down/up"),
265        ("⌖", "Ctrl+j, k, Up, Down", "Move focus down/up"),
266        ("🔃", "Alt+j, k, Up, Down", "Move focused item down/up"),
267        ("🔎", "Ctrl+Scroll", "Zoom"),
268        (
269            icons::LAYOUT_LEFT_2_FILL,
270            "b",
271            "Show or hide the design hierarchy",
272        ),
273        (icons::MENU_FILL, "Alt+m", "Show or hide menu"),
274        (icons::TOOLS_FILL, "t", "Show or hide toolbar"),
275    ];
276
277    Grid::new("controls")
278        .num_columns(2)
279        .spacing([20., 5.])
280        .show(ui, |ui| {
281            for (symbol, control, description) in controls {
282                let control = ctrl_to_cmd(control);
283                ui.label(format!("{symbol}  {control}"));
284                ui.label(description);
285                ui.end_row();
286            }
287        });
288    add_hint_text(ui);
289}
290
291fn add_hint_text(ui: &mut Ui) {
292    ui.add_space(20.);
293    ui.label(RichText::new("Hint: You can repeat keybinds by typing Alt+0-9 before them. For example, Alt+1 Alt+0 k scrolls 10 steps up."));
294}
295
296// Display information about licenses for Surfer and used crates.
297pub fn draw_license_window(ctx: &Context, msgs: &mut Vec<Message>) {
298    let mut open = true;
299    let text = include_str!("../../LICENSE-EUPL-1.2.txt");
300    Window::new("Surfer License")
301        .open(&mut open)
302        .collapsible(false)
303        .max_height(600.)
304        .default_width(600.)
305        .show(ctx, |ui| {
306            ScrollArea::vertical().show(ui, |ui| {
307                ui.label(text);
308            });
309            ui.add_space(10.);
310            ui.horizontal(|ui| {
311                if ui.button("Dependency licenses").clicked() {
312                    ctx.open_url(OpenUrl {
313                        url: "https://docs.surfer-project.org/licenses.html".to_string(),
314                        new_tab: true,
315                    });
316                }
317                if ui.button("Close").clicked() {
318                    msgs.push(Message::SetLicenseVisible(false));
319                }
320            });
321        });
322    if !open {
323        msgs.push(Message::SetLicenseVisible(false));
324    }
325}
326
327// Replace Ctrl with Cmd in case of macos, unless we are running tests
328fn ctrl_to_cmd(instr: &str) -> String {
329    #[cfg(all(target_os = "macos", not(test)))]
330    let instring = instr.to_string().replace("Ctrl", "Cmd");
331    #[cfg(any(not(target_os = "macos"), test))]
332    let instring = instr.to_string();
333    instring
334}