1use crate::message::Message;
2use crate::view::{DrawConfig, DrawingContext};
3use crate::viewport::Viewport;
4use crate::{
5 SystemState,
6 wave_data::{TimeRange, WaveData},
7};
8use egui::{Frame, Panel, PointerButton, Sense, Ui};
9use emath::{Align2, Pos2, Rect, RectTransform};
10use epaint::CornerRadius;
11
12impl SystemState {
13 pub(crate) fn add_overview_panel(
14 &self,
15 ui: &mut Ui,
16 waves: &WaveData,
17 msgs: &mut Vec<Message>,
18 ) {
19 Panel::bottom("overview")
20 .frame(Frame {
21 fill: self.user.config.theme.primary_ui_color.background,
22 ..Default::default()
23 })
24 .show(ui, |ui| {
25 self.draw_overview(ui, waves, msgs);
26 });
27 }
28
29 fn draw_overview(&self, ui: &mut Ui, waves: &WaveData, msgs: &mut Vec<Message>) {
30 let (response, mut painter) = ui.allocate_painter(ui.available_size(), Sense::drag());
31 let frame_size = response.rect.size();
32 let cfg = DrawConfig::new(
33 frame_size,
34 self.user.config.layout.waveforms_line_height,
35 self.user.config.layout.waveforms_text_size,
36 );
37 let container_rect = Rect::from_min_size(Pos2::ZERO, frame_size);
38 let to_screen = RectTransform::from_to(container_rect, response.rect);
39
40 let mut ctx = DrawingContext {
41 painter: &mut painter,
42 cfg: &cfg,
43 to_screen: &|x, y| to_screen.transform_pos(Pos2::new(x, y)),
44 theme: &self.user.config.theme,
45 };
46
47 let range = waves.time_range();
48 let viewport_all = waves.viewport_all();
49 let base_fill_color = self.user.config.theme.canvas_colors.foreground;
50
51 waves
53 .viewports
54 .iter()
55 .enumerate()
56 .map(|(idx, viewport)| (idx, get_viewport_rect(&ctx, range, &viewport_all, viewport)))
57 .for_each(|(idx, rect)| {
58 let gamma = if idx == waves.last_active_viewport_idx {
59 0.6
60 } else {
61 0.3
62 };
63 ctx.painter.rect_filled(
64 rect,
65 CornerRadius::ZERO,
66 base_fill_color.gamma_multiply(gamma),
67 );
68 });
69
70 waves.draw_cursor(&self.user.config.theme, &mut ctx, &viewport_all);
72
73 let mut ticks = self.get_ticks_for_viewport(waves, &viewport_all, &cfg);
75
76 if ticks.len() >= 2 {
77 ticks.pop();
79 ticks.remove(0);
80 waves.draw_ticks(
82 self.user.config.theme.foreground,
83 &ticks,
84 &ctx,
85 frame_size.y * 0.5,
86 Align2::CENTER_CENTER,
87 );
88 }
89
90 waves.draw_markers(&self.user.config.theme, &mut ctx, &viewport_all);
92 waves.draw_marker_number_boxes(&mut ctx, &self.user.config.theme, &viewport_all);
93
94 response.dragged_by(PointerButton::Primary).then(|| {
96 let pointer_pos_global = ui.input(|i| i.pointer.interact_pos());
97 let pos = pointer_pos_global.map(|p| to_screen.inverse().transform_pos(p));
98 if let Some(pos) = pos {
99 let timestamp = viewport_all.as_time_bigint(pos.x, frame_size.x, range);
100 msgs.push(Message::GoToTime(Some(timestamp), 0));
101 }
102 });
103 }
104}
105
106fn get_viewport_rect(
107 ctx: &DrawingContext<'_>,
108 range: &TimeRange,
109 viewport_all: &Viewport,
110 viewport: &Viewport,
111) -> Rect {
112 let minx = viewport_all.pixel_from_absolute_time(
113 viewport.curr_left.absolute(range),
114 ctx.cfg.canvas_size.x,
115 range,
116 );
117 let maxx = viewport_all.pixel_from_absolute_time(
118 viewport.curr_right.absolute(range),
119 ctx.cfg.canvas_size.x,
120 range,
121 );
122 let mut min = (ctx.to_screen)(minx, 0.);
123 let mut max = (ctx.to_screen)(maxx, ctx.cfg.canvas_size.y);
124
125 if max.x < min.x {
126 std::mem::swap(&mut min.x, &mut max.x);
127 }
128
129 if max.x - min.x < 1.0 {
130 let center_x = min.x.midpoint(max.x);
131 min.x = center_x - 0.5;
132 max.x = center_x + 0.5;
133 }
134
135 Rect::from_min_max(min, max)
136}