1use crate::SystemState;
3use crate::data_container::{DataContainer, VariableType as VarType};
4use crate::displayed_item_tree::VisibleItemIndex;
5use crate::message::Message;
6use crate::tooltips::{scope_tooltip_text, variable_tooltip_text};
7use crate::transaction_container::StreamScopeRef;
8use crate::transactions::{draw_transaction_root, draw_transaction_variable_list};
9use crate::variable_direction::get_direction_string;
10use crate::view::draw_true_name;
11use crate::wave_container::{
12 ScopeRef, ScopeRefExt, VariableMeta, VariableRef, VariableRefExt, WaveContainer,
13};
14use crate::wave_data::{ScopeType, WaveData};
15use derive_more::{Display, FromStr};
16use ecolor::Color32;
17use egui::text::LayoutJob;
18use egui::{CentralPanel, Frame, Layout, Panel, ScrollArea, TextStyle, Ui};
19use egui_remixicon::icons;
20use emath::Align;
21use enum_iterator::Sequence;
22use epaint::{
23 Margin,
24 text::{TextFormat, TextWrapMode},
25};
26use eyre::WrapErr as _;
27use itertools::Itertools;
28use num::BigUint;
29use serde::{Deserialize, Serialize};
30use std::ops::Range;
31use std::rc::Rc;
32use surfer_translation_types::translator::VariableNameInfo;
33use tracing::warn;
34#[derive(Clone, Copy, Debug, Deserialize, Display, FromStr, PartialEq, Eq, Serialize, Sequence)]
35pub enum HierarchyStyle {
36 Separate,
37 Tree,
38 Variables,
39}
40
41#[derive(Clone, Copy, Debug, Deserialize, Display, FromStr, PartialEq, Eq, Serialize, Sequence)]
42pub enum ParameterDisplayLocation {
43 Variables,
44 Scopes,
45 Tooltips,
46 None,
47}
48
49#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
50pub enum ScopeExpandType {
51 ExpandSpecific(ScopeRef),
52 ExpandAll,
53 CollapseAll,
54}
55
56use crate::variable_filter::VariableNameFilterType;
57use crate::wave_source::WaveSource;
58
59#[derive(Clone)]
60pub(crate) struct VariableListRow {
61 pub(crate) variable: VariableRef,
62 pub(crate) meta: Option<VariableMeta>,
63 pub(crate) name_info: Option<VariableNameInfo>,
64}
65
66#[derive(PartialEq)]
68pub(crate) struct AllVariableCacheKey {
69 cache_generation: u64,
70 translator_generation: u64,
71 wave_source: WaveSource,
72 filter_str: String,
73 filter_type: VariableNameFilterType,
74 case_insensitive: bool,
75 include_inputs: bool,
76 include_outputs: bool,
77 include_inouts: bool,
78 include_others: bool,
79 group_by_direction: bool,
80}
81
82impl SystemState {
83 pub fn separate(&mut self, ui: &mut Ui, msgs: &mut Vec<Message>) {
85 ui.visuals_mut().override_text_color =
86 Some(self.user.config.theme.primary_ui_color.foreground);
87
88 let total_space = ui.available_height();
89 Panel::top("scopes")
90 .resizable(true)
91 .default_size(total_space * 0.5)
92 .max_size(total_space - 64.0)
93 .frame(Frame::new().inner_margin(Margin::same(5)))
94 .show(ui, |ui| {
95 ui.horizontal(|ui| {
96 ui.heading("Scopes")
97 .context_menu(|ui| self.hierarchy_menu(msgs, ui));
98 if self.user.waves.is_some() {
99 let default_padding = ui.spacing().button_padding;
100 ui.spacing_mut().button_padding = egui::vec2(0.0, default_padding.y);
101 ui.button(icons::MENU_UNFOLD_FILL)
102 .on_hover_text("Expand all scopes")
103 .clicked()
104 .then(|| msgs.push(Message::ExpandScope(ScopeExpandType::ExpandAll)));
105 ui.button(icons::MENU_FOLD_FILL)
106 .on_hover_text("Collapse all scopes")
107 .clicked()
108 .then(|| msgs.push(Message::ExpandScope(ScopeExpandType::CollapseAll)));
109 ui.spacing_mut().button_padding = default_padding;
110 }
111 });
112 ui.add_space(3.0);
113
114 ScrollArea::both()
115 .id_salt("scopes")
116 .auto_shrink([false; 2])
117 .show(ui, |ui| {
118 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
119 if let Some(waves) = &self.user.waves {
120 self.draw_all_scopes(msgs, waves, false, ui);
121 }
122 });
123 });
124 CentralPanel::default()
125 .frame(Frame::new().inner_margin(Margin::same(5)))
126 .show(ui, |ui| {
127 ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
128 ui.heading("Variables")
129 .context_menu(|ui| self.hierarchy_menu(msgs, ui));
130 ui.add_space(3.0);
131 self.draw_variable_filter_edit(ui, msgs, false);
132 });
133 ui.add_space(3.0);
134
135 self.draw_variables(msgs, ui);
136 });
137 *self.scope_ref_to_expand.borrow_mut() = None;
138 }
139
140 fn draw_variable_list_header(&self, ui: &mut Ui) {
141 let show_icons = self.show_hierarchy_icons();
142 let show_direction = self.show_variable_direction();
143
144 if !show_icons && !show_direction {
146 return;
147 }
148
149 ui.with_layout(
150 Layout::top_down(Align::LEFT).with_cross_justify(true),
151 |ui| {
152 let monospace_font = ui
153 .style()
154 .text_styles
155 .get(&TextStyle::Monospace)
156 .cloned()
157 .unwrap();
158
159 let text_format = TextFormat {
160 font_id: monospace_font,
161 color: self.user.config.theme.foreground,
162 ..Default::default()
163 };
164
165 let mut label = LayoutJob::default();
166 if show_icons {
168 label.append("T ", 0.0, text_format.clone());
169 }
170 if show_direction {
172 label.append("D ", 0.0, text_format.clone());
173 }
174 label.append("Name", 0.0, text_format);
176
177 ui.add(egui::Button::selectable(false, label));
178 },
179 );
180 ui.separator();
181 }
182
183 fn draw_variables(&mut self, msgs: &mut Vec<Message>, ui: &mut Ui) {
184 if let Some(waves) = &self.user.waves {
185 let empty_scope = if waves.inner.is_waves() {
186 ScopeType::WaveScope(ScopeRef::empty())
187 } else {
188 ScopeType::StreamScope(StreamScopeRef::Empty(String::default()))
189 };
190 let active_scope = waves.active_scope.as_ref().unwrap_or(&empty_scope);
191 match active_scope {
192 ScopeType::WaveScope(scope) => {
193 let Some(wave_container) = waves.inner.as_waves() else {
194 return;
195 };
196 let variables = self.filtered_variables_unsorted(
197 &wave_container.variables_in_scope(scope),
198 false,
199 );
200 let variable_rows = self.build_variable_rows(wave_container, &variables);
201 self.draw_variable_list_header(ui);
203 if self.parameter_display_location() == ParameterDisplayLocation::Variables {
205 let parameters = wave_container.parameters_in_scope(scope);
206 if !parameters.is_empty() {
207 ScrollArea::both()
208 .auto_shrink([false; 2])
209 .id_salt("variables")
210 .show(ui, |ui| {
211 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
212 self.draw_parameters(msgs, wave_container, ¶meters, ui);
213 self.draw_variable_rows(
214 msgs,
215 wave_container,
216 ui,
217 &variable_rows,
218 None,
219 false,
220 );
221 });
222 return; }
224 }
225 let row_height = ui
227 .text_style_height(&TextStyle::Monospace)
228 .max(ui.text_style_height(&TextStyle::Body));
229 ScrollArea::both()
230 .auto_shrink([false; 2])
231 .id_salt("variables")
232 .show_rows(ui, row_height, variable_rows.len(), |ui, row_range| {
233 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
234
235 self.draw_variable_rows(
236 msgs,
237 wave_container,
238 ui,
239 &variable_rows,
240 Some(&row_range),
241 false,
242 );
243 });
244 }
245 ScopeType::StreamScope(s) => {
246 ScrollArea::both()
247 .auto_shrink([false; 2])
248 .id_salt("variables")
249 .show(ui, |ui| {
250 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
251
252 draw_transaction_variable_list(msgs, waves, ui, s);
253 });
254 }
255 }
256 }
257 }
258
259 fn draw_parameters(
260 &self,
261 msgs: &mut Vec<Message>,
262 wave_container: &WaveContainer,
263 parameters: &[VariableRef],
264 ui: &mut Ui,
265 ) {
266 egui::collapsing_header::CollapsingState::load_with_default_open(
267 ui.ctx(),
268 egui::Id::new(parameters),
269 self.expand_parameter_section,
270 )
271 .show_header(ui, |ui| {
272 ui.with_layout(
273 Layout::top_down(Align::LEFT).with_cross_justify(true),
274 |ui| {
275 ui.label("Parameters");
276 },
277 );
278 })
279 .body(|ui| {
280 self.filter_and_draw_variable_list(msgs, wave_container, ui, parameters, None);
281 });
282 }
283
284 pub fn tree(&mut self, ui: &mut Ui, msgs: &mut Vec<Message>) {
286 ui.visuals_mut().override_text_color =
287 Some(self.user.config.theme.primary_ui_color.foreground);
288
289 ui.with_layout(
290 Layout::top_down(Align::LEFT).with_cross_justify(true),
291 |ui| {
292 Frame::new().inner_margin(Margin::same(5)).show(ui, |ui| {
293 ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
294 ui.heading("Hierarchy")
295 .context_menu(|ui| self.hierarchy_menu(msgs, ui));
296 ui.add_space(3.0);
297 self.draw_variable_filter_edit(ui, msgs, false);
298 });
299 ui.add_space(3.0);
300
301 ScrollArea::both().id_salt("hierarchy").show(ui, |ui| {
302 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
303 if let Some(waves) = &self.user.waves {
304 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
305 self.draw_all_scopes(msgs, waves, true, ui);
306 }
307 });
308 });
309 },
310 );
311 }
312
313 pub fn variable_list(&mut self, ui: &mut Ui, msgs: &mut Vec<Message>) {
315 ui.visuals_mut().override_text_color =
316 Some(self.user.config.theme.primary_ui_color.foreground);
317
318 ui.with_layout(
319 Layout::top_down(Align::LEFT).with_cross_justify(true),
320 |ui| {
321 Frame::new().inner_margin(Margin::same(5)).show(ui, |ui| {
322 ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
323 ui.heading("Variables")
324 .context_menu(|ui| self.hierarchy_menu(msgs, ui));
325 ui.add_space(3.0);
326 self.draw_variable_filter_edit(ui, msgs, true);
327 });
328 ui.add_space(3.0);
329 self.draw_all_variables(msgs, ui);
330 });
331 },
332 );
333 }
334
335 fn make_all_variable_cache_key(
336 &self,
337 cache_generation: u64,
338 wave_source: &WaveSource,
339 ) -> AllVariableCacheKey {
340 let f = &self.user.variable_filter;
341 AllVariableCacheKey {
342 cache_generation,
343 translator_generation: self.translator_generation,
344 wave_source: wave_source.clone(),
345 filter_str: f.name_filter_str.clone(),
346 filter_type: f.name_filter_type.clone(),
347 case_insensitive: f.name_filter_case_insensitive,
348 include_inputs: f.include_inputs,
349 include_outputs: f.include_outputs,
350 include_inouts: f.include_inouts,
351 include_others: f.include_others,
352 group_by_direction: f.group_by_direction,
353 }
354 }
355
356 fn draw_all_variables(&mut self, msgs: &mut Vec<Message>, ui: &mut Ui) {
357 if let Some(waves) = &self.user.waves
360 && let DataContainer::Waves(wave_container) = &waves.inner
361 {
362 let key = self.make_all_variable_cache_key(waves.cache_generation, &waves.source);
363 let is_stale = self
364 .all_variable_rows_cache
365 .as_ref()
366 .is_none_or(|(k, _)| k != &key);
367 if is_stale {
368 let variables = self.filtered_variables_unsorted(&wave_container.variables(), true);
369 let rows = self.build_variable_rows(wave_container, &variables);
370 self.all_variable_rows_cache = Some((key, Rc::new(rows)));
371 }
372 }
373
374 if let Some(waves) = &self.user.waves {
376 match &waves.inner {
377 DataContainer::Waves(wave_container) => {
378 let variable_rows = self
380 .all_variable_rows_cache
381 .as_ref()
382 .map_or_else(|| Rc::new(Vec::new()), |(_, rows)| Rc::clone(rows));
383 let row_height = ui
384 .text_style_height(&TextStyle::Monospace)
385 .max(ui.text_style_height(&TextStyle::Body));
386 self.draw_variable_list_header(ui);
388 ScrollArea::both()
389 .auto_shrink([false; 2])
390 .id_salt("variables")
391 .show_rows(ui, row_height, variable_rows.len(), |ui, row_range| {
392 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
393 self.draw_variable_rows(
394 msgs,
395 wave_container,
396 ui,
397 &variable_rows,
398 Some(&row_range),
399 true,
400 );
401 });
402 }
403 DataContainer::Transactions(_) => {
404 ui.with_layout(
406 Layout::top_down(Align::LEFT).with_cross_justify(true),
407 |ui| {
408 ui.label("Streams are not yet supported.");
409 ui.label("Select another view.");
410 },
411 );
412 }
413 DataContainer::Empty => {}
414 }
415 }
416 }
417
418 fn draw_all_scopes(
419 &self,
420 msgs: &mut Vec<Message>,
421 wave: &WaveData,
422 draw_variables: bool,
423 ui: &mut Ui,
424 ) {
425 for scope in wave.inner.root_scopes() {
426 match scope {
427 ScopeType::WaveScope(scope) => {
428 self.draw_selectable_child_or_orphan_scope(
429 msgs,
430 wave,
431 &scope,
432 draw_variables,
433 ui,
434 );
435 }
436 ScopeType::StreamScope(_) => {
437 draw_transaction_root(msgs, wave, ui);
438 }
439 }
440 }
441 if draw_variables && let Some(wave_container) = wave.inner.as_waves() {
442 let scope = ScopeRef::empty();
443 let variables = wave_container.variables_in_scope(&scope);
444 self.filter_and_draw_variable_list(msgs, wave_container, ui, &variables, None);
445 }
446 }
447
448 fn add_scope_selectable_label(
449 &self,
450 msgs: &mut Vec<Message>,
451 wave: &WaveData,
452 scope: &ScopeRef,
453 ui: &mut Ui,
454 scroll_to_label: bool,
455 ) {
456 let name = scope.name();
457 let is_selected = wave.active_scope == Some(ScopeType::WaveScope(scope.clone()));
458 let mut response = if self.show_hierarchy_icons() {
459 let scope_type = wave
460 .inner
461 .as_waves()
462 .and_then(|wc| wc.get_scope_type(scope));
463 let (icon, icon_color) = self.user.config.theme.scope_icons.get_icon(scope_type);
464
465 let body_font = ui
466 .style()
467 .text_styles
468 .get(&TextStyle::Body)
469 .cloned()
470 .unwrap_or_default();
471 let icon_format = TextFormat {
472 font_id: body_font.clone(),
473 color: icon_color,
474 ..Default::default()
475 };
476 let name_format = TextFormat {
477 font_id: body_font,
478 color: self.user.config.theme.foreground,
479 ..Default::default()
480 };
481 let mut label = LayoutJob::default();
482 label.append(icon, 0.0, icon_format);
483 label.append(" ", 0.0, name_format.clone());
484 label.append(&name, 0.0, name_format);
485
486 ui.add(egui::Button::selectable(is_selected, label))
487 } else {
488 ui.add(egui::Button::selectable(is_selected, name))
489 };
490 let _ = response.interact(egui::Sense::click_and_drag());
491 response.drag_started().then(|| {
492 msgs.push(Message::VariableDragStarted(VisibleItemIndex(
493 wave.display_item_ref_counter,
494 )));
495 });
496
497 if scroll_to_label {
498 response.scroll_to_me(Some(Align::Center));
499 }
500
501 response.drag_stopped().then(|| {
502 if ui.input(|i| i.pointer.hover_pos().unwrap_or_default().x)
503 > self.user.sidepanel_width.unwrap_or_default()
504 {
505 let scope_t = ScopeType::WaveScope(scope.clone());
506 let variables = wave
507 .inner
508 .variables_in_scope(&scope_t)
509 .iter()
510 .filter_map(|var| match var {
511 VarType::Variable(var) => Some(var.clone()),
512 VarType::Generator(_) => None,
513 })
514 .collect_vec();
515
516 msgs.push(Message::AddDraggedVariables(
517 self.filtered_variables(variables.as_slice(), false),
518 ));
519 }
520 });
521 if self.show_scope_tooltip() {
522 response = response.on_hover_ui(|ui| {
523 ui.set_max_width(ui.spacing().tooltip_width);
524 ui.add(egui::Label::new(scope_tooltip_text(
525 wave,
526 scope,
527 self.parameter_display_location() == ParameterDisplayLocation::Tooltips,
528 )));
529 });
530 }
531 response.context_menu(|ui| {
532 if ui.button("Add scope").clicked() {
533 msgs.push(Message::AddScope(scope.clone(), false));
534 }
535 if ui.button("Add scope recursively").clicked() {
536 msgs.push(Message::AddScope(scope.clone(), true));
537 }
538 if ui.button("Add scope as group").clicked() {
539 msgs.push(Message::AddScopeAsGroup(scope.clone(), false));
540 }
541 if ui.button("Add scope as group recursively").clicked() {
542 msgs.push(Message::AddScopeAsGroup(scope.clone(), true));
543 }
544
545 let is_array_scope = wave
546 .inner
547 .as_waves()
548 .is_some_and(|wc| wc.scope_is_array(scope));
549
550 if is_array_scope && ui.button("Show frame buffer").clicked() {
551 msgs.push(Message::SetFrameBufferArray(scope.clone()));
552 }
553
554 if is_array_scope && ui.button("Show Memory Viewer").clicked() {
555 msgs.push(Message::OpenMemoryViewer {
556 scope: scope.clone(),
557 name: Some(scope.name()),
558 });
559 }
560 });
561 response.clicked().then(|| {
562 msgs.push(Message::SetActiveScope(if is_selected {
563 None
564 } else {
565 Some(ScopeType::WaveScope(scope.clone()))
566 }));
567 });
568 }
569
570 fn draw_selectable_child_or_orphan_scope(
571 &self,
572 msgs: &mut Vec<Message>,
573 wave: &WaveData,
574 scope: &ScopeRef,
575 draw_variables: bool,
576 ui: &mut Ui,
577 ) {
578 let Some(wave_container) = wave.inner.as_waves() else {
580 return;
581 };
582
583 let Some(child_scopes) = wave_container
584 .child_scopes(scope)
585 .context("Failed to get child scopes")
586 .map_err(|e| warn!("{e:#?}"))
587 .ok()
588 else {
589 return;
590 };
591
592 let no_variables_in_scope = wave_container.no_variables_in_scope(scope);
593 if child_scopes.is_empty() && no_variables_in_scope && !self.show_empty_scopes() {
594 return;
595 }
596
597 if child_scopes.is_empty() && (!draw_variables || no_variables_in_scope) {
598 ui.horizontal(|ui| {
601 ui.add_space(ui.spacing().icon_width + ui.spacing().icon_spacing);
602 self.add_scope_selectable_label(msgs, wave, scope, ui, false);
603 });
604 } else {
605 let should_open_header = self.should_open_header_and_scroll_to(scope);
606 let mut collapsing_header =
607 egui::collapsing_header::CollapsingState::load_with_default_open(
608 ui.ctx(),
609 egui::Id::new(scope),
610 false,
611 );
612 if let Some((header_state, _)) = should_open_header {
613 collapsing_header.set_open(header_state);
614 }
615 collapsing_header
616 .show_header(ui, |ui| {
617 ui.with_layout(
618 Layout::top_down(Align::LEFT).with_cross_justify(true),
619 |ui| {
620 self.add_scope_selectable_label(
621 msgs,
622 wave,
623 scope,
624 ui,
625 should_open_header.is_some_and(|(_, scroll)| scroll),
626 );
627 },
628 );
629 })
630 .body(|ui| {
631 if (draw_variables
632 && !(matches!(
633 self.parameter_display_location(),
634 ParameterDisplayLocation::Tooltips | ParameterDisplayLocation::None
635 )))
636 || self.parameter_display_location() == ParameterDisplayLocation::Scopes
637 {
638 let parameters = wave_container.parameters_in_scope(scope);
639 if !parameters.is_empty() {
640 self.draw_parameters(msgs, wave_container, ¶meters, ui);
641 }
642 }
643 self.draw_root_scope_view(msgs, wave, scope, draw_variables, ui);
644 if draw_variables {
645 let variables = wave_container.variables_in_scope(scope);
646 self.filter_and_draw_variable_list(
647 msgs,
648 wave_container,
649 ui,
650 &variables,
651 None,
652 );
653 }
654 });
655 }
656 }
657
658 fn draw_root_scope_view(
659 &self,
660 msgs: &mut Vec<Message>,
661 wave: &WaveData,
662 root_scope: &ScopeRef,
663 draw_variables: bool,
664 ui: &mut Ui,
665 ) {
666 let Some(wave_container) = wave.inner.as_waves() else {
668 return;
669 };
670
671 wave_container
672 .child_scopes(root_scope)
673 .context("Failed to get child scopes")
674 .map_err(|e| warn!("{e:#?}"))
675 .ok()
676 .into_iter()
677 .flatten()
678 .sorted_by(|a, b| numeric_sort::cmp(&a.name(), &b.name()))
679 .for_each(|child_scope| {
680 self.draw_selectable_child_or_orphan_scope(
681 msgs,
682 wave,
683 &child_scope,
684 draw_variables,
685 ui,
686 );
687 });
688 }
689
690 fn filter_and_draw_variable_list(
691 &self,
692 msgs: &mut Vec<Message>,
693 wave_container: &WaveContainer,
694 ui: &mut Ui,
695 variables: &[VariableRef],
696 row_range: Option<&Range<usize>>,
697 ) {
698 let filtered_variables = self.filtered_variables_unsorted(variables, false);
699 self.draw_variable_list(
700 msgs,
701 wave_container,
702 ui,
703 &filtered_variables,
704 row_range,
705 false,
706 );
707 }
708
709 fn draw_variable_list(
710 &self,
711 msgs: &mut Vec<Message>,
712 wave_container: &WaveContainer,
713 ui: &mut Ui,
714 variables: &[VariableRef],
715 row_range: Option<&Range<usize>>,
716 display_full_path: bool,
717 ) {
718 let variable_rows = self.build_variable_rows(wave_container, variables);
719 self.draw_variable_rows(
720 msgs,
721 wave_container,
722 ui,
723 &variable_rows,
724 row_range,
725 display_full_path,
726 );
727 }
728
729 fn build_variable_rows(
730 &self,
731 wave_container: &WaveContainer,
732 variables: &[VariableRef],
733 ) -> Vec<VariableListRow> {
734 let mut rows = variables
735 .iter()
736 .map(|var| {
737 let meta = wave_container.variable_meta(var).ok();
738 let name_info = self.get_variable_name_info(var, meta.as_ref());
739 VariableListRow {
740 variable: var.clone(),
741 meta,
742 name_info,
743 }
744 })
745 .collect::<Vec<_>>();
746
747 rows.sort_by(|a, b| {
748 let a_priority = a
749 .name_info
750 .as_ref()
751 .and_then(|info| info.priority)
752 .unwrap_or_default();
753 let b_priority = b
754 .name_info
755 .as_ref()
756 .and_then(|info| info.priority)
757 .unwrap_or_default();
758 b_priority
759 .cmp(&a_priority)
760 .then_with(|| self.variable_cmp(&a.variable, &b.variable, Some(wave_container)))
761 });
762 rows
763 }
764
765 fn draw_variable_rows(
766 &self,
767 msgs: &mut Vec<Message>,
768 wave_container: &WaveContainer,
769 ui: &mut Ui,
770 variable_rows: &[VariableListRow],
771 row_range: Option<&Range<usize>>,
772 display_full_path: bool,
773 ) {
774 let variable_rows = if let Some(range) = row_range {
775 let start = range.start.min(variable_rows.len());
776 let end = range.end.min(variable_rows.len());
777 &variable_rows[start..end]
778 } else {
779 variable_rows
780 };
781
782 let monospace_font = ui
786 .style()
787 .text_styles
788 .get(&TextStyle::Monospace)
789 .cloned()
790 .unwrap();
791 let body_font = ui
792 .style()
793 .text_styles
794 .get(&TextStyle::Body)
795 .cloned()
796 .unwrap();
797 let char_width_mono = ui.fonts_mut(|fonts| {
798 fonts
799 .layout_no_wrap(" ".to_string(), monospace_font.clone(), Color32::BLACK)
800 .size()
801 .x
802 });
803 let available_space = ui.available_width() - ui.spacing().button_padding.x * 2.;
805
806 for row in variable_rows {
808 let variable = &row.variable;
809 let meta = row.meta.as_ref();
810 let name_info = row.name_info.clone();
811
812 let index = meta
814 .and_then(|meta| meta.index)
815 .map(|index| {
816 if self.show_variable_indices() {
817 format!(" {index}")
818 } else {
819 String::new()
820 }
821 })
822 .unwrap_or_default();
823
824 let (type_icon, icon_color) = if self.show_hierarchy_icons() {
826 let (icon, color) = self.user.config.theme.variable_icons.get_icon(meta);
827 (format!("{icon} "), color)
828 } else {
829 (String::new(), self.user.config.theme.foreground)
830 };
831
832 let direction = self
834 .show_variable_direction()
835 .then(|| get_direction_string(meta, name_info.as_ref()))
836 .flatten()
837 .unwrap_or_default();
838 let value = if meta.is_some_and(surfer_translation_types::VariableMeta::is_parameter) {
840 let res = wave_container.query_variable(variable, &BigUint::ZERO).ok();
841 res.and_then(|o| o.and_then(|q| q.current.map(|v| format!(": {}", v.1))))
842 .unwrap_or_else(|| ": Undefined".to_string())
843 } else {
844 String::new()
845 };
846
847 ui.with_layout(
848 Layout::top_down(Align::LEFT).with_cross_justify(true),
849 |ui| {
850 let mut label = LayoutJob::default();
851 let true_name = name_info.and_then(|info| info.true_name);
852
853 let font = if true_name.is_some() {
854 monospace_font.clone()
855 } else {
856 body_font.clone()
857 };
858 let icon_format = TextFormat {
859 font_id: font.clone(),
860 color: icon_color,
861 ..Default::default()
862 };
863 let text_format = TextFormat {
864 font_id: font,
865 color: self.user.config.theme.foreground,
866 ..Default::default()
867 };
868
869 if let Some(name) = true_name {
870 let type_icon_size = type_icon.chars().count();
871 let direction_size = direction.chars().count();
872 let index_size = index.chars().count();
873 let value_size = value.chars().count();
874 let used_space = (type_icon_size + direction_size + index_size + value_size)
875 as f32
876 * char_width_mono;
877 let space_for_name = available_space - used_space;
878
879 label.append(&type_icon, 0.0, icon_format);
880 label.append(&direction, 0.0, text_format.clone());
881
882 draw_true_name(
883 &name,
884 &mut label,
885 &monospace_font,
886 self.user.config.theme.foreground,
887 char_width_mono,
888 space_for_name,
889 self.user.config.layout.waveforms_line_height,
890 );
891
892 label.append(&index, 0.0, text_format.clone());
893 label.append(&value, 0.0, text_format);
894 } else {
895 let name = if display_full_path {
896 variable.full_path_string()
897 } else {
898 variable.name.clone()
899 };
900 label.append(&type_icon, 0.0, icon_format);
901 label.append(&direction, 0.0, text_format.clone());
902 label.append(&name, 0.0, text_format.clone());
903 label.append(&index, 0.0, text_format.clone());
904 label.append(&value, 0.0, text_format);
905 }
906
907 let mut response = ui.add(egui::Button::selectable(false, label));
908
909 let _ = response.interact(egui::Sense::click_and_drag());
910
911 if self.show_tooltip() {
912 let tooltip_meta = meta;
915 let tooltip_var = variable.clone();
916 response = response.on_hover_ui(move |ui| {
917 ui.set_max_width(ui.spacing().tooltip_width);
918 ui.add(egui::Label::new(variable_tooltip_text(
919 tooltip_meta,
920 &tooltip_var,
921 )));
922 });
923 }
924 response.drag_started().then(|| {
925 msgs.push(Message::VariableDragStarted(VisibleItemIndex(
926 self.user.waves.as_ref().unwrap().display_item_ref_counter,
927 )));
928 });
929 response.drag_stopped().then(|| {
930 if ui.input(|i| i.pointer.hover_pos().unwrap_or_default().x)
931 > self.user.sidepanel_width.unwrap_or_default()
932 {
933 msgs.push(Message::AddDraggedVariables(vec![variable.clone()]));
934 }
935 });
936 response
937 .clicked()
938 .then(|| msgs.push(Message::AddVariables(vec![variable.clone()])));
939 },
940 );
941 }
942 }
943
944 fn should_open_header_and_scroll_to(&self, scope: &ScopeRef) -> Option<(bool, bool)> {
945 let mut scope_ref_cell = self.scope_ref_to_expand.borrow_mut();
946 if let Some(state) = scope_ref_cell.as_mut() {
947 match state {
948 ScopeExpandType::ExpandAll => return Some((true, false)),
949 ScopeExpandType::CollapseAll => return Some((false, false)),
950 ScopeExpandType::ExpandSpecific(state) => {
951 if state.strs.starts_with(&scope.strs) {
952 if (state.strs.len() - 1) == scope.strs.len() {
953 *scope_ref_cell = None;
955 }
956 return Some((true, true));
957 }
958 }
959 }
960 }
961 None
962 }
963}