1use bevy::prelude::*;
4
5use crate::{ContainerStyle, SetContainerStyle, Widget, events::*, utils::*};
6use super::*;
7
8#[derive(Component)]
10pub struct MakaraColumn;
11
12pub struct ColumnWidget<'a, 'w, 's> {
14 pub entity: Entity,
15 pub class: &'a mut Class,
16 pub style: WidgetStyle<'a>,
17 pub(crate) commands: &'a mut Commands<'w, 's>,
18 pub(crate) child_entities: Vec<Entity>
19}
20
21type IsColumnOnly = (
22 (
23 With<MakaraColumn>,
24 Without<MakaraCheckbox>,
25 Without<MakaraCheckboxButton>,
26 Without<MakaraCircular>,
27 Without<MakaraRow>,
28 Without<MakaraRoot>,
29 Without<MakaraButton>,
30 Without<MakaraDropdown>,
31 Without<MakaraDropdownOverlay>,
32 Without<MakaraImage>,
33 Without<MakaraLink>,
34 Without<MakaraModal>,
35 Without<MakaraModalBackdrop>,
36 ),
37 (
38 Without<MakaraProgressBar>,
39 Without<MakaraRadio>,
40 Without<MakaraRadioGroup>,
41 Without<MakaraScroll>,
42 Without<MakaraScrollbar>,
43 Without<MakaraTextInput>,
44 Without<MakaraTextInputCursor>,
45 Without<MakaraSlider>,
46 Without<MakaraSliderThumb>,
47 Without<MakaraSelect>,
48 Without<MakaraSelectOverlay>,
49 )
50);
51
52#[derive(SystemParam)]
54pub struct ColumnQuery<'w, 's> {
55 pub id: Query<'w, 's, (Entity, &'static Id), With<MakaraColumn>>,
56 pub class: Query<'w, 's, (Entity, &'static mut Class), IsColumnOnly>,
57 pub style: StyleQuery<'w, 's, IsColumnOnly>,
58 pub children: Query<'w, 's, &'static Children, With<MakaraColumn>>,
59 pub commands: Commands<'w, 's>
60}
61
62impl<'a, 'w, 's> WidgetChildren for ColumnWidget<'a, 'w, 's> {
63 fn add_child(&mut self, child_bundle: impl Bundle) {
64 let child_entity = self.commands.spawn(child_bundle).id();
65 self.commands.entity(self.entity).add_child(child_entity);
66 }
67
68 fn add_children(&mut self, bundles: impl IntoIterator<Item = impl Bundle>) {
69 let mut child_entities = Vec::new();
70
71 for bundle in bundles {
72 let child_entity = self.commands.spawn(bundle).id();
73 child_entities.push(child_entity);
74 }
75 self.commands.entity(self.entity).add_children(&child_entities);
76 }
77
78 fn insert_at(
79 &mut self,
80 index: usize,
81 bundles: impl IntoIterator<Item = impl Bundle>
82 ) {
83 let mut child_entities = Vec::new();
84
85 for bundle in bundles {
86 let child_entity = self.commands.spawn(bundle).id();
87 child_entities.push(child_entity);
88 }
89 self.commands
90 .entity(self.entity)
91 .insert_children(index, &child_entities);
92 }
93
94 fn insert_first(&mut self, bundles: impl IntoIterator<Item = impl Bundle>) {
95 self.insert_at(0, bundles);
96 }
97
98 fn insert_last(&mut self, bundles: impl IntoIterator<Item = impl Bundle>) {
99 let last_index = self.child_entities.len();
100 self.insert_at(last_index, bundles);
101 }
102
103 fn remove_at(&mut self, index: usize) {
104 if let Some(entity) = self.child_entities.get(index) {
105 self.commands.entity(self.entity).detach_child(*entity);
106 self.commands.entity(*entity).despawn();
107 }
108 }
109
110 fn remove_first(&mut self) {
111 self.remove_at(0);
112 }
113
114 fn remove_last(&mut self) {
115 if let Some(last_index) = self.child_entities.len().checked_sub(1) {
117 self.remove_at(last_index);
118 }
119 }
120}
121
122impl<'w, 's> WidgetQuery<'w, 's> for ColumnQuery<'w, 's> {
123 type WidgetView<'a> = ColumnWidget<'a, 'w, 's> where Self: 'a;
124
125 fn get_components<'a>(&'a mut self, entity: Entity) -> Option<Self::WidgetView<'a>> {
126 let ColumnQuery { id: _, class, style, children, commands } = self;
127
128 let entities = children.get(entity).ok()?
129 .iter()
130 .map(|e| e)
131 .collect::<Vec<Entity>>();
132
133 let style_bundle = style.query.get_mut(entity).ok()?;
134 let (node, bg, border_color, shadow, z_index) = style_bundle;
135
136 return Some(ColumnWidget {
137 entity,
138 class: class.get_mut(entity).ok()?.1.into_inner(),
139 style: WidgetStyle {
140 node: node.into_inner(),
141 background_color: bg.into_inner(),
142 border_color: border_color.into_inner(),
143 shadow: shadow.into_inner(),
144 z_index: z_index.into_inner(),
145 },
146 child_entities: entities,
147 commands: commands
148 });
149 }
150
151 fn find_by_id<'a>(&'a mut self, target_id: &str) -> Option<Self::WidgetView<'a>> {
152 let entity = self.id.iter()
153 .find(|(_, id)| id.0 == target_id)
154 .map(|(e, _)| e)?;
155
156 self.get_components(entity)
157 }
158
159 fn find_by_entity<'a>(&'a mut self, target_entity: Entity) -> Option<Self::WidgetView<'a>> {
160 self.get_components(target_entity)
161 }
162
163 fn find_by_class(&self, target_class: &str) -> Vec<Entity> {
164 self.class.iter()
165 .filter(|(_, class)| class.0.split(" ").any(|word| word == target_class))
166 .map(|(e, _)| e)
167 .collect()
168 }
169}
170
171#[derive(Bundle)]
173pub struct ColumnBundle {
174 pub id_class: IdAndClass,
175 pub style: ContainerStyle
176}
177
178impl Default for ColumnBundle {
179 fn default() -> Self {
180 let style = ContainerStyle {
181 node: Node {
182 width: percent(100),
183 height: auto(),
184 flex_direction: FlexDirection::Column,
185 display: Display::Flex,
186 justify_content: JustifyContent::Center,
187 align_items: AlignItems::Start,
188 ..default()
189 },
190 background_color: BackgroundColor(Color::NONE),
191 shadow: BoxShadow::default(),
192 ..default()
193 };
194
195 Self { style, id_class: IdAndClass::default() }
196 }
197}
198
199impl Widget for ColumnBundle {
200 fn build(mut self) -> impl Bundle {
202 process_built_in_spacing_class(&self.id_class.class, &mut self.style.node);
203 (
204 self.id_class,
205 self.style,
206 MakaraColumn,
207 )
208 }
209}
210
211impl SetContainerStyle for ColumnBundle {
212 fn container_style(&mut self) -> &mut ContainerStyle {
213 &mut self.style
214 }
215}
216
217impl SetIdAndClass for ColumnBundle {
218 fn id_and_class(&mut self) -> &mut IdAndClass {
219 &mut self.id_class
220 }
221}
222
223pub(crate) fn detect_column_built(
224 mut commands: Commands,
225 q: Query<Entity, Added<MakaraColumn>>
226) {
227 for entity in q.iter() {
228 commands.trigger(WidgetBuilt {
229 entity
230 });
231 }
232}
233
234pub(crate) fn detect_column_class_change_for_built_in(
235 mut columns: Query<(&Class, &mut Node), IsColumnOnly>
236) {
237 for (class, mut node) in columns.iter_mut() {
238 process_built_in_spacing_class(class, &mut node);
239 }
240}
241
242pub fn column() -> ColumnBundle {
244 ColumnBundle::default()
245}