1use egui::{Response, Ui};
2use egui_extras::{Column, TableBuilder};
3use ftr_parser::types::Transaction;
4use num::BigUint;
5
6use crate::{
7 transaction_container::{TransactionRef, TransactionStreamRef},
8 wave_container::{ScopeRef, VariableMeta, VariableRef, VariableRefExt},
9 wave_data::WaveData,
10};
11
12fn find_transaction<'a>(
14 waves: &'a WaveData,
15 gen_ref: &TransactionStreamRef,
16 tx_ref: &TransactionRef,
17) -> Option<&'a Transaction> {
18 let txs = waves.inner.as_transactions()?;
19 let gen_id = gen_ref.gen_id?;
20 let generator = txs.get_generator(gen_id)?;
21 generator
22 .transactions
23 .iter()
24 .find(|transaction| transaction.get_tx_id() == tx_ref.id)
25}
26
27#[must_use]
28pub(crate) fn variable_tooltip_text(meta: Option<&VariableMeta>, variable: &VariableRef) -> String {
29 if let Some(meta) = meta {
30 format!(
31 "{}\nNum bits: {}\nType: {}\nDirection: {}",
32 variable.full_path_string(),
33 meta.num_bits
34 .map_or_else(|| "unknown".to_string(), |bits| bits.to_string()),
35 meta.variable_type_name
36 .clone()
37 .or_else(|| meta.variable_type.map(|t| t.to_string()))
38 .unwrap_or_else(|| "unknown".to_string()),
39 meta.direction
40 .map_or_else(|| "unknown".to_string(), |direction| format!("{direction}"))
41 )
42 } else {
43 variable.full_path_string()
44 }
45}
46
47#[must_use]
48pub(crate) fn scope_tooltip_text(
49 wave: &WaveData,
50 scope: &ScopeRef,
51 include_parameters: bool,
52) -> String {
53 let mut parts = vec![format!("{scope}")];
54 if let Some(wave_container) = &wave.inner.as_waves() {
55 if include_parameters && let Some(waves) = &wave.inner.as_waves() {
56 for param in &waves.parameters_in_scope(scope) {
57 let value = wave_container
58 .query_variable(param, &BigUint::ZERO)
59 .ok()
60 .and_then(|o| o.and_then(|q| q.current.map(|v| format!("{}", v.1))))
61 .unwrap_or_else(|| "Undefined".to_string());
62 parts.push(format!("{}: {}", param.name, value));
63 }
64 }
65 let other = wave_container.get_scope_tooltip_data(scope);
66 if !other.is_empty() {
67 parts.push(other);
68 }
69 }
70 parts.join("\n")
71}
72
73#[must_use]
74pub(crate) fn handle_transaction_tooltip(
75 response: Response,
76 waves: &WaveData,
77 gen_ref: &TransactionStreamRef,
78 tx_ref: &TransactionRef,
79) -> Response {
80 response
81 .on_hover_ui(|ui| {
82 if let Some(tx) = find_transaction(waves, gen_ref, tx_ref) {
83 ui.set_max_width(ui.spacing().tooltip_width);
84 ui.add(egui::Label::new(transaction_tooltip_text(waves, tx)));
85 } else {
86 ui.label("Transaction unavailable");
87 }
88 })
89 .on_hover_ui(|ui| {
90 if let Some(tx) = find_transaction(waves, gen_ref, tx_ref) {
95 transaction_tooltip_table(ui, tx);
96 } else {
97 ui.label("Transaction details unavailable");
98 }
99 })
100}
101
102fn transaction_tooltip_text(waves: &WaveData, tx: &Transaction) -> String {
103 let time_scale = waves
104 .inner
105 .as_transactions()
106 .map(|t| t.inner.time_scale.to_string())
107 .unwrap_or_default();
108
109 format!(
110 "tx#{}: {}{} - {}{}\nType: {}",
111 tx.event.tx_id,
112 tx.event.start_time,
113 time_scale,
114 tx.event.end_time,
115 time_scale,
116 waves
117 .inner
118 .as_transactions()
119 .and_then(|t| t.get_generator(tx.get_gen_id()))
120 .map_or_else(|| "unknown".to_string(), |g| g.name.clone()),
121 )
122}
123
124fn transaction_tooltip_table(ui: &mut Ui, tx: &Transaction) {
125 TableBuilder::new(ui)
126 .column(Column::exact(80.))
127 .column(Column::exact(80.))
128 .header(20.0, |mut header| {
129 header.col(|ui| {
130 ui.heading("Attribute");
131 });
132 header.col(|ui| {
133 ui.heading("Value");
134 });
135 })
136 .body(|body| {
137 let total_rows = tx.attributes.len();
138 let attributes = &tx.attributes;
139 body.rows(15., total_rows, |mut row| {
140 if let Some(attribute) = attributes.get(row.index()) {
141 row.col(|ui| {
142 ui.label(attribute.name.clone());
143 });
144 row.col(|ui| {
145 ui.label(attribute.value());
146 });
147 }
148 });
149 });
150}