paiagram/interface/tabs/diagram/
edit_line.rs

1use bevy::{
2    ecs::{
3        entity::Entity,
4        name::Name,
5        query::With,
6        system::{In, InMut, Query},
7    },
8    log::info,
9};
10use egui::{Color32, Popup, Pos2, ScrollArea, Sense, Stroke, Ui, UiBuilder, Vec2};
11use moonshine_core::kind::Instance;
12
13use crate::graph::Station;
14
15pub fn edit_line(
16    (InMut(ui), In(displayed_line_entity)): (InMut<Ui>, In<Entity>),
17    stations: Query<(Entity, &Name), With<Station>>,
18    mut displayed_lines: Query<&mut super::DisplayedLine>,
19) {
20    let mut insertion: Option<(usize, Instance<Station>)> = None;
21    let mut deletion: Option<usize> = None;
22    let Ok(mut displayed_line) = displayed_lines.get_mut(displayed_line_entity) else {
23        ui.label("Error: Displayed line not found.");
24        return;
25    };
26    ui.spacing_mut().item_spacing.y = 0.0;
27    let label_height = 20.0;
28    let addition_button_height = 30.0;
29    let addition_button_offset = 40.0;
30    ui.painter().line_segment(
31        [
32            Pos2 {
33                x: ui.max_rect().left() + addition_button_offset,
34                y: ui.min_rect().bottom(),
35            },
36            Pos2 {
37                x: ui.max_rect().left() + addition_button_offset,
38                y: ui.min_rect().bottom()
39                    + (label_height + addition_button_height)
40                        * (displayed_line.stations().len() + 1) as f32,
41            },
42        ],
43        ui.visuals().widgets.hovered.bg_stroke,
44    );
45
46    let mut add_station_between = |ui: &mut Ui, index: usize| {
47        let (rect, resp) = ui.allocate_exact_size(
48            Vec2 {
49                x: ui.available_width(),
50                y: addition_button_height,
51            },
52            Sense::click(),
53        );
54        let stroke = if resp.interact_pointer_pos().is_some() {
55            ui.visuals().widgets.hovered.fg_stroke
56        } else if resp.hovered() {
57            ui.visuals().widgets.hovered.bg_stroke
58        } else {
59            Stroke::NONE
60        };
61        let fill = if resp.hovered() || resp.interact_pointer_pos().is_some() {
62            ui.visuals().window_fill
63        } else {
64            Color32::TRANSPARENT
65        };
66        ui.painter()
67            .line_segment([rect.left_center(), rect.right_center()], stroke);
68        ui.painter().circle(
69            rect.left_center()
70                + Vec2 {
71                    x: addition_button_offset,
72                    y: 0.0,
73                },
74            6.0,
75            fill,
76            stroke,
77        );
78        Popup::menu(&resp).show(|ui| {
79            // TODO: improve station selection UI
80            ScrollArea::vertical().show(ui, |ui| {
81                for (entity, name) in stations.iter() {
82                    if ui.button(name.as_str()).clicked() {
83                        insertion =
84                            Some((index, unsafe { Instance::from_entity_unchecked(entity) }));
85                    }
86                }
87            });
88        });
89    };
90
91    let station_names = displayed_line.stations().iter().copied().map(|(e, _)| {
92        stations
93            .get(e.entity())
94            .map_or("<Unknown>", |(_, n)| n.as_str())
95    });
96    add_station_between(ui, 0);
97    for (i, name) in station_names.enumerate() {
98        let (rect, resp) = ui.allocate_exact_size(
99            Vec2 {
100                x: ui.available_width(),
101                y: label_height,
102            },
103            Sense::click(),
104        );
105        ui.scope_builder(UiBuilder::new().max_rect(rect).sense(resp.sense), |ui| {
106            ui.horizontal_centered(|ui| {
107                ui.add_space(addition_button_offset + 8.0 + 10.0);
108                ui.label(name)
109            });
110        });
111        ui.painter().circle(
112            rect.left_center()
113                + Vec2 {
114                    x: addition_button_offset,
115                    y: 0.0,
116                },
117            8.0,
118            ui.visuals().widgets.hovered.bg_fill,
119            ui.visuals().widgets.hovered.bg_stroke,
120        );
121        add_station_between(ui, i + 1);
122    }
123    if let Some((index, entity)) = insertion {
124        info!("Inserting station at index {index}");
125        // TODO: fix the 10.0 thingy
126        displayed_line.insert(index, (entity, 10.0));
127    }
128}