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, WaveContainer};
12use crate::wave_data::{ScopeType, WaveData};
13use derive_more::{Display, FromStr};
14use ecolor::Color32;
15use egui::text::LayoutJob;
16use egui::{CentralPanel, Frame, Layout, ScrollArea, TextStyle, TopBottomPanel, 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 TopBottomPanel::top("scopes")
60 .resizable(true)
61 .default_height(total_space / 2.0)
62 .max_height(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 self.draw_parameters(msgs, wave_container, ¶meters, ui);
179 self.draw_variable_list(
180 msgs,
181 wave_container,
182 ui,
183 &variables,
184 None,
185 false,
186 );
187 });
188 return; }
190 }
191 let row_height = ui
193 .text_style_height(&TextStyle::Monospace)
194 .max(ui.text_style_height(&TextStyle::Body));
195 ScrollArea::both()
196 .auto_shrink([false; 2])
197 .id_salt("variables")
198 .show_rows(ui, row_height, variables.len(), |ui, row_range| {
199 self.draw_variable_list(
200 msgs,
201 wave_container,
202 ui,
203 &variables,
204 Some(&row_range),
205 false,
206 );
207 });
208 }
209 ScopeType::StreamScope(s) => {
210 ScrollArea::both()
211 .auto_shrink([false; 2])
212 .id_salt("variables")
213 .show(ui, |ui| {
214 draw_transaction_variable_list(msgs, waves, ui, s);
215 });
216 }
217 }
218 }
219 }
220
221 fn draw_parameters(
222 &self,
223 msgs: &mut Vec<Message>,
224 wave_container: &WaveContainer,
225 parameters: &[VariableRef],
226 ui: &mut Ui,
227 ) {
228 egui::collapsing_header::CollapsingState::load_with_default_open(
229 ui.ctx(),
230 egui::Id::new(parameters),
231 self.expand_parameter_section,
232 )
233 .show_header(ui, |ui| {
234 ui.with_layout(
235 Layout::top_down(Align::LEFT).with_cross_justify(true),
236 |ui| {
237 ui.label("Parameters");
238 },
239 );
240 })
241 .body(|ui| {
242 self.filter_and_draw_variable_list(msgs, wave_container, ui, parameters, None);
243 });
244 }
245
246 pub fn tree(&mut self, ui: &mut Ui, msgs: &mut Vec<Message>) {
248 ui.visuals_mut().override_text_color =
249 Some(self.user.config.theme.primary_ui_color.foreground);
250
251 ui.with_layout(
252 Layout::top_down(Align::LEFT).with_cross_justify(true),
253 |ui| {
254 Frame::new().inner_margin(Margin::same(5)).show(ui, |ui| {
255 ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
256 ui.heading("Hierarchy")
257 .context_menu(|ui| self.hierarchy_menu(msgs, ui));
258 ui.add_space(3.0);
259 self.draw_variable_filter_edit(ui, msgs, false);
260 });
261 ui.add_space(3.0);
262
263 ScrollArea::both().id_salt("hierarchy").show(ui, |ui| {
264 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
265 if let Some(waves) = &self.user.waves {
266 self.draw_all_scopes(msgs, waves, true, ui);
267 }
268 });
269 });
270 },
271 );
272 }
273
274 pub fn variable_list(&mut self, ui: &mut Ui, msgs: &mut Vec<Message>) {
276 ui.visuals_mut().override_text_color =
277 Some(self.user.config.theme.primary_ui_color.foreground);
278
279 ui.with_layout(
280 Layout::top_down(Align::LEFT).with_cross_justify(true),
281 |ui| {
282 Frame::new().inner_margin(Margin::same(5)).show(ui, |ui| {
283 ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
284 ui.heading("Variables")
285 .context_menu(|ui| self.hierarchy_menu(msgs, ui));
286 ui.add_space(3.0);
287 self.draw_variable_filter_edit(ui, msgs, true);
288 });
289 ui.add_space(3.0);
290
291 ScrollArea::both().id_salt("variables").show(ui, |ui| {
292 ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
293 self.draw_all_variables(msgs, ui);
294 });
295 });
296 },
297 );
298 }
299
300 fn draw_all_variables(&mut self, msgs: &mut Vec<Message>, ui: &mut Ui) {
301 if let Some(waves) = &self.user.waves {
302 match &waves.inner {
303 DataContainer::Waves(wave_container) => {
304 let variables = self.filtered_variables(&wave_container.variables(), true);
305 let row_height = ui
306 .text_style_height(&TextStyle::Monospace)
307 .max(ui.text_style_height(&TextStyle::Body));
308 ScrollArea::both()
309 .auto_shrink([false; 2])
310 .id_salt("variables")
311 .show_rows(ui, row_height, variables.len(), |ui, row_range| {
312 self.draw_variable_list(
313 msgs,
314 wave_container,
315 ui,
316 &variables,
317 Some(&row_range),
318 true,
319 );
320 });
321 }
322 DataContainer::Transactions(_) => {
323 ui.with_layout(
325 Layout::top_down(Align::LEFT).with_cross_justify(true),
326 |ui| {
327 ui.label("Streams are not yet supported.");
328 ui.label("Select another view.");
329 },
330 );
331 }
332 DataContainer::Empty => {}
333 }
334 }
335 }
336
337 fn draw_all_scopes(
338 &self,
339 msgs: &mut Vec<Message>,
340 wave: &WaveData,
341 draw_variables: bool,
342 ui: &mut Ui,
343 ) {
344 for scope in wave.inner.root_scopes() {
345 match scope {
346 ScopeType::WaveScope(scope) => {
347 self.draw_selectable_child_or_orphan_scope(
348 msgs,
349 wave,
350 &scope,
351 draw_variables,
352 ui,
353 );
354 }
355 ScopeType::StreamScope(_) => {
356 draw_transaction_root(msgs, wave, ui);
357 }
358 }
359 }
360 if draw_variables && let Some(wave_container) = wave.inner.as_waves() {
361 let scope = ScopeRef::empty();
362 let variables = wave_container.variables_in_scope(&scope);
363 self.filter_and_draw_variable_list(msgs, wave_container, ui, &variables, None);
364 }
365 }
366
367 fn add_scope_selectable_label(
368 &self,
369 msgs: &mut Vec<Message>,
370 wave: &WaveData,
371 scope: &ScopeRef,
372 ui: &mut Ui,
373 scroll_to_label: bool,
374 ) {
375 let name = scope.name();
376 let is_selected = wave.active_scope == Some(ScopeType::WaveScope(scope.clone()));
377 let mut response = if self.show_hierarchy_icons() {
378 let scope_type = wave
379 .inner
380 .as_waves()
381 .and_then(|wc| wc.get_scope_type(scope));
382 let (icon, icon_color) = self.user.config.theme.scope_icons.get_icon(scope_type);
383
384 let body_font = ui
385 .style()
386 .text_styles
387 .get(&TextStyle::Body)
388 .cloned()
389 .unwrap_or_default();
390 let icon_format = TextFormat {
391 font_id: body_font.clone(),
392 color: icon_color,
393 ..Default::default()
394 };
395 let name_format = TextFormat {
396 font_id: body_font,
397 color: self.user.config.theme.foreground,
398 ..Default::default()
399 };
400 let mut label = LayoutJob::default();
401 label.append(icon, 0.0, icon_format);
402 label.append(" ", 0.0, name_format.clone());
403 label.append(&name, 0.0, name_format);
404
405 ui.add(egui::Button::selectable(is_selected, label))
406 } else {
407 ui.add(egui::Button::selectable(is_selected, name))
408 };
409 let _ = response.interact(egui::Sense::click_and_drag());
410 response.drag_started().then(|| {
411 msgs.push(Message::VariableDragStarted(VisibleItemIndex(
412 wave.display_item_ref_counter,
413 )));
414 });
415
416 if scroll_to_label {
417 response.scroll_to_me(Some(Align::Center));
418 }
419
420 response.drag_stopped().then(|| {
421 if ui.input(|i| i.pointer.hover_pos().unwrap_or_default().x)
422 > self.user.sidepanel_width.unwrap_or_default()
423 {
424 let scope_t = ScopeType::WaveScope(scope.clone());
425 let variables = wave
426 .inner
427 .variables_in_scope(&scope_t)
428 .iter()
429 .filter_map(|var| match var {
430 VarType::Variable(var) => Some(var.clone()),
431 VarType::Generator(_) => None,
432 })
433 .collect_vec();
434
435 msgs.push(Message::AddDraggedVariables(
436 self.filtered_variables(variables.as_slice(), false),
437 ));
438 }
439 });
440 if self.show_scope_tooltip() {
441 response = response.on_hover_ui(|ui| {
442 ui.set_max_width(ui.spacing().tooltip_width);
443 ui.add(egui::Label::new(scope_tooltip_text(
444 wave,
445 scope,
446 self.parameter_display_location() == ParameterDisplayLocation::Tooltips,
447 )));
448 });
449 }
450 response.context_menu(|ui| {
451 if ui.button("Add scope").clicked() {
452 msgs.push(Message::AddScope(scope.clone(), false));
453 }
454 if ui.button("Add scope recursively").clicked() {
455 msgs.push(Message::AddScope(scope.clone(), true));
456 }
457 if ui.button("Add scope as group").clicked() {
458 msgs.push(Message::AddScopeAsGroup(scope.clone(), false));
459 }
460 if ui.button("Add scope as group recursively").clicked() {
461 msgs.push(Message::AddScopeAsGroup(scope.clone(), true));
462 }
463 });
464 response.clicked().then(|| {
465 msgs.push(Message::SetActiveScope(if is_selected {
466 None
467 } else {
468 Some(ScopeType::WaveScope(scope.clone()))
469 }));
470 });
471 }
472
473 fn draw_selectable_child_or_orphan_scope(
474 &self,
475 msgs: &mut Vec<Message>,
476 wave: &WaveData,
477 scope: &ScopeRef,
478 draw_variables: bool,
479 ui: &mut Ui,
480 ) {
481 let Some(wave_container) = wave.inner.as_waves() else {
483 return;
484 };
485
486 let Some(child_scopes) = wave_container
487 .child_scopes(scope)
488 .context("Failed to get child scopes")
489 .map_err(|e| warn!("{e:#?}"))
490 .ok()
491 else {
492 return;
493 };
494
495 let no_variables_in_scope = wave_container.no_variables_in_scope(scope);
496 if child_scopes.is_empty() && no_variables_in_scope && !self.show_empty_scopes() {
497 return;
498 }
499
500 if child_scopes.is_empty() && (!draw_variables || no_variables_in_scope) {
501 ui.horizontal(|ui| {
504 ui.add_space(ui.spacing().icon_width + ui.spacing().icon_spacing);
505 self.add_scope_selectable_label(msgs, wave, scope, ui, false);
506 });
507 } else {
508 let should_open_header = self.should_open_header_and_scroll_to(scope);
509 let mut collapsing_header =
510 egui::collapsing_header::CollapsingState::load_with_default_open(
511 ui.ctx(),
512 egui::Id::new(scope),
513 false,
514 );
515 if let Some((header_state, _)) = should_open_header {
516 collapsing_header.set_open(header_state);
517 }
518 collapsing_header
519 .show_header(ui, |ui| {
520 ui.with_layout(
521 Layout::top_down(Align::LEFT).with_cross_justify(true),
522 |ui| {
523 self.add_scope_selectable_label(
524 msgs,
525 wave,
526 scope,
527 ui,
528 should_open_header.is_some_and(|(_, scroll)| scroll),
529 );
530 },
531 );
532 })
533 .body(|ui| {
534 if (draw_variables
535 && !(matches!(
536 self.parameter_display_location(),
537 ParameterDisplayLocation::Tooltips | ParameterDisplayLocation::None
538 )))
539 || self.parameter_display_location() == ParameterDisplayLocation::Scopes
540 {
541 let parameters = wave_container.parameters_in_scope(scope);
542 if !parameters.is_empty() {
543 self.draw_parameters(msgs, wave_container, ¶meters, ui);
544 }
545 }
546 self.draw_root_scope_view(msgs, wave, scope, draw_variables, ui);
547 if draw_variables {
548 let variables = wave_container.variables_in_scope(scope);
549 self.filter_and_draw_variable_list(
550 msgs,
551 wave_container,
552 ui,
553 &variables,
554 None,
555 );
556 }
557 });
558 }
559 }
560
561 fn draw_root_scope_view(
562 &self,
563 msgs: &mut Vec<Message>,
564 wave: &WaveData,
565 root_scope: &ScopeRef,
566 draw_variables: bool,
567 ui: &mut Ui,
568 ) {
569 let Some(wave_container) = wave.inner.as_waves() else {
571 return;
572 };
573
574 let Some(child_scopes) = wave_container
575 .child_scopes(root_scope)
576 .context("Failed to get child scopes")
577 .map_err(|e| warn!("{e:#?}"))
578 .ok()
579 else {
580 return;
581 };
582
583 let child_scopes_sorted = child_scopes
584 .iter()
585 .sorted_by(|a, b| numeric_sort::cmp(&a.name(), &b.name()))
586 .collect_vec();
587
588 for child_scope in child_scopes_sorted {
589 self.draw_selectable_child_or_orphan_scope(msgs, wave, child_scope, draw_variables, ui);
590 }
591 }
592
593 fn filter_and_draw_variable_list(
594 &self,
595 msgs: &mut Vec<Message>,
596 wave_container: &WaveContainer,
597 ui: &mut Ui,
598 variables: &[VariableRef],
599 row_range: Option<&Range<usize>>,
600 ) {
601 let filtered_variables = self.filtered_variables(variables, false);
602 self.draw_variable_list(
603 msgs,
604 wave_container,
605 ui,
606 &filtered_variables,
607 row_range,
608 false,
609 );
610 }
611
612 fn draw_variable_list(
613 &self,
614 msgs: &mut Vec<Message>,
615 wave_container: &WaveContainer,
616 ui: &mut Ui,
617 variables: &[VariableRef],
618 row_range: Option<&Range<usize>>,
619 display_full_path: bool,
620 ) {
621 let variable_infos = variables
623 .iter()
624 .map(|var| {
625 let meta = wave_container.variable_meta(var).ok();
626 let name_info = self.get_variable_name_info(var, meta.as_ref());
627 (var, meta, name_info)
628 })
629 .sorted_by_key(|(_, _, name_info)| {
630 -name_info
631 .as_ref()
632 .and_then(|info| info.priority)
633 .unwrap_or_default()
634 })
635 .skip(row_range.as_ref().map_or(0, |r| r.start))
636 .take(
637 row_range
638 .as_ref()
639 .map_or(variables.len(), |r| r.end - r.start),
640 );
641
642 let monospace_font = ui
646 .style()
647 .text_styles
648 .get(&TextStyle::Monospace)
649 .cloned()
650 .unwrap();
651 let body_font = ui
652 .style()
653 .text_styles
654 .get(&TextStyle::Body)
655 .cloned()
656 .unwrap();
657 let char_width_mono = ui.fonts_mut(|fonts| {
658 fonts
659 .layout_no_wrap(" ".to_string(), monospace_font.clone(), Color32::BLACK)
660 .size()
661 .x
662 });
663 let available_space = ui.available_width() - ui.spacing().button_padding.x * 2.;
665
666 for (variable, meta, name_info) in variable_infos {
668 let index = meta
670 .as_ref()
671 .and_then(|meta| meta.index)
672 .map(|index| {
673 if self.show_variable_indices() {
674 format!(" {index}")
675 } else {
676 String::new()
677 }
678 })
679 .unwrap_or_default();
680
681 let (type_icon, icon_color) = if self.show_hierarchy_icons() {
683 let (icon, color) = self
684 .user
685 .config
686 .theme
687 .variable_icons
688 .get_icon(meta.as_ref());
689 (format!("{} ", icon), color)
690 } else {
691 (String::new(), self.user.config.theme.foreground)
692 };
693
694 let direction = self
696 .show_variable_direction()
697 .then(|| get_direction_string(meta.as_ref(), name_info.as_ref()))
698 .flatten()
699 .unwrap_or_default();
700 let value = if meta
702 .as_ref()
703 .is_some_and(surfer_translation_types::VariableMeta::is_parameter)
704 {
705 let res = wave_container.query_variable(variable, &BigUint::ZERO).ok();
706 res.and_then(|o| o.and_then(|q| q.current.map(|v| format!(": {}", v.1))))
707 .unwrap_or_else(|| ": Undefined".to_string())
708 } else {
709 String::new()
710 };
711
712 ui.with_layout(
713 Layout::top_down(Align::LEFT).with_cross_justify(true),
714 |ui| {
715 let mut label = LayoutJob::default();
716 let true_name = name_info.and_then(|info| info.true_name);
717
718 let font = if true_name.is_some() {
719 monospace_font.clone()
720 } else {
721 body_font.clone()
722 };
723 let icon_format = TextFormat {
724 font_id: font.clone(),
725 color: icon_color,
726 ..Default::default()
727 };
728 let text_format = TextFormat {
729 font_id: font,
730 color: self.user.config.theme.foreground,
731 ..Default::default()
732 };
733
734 if let Some(name) = true_name {
735 let type_icon_size = type_icon.chars().count();
736 let direction_size = direction.chars().count();
737 let index_size = index.chars().count();
738 let value_size = value.chars().count();
739 let used_space = (type_icon_size + direction_size + index_size + value_size)
740 as f32
741 * char_width_mono;
742 let space_for_name = available_space - used_space;
743
744 label.append(&type_icon, 0.0, icon_format);
745 label.append(&direction, 0.0, text_format.clone());
746
747 draw_true_name(
748 &name,
749 &mut label,
750 monospace_font.clone(),
751 self.user.config.theme.foreground,
752 char_width_mono,
753 space_for_name,
754 );
755
756 label.append(&index, 0.0, text_format.clone());
757 label.append(&value, 0.0, text_format);
758 } else {
759 let name = if display_full_path {
760 variable.full_path().join(".")
761 } else {
762 variable.name.clone()
763 };
764 label.append(&type_icon, 0.0, icon_format);
765 label.append(&direction, 0.0, text_format.clone());
766 label.append(&name, 0.0, text_format.clone());
767 label.append(&index, 0.0, text_format.clone());
768 label.append(&value, 0.0, text_format);
769 }
770
771 let mut response = ui.add(egui::Button::selectable(false, label));
772
773 let _ = response.interact(egui::Sense::click_and_drag());
774
775 if self.show_tooltip() {
776 let tooltip_meta = meta.clone();
779 let tooltip_var = variable.clone();
780 response = response.on_hover_ui(move |ui| {
781 ui.set_max_width(ui.spacing().tooltip_width);
782 ui.add(egui::Label::new(variable_tooltip_text(
783 tooltip_meta.as_ref(),
784 &tooltip_var,
785 )));
786 });
787 }
788 response.drag_started().then(|| {
789 msgs.push(Message::VariableDragStarted(VisibleItemIndex(
790 self.user.waves.as_ref().unwrap().display_item_ref_counter,
791 )));
792 });
793 response.drag_stopped().then(|| {
794 if ui.input(|i| i.pointer.hover_pos().unwrap_or_default().x)
795 > self.user.sidepanel_width.unwrap_or_default()
796 {
797 msgs.push(Message::AddDraggedVariables(vec![variable.clone()]));
798 }
799 });
800 response
801 .clicked()
802 .then(|| msgs.push(Message::AddVariables(vec![variable.clone()])));
803 },
804 );
805 }
806 }
807
808 fn should_open_header_and_scroll_to(&self, scope: &ScopeRef) -> Option<(bool, bool)> {
809 let mut scope_ref_cell = self.scope_ref_to_expand.borrow_mut();
810 if let Some(state) = scope_ref_cell.as_mut() {
811 match state {
812 ScopeExpandType::ExpandAll => return Some((true, false)),
813 ScopeExpandType::CollapseAll => return Some((false, false)),
814 ScopeExpandType::ExpandSpecific(state) => {
815 if state.strs.starts_with(&scope.strs) {
816 if (state.strs.len() - 1) == scope.strs.len() {
817 *scope_ref_cell = None;
819 }
820 return Some((true, true));
821 }
822 }
823 }
824 }
825 None
826 }
827}