bevy_compose/
lib.rs

1use bevy::{
2    ecs::{component::Component, entity::Entity, query::Changed, system::IntoSystem, world::World},
3    hierarchy::BuildWorldChildren,
4    render::color::Color,
5    text::{Text, TextSection, TextStyle},
6    ui::{
7        node_bundles::{ButtonBundle, NodeBundle, TextBundle},
8        AlignItems, BackgroundColor, FlexDirection, Interaction, JustifyContent, Style, UiRect,
9        Val,
10    },
11    utils::default,
12};
13use std::{any::Any, mem};
14
15pub trait Compose: Send + Sync + 'static {
16    type State: Send + Sync + 'static;
17
18    fn build(&mut self, world: &mut World, children: &mut Vec<Entity>) -> Self::State;
19
20    fn rebuild(
21        &mut self,
22        target: &mut Self,
23        state: &mut Self::State,
24        world: &mut World,
25        children: &mut Vec<Entity>,
26    );
27}
28
29impl Compose for () {
30    type State = ();
31
32    fn build(&mut self, _world: &mut World, _children: &mut Vec<Entity>) -> Self::State {}
33
34    fn rebuild(
35        &mut self,
36        _target: &mut Self,
37        _state: &mut Self::State,
38        _world: &mut World,
39        _children: &mut Vec<Entity>,
40    ) {
41    }
42}
43
44impl Compose for &'static str {
45    type State = Entity;
46
47    fn build(&mut self, world: &mut World, children: &mut Vec<Entity>) -> Self::State {
48        let entity = world.spawn(TextBundle::from_section(
49            self.to_owned(),
50            Default::default(),
51        ));
52        let id = entity.id();
53        children.push(id);
54        id
55    }
56
57    fn rebuild(
58        &mut self,
59        target: &mut Self,
60        state: &mut Self::State,
61        world: &mut World,
62        children: &mut Vec<Entity>,
63    ) {
64        children.push(*state);
65
66        if self != target {
67            world.get_mut::<Text>(*state).unwrap().sections[0] =
68                TextSection::new(self.to_owned(), TextStyle::default());
69        }
70    }
71}
72
73impl Compose for String {
74    type State = Entity;
75
76    fn build(&mut self, world: &mut World, children: &mut Vec<Entity>) -> Self::State {
77        let entity = world.spawn(TextBundle::from_section(self.clone(), Default::default()));
78        let id = entity.id();
79        children.push(id);
80        id
81    }
82
83    fn rebuild(
84        &mut self,
85        target: &mut Self,
86        state: &mut Self::State,
87        world: &mut World,
88        children: &mut Vec<Entity>,
89    ) {
90        children.push(*state);
91
92        if self != target {
93            world.get_mut::<Text>(*state).unwrap().sections[0] =
94                TextSection::new(self.clone(), TextStyle::default());
95        }
96    }
97}
98
99impl<C1: Compose, C2: Compose, C3: Compose> Compose for (C1, C2, C3) {
100    type State = (C1::State, C2::State, C3::State);
101
102    fn build(&mut self, world: &mut World, children: &mut Vec<Entity>) -> Self::State {
103        (
104            self.0.build(world, children),
105            self.1.build(world, children),
106            self.2.build(world, children),
107        )
108    }
109
110    fn rebuild(
111        &mut self,
112        target: &mut Self,
113        state: &mut Self::State,
114        world: &mut World,
115        children: &mut Vec<Entity>,
116    ) {
117        self.0.rebuild(&mut target.0, &mut state.0, world, children);
118        self.1.rebuild(&mut target.1, &mut state.1, world, children);
119        self.2.rebuild(&mut target.2, &mut state.2, world, children);
120    }
121}
122
123pub fn flex<C: Compose>(content: C) -> Flex<C> {
124    Flex {
125        content,
126        on_click: None,
127    }
128}
129
130pub struct Flex<C> {
131    content: C,
132    on_click: Option<Box<dyn FnMut(&mut World) + Send + Sync>>,
133}
134
135impl<C> Flex<C> {
136    pub fn on_click<Marker>(mut self, system: impl IntoSystem<(), (), Marker>) -> Self {
137        let mut cell = Some(IntoSystem::<(), (), Marker>::into_system(system));
138        let mut id_cell = None;
139        self.on_click = Some(Box::new(move |world| {
140            if let Some(system) = cell.take() {
141                let id = world.register_system(system);
142                id_cell = Some(id);
143            }
144
145            let id = id_cell.unwrap();
146            world.run_system(id).unwrap();
147        }));
148        self
149    }
150}
151
152impl<C: Compose> Compose for Flex<C> {
153    type State = (Entity, C::State);
154
155    fn build(&mut self, world: &mut World, children: &mut Vec<Entity>) -> Self::State {
156        let parent_children = mem::take(children);
157        let content_state = self.content.build(world, children);
158        let my_children = mem::replace(children, parent_children);
159
160        let mut entity = world.spawn(ButtonBundle {
161            style: Style {
162                width: Val::Px(150.0),
163                height: Val::Px(65.0),
164                border: UiRect::all(Val::Px(5.0)),
165                justify_content: JustifyContent::Center,
166                align_items: AlignItems::Center,
167                ..default()
168            },
169            background_color: BackgroundColor(Color::BLACK),
170            ..default()
171        });
172        entity.push_children(&my_children);
173
174        let id = entity.id();
175        children.push(id);
176
177        if let Some(handler) = self.on_click.take() {
178            entity.insert(ClickHandler {
179                handler: Some(handler),
180            });
181        }
182
183        (id, content_state)
184    }
185
186    fn rebuild(
187        &mut self,
188        target: &mut Self,
189        state: &mut Self::State,
190        world: &mut World,
191        children: &mut Vec<Entity>,
192    ) {
193        let parent_children = mem::take(children);
194        self.content
195            .rebuild(&mut target.content, &mut state.1, world, children);
196        let _my_children = mem::replace(children, parent_children);
197
198        children.push(state.0);
199    }
200}
201
202#[derive(Component)]
203pub struct ClickHandler {
204    handler: Option<Box<dyn FnMut(&mut World) + Send + Sync>>,
205}
206
207pub fn handler_system(world: &mut World) {
208    let mut query =
209        world.query_filtered::<(&Interaction, &mut ClickHandler), Changed<Interaction>>();
210
211    let mut handlers: Vec<_> = query
212        .iter_mut(world)
213        .map(|(interaction, mut handler)| (*interaction, handler.handler.take()))
214        .collect();
215
216    for (interaction, f) in &mut handlers {
217        match interaction {
218            Interaction::Pressed => {
219                if let Some(ref mut f) = f {
220                    f(world)
221                }
222            }
223            _ => {}
224        }
225    }
226
227    for (idx, (_, mut handler)) in query.iter_mut(world).enumerate() {
228        handler.handler = handlers[idx].1.take();
229    }
230}
231
232pub trait AnyCompose: Send + Sync {
233    fn as_any_mut(&mut self) -> &mut dyn Any;
234
235    fn build_any(
236        &mut self,
237        world: &mut World,
238        children: &mut Vec<Entity>,
239    ) -> Box<dyn Any + Send + Sync>;
240
241    fn rebuild_any(
242        &mut self,
243        target: &mut dyn Any,
244        state: &mut dyn Any,
245        world: &mut World,
246        children: &mut Vec<Entity>,
247    );
248}
249
250impl<C: Compose> AnyCompose for C {
251    fn as_any_mut(&mut self) -> &mut dyn Any {
252        self
253    }
254
255    fn build_any(
256        &mut self,
257        world: &mut World,
258        children: &mut Vec<Entity>,
259    ) -> Box<dyn Any + Send + Sync> {
260        Box::new(self.build(world, children))
261    }
262
263    fn rebuild_any(
264        &mut self,
265        target: &mut dyn Any,
266        state: &mut dyn Any,
267        world: &mut World,
268        children: &mut Vec<Entity>,
269    ) {
270        self.rebuild(
271            target.downcast_mut().unwrap(),
272            state.downcast_mut().unwrap(),
273            world,
274            children,
275        )
276    }
277}
278
279#[derive(Component)]
280pub struct Composer {
281    compose: Option<Box<dyn FnMut(&mut World) -> Box<dyn AnyCompose> + Send + Sync>>,
282    state: Option<(Box<dyn AnyCompose>, Box<dyn Any + Send + Sync>)>,
283}
284
285impl Composer {
286    pub fn new<Marker, C: Compose>(compose_fn: impl IntoSystem<(), C, Marker>) -> Self {
287        let mut system_cell = Some(IntoSystem::<(), C, Marker>::into_system(compose_fn));
288        let mut id_cell = None;
289        Self {
290            compose: Some(Box::new(move |world| {
291                if let Some(system) = system_cell.take() {
292                    let id = world.register_system(system);
293                    id_cell = Some(id);
294                }
295
296                let id = id_cell.unwrap();
297                Box::new(world.run_system(id).unwrap())
298            })),
299            state: None,
300        }
301    }
302}
303
304pub fn compose(world: &mut World) {
305    let mut query = world.query::<&mut Composer>();
306    let mut composers = query
307        .iter_mut(world)
308        .map(|mut composer| (composer.compose.take(), composer.state.take()))
309        .collect::<Vec<_>>();
310
311    for (compose_fn, state) in &mut composers {
312        let mut compose = compose_fn.as_mut().unwrap()(world);
313        let mut children = Vec::new();
314
315        if let Some((target, state)) = state {
316            compose.rebuild_any(target.as_any_mut(), &mut **state, world, &mut children)
317        } else {
318            let s = compose.build_any(world, &mut children);
319            *state = Some((compose, s));
320
321            world
322                .spawn(NodeBundle {
323                    style: Style {
324                        width: Val::Percent(100.),
325                        height: Val::Percent(100.),
326                        flex_direction: FlexDirection::Column,
327                        align_items: AlignItems::Center,
328                        ..Default::default()
329                    },
330                    background_color: BackgroundColor(Color::BLACK),
331                    ..Default::default()
332                })
333                .push_children(&children);
334        }
335    }
336
337    for (idx, mut composer) in query.iter_mut(world).enumerate() {
338        composer.compose = composers[idx].0.take();
339        composer.state = composers[idx].1.take();
340    }
341}
342
343pub fn lazy<C: Compose, Marker>(system: impl IntoSystem<(), C, Marker>) -> Lazy {
344    let mut cell = Some(IntoSystem::<(), C, Marker>::into_system(system));
345
346    let system: Option<
347        Box<
348            dyn FnMut(Option<&mut dyn Any>, &mut World, &mut Option<LazyState>, &mut Vec<Entity>)
349                + Send
350                + Sync,
351        >,
352    > = Some(Box::new(move |target, world, state_cell, children| {
353        if let Some(ref mut state) = state_cell {
354            let target = target.unwrap();
355
356            let mut compose = (state.system)(world);
357            compose.rebuild_any(state.compose.as_any_mut(), &mut *state.state, world, children);
358            
359        } else {
360            let system = cell.take().unwrap();
361            let system_id = world.register_system(system);
362
363            let mut compose = world.run_system(system_id).unwrap();
364            let state = compose.build_any(world, children);
365
366            *state_cell = Some(LazyState {
367                system: Box::new(move |world| {
368                    let compose = world.run_system(system_id).unwrap();
369                    Box::new(compose)
370                }),
371                compose: Box::new(compose),
372                state,
373            })
374        }
375    }));
376
377    Lazy { system }
378}
379
380pub struct LazyState {
381    system: Box<dyn FnMut(&mut World) -> Box<dyn AnyCompose>+ Send + Sync> ,
382    compose: Box<dyn AnyCompose>,
383    state: Box<dyn Any + Send + Sync>,
384}
385
386pub struct Lazy {
387    system: Option<
388        Box<
389            dyn FnMut(Option<&mut dyn Any>, &mut World, &mut Option<LazyState>, &mut Vec<Entity>)
390                + Send
391                + Sync,
392        >,
393    >,
394}
395
396impl Compose for Lazy {
397    type State = Option<LazyState>;
398
399    fn build(&mut self, world: &mut World, children: &mut Vec<Entity>) -> Self::State {
400        let mut state_cell = None;
401        self.system.as_mut().unwrap()(None, world, &mut state_cell, children);
402        state_cell
403    }
404
405    fn rebuild(
406        &mut self,
407        target: &mut Self,
408        state: &mut Self::State,
409        world: &mut World,
410        children: &mut Vec<Entity>,
411    ) {
412        self.system.as_mut().unwrap()(Some(target), world, state, children);
413    }
414}