Skip to main content

libsurfer/
overview.rs

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        // Draw rectangles for each viewport
52        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        // Draw cursor
71        waves.draw_cursor(&self.user.config.theme, &mut ctx, &viewport_all);
72
73        // Draw ticks
74        let mut ticks = self.get_ticks_for_viewport(waves, &viewport_all, &cfg);
75
76        if ticks.len() >= 2 {
77            // Remove first and last tick
78            ticks.pop();
79            ticks.remove(0);
80            // Draw ticks
81            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        // Draw markers
91        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        // Handle dragging of the primary viewport
95        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}