1use ecolor::Color32;
3use egui::{FontSelection, RichText, Style, WidgetText};
4use emath::Align;
5use epaint::text::LayoutJob;
6use serde::{Deserialize, Serialize};
7use std::sync::Arc;
8
9use crate::analog_signal_cache::AnalogCacheEntry;
10use surfer_translation_types::VariableInfo;
11
12use crate::config::SurferConfig;
13use crate::transaction_container::TransactionStreamRef;
14use crate::wave_container::{FieldRef, VariableRef, VariableRefExt, WaveContainer};
15use crate::{
16 marker::DEFAULT_MARKER_NAME, time::DEFAULT_TIMELINE_NAME, variable_name_type::VariableNameType,
17};
18
19const DEFAULT_DIVIDER_NAME: &str = "";
20
21#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
23#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen)]
24pub struct DisplayedItemRef(pub usize);
25
26impl From<usize> for DisplayedItemRef {
27 fn from(item: usize) -> Self {
28 DisplayedItemRef(item)
29 }
30}
31
32#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash)]
33pub struct DisplayedFieldRef {
34 pub item: DisplayedItemRef,
35 pub field: Vec<String>,
36}
37
38impl DisplayedFieldRef {
39 #[must_use]
40 pub fn without_field(&self) -> DisplayedFieldRef {
41 DisplayedFieldRef {
42 item: self.item,
43 field: vec![],
44 }
45 }
46}
47
48impl From<DisplayedItemRef> for DisplayedFieldRef {
49 fn from(item: DisplayedItemRef) -> Self {
50 DisplayedFieldRef {
51 item,
52 field: vec![],
53 }
54 }
55}
56
57#[derive(Serialize, Deserialize, Clone)]
58pub enum DisplayedItem {
59 Variable(DisplayedVariable),
60 Divider(DisplayedDivider),
61 Marker(DisplayedMarker),
62 TimeLine(DisplayedTimeLine),
63 Placeholder(DisplayedPlaceholder),
64 Stream(DisplayedStream),
65 Group(DisplayedGroup),
66}
67
68#[derive(Serialize, Deserialize, Clone)]
69pub struct FieldFormat {
70 pub field: Vec<String>,
71 pub format: String,
72}
73
74#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Default)]
75pub enum AnalogRenderStyle {
76 #[default]
77 Step,
78 Interpolated,
79}
80
81#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Default)]
82pub enum AnalogYAxisScale {
83 #[default]
84 Viewport,
85 Global,
86}
87
88#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
89pub struct AnalogSettings {
90 pub render_style: AnalogRenderStyle,
91 pub y_axis_scale: AnalogYAxisScale,
92}
93
94impl AnalogSettings {
95 #[must_use]
96 pub fn step_viewport() -> Self {
97 Self {
98 render_style: AnalogRenderStyle::Step,
99 y_axis_scale: AnalogYAxisScale::Viewport,
100 }
101 }
102
103 #[must_use]
104 pub fn step_global() -> Self {
105 Self {
106 render_style: AnalogRenderStyle::Step,
107 y_axis_scale: AnalogYAxisScale::Global,
108 }
109 }
110
111 #[must_use]
112 pub fn interpolated_viewport() -> Self {
113 Self {
114 render_style: AnalogRenderStyle::Interpolated,
115 y_axis_scale: AnalogYAxisScale::Viewport,
116 }
117 }
118
119 #[must_use]
120 pub fn interpolated_global() -> Self {
121 Self {
122 render_style: AnalogRenderStyle::Interpolated,
123 y_axis_scale: AnalogYAxisScale::Global,
124 }
125 }
126}
127
128#[derive(Serialize, Deserialize)]
131pub struct AnalogVarState {
132 pub settings: AnalogSettings,
133 #[serde(skip)]
134 pub cache: Option<Arc<AnalogCacheEntry>>,
135}
136
137impl std::fmt::Debug for AnalogVarState {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 f.write_str("AnalogVarState")
140 }
141}
142
143impl Clone for AnalogVarState {
146 fn clone(&self) -> Self {
147 Self {
148 settings: self.settings,
149 cache: None, }
151 }
152}
153
154impl PartialEq for AnalogVarState {
155 fn eq(&self, other: &Self) -> bool {
156 self.settings == other.settings
157 }
158}
159
160impl AnalogVarState {
161 #[must_use]
162 pub fn new(settings: AnalogSettings) -> Self {
163 Self {
164 settings,
165 cache: None,
166 }
167 }
168
169 #[must_use]
170 pub fn step_viewport() -> Self {
171 Self::new(AnalogSettings::step_viewport())
172 }
173
174 #[must_use]
175 pub fn step_global() -> Self {
176 Self::new(AnalogSettings::step_global())
177 }
178
179 #[must_use]
180 pub fn interpolated_viewport() -> Self {
181 Self::new(AnalogSettings::interpolated_viewport())
182 }
183
184 #[must_use]
185 pub fn interpolated_global() -> Self {
186 Self::new(AnalogSettings::interpolated_global())
187 }
188}
189
190#[derive(Serialize, Deserialize, Clone)]
191pub struct DisplayedVariable {
192 pub variable_ref: VariableRef,
193 #[serde(skip)]
194 pub info: VariableInfo,
195 pub color: Option<String>,
196 pub background_color: Option<String>,
197 pub display_name: String,
198 pub display_name_type: VariableNameType,
199 pub manual_name: Option<String>,
200 pub format: Option<String>,
201 pub field_formats: Vec<FieldFormat>,
202 pub height_scaling_factor: Option<f32>,
203 pub analog: Option<AnalogVarState>,
204}
205
206impl DisplayedVariable {
207 #[must_use]
208 pub fn get_format(&self, field: &[String]) -> Option<&String> {
209 if field.is_empty() {
210 self.format.as_ref()
211 } else {
212 self.field_formats
213 .iter()
214 .find(|ff| ff.field == field)
215 .map(|ff| &ff.format)
216 }
217 }
218
219 #[must_use]
221 pub fn update(
222 &self,
223 new_waves: &WaveContainer,
224 keep_unavailable: bool,
225 ) -> Option<DisplayedItem> {
226 match new_waves.update_variable_ref(&self.variable_ref) {
227 None if keep_unavailable => {
229 Some(DisplayedItem::Placeholder(self.clone().into_placeholder()))
230 }
231 None => None,
232 Some(new_ref) => {
233 let mut res = self.clone();
234 res.variable_ref = new_ref;
235 Some(DisplayedItem::Variable(res))
236 }
237 }
238 }
239
240 #[must_use]
241 pub fn into_placeholder(mut self) -> DisplayedPlaceholder {
242 self.variable_ref.clear_id(); DisplayedPlaceholder {
244 variable_ref: self.variable_ref,
245 color: self.color,
246 background_color: self.background_color,
247 display_name: self.display_name,
248 display_name_type: self.display_name_type,
249 manual_name: self.manual_name,
250 format: self.format,
251 field_formats: self.field_formats,
252 height_scaling_factor: self.height_scaling_factor,
253 analog: self.analog,
254 }
255 }
256}
257
258#[derive(Serialize, Deserialize, Clone)]
259pub struct DisplayedDivider {
260 pub color: Option<String>,
261 pub background_color: Option<String>,
262 pub name: Option<String>,
263}
264
265#[derive(Serialize, Deserialize, Clone)]
266pub struct DisplayedMarker {
267 pub color: Option<String>,
268 pub background_color: Option<String>,
269 pub name: Option<String>,
270 pub idx: u8,
271}
272
273impl DisplayedMarker {
274 #[must_use]
275 pub fn marker_text(&self, color: Color32) -> WidgetText {
276 let style = Style::default();
277 let mut layout_job = LayoutJob::default();
278 self.rich_text(color, &style, &mut layout_job);
279 WidgetText::LayoutJob(layout_job.into())
280 }
281
282 pub fn rich_text(&self, color: Color32, style: &Style, layout_job: &mut LayoutJob) {
283 RichText::new(format!("{idx}: ", idx = self.idx))
284 .color(color)
285 .append_to(layout_job, style, FontSelection::Default, Align::Center);
286 RichText::new(self.marker_name())
287 .color(color)
288 .italics()
289 .append_to(layout_job, style, FontSelection::Default, Align::Center);
290 }
291
292 fn marker_name(&self) -> String {
293 self.name
294 .clone()
295 .unwrap_or_else(|| DEFAULT_MARKER_NAME.to_string())
296 }
297}
298
299#[derive(Serialize, Deserialize, Clone)]
300pub struct DisplayedTimeLine {
301 pub color: Option<String>,
302 pub background_color: Option<String>,
303 pub name: Option<String>,
304}
305
306#[derive(Serialize, Deserialize, Clone)]
307pub struct DisplayedPlaceholder {
308 pub variable_ref: VariableRef,
309 pub color: Option<String>,
310 pub background_color: Option<String>,
311 pub display_name: String,
312 pub display_name_type: VariableNameType,
313 pub manual_name: Option<String>,
314 pub format: Option<String>,
315 pub field_formats: Vec<FieldFormat>,
316 pub height_scaling_factor: Option<f32>,
317 pub analog: Option<AnalogVarState>,
318}
319
320impl DisplayedPlaceholder {
321 #[must_use]
322 pub fn into_variable(
323 self,
324 variable_info: VariableInfo,
325 updated_variable_ref: VariableRef,
326 ) -> DisplayedVariable {
327 DisplayedVariable {
328 variable_ref: updated_variable_ref,
329 info: variable_info,
330 color: self.color,
331 background_color: self.background_color,
332 display_name: self.display_name,
333 display_name_type: self.display_name_type,
334 manual_name: self.manual_name,
335 format: self.format,
336 field_formats: self.field_formats,
337 height_scaling_factor: self.height_scaling_factor,
338 analog: self.analog,
339 }
340 }
341
342 pub fn rich_text(&self, text_color: Color32, style: &Style, layout_job: &mut LayoutJob) {
343 let s = self.manual_name.as_ref().unwrap_or(&self.display_name);
344 RichText::new("Not available: ".to_owned() + s)
345 .color(text_color)
346 .italics()
347 .append_to(layout_job, style, FontSelection::Default, Align::Center);
348 }
349}
350
351#[derive(Serialize, Deserialize, Clone)]
352pub struct DisplayedStream {
353 pub transaction_stream_ref: TransactionStreamRef,
354 pub color: Option<String>,
355 pub background_color: Option<String>,
356 pub display_name: String,
357 pub manual_name: Option<String>,
358 pub rows: usize,
359}
360
361impl DisplayedStream {
362 pub fn rich_text(
363 &self,
364 text_color: Color32,
365 style: &Style,
366 config: &SurferConfig,
367 layout_job: &mut LayoutJob,
368 ) {
369 RichText::new(format!(
370 "{}{}",
371 self.manual_name.as_ref().unwrap_or(&self.display_name),
372 "\n".repeat(self.rows - 1)
373 ))
374 .color(text_color)
375 .line_height(Some(config.layout.transactions_line_height))
377 .append_to(layout_job, style, FontSelection::Default, Align::Center);
378 }
379}
380
381#[derive(Serialize, Deserialize, Clone)]
382pub struct DisplayedGroup {
383 pub name: String,
384 pub color: Option<String>,
385 pub background_color: Option<String>,
386 pub content: Vec<DisplayedItemRef>,
387 pub is_open: bool,
388}
389
390impl DisplayedGroup {
391 pub fn rich_text(&self, text_color: Color32, style: &Style, layout_job: &mut LayoutJob) {
392 RichText::new(self.name.clone())
393 .color(text_color)
394 .append_to(layout_job, style, FontSelection::Default, Align::Center);
395 }
396}
397
398impl DisplayedItem {
399 #[must_use]
400 pub fn color(&self) -> Option<&str> {
401 match self {
402 DisplayedItem::Variable(variable) => variable.color.as_deref(),
403 DisplayedItem::Divider(divider) => divider.color.as_deref(),
404 DisplayedItem::Marker(marker) => marker.color.as_deref(),
405 DisplayedItem::TimeLine(timeline) => timeline.color.as_deref(),
406 DisplayedItem::Placeholder(_) => None,
407 DisplayedItem::Stream(stream) => stream.color.as_deref(),
408 DisplayedItem::Group(group) => group.color.as_deref(),
409 }
410 }
411
412 pub fn set_color(&mut self, color_name: &Option<String>) {
413 match self {
414 DisplayedItem::Variable(variable) => variable.color.clone_from(color_name),
415 DisplayedItem::Divider(divider) => divider.color.clone_from(color_name),
416 DisplayedItem::Marker(marker) => marker.color.clone_from(color_name),
417 DisplayedItem::TimeLine(timeline) => timeline.color.clone_from(color_name),
418 DisplayedItem::Placeholder(placeholder) => placeholder.color.clone_from(color_name),
419 DisplayedItem::Stream(stream) => stream.color.clone_from(color_name),
420 DisplayedItem::Group(group) => group.color.clone_from(color_name),
421 }
422 }
423
424 #[must_use]
425 pub fn name(&self) -> String {
426 match self {
427 DisplayedItem::Variable(variable) => variable
428 .manual_name
429 .as_ref()
430 .unwrap_or(&variable.display_name)
431 .clone(),
432 DisplayedItem::Divider(divider) => divider
433 .name
434 .as_ref()
435 .unwrap_or(&DEFAULT_DIVIDER_NAME.to_string())
436 .clone(),
437 DisplayedItem::Marker(marker) => marker.marker_name(),
438 DisplayedItem::TimeLine(timeline) => timeline
439 .name
440 .as_ref()
441 .unwrap_or(&DEFAULT_TIMELINE_NAME.to_string())
442 .clone(),
443 DisplayedItem::Placeholder(placeholder) => placeholder
444 .manual_name
445 .as_ref()
446 .unwrap_or(&placeholder.display_name)
447 .clone(),
448 DisplayedItem::Stream(stream) => stream
449 .manual_name
450 .as_ref()
451 .unwrap_or(&stream.display_name)
452 .clone(),
453 DisplayedItem::Group(group) => group.name.clone(),
454 }
455 }
456
457 pub fn add_to_layout_job(
459 &self,
460 color: Color32,
461 style: &Style,
462 layout_job: &mut LayoutJob,
463 field: Option<&FieldRef>,
464 config: &SurferConfig,
465 ) {
466 match self {
467 DisplayedItem::Variable(_) => {
468 let name = field
469 .and_then(|f| f.field.last())
470 .cloned()
471 .unwrap_or_else(|| self.name());
472 RichText::new(name)
473 .color(color)
474 .line_height(Some(
475 config.layout.waveforms_line_height * self.height_scaling_factor(),
476 ))
477 .append_to(layout_job, style, FontSelection::Default, Align::Center);
478 }
479 DisplayedItem::TimeLine(_) | DisplayedItem::Divider(_) => {
480 RichText::new(self.name()).color(color).italics().append_to(
481 layout_job,
482 style,
483 FontSelection::Default,
484 Align::Center,
485 );
486 }
487 DisplayedItem::Marker(marker) => {
488 marker.rich_text(color, style, layout_job);
489 }
490 DisplayedItem::Placeholder(placeholder) => {
491 let s = placeholder
492 .manual_name
493 .as_ref()
494 .unwrap_or(&placeholder.display_name);
495 RichText::new("Not available: ".to_owned() + s)
496 .color(color)
497 .italics()
498 .append_to(layout_job, style, FontSelection::Default, Align::Center);
499 }
500 DisplayedItem::Stream(stream) => {
501 RichText::new(format!("{}{}", self.name(), "\n".repeat(stream.rows - 1)))
502 .color(color)
503 .line_height(Some(config.layout.transactions_line_height))
504 .append_to(layout_job, style, FontSelection::Default, Align::Center);
505 }
506 DisplayedItem::Group(group) => {
507 group.rich_text(color, style, layout_job);
508 }
509 }
510 }
511
512 pub fn set_name(&mut self, name: Option<String>) {
513 match self {
514 DisplayedItem::Variable(variable) => {
515 variable.manual_name = name;
516 }
517 DisplayedItem::Divider(divider) => {
518 divider.name = name;
519 }
520 DisplayedItem::Marker(marker) => {
521 marker.name = name;
522 }
523 DisplayedItem::TimeLine(timeline) => {
524 timeline.name = name;
525 }
526 DisplayedItem::Placeholder(placeholder) => {
527 placeholder.manual_name = name;
528 }
529 DisplayedItem::Stream(stream) => {
530 stream.manual_name = name;
531 }
532 DisplayedItem::Group(group) => {
533 group.name = name.unwrap_or_default();
534 }
535 }
536 }
537
538 #[must_use]
539 pub fn has_overwritten_name(&self) -> bool {
540 match self {
541 DisplayedItem::Variable(variable) => variable.manual_name.is_some(),
542 DisplayedItem::Placeholder(placeholder) => placeholder.manual_name.is_some(),
543 DisplayedItem::Stream(stream) => stream.manual_name.is_some(),
544 DisplayedItem::Divider(_)
545 | DisplayedItem::Marker(_)
546 | DisplayedItem::TimeLine(_)
547 | DisplayedItem::Group(_) => false,
548 }
549 }
550
551 #[must_use]
552 pub fn background_color(&self) -> Option<&str> {
553 match self {
554 DisplayedItem::Variable(variable) => variable.background_color.as_deref(),
555 DisplayedItem::Divider(divider) => divider.background_color.as_deref(),
556 DisplayedItem::Marker(marker) => marker.background_color.as_deref(),
557 DisplayedItem::TimeLine(timeline) => timeline.background_color.as_deref(),
558 DisplayedItem::Placeholder(_) => None,
559 DisplayedItem::Stream(stream) => stream.background_color.as_deref(),
560 DisplayedItem::Group(group) => group.background_color.as_deref(),
561 }
562 }
563
564 pub fn set_background_color(&mut self, color_name: &Option<String>) {
565 match self {
566 DisplayedItem::Variable(variable) => {
567 variable.background_color.clone_from(color_name);
568 }
569 DisplayedItem::Divider(divider) => {
570 divider.background_color.clone_from(color_name);
571 }
572 DisplayedItem::Marker(marker) => {
573 marker.background_color.clone_from(color_name);
574 }
575 DisplayedItem::TimeLine(timeline) => {
576 timeline.background_color.clone_from(color_name);
577 }
578 DisplayedItem::Placeholder(placeholder) => {
579 placeholder.background_color.clone_from(color_name);
580 }
581 DisplayedItem::Stream(stream) => {
582 stream.background_color.clone_from(color_name);
583 }
584 DisplayedItem::Group(group) => {
585 group.background_color.clone_from(color_name);
586 }
587 }
588 }
589
590 #[must_use]
591 pub fn height_scaling_factor(&self) -> f32 {
592 match self {
593 DisplayedItem::Variable(variable) => variable.height_scaling_factor,
594 DisplayedItem::Placeholder(placeholder) => placeholder.height_scaling_factor,
595 _ => None,
596 }
597 .unwrap_or(1.0)
598 }
599
600 pub fn set_height_scaling_factor(&mut self, scale: f32) {
601 match self {
602 DisplayedItem::Variable(variable) => variable.height_scaling_factor = Some(scale),
603 DisplayedItem::Placeholder(placeholder) => {
604 placeholder.height_scaling_factor = Some(scale);
605 }
606 _ => {}
607 }
608 }
609}