Skip to main content

traaains/ui/
logical_blocks.rs

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}