paiagram/interface/tabs/diagram/
edit_line.rs1use 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 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 displayed_line.insert(index, (entity, 10.0));
127 }
128}