paiagram/interface/tabs/
displayed_lines.rs

1use bevy::ecs::{
2    entity::Entity,
3    name::Name,
4    query::With,
5    system::{InMut, Local, Query},
6};
7use egui::{Frame, Response, ScrollArea, Sense, Ui, UiBuilder, Vec2};
8use serde::{Deserialize, Serialize};
9
10const PANEL_DEFAULT_SIZE: f32 = 20.0;
11
12use crate::{
13    graph::{Station, StationEntries},
14    interface::tabs::{PageCache, Tab},
15    lines::DisplayedLine,
16};
17
18#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
19pub struct DisplayedLinesTab;
20
21impl Tab for DisplayedLinesTab {
22    const NAME: &'static str = "Available Lines";
23    fn main_display(&mut self, world: &mut bevy::ecs::world::World, ui: &mut Ui) {
24        if let Err(e) = world.run_system_cached_with(show_displayed_lines, ui) {
25            bevy::log::error!("UI Error while displaying displayed lines page: {}", e)
26        }
27    }
28    fn scroll_bars(&self) -> [bool; 2] {
29        [false; 2]
30    }
31}
32
33fn full_width_button(ui: &mut Ui, text: &str) -> Response {
34    let (rect, resp) = ui.allocate_exact_size(
35        Vec2 {
36            x: ui.available_width(),
37            y: 15.0,
38        },
39        Sense::click(),
40    );
41    let res = ui.scope_builder(UiBuilder::new().sense(resp.sense).max_rect(rect), |ui| {
42        let response = ui.response();
43        let visuals = ui.style().interact(&response);
44        let mut stroke = visuals.bg_stroke;
45        stroke.width = 1.5;
46        Frame::canvas(ui.style())
47            .fill(visuals.bg_fill.gamma_multiply(0.2))
48            .stroke(stroke)
49            .show(ui, |ui| {
50                ui.set_min_size(ui.available_size());
51                ui.add(egui::Label::new(text).truncate())
52            })
53    });
54    res.response
55}
56
57fn show_displayed_lines(
58    InMut(ui): InMut<Ui>,
59    displayed_lines: Query<(Entity, &mut DisplayedLine, &Name)>,
60    station_names: Query<(Entity, &Name, &StationEntries), With<Station>>,
61    mut selected_line: Local<Option<Entity>>,
62    mut selected_station_cache: Local<PageCache<Entity, Option<Entity>>>,
63) {
64    egui::SidePanel::left("left_panel")
65        .resizable(true)
66        .min_width(PANEL_DEFAULT_SIZE)
67        .show_inside(ui, |ui| {
68            if full_width_button(ui, "Overview").clicked() {
69                *selected_line = None;
70            }
71            for (line_entity, _, name) in displayed_lines.iter() {
72                if full_width_button(ui, name.as_str()).clicked() {
73                    *selected_line = Some(line_entity);
74                }
75            }
76        });
77    if let Some(line_entity) = *selected_line {
78        let selected_station = selected_station_cache.get_mut_or_insert_with(line_entity, || None);
79        if let Ok((_, displayed_line, _)) = displayed_lines.get(line_entity) {
80            show_line(ui, displayed_line, selected_station, |e| {
81                station_names.get(e).ok()
82            });
83        }
84    } else {
85        show_overview(ui);
86    }
87}
88
89pub fn show_overview(ui: &mut Ui) {
90    ui.heading("Overview");
91}
92
93pub fn show_line<'a, F>(
94    ui: &mut Ui,
95    line: &DisplayedLine,
96    selected_station: &mut Option<Entity>,
97    get_station_info: F,
98) where
99    F: Fn(Entity) -> Option<(Entity, &'a Name, &'a StationEntries)> + Copy + 'a,
100{
101    egui::SidePanel::left("inner_left_panel")
102        .resizable(true)
103        .min_width(PANEL_DEFAULT_SIZE)
104        .show_inside(ui, |ui| {
105            ScrollArea::vertical().show(ui, |ui| {
106                for (station_entity, station_name, _) in line
107                    .stations()
108                    .iter()
109                    .filter_map(|(e, _)| get_station_info(e.entity()))
110                {
111                    if full_width_button(ui, station_name.as_str()).clicked() {
112                        *selected_station = Some(station_entity);
113                    }
114                }
115            })
116        });
117    let Some(station_entity) = selected_station else {
118        return;
119    };
120    let Some((_, station_name, station_cache)) = get_station_info(*station_entity) else {
121        *selected_station = None;
122        return;
123    };
124    ui.heading(station_name.as_str());
125}