mixed_lighting/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>(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 ..default()
87 },
88 BUTTON_BORDER_COLOR,
89 BorderRadius::ZERO
90 .with_left(if is_first {
91 BUTTON_BORDER_RADIUS_SIZE
92 } else {
93 px(0)
94 })
95 .with_right(if is_last {
96 BUTTON_BORDER_RADIUS_SIZE
97 } else {
98 px(0)
99 }),
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::BLACK),
143 Node {
144 width: px(125),
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: 18.0,
159 ..default()
160 },
161 TextColor(color),
162 )
163}
164
165pub fn handle_ui_interactions<T>(
168 mut interactions: Query<
169 (&Interaction, &WidgetClickSender<T>),
170 (With<Button>, With<RadioButton>),
171 >,
172 mut widget_click_events: MessageWriter<WidgetClickEvent<T>>,
173) where
174 T: Clone + Send + Sync + 'static,
175{
176 for (interaction, click_event) in interactions.iter_mut() {
177 if *interaction == Interaction::Pressed {
178 widget_click_events.write(WidgetClickEvent((**click_event).clone()));
179 }
180 }
181}
182
183pub fn update_ui_radio_button(background_color: &mut BackgroundColor, selected: bool) {
186 background_color.0 = if selected { Color::WHITE } else { Color::BLACK };
187}
188
189pub fn update_ui_radio_button_text(entity: Entity, writer: &mut TextUiWriter, selected: bool) {
192 let text_color = if selected { Color::BLACK } else { Color::WHITE };
193
194 writer.for_each_color(entity, |mut color| {
195 color.0 = text_color;
196 });
197}