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