paiagram/interface/tabs/
displayed_lines.rs1use 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}