1use 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
182fn 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
259fn 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
296pub 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
327fn 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}