libsurfer/
hierarchy.rs

1//! Functions for drawing the left hand panel showing scopes and variables.
2use crate::message::Message;
3use crate::transaction_container::StreamScopeRef;
4use crate::variable_filter::VariableFilter;
5use crate::wave_container::{ScopeRef, ScopeRefExt};
6use crate::wave_data::ScopeType;
7use crate::SystemState;
8use derive_more::{Display, FromStr};
9use egui::{CentralPanel, Frame, Layout, Margin, ScrollArea, TextWrapMode, TopBottomPanel, Ui};
10use emath::Align;
11use enum_iterator::Sequence;
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, Copy, Debug, Deserialize, Display, FromStr, PartialEq, Eq, Serialize, Sequence)]
15pub enum HierarchyStyle {
16    Separate,
17    Tree,
18}
19
20/// Scopes and variables in two separate lists
21pub fn separate(state: &mut SystemState, ui: &mut Ui, msgs: &mut Vec<Message>) {
22    ui.visuals_mut().override_text_color =
23        Some(state.user.config.theme.primary_ui_color.foreground);
24
25    let total_space = ui.available_height();
26    TopBottomPanel::top("scopes")
27        .resizable(true)
28        .default_height(total_space / 2.0)
29        .max_height(total_space - 64.0)
30        .frame(Frame::new().inner_margin(Margin::same(5)))
31        .show_inside(ui, |ui| {
32            ui.heading("Scopes");
33            ui.add_space(3.0);
34
35            ScrollArea::both()
36                .id_salt("scopes")
37                .auto_shrink([false; 2])
38                .show(ui, |ui| {
39                    ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
40                    if let Some(waves) = &state.user.waves {
41                        let empty_filter = VariableFilter::new();
42                        state.draw_all_scopes(msgs, waves, false, ui, &empty_filter);
43                    }
44                });
45        });
46    CentralPanel::default()
47        .frame(Frame::new().inner_margin(Margin::same(5)))
48        .show_inside(ui, |ui| {
49            ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
50                ui.heading("Variables");
51                ui.add_space(3.0);
52                state.draw_variable_filter_edit(ui, msgs);
53            });
54            ui.add_space(3.0);
55
56            draw_variables(state, msgs, ui);
57        });
58}
59
60fn draw_variables(state: &mut SystemState, msgs: &mut Vec<Message>, ui: &mut Ui) {
61    let filter = &state.user.variable_filter;
62
63    if let Some(waves) = &state.user.waves {
64        let empty_scope = if waves.inner.is_waves() {
65            ScopeType::WaveScope(ScopeRef::empty())
66        } else {
67            ScopeType::StreamScope(StreamScopeRef::Empty(String::default()))
68        };
69        let active_scope = waves.active_scope.as_ref().unwrap_or(&empty_scope);
70        match active_scope {
71            ScopeType::WaveScope(scope) => {
72                let wave_container = waves.inner.as_waves().unwrap();
73                let variables =
74                    state.filtered_variables(&wave_container.variables_in_scope(scope), filter);
75                // Parameters shown in variable list
76                if !state.show_parameters_in_scopes() {
77                    let parameters = wave_container.parameters_in_scope(scope);
78                    if !parameters.is_empty() {
79                        ScrollArea::both()
80                            .auto_shrink([false; 2])
81                            .id_salt("variables")
82                            .show(ui, |ui| {
83                                egui::collapsing_header::CollapsingState::load_with_default_open(
84                                    ui.ctx(),
85                                    egui::Id::new(&parameters),
86                                    state.expand_parameter_section,
87                                )
88                                .show_header(ui, |ui| {
89                                    ui.with_layout(
90                                        Layout::top_down(Align::LEFT).with_cross_justify(true),
91                                        |ui| {
92                                            ui.label("Parameters");
93                                        },
94                                    );
95                                })
96                                .body(|ui| {
97                                    state.draw_variable_list(
98                                        msgs,
99                                        wave_container,
100                                        ui,
101                                        &parameters,
102                                        None,
103                                        filter,
104                                    );
105                                });
106                                state.draw_filtered_variable_list(
107                                    msgs,
108                                    wave_container,
109                                    ui,
110                                    &variables,
111                                    None,
112                                );
113                            });
114                        return; // Early exit
115                    }
116                }
117                // Parameters not shown here or no parameters: use fast approach only drawing visible rows
118                let row_height = ui
119                    .text_style_height(&egui::TextStyle::Monospace)
120                    .max(ui.text_style_height(&egui::TextStyle::Body));
121                ScrollArea::both()
122                    .auto_shrink([false; 2])
123                    .id_salt("variables")
124                    .show_rows(ui, row_height, variables.len(), |ui, row_range| {
125                        state.draw_filtered_variable_list(
126                            msgs,
127                            wave_container,
128                            ui,
129                            &variables,
130                            Some(row_range),
131                        );
132                    });
133            }
134            ScopeType::StreamScope(s) => {
135                ScrollArea::both()
136                    .auto_shrink([false; 2])
137                    .id_salt("variables")
138                    .show(ui, |ui| {
139                        state.draw_transaction_variable_list(msgs, waves, ui, s);
140                    });
141            }
142        }
143    }
144}
145
146/// Scopes and variables in a joint tree.
147pub fn tree(state: &mut SystemState, ui: &mut Ui, msgs: &mut Vec<Message>) {
148    ui.visuals_mut().override_text_color =
149        Some(state.user.config.theme.primary_ui_color.foreground);
150
151    ui.with_layout(
152        Layout::top_down(Align::LEFT).with_cross_justify(true),
153        |ui| {
154            Frame::new().inner_margin(Margin::same(5)).show(ui, |ui| {
155                ui.with_layout(Layout::left_to_right(Align::TOP), |ui| {
156                    ui.heading("Hierarchy");
157                    ui.add_space(3.0);
158                    state.draw_variable_filter_edit(ui, msgs);
159                });
160                ui.add_space(3.0);
161
162                ScrollArea::both().id_salt("hierarchy").show(ui, |ui| {
163                    ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
164                    if let Some(waves) = &state.user.waves {
165                        let filter = &state.user.variable_filter;
166                        state.draw_all_scopes(msgs, waves, true, ui, filter);
167                    }
168                });
169            });
170        },
171    );
172}