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