dynamic_mip_generation/helpers/
widgets.rs1use bevy::prelude::*;
6
7#[derive(Clone, Message, Deref, DerefMut)]
10pub struct WidgetClickEvent<T>(T);
11
12#[derive(Clone, Component, Deref, DerefMut)]
15pub struct WidgetClickSender<T>(pub T)
16where
17 T: Clone + Send + Sync + 'static;
18
19#[derive(Clone, Copy, Component)]
21pub struct RadioButton;
22
23#[derive(Clone, Copy, Component)]
25pub struct RadioButtonText;
26
27pub const BUTTON_BORDER: UiRect = UiRect::all(Val::Px(1.0));
29
30pub const BUTTON_BORDER_COLOR: BorderColor = BorderColor {
32 left: Color::WHITE,
33 right: Color::WHITE,
34 top: Color::WHITE,
35 bottom: Color::WHITE,
36};
37
38pub const BUTTON_BORDER_RADIUS_SIZE: Val = Val::Px(6.0);
40
41pub const BUTTON_PADDING: UiRect = UiRect::axes(Val::Px(12.0), Val::Px(6.0));
43
44pub fn main_ui_node() -> Node {
48 Node {
49 flex_direction: FlexDirection::Column,
50 position_type: PositionType::Absolute,
51 row_gap: px(6),
52 left: px(10),
53 bottom: px(10),
54 ..default()
55 }
56}
57
58pub fn option_button<T>(
63 option_value: T,
64 option_name: &str,
65 is_selected: bool,
66 is_first: bool,
67 is_last: bool,
68) -> impl Bundle
69where
70 T: Clone + Send + Sync + 'static,
71{
72 let (bg_color, fg_color) = if is_selected {
73 (Color::WHITE, Color::BLACK)
74 } else {
75 (Color::BLACK, Color::WHITE)
76 };
77
78 (
80 Button,
81 Node {
82 border: BUTTON_BORDER.with_left(if is_first { px(1) } else { px(0) }),
83 justify_content: JustifyContent::Center,
84 align_items: AlignItems::Center,
85 padding: BUTTON_PADDING,
86 border_radius: BorderRadius::ZERO
87 .with_left(if is_first {
88 BUTTON_BORDER_RADIUS_SIZE
89 } else {
90 px(0)
91 })
92 .with_right(if is_last {
93 BUTTON_BORDER_RADIUS_SIZE
94 } else {
95 px(0)
96 }),
97 ..default()
98 },
99 BUTTON_BORDER_COLOR,
100 BackgroundColor(bg_color),
101 RadioButton,
102 WidgetClickSender(option_value.clone()),
103 children![(
104 ui_text(option_name, fg_color),
105 RadioButtonText,
106 WidgetClickSender(option_value),
107 )],
108 )
109}
110
111pub fn option_buttons<T>(title: &str, options: &[(T, &str)]) -> impl Bundle
117where
118 T: Clone + Send + Sync + 'static,
119{
120 let buttons = options
121 .iter()
122 .cloned()
123 .enumerate()
124 .map(|(option_index, (option_value, option_name))| {
125 option_button(
126 option_value,
127 option_name,
128 option_index == 0,
129 option_index == 0,
130 option_index == options.len() - 1,
131 )
132 })
133 .collect::<Vec<_>>();
134 (
136 Node {
137 align_items: AlignItems::Center,
138 ..default()
139 },
140 Children::spawn((
141 Spawn((
142 ui_text(title, Color::WHITE),
143 Node {
144 width: px(150),
145 ..default()
146 },
147 )),
148 SpawnIter(buttons.into_iter()),
149 )),
150 )
151}
152
153pub fn ui_text(label: &str, color: Color) -> impl Bundle + use<> {
155 (
156 Text::new(label),
157 TextFont {
158 font_size: FontSize::Px(18.0),
159 ..default()
160 },
161 TextColor(color),
162 )
163}
164
165pub fn handle_ui_interactions<T>(
168 mut interactions: Query<(&Interaction, &WidgetClickSender<T>), With<Button>>,
169 mut widget_click_events: MessageWriter<WidgetClickEvent<T>>,
170) where
171 T: Clone + Send + Sync + 'static,
172{
173 for (interaction, click_event) in interactions.iter_mut() {
174 if *interaction == Interaction::Pressed {
175 widget_click_events.write(WidgetClickEvent((**click_event).clone()));
176 }
177 }
178}
179
180pub fn update_ui_radio_button(background_color: &mut BackgroundColor, selected: bool) {
183 background_color.0 = if selected { Color::WHITE } else { Color::BLACK };
184}
185
186pub fn update_ui_radio_button_text(entity: Entity, writer: &mut TextUiWriter, selected: bool) {
189 let text_color = if selected { Color::BLACK } else { Color::WHITE };
190
191 writer.for_each_color(entity, |mut color| {
192 color.0 = text_color;
193 });
194}