1use bevy::{
2 ecs::{
3 component::Component,
4 entity::Entity,
5 event::EntityEvent,
6 hierarchy::Children,
7 observer::On,
8 system::{Query, Res, ResMut},
9 },
10 feathers::{
11 constants::icons,
12 containers::{
13 flex_spacer, pane, pane_body, pane_header, subpane, subpane_body, subpane_header,
14 },
15 controls::{ButtonVariant, FeathersToolButton},
16 display::{icon, label},
17 theme::ThemedText,
18 },
19 picking::events::{PointerEnter, PointerLeave, PointerOver},
20 scene::{Scene, bsn, on},
21 sprite_render::{ColorMaterial, MeshMaterial2d},
22 state::{state::NextState, state_scoped::DespawnOnExit},
23 ui::{
24 AlignItems, Display, FlexDirection, JustifyContent, Node, PositionType, percent, px,
25 widget::Text,
26 },
27 ui_widgets::Activate,
28};
29
30use crate::{
31 AssetType, AssetsManager,
32 sim::{
33 logical_graph::{LogicalBlock, LogicalConnection, LogicalConnectionKey},
34 track::segment::TrackSegment,
35 },
36 ui::{EditingWindow, IsHoveringUi, UiState, target::Target},
37};
38
39#[derive(Component, Clone, Default)]
40pub struct LogicalBlocksWindow;
41
42fn logical_block_conn(key: &LogicalConnectionKey, val: &LogicalConnection) -> impl Scene {
43 bsn! {
44 Node {
45 display: Display::Flex,
46 flex_direction: FlexDirection::Column,
47 padding: px(4),
48 }
49 Children [
50 @label(format!("To (block): {}", val.block))
51 --
52 @label(format!("Through node {} and segment {}", key.node, key.segment))
53 ]
54 }
55}
56
57fn logical_blocks_item(block_entity: Entity, block: &LogicalBlock) -> impl Scene {
58 let connections_list: Vec<_> = block
59 .connections
60 .iter()
61 .map(|(key, val)| logical_block_conn(key, val))
62 .collect();
63
64 bsn! {
65 @subpane() Children [
66 @subpane_header() Children [ Text(format!("Block: {}", block_entity)) ThemedText ]
67 --
68 @subpane_body() Children [
69 Node {
70 display: Display::Flex,
71 flex_direction: FlexDirection::Column,
72 padding: px(4)
73 }
74 Children [
75 @label(format!("Number of segments: {}", block.number_of_segments))
76 --
77 @label("Connections:".to_string())
78 --
79 Node {
80 display: Display::Flex,
81 flex_direction: FlexDirection::Column,
82 row_gap: px(2)
83 }
84 Children [{connections_list}]
85 ]
86 ]
87 ]
88 Target(block_entity)
89 on(|e: On<PointerOver>, q_targets: Query<&Target>, q_segments: Query<(&TrackSegment, &mut MeshMaterial2d<ColorMaterial>)>, assets_manager: Res<AssetsManager>| {
90 let block_entity = q_targets.get(e.event_target()).unwrap().0;
91 for (segment, mut material) in q_segments {
92 if segment.block == block_entity {
93 material.0 = assets_manager.materials.get(&AssetType::TrackSegmentHighlight).cloned().unwrap();
94 }
95 }
96 })
97 on(|e: On<PointerLeave>, q_targets: Query<&Target>, q_segments: Query<(&TrackSegment, &mut MeshMaterial2d<ColorMaterial>)>, assets_manager: Res<AssetsManager>| {
98 let block_entity = q_targets.get(e.event_target()).unwrap().0;
99 for (segment, mut material) in q_segments {
100 if segment.block == block_entity {
101 material.0 = assets_manager.materials.get(&AssetType::TrackSegmentNormal).cloned().unwrap();
102 }
103 }
104 })
105 }
106}
107
108pub fn logical_blocks_window<'a, I>(blocks: I) -> impl Scene
109where
110 I: Iterator<Item = (Entity, &'a LogicalBlock)>,
111{
112 let items = blocks
113 .map(|(entity, block)| logical_blocks_item(entity, block))
114 .collect::<Vec<_>>();
115
116 bsn! {
117 EditingWindow
118 LogicalBlocksWindow
119 Node {
120 position_type: PositionType::Absolute,
121 top: px(16),
122 left: px(16),
123 display: Display::Flex,
124 flex_direction: FlexDirection::Column,
125 align_items: AlignItems::Stretch,
126 justify_content: JustifyContent::Start,
127 width: percent(30),
128 min_width: px(200),
129 max_height: percent(80),
130 }
131 DespawnOnExit::<UiState>(UiState::BlocksWindow)
132 Children [
133 @pane() Children [
134 @pane_header() Children [
135 @label("Logical blocks")
136 --
137 @flex_spacer()
138 --
139 @FeathersToolButton {
140 @variant: ButtonVariant::Plain,
141 }
142 Children [
143 @icon(icons::X)
144 ]
145 on(|_: On<Activate>, mut next_state: ResMut<NextState<UiState>>| {
146 next_state.set(UiState::Default);
147 })
148 ]
149 --
150 @pane_body()
151 Children [{items}]
152 ]
153 ]
154 on(|_: On<PointerEnter>, mut next_state: ResMut<NextState<IsHoveringUi>>| {
155 next_state.set(IsHoveringUi(true));
156 })
157 on(|_: On<PointerLeave>, mut next_state: ResMut<NextState<IsHoveringUi>>| {
158 next_state.set(IsHoveringUi(false));
159 })
160 }
161}