Skip to main content

traaains/ui/track/segment/signals_list/item/
data.rs

1use std::panic;
2
3use bevy::{
4    ecs::{
5        component::Component,
6        entity::Entity,
7        event::EntityEvent,
8        hierarchy::{ChildOf, Children},
9        observer::On,
10        query::With,
11        system::{Commands, Query},
12    },
13    feathers::{
14        controls::{FeathersCheckbox, FeathersSelect},
15        display::{label_dim, label_small},
16    },
17    log::tracing,
18    scene::{Scene, SceneList, bsn, on},
19    ui::{Checked, Display, FlexDirection, InteractionDisabled, Node, px},
20    ui_widgets::ValueChange,
21};
22
23use crate::{
24    sim::signal::{
25        Signals,
26        main::{MainSignal, MainSignalState, MainSignalStateDiscriminants, MaxSpeed},
27    },
28    ui::{
29        Target,
30        segment::signals_list::item::{
31            SignalListItem, data::max_speed_select::max_speed_toggle, find_root_with_target,
32        },
33    },
34};
35
36mod is_repeat;
37mod main_state_select;
38mod max_speed_select;
39
40#[derive(Clone, Copy, Component, Default)]
41enum MaxSpeedField {
42    #[default]
43    MaxSpeed,
44    NextSpeed,
45}
46
47#[derive(Component, Clone, Default)]
48pub struct SignalData;
49
50fn is_checkbox_checked(value: bool) -> Box<dyn Scene> {
51    if value {
52        Box::new(bsn! {Checked})
53    } else {
54        Box::new(bsn! {})
55    }
56}
57
58fn update_checkbox_visual(checked: bool, checkbox: Entity, cmd: &mut Commands) {
59    if checked {
60        cmd.entity(checkbox).insert(Checked);
61    } else {
62        cmd.entity(checkbox).remove::<Checked>();
63    }
64}
65
66fn on_value_change_summon(
67    e: On<ValueChange<bool>>,
68    mut cmd: Commands,
69    target: Query<(Entity, &Target), With<SignalListItem>>,
70    children: Query<&ChildOf>,
71    mut signals: Query<&mut Signals>,
72) {
73    update_checkbox_visual(e.value, e.event_target(), &mut cmd);
74
75    let Some((_, target_signal)) = find_root_with_target(e.event_target(), &target, &children)
76    else {
77        tracing::error!(
78            "Could not find parent UI entity with a target component pointing to a signal!"
79        );
80        return;
81    };
82
83    if let Ok(mut signal) = signals.get_mut(target_signal) {
84        match &mut *signal {
85            Signals::Main(main_signal) => match &mut main_signal.state {
86                MainSignalState::Stop { summon } => *summon = e.value,
87                _ => panic!("Unexpected signal type!"),
88            },
89            _ => panic!("Unexpected signal type!"),
90        }
91    }
92}
93
94fn is_summon_checkbox(is_summon: bool) -> impl Scene {
95    bsn! {
96        @FeathersCheckbox
97        @is_checkbox_checked(is_summon)
98        on(on_value_change_summon)
99    }
100}
101
102fn main_stop(is_summon: bool) -> impl SceneList {
103    bsn! {
104        @label_small("Summon?")
105        --
106        @is_summon_checkbox(is_summon)
107    }
108}
109
110fn max_speed_input(curr_max_speed: &Option<MaxSpeed>, field: MaxSpeedField) -> Box<dyn Scene> {
111    match curr_max_speed {
112        Some(max_speed) => Box::new(bsn! {
113            Node {
114                display: Display::Flex,
115                flex_direction: FlexDirection::Row
116            }
117            Children [
118                @max_speed_toggle(curr_max_speed.is_some(), field)
119                @max_speed_select::max_speed_select(max_speed, field)
120            ]
121        }),
122        None => Box::new(bsn! {
123            Node {
124                display: Display::Flex,
125                flex_direction: FlexDirection::Row
126            }
127            Children [
128                @max_speed_toggle(curr_max_speed.is_some(), field)
129                --
130                @FeathersSelect
131                InteractionDisabled
132            ]
133        }),
134    }
135}
136
137fn main_warning(max_speed: &Option<MaxSpeed>, next_speed: &Option<MaxSpeed>) -> impl SceneList {
138    bsn! {
139        @label_small("Max speed:")
140        --
141        @max_speed_input(max_speed, MaxSpeedField::MaxSpeed)
142        --
143        @label_small("Next speed:")
144        --
145        @max_speed_input(next_speed, MaxSpeedField::NextSpeed)
146    }
147}
148
149fn main_free(max_speed: &Option<MaxSpeed>) -> impl SceneList {
150    bsn! {
151        @label_small("Max speed:")
152        --
153        @max_speed_input(max_speed, MaxSpeedField::MaxSpeed)
154    }
155}
156
157fn main_data(main_signal: &MainSignal) -> Box<dyn SceneList> {
158    match &main_signal.state {
159        MainSignalState::Stop { summon } => Box::new(main_stop(*summon)),
160        MainSignalState::Warning {
161            max_speed,
162            next_speed,
163        } => Box::new(main_warning(max_speed, next_speed)),
164        MainSignalState::Free { max_speed } => Box::new(main_free(max_speed)),
165    }
166}
167
168// Having the two nested children in here is a bit redundant, but it helps with with rebuilding the UI on signal change
169
170pub fn signal_data_main(main_signal: &MainSignal) -> impl Scene {
171    let state_var = match &main_signal.state {
172        MainSignalState::Stop { summon: _ } => MainSignalStateDiscriminants::Stop,
173        MainSignalState::Warning {
174            max_speed: _,
175            next_speed: _,
176        } => MainSignalStateDiscriminants::Warning,
177        MainSignalState::Free { max_speed: _ } => MainSignalStateDiscriminants::Free,
178    };
179
180    bsn! {
181        Node {
182            display: Display::Flex,
183            flex_direction: FlexDirection::Column,
184            padding: px(4)
185        }
186        Children [
187            @label_small("Is a repeat signal?")
188            --
189            @is_repeat::is_repeat_checkbox(main_signal.is_repeat)
190            --
191            @label_small("State")
192            --
193            @main_state_select::main_state_select(state_var)
194            --
195            @label_small("Main signal data")
196            --
197            {main_data(main_signal)}
198        ]
199    }
200}
201
202pub fn signal_data_stop() -> impl Scene {
203    bsn! {
204        Node {
205            display: Display::Flex,
206            flex_direction: FlexDirection::Column,
207            padding: px(4)
208        }
209        Children [
210            @label_dim("No data")
211        ]
212    }
213}
214
215pub fn signal_data(signal_var: &Signals) -> impl Scene {
216    let signal_data_inner: Box<dyn Scene> = match signal_var {
217        Signals::Main(main_signal) => Box::new(signal_data_main(main_signal)),
218        Signals::Stop => Box::new(signal_data_stop()),
219    };
220
221    bsn! {
222        SignalData
223        Node {}
224        Children [@signal_data_inner]
225    }
226}