1use alloc::vec::Vec;
2use core::marker::PhantomData;
3
4use crate::{App, Plugin};
5#[cfg(feature = "bevy_reflect")]
6use bevy_ecs::reflect::ReflectComponent;
7use bevy_ecs::{
8 change_detection::DetectChangesMut,
9 component::Component,
10 entity::Entity,
11 hierarchy::ChildOf,
12 intern::Interned,
13 lifecycle::{Insert, Remove, RemovedComponents},
14 observer::On,
15 query::{Changed, Has, Or, QueryFilter, With, Without},
16 relationship::{Relationship, RelationshipTarget},
17 schedule::{IntoScheduleConfigs, ScheduleLabel, SystemSet},
18 system::{Commands, Local, Query},
19};
20#[cfg(feature = "bevy_reflect")]
21use bevy_reflect::Reflect;
22
23pub struct HierarchyPropagatePlugin<
44 C: Component + Clone + PartialEq,
45 F: QueryFilter = (),
46 R: Relationship = ChildOf,
47> {
48 schedule: Interned<dyn ScheduleLabel>,
49 _marker: PhantomData<fn() -> (C, F, R)>,
50}
51
52impl<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>
53 HierarchyPropagatePlugin<C, F, R>
54{
55 pub fn new(schedule: impl ScheduleLabel) -> Self {
57 Self {
58 schedule: schedule.intern(),
59 _marker: PhantomData,
60 }
61 }
62}
63
64#[derive(Component, Clone, PartialEq)]
68#[cfg_attr(
69 feature = "bevy_reflect",
70 derive(Reflect),
71 reflect(Component, Clone, PartialEq)
72)]
73pub struct Propagate<C: Component + Clone + PartialEq>(pub C);
74
75#[derive(Component, Clone)]
78#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
79pub struct PropagateOver<C>(PhantomData<fn() -> C>);
80
81#[derive(Component, Clone)]
83#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
84pub struct PropagateStop<C>(PhantomData<fn() -> C>);
85
86#[derive(SystemSet, Clone, PartialEq, PartialOrd, Ord)]
88pub struct PropagateSet<C: Component + Clone + PartialEq> {
89 _p: PhantomData<fn() -> C>,
90}
91
92#[derive(Component, Clone, PartialEq, Debug)]
94#[cfg_attr(
95 feature = "bevy_reflect",
96 derive(Reflect),
97 reflect(Component, Clone, PartialEq)
98)]
99pub struct Inherited<C: Component + Clone + PartialEq>(pub C);
100
101impl<C> Default for PropagateOver<C> {
102 fn default() -> Self {
103 Self(Default::default())
104 }
105}
106
107impl<C> Default for PropagateStop<C> {
108 fn default() -> Self {
109 Self(Default::default())
110 }
111}
112
113impl<C: Component + Clone + PartialEq> core::fmt::Debug for PropagateSet<C> {
114 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115 f.debug_struct("PropagateSet")
116 .field("_p", &self._p)
117 .finish()
118 }
119}
120
121impl<C: Component + Clone + PartialEq> Eq for PropagateSet<C> {}
122
123impl<C: Component + Clone + PartialEq> core::hash::Hash for PropagateSet<C> {
124 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
125 self._p.hash(state);
126 }
127}
128
129impl<C: Component + Clone + PartialEq> Default for PropagateSet<C> {
130 fn default() -> Self {
131 Self {
132 _p: Default::default(),
133 }
134 }
135}
136
137impl<C: Component + Clone + PartialEq, F: QueryFilter + 'static, R: Relationship> Plugin
138 for HierarchyPropagatePlugin<C, F, R>
139{
140 fn build(&self, app: &mut App) {
141 app.add_systems(
142 self.schedule,
143 (
144 update_source::<C, F, R>,
145 update_removed_limit::<C, F, R>,
146 propagate_inherited::<C, F, R>,
147 propagate_output::<C, F>,
148 )
149 .chain()
150 .in_set(PropagateSet::<C>::default()),
151 );
152 app.add_observer(on_r_inserted::<C, F, R>);
153 app.add_observer(on_r_removed::<C, F, R>);
154 }
155}
156
157pub fn update_source<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
159 mut commands: Commands,
160 changed: Query<(Entity, &Propagate<C>), (Or<(Changed<Propagate<C>>, Without<Inherited<C>>)>,)>,
161 mut removed: RemovedComponents<Propagate<C>>,
162 relationship: Query<&R>,
163 relations: Query<&Inherited<C>, Without<PropagateStop<C>>>,
164 sources: Query<(), With<Propagate<C>>>,
165) {
166 for (entity, source) in &changed {
167 commands
168 .entity(entity)
169 .try_insert(Inherited(source.0.clone()));
170 }
171
172 for removed in removed.read() {
174 if !sources.contains(removed)
175 && let Ok(mut commands) = commands.get_entity(removed)
176 {
177 if let Some(inherited) = relationship
178 .get(removed)
179 .ok()
180 .and_then(|r| relations.get(r.get()).ok())
181 {
182 commands.insert(inherited.clone());
183 } else {
184 commands.try_remove::<Inherited<C>>();
185 }
186 }
187 }
188}
189
190pub fn on_r_inserted<
192 C: Component + Clone + PartialEq,
193 F: QueryFilter + 'static,
194 R: Relationship,
195>(
196 event: On<Insert, R>,
197 mut commands: Commands,
198 query: Query<(&R, Has<Inherited<C>>), (Without<Propagate<C>>, F)>,
199 relations: Query<&Inherited<C>, Without<PropagateStop<C>>>,
200) {
201 let Ok((relation, has_inherited)) = query.get(event.entity) else {
202 return;
203 };
204 if let Ok(inherited) = relations.get(relation.get()) {
205 commands.entity(event.entity).try_insert(inherited.clone());
206 } else if has_inherited {
207 commands.entity(event.entity).try_remove::<Inherited<C>>();
208 }
209}
210
211pub fn on_r_removed<C: Component + Clone + PartialEq, F: QueryFilter + 'static, R: Relationship>(
213 event: On<Remove, R>,
214 mut commands: Commands,
215 query: Query<(), (With<Inherited<C>>, Without<Propagate<C>>, F)>,
216) {
217 if query.contains(event.entity) {
218 commands.entity(event.entity).try_remove::<Inherited<C>>();
219 }
220}
221
222pub fn update_removed_limit<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
224 mut inherited: Query<&mut Inherited<C>>,
225 mut removed_skip: RemovedComponents<PropagateOver<C>>,
226 mut removed_stop: RemovedComponents<PropagateStop<C>>,
227) {
228 for entity in removed_skip.read() {
229 if let Ok(mut inherited) = inherited.get_mut(entity) {
230 inherited.set_changed();
231 }
232 }
233 for entity in removed_stop.read() {
234 if let Ok(mut inherited) = inherited.get_mut(entity) {
235 inherited.set_changed();
236 }
237 }
238}
239
240pub fn propagate_inherited<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
242 mut commands: Commands,
243 changed: Query<
244 (&Inherited<C>, &R::RelationshipTarget),
245 (Changed<Inherited<C>>, Without<PropagateStop<C>>, F),
246 >,
247 recurse: Query<
248 (
249 Option<&R::RelationshipTarget>,
250 Option<&Inherited<C>>,
251 Option<&PropagateStop<C>>,
252 ),
253 (Without<Propagate<C>>, F),
254 >,
255 mut removed: RemovedComponents<Inherited<C>>,
256 mut to_process: Local<Vec<(Entity, Option<Inherited<C>>)>>,
257) {
258 for (inherited, targets) in &changed {
260 to_process.extend(
261 targets
262 .iter()
263 .map(|target| (target, Some(inherited.clone()))),
264 );
265 }
266
267 for entity in removed.read() {
269 if let Ok((Some(targets), None, _)) = recurse.get(entity) {
270 to_process.extend(targets.iter().map(|target| (target, None)));
271 }
272 }
273
274 while let Some((entity, maybe_inherited)) = (*to_process).pop() {
276 let Ok((maybe_targets, maybe_current, maybe_stop)) = recurse.get(entity) else {
277 continue;
278 };
279
280 if maybe_current == maybe_inherited.as_ref() {
281 continue;
282 }
283
284 if maybe_stop.is_none()
286 && let Some(targets) = maybe_targets
287 {
288 to_process.extend(
289 targets
290 .iter()
291 .map(|target| (target, maybe_inherited.clone())),
292 );
293 }
294
295 if let Some(inherited) = maybe_inherited {
297 commands.entity(entity).try_insert(inherited);
298 } else {
299 commands.entity(entity).try_remove::<Inherited<C>>();
300 }
301 }
302}
303
304pub fn propagate_output<C: Component + Clone + PartialEq, F: QueryFilter>(
306 mut commands: Commands,
307 changed: Query<
308 (Entity, &Inherited<C>, Option<&C>),
309 (Changed<Inherited<C>>, Without<PropagateOver<C>>, F),
310 >,
311 mut inherited_removed: RemovedComponents<Inherited<C>>,
312 without_propagation_components: Query<(), (Without<PropagateOver<C>>, Without<Inherited<C>>)>,
313) {
314 for (entity, inherited, maybe_current) in &changed {
315 if maybe_current.is_some_and(|c| &inherited.0 == c) {
316 continue;
317 }
318
319 commands.entity(entity).try_insert(inherited.0.clone());
320 }
321
322 for inherited_removed in inherited_removed.read() {
323 if without_propagation_components.contains(inherited_removed) {
325 commands.entity(inherited_removed).try_remove::<C>();
326 }
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use bevy_ecs::schedule::Schedule;
333
334 use crate::{App, Update};
335
336 use super::*;
337
338 #[derive(Component, Clone, PartialEq, Debug)]
339 struct TestValue(u32);
340
341 #[test]
342 fn test_simple_propagate() {
343 let mut app = App::new();
344 app.add_schedule(Schedule::new(Update));
345 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
346
347 let mut query = app.world_mut().query::<&TestValue>();
348
349 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
350 let intermediate = app
351 .world_mut()
352 .spawn_empty()
353 .insert(ChildOf(propagator))
354 .id();
355 let propagatee = app
356 .world_mut()
357 .spawn_empty()
358 .insert(ChildOf(intermediate))
359 .id();
360
361 app.update();
362
363 assert_eq!(
364 query.get_many(app.world(), [propagator, intermediate, propagatee]),
365 Ok([&TestValue(1), &TestValue(1), &TestValue(1)])
366 );
367 }
368
369 #[test]
370 fn test_remove_propagate() {
371 let mut app = App::new();
372 app.add_schedule(Schedule::new(Update));
373 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
374
375 let mut query = app.world_mut().query::<&TestValue>();
376
377 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
378 let propagatee = app
379 .world_mut()
380 .spawn_empty()
381 .insert(ChildOf(propagator))
382 .id();
383
384 app.update();
385
386 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
387
388 app.world_mut()
389 .commands()
390 .entity(propagator)
391 .remove::<Propagate<TestValue>>();
392 app.update();
393
394 assert!(query.get(app.world(), propagator).is_err());
395 assert!(query.get(app.world(), propagatee).is_err());
396 }
397
398 #[test]
399 fn test_remove_and_reinsert_propagate() {
400 let mut app = App::new();
401 app.add_schedule(Schedule::new(Update));
402 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
403
404 let parent = app.world_mut().spawn(Propagate(TestValue(1))).id();
405 let child = app.world_mut().spawn_empty().insert(ChildOf(parent)).id();
406
407 app.update();
408
409 app.world_mut()
410 .entity_mut(parent)
411 .remove::<Propagate<TestValue>>()
412 .insert(Propagate(TestValue(2)));
413
414 app.update();
415
416 assert_eq!(
417 app.world_mut()
418 .query::<&TestValue>()
419 .get_many(app.world(), [parent, child]),
420 Ok([&TestValue(2), &TestValue(2)])
421 );
422 }
423
424 #[test]
425 fn test_remove_orphan() {
426 let mut app = App::new();
427 app.add_schedule(Schedule::new(Update));
428 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
429
430 let mut query = app.world_mut().query::<&TestValue>();
431
432 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
433 let propagatee = app
434 .world_mut()
435 .spawn_empty()
436 .insert(ChildOf(propagator))
437 .id();
438
439 app.update();
440
441 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
442
443 app.world_mut()
444 .commands()
445 .entity(propagatee)
446 .remove::<ChildOf>();
447 app.update();
448
449 assert!(query.get(app.world(), propagatee).is_err());
450 }
451
452 #[test]
453 fn test_reparented() {
454 let mut app = App::new();
455 app.add_schedule(Schedule::new(Update));
456 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
457
458 let mut query = app.world_mut().query::<&TestValue>();
459
460 let propagator_one = app.world_mut().spawn(Propagate(TestValue(1))).id();
461 let other_parent = app.world_mut().spawn_empty().id();
462 let propagatee = app
463 .world_mut()
464 .spawn_empty()
465 .insert(ChildOf(propagator_one))
466 .id();
467
468 app.update();
469
470 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
471
472 app.world_mut()
473 .commands()
474 .entity(propagatee)
475 .insert(ChildOf(other_parent));
476
477 app.update();
478
479 assert!(query.get(app.world(), propagatee).is_err());
480 }
481
482 #[test]
483 fn test_reparented_with_prior() {
484 let mut app = App::new();
485 app.add_schedule(Schedule::new(Update));
486 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
487
488 let mut query = app.world_mut().query::<&TestValue>();
489
490 let propagator_a = app.world_mut().spawn(Propagate(TestValue(1))).id();
491 let propagator_b = app.world_mut().spawn(Propagate(TestValue(2))).id();
492 let propagatee = app
493 .world_mut()
494 .spawn_empty()
495 .insert(ChildOf(propagator_a))
496 .id();
497
498 app.update();
499
500 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
501
502 app.world_mut()
503 .commands()
504 .entity(propagatee)
505 .insert(ChildOf(propagator_b));
506 app.update();
507
508 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(2)));
509 }
510
511 #[test]
512 fn test_detach_and_reattach_propagates_to_descendants() {
513 let mut app = App::new();
514 app.add_schedule(Schedule::new(Update));
515 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
516
517 let mut query = app.world_mut().query::<&TestValue>();
518
519 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
520 let intermediate = app
521 .world_mut()
522 .spawn_empty()
523 .insert(ChildOf(propagator))
524 .id();
525 let propagatee = app
526 .world_mut()
527 .spawn_empty()
528 .insert(ChildOf(intermediate))
529 .id();
530
531 app.update();
532
533 assert_eq!(
534 query.get_many(app.world(), [intermediate, propagatee]),
535 Ok([&TestValue(1), &TestValue(1)])
536 );
537
538 app.world_mut()
539 .entity_mut(intermediate)
540 .remove::<ChildOf>()
541 .insert(ChildOf(propagator));
542 app.update();
543
544 assert_eq!(
545 query.get_many(app.world(), [intermediate, propagatee]),
546 Ok([&TestValue(1), &TestValue(1)])
547 );
548 }
549
550 #[test]
551 fn test_propagate_over() {
552 let mut app = App::new();
553 app.add_schedule(Schedule::new(Update));
554 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
555
556 let mut query = app.world_mut().query::<&TestValue>();
557
558 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
559 let propagate_over = app
560 .world_mut()
561 .spawn(TestValue(2))
562 .insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
563 .id();
564 let propagatee = app
565 .world_mut()
566 .spawn_empty()
567 .insert(ChildOf(propagate_over))
568 .id();
569
570 app.update();
571
572 assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
573 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
574 }
575
576 #[test]
577 fn test_remove_propagate_over() {
578 let mut app = App::new();
579 app.add_schedule(Schedule::new(Update));
580 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
581
582 let mut query = app.world_mut().query::<&TestValue>();
583
584 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
585 let propagate_over = app
586 .world_mut()
587 .spawn(TestValue(2))
588 .insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
589 .id();
590 let propagatee = app
591 .world_mut()
592 .spawn_empty()
593 .insert(ChildOf(propagate_over))
594 .id();
595
596 app.update();
597 assert_eq!(
598 app.world_mut()
599 .query::<&Inherited<TestValue>>()
600 .get(app.world(), propagate_over),
601 Ok(&Inherited(TestValue(1)))
602 );
603 assert_eq!(
604 app.world_mut()
605 .query::<&Inherited<TestValue>>()
606 .get(app.world(), propagatee),
607 Ok(&Inherited(TestValue(1)))
608 );
609 assert_eq!(
610 query.get_many(app.world(), [propagate_over, propagatee]),
611 Ok([&TestValue(2), &TestValue(1)])
612 );
613
614 app.world_mut()
615 .commands()
616 .entity(propagate_over)
617 .remove::<PropagateOver<TestValue>>();
618 app.update();
619
620 assert_eq!(
621 query.get_many(app.world(), [propagate_over, propagatee]),
622 Ok([&TestValue(1), &TestValue(1)])
623 );
624 }
625
626 #[test]
627 fn test_propagate_over_parent_removed() {
628 let mut app = App::new();
629 app.add_schedule(Schedule::new(Update));
630 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
631
632 let mut query = app.world_mut().query::<&TestValue>();
633
634 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
635 let propagate_over = app
636 .world_mut()
637 .spawn(TestValue(2))
638 .insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
639 .id();
640
641 app.update();
642
643 assert_eq!(
644 query.get_many(app.world(), [propagator, propagate_over]),
645 Ok([&TestValue(1), &TestValue(2)])
646 );
647
648 app.world_mut()
649 .commands()
650 .entity(propagator)
651 .remove::<Propagate<TestValue>>();
652 app.update();
653
654 assert!(query.get(app.world(), propagator).is_err(),);
655 assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
656 }
657
658 #[test]
659 fn test_orphaned_propagate_over() {
660 let mut app = App::new();
661 app.add_schedule(Schedule::new(Update));
662 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
663
664 let mut query = app.world_mut().query::<&TestValue>();
665
666 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
667 let propagate_over = app
668 .world_mut()
669 .spawn(TestValue(2))
670 .insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
671 .id();
672 let propagatee = app
673 .world_mut()
674 .spawn_empty()
675 .insert(ChildOf(propagate_over))
676 .id();
677
678 app.update();
679
680 assert_eq!(
681 query.get_many(app.world(), [propagate_over, propagatee]),
682 Ok([&TestValue(2), &TestValue(1)])
683 );
684
685 app.world_mut()
686 .commands()
687 .entity(propagate_over)
688 .remove::<ChildOf>();
689 app.update();
690
691 assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
692 assert!(query.get(app.world(), propagatee).is_err());
693 }
694
695 #[test]
696 fn test_propagate_stop() {
697 let mut app = App::new();
698 app.add_schedule(Schedule::new(Update));
699 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
700
701 let mut query = app.world_mut().query::<&TestValue>();
702
703 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
704 let propagate_stop = app
705 .world_mut()
706 .spawn(PropagateStop::<TestValue>::default())
707 .insert(ChildOf(propagator))
708 .id();
709 let no_propagatee = app
710 .world_mut()
711 .spawn_empty()
712 .insert(ChildOf(propagate_stop))
713 .id();
714
715 app.update();
716
717 assert_eq!(query.get(app.world(), propagate_stop), Ok(&TestValue(1)));
718 assert!(query.get(app.world(), no_propagatee).is_err());
719 }
720
721 #[test]
722 fn test_remove_propagate_stop() {
723 let mut app = App::new();
724 app.add_schedule(Schedule::new(Update));
725 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
726
727 let mut query = app.world_mut().query::<&TestValue>();
728
729 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
730 let propagate_stop = app
731 .world_mut()
732 .spawn(PropagateStop::<TestValue>::default())
733 .insert(ChildOf(propagator))
734 .id();
735 let no_propagatee = app
736 .world_mut()
737 .spawn_empty()
738 .insert(ChildOf(propagate_stop))
739 .id();
740
741 app.update();
742
743 assert_eq!(query.get(app.world(), propagate_stop), Ok(&TestValue(1)));
744 assert!(query.get(app.world(), no_propagatee).is_err());
745
746 app.world_mut()
747 .commands()
748 .entity(propagate_stop)
749 .remove::<PropagateStop<TestValue>>();
750 app.update();
751
752 assert_eq!(
753 query.get_many(app.world(), [propagate_stop, no_propagatee]),
754 Ok([&TestValue(1), &TestValue(1)])
755 );
756 }
757
758 #[test]
759 fn test_intermediate_override() {
760 let mut app = App::new();
761 app.add_schedule(Schedule::new(Update));
762 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
763
764 let mut query = app.world_mut().query::<&TestValue>();
765
766 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
767 let intermediate = app
768 .world_mut()
769 .spawn_empty()
770 .insert(ChildOf(propagator))
771 .id();
772 let propagatee = app
773 .world_mut()
774 .spawn_empty()
775 .insert(ChildOf(intermediate))
776 .id();
777
778 app.update();
779
780 assert_eq!(
781 query.get_many(app.world(), [propagator, intermediate, propagatee]),
782 Ok([&TestValue(1), &TestValue(1), &TestValue(1)])
783 );
784
785 app.world_mut()
786 .entity_mut(intermediate)
787 .insert(Propagate(TestValue(2)));
788 app.update();
789
790 assert_eq!(
791 app.world_mut()
792 .query::<&TestValue>()
793 .get_many(app.world(), [propagator, intermediate, propagatee]),
794 Ok([&TestValue(1), &TestValue(2), &TestValue(2)])
795 );
796 }
797
798 #[test]
799 fn test_filter() {
800 #[derive(Component)]
801 struct Marker;
802
803 let mut app = App::new();
804 app.add_schedule(Schedule::new(Update));
805 app.add_plugins(HierarchyPropagatePlugin::<TestValue, With<Marker>>::new(
806 Update,
807 ));
808
809 let mut query = app.world_mut().query::<&TestValue>();
810
811 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
812 let propagatee = app
813 .world_mut()
814 .spawn_empty()
815 .insert(ChildOf(propagator))
816 .id();
817
818 app.update();
819
820 assert!(query.get(app.world(), propagator).is_err());
821 assert!(query.get(app.world(), propagatee).is_err());
822
823 app.world_mut().entity_mut(propagator).insert(Marker);
825 app.update();
826
827 assert!(query.get(app.world(), propagator).is_err());
828 assert!(query.get(app.world(), propagatee).is_err());
829
830 app.world_mut()
831 .entity_mut(propagator)
832 .insert(Propagate(TestValue(1)));
833 app.update();
834
835 assert_eq!(query.get(app.world(), propagator), Ok(&TestValue(1)));
836 assert!(query.get(app.world(), propagatee).is_err());
837
838 app.world_mut().entity_mut(propagatee).insert(Marker);
839 app.update();
840
841 assert_eq!(query.get(app.world(), propagator), Ok(&TestValue(1)));
842 assert!(query.get(app.world(), propagatee).is_err());
843
844 app.world_mut()
845 .entity_mut(propagator)
846 .insert(Propagate(TestValue(1)));
847 app.update();
848
849 assert_eq!(
850 app.world_mut()
851 .query::<&TestValue>()
852 .get_many(app.world(), [propagator, propagatee]),
853 Ok([&TestValue(1), &TestValue(1)])
854 );
855 }
856
857 #[test]
858 fn test_removed_propagate_still_inherits() {
859 let mut app = App::new();
860 app.add_schedule(Schedule::new(Update));
861 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
862
863 let mut query = app.world_mut().query::<&TestValue>();
864
865 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
866 let propagatee = app
867 .world_mut()
868 .spawn(Propagate(TestValue(2)))
869 .insert(ChildOf(propagator))
870 .id();
871
872 app.update();
873
874 assert_eq!(
875 query.get_many(app.world(), [propagator, propagatee]),
876 Ok([&TestValue(1), &TestValue(2)])
877 );
878
879 app.world_mut()
880 .commands()
881 .entity(propagatee)
882 .remove::<Propagate<TestValue>>();
883 app.update();
884
885 assert_eq!(
886 query.get_many(app.world(), [propagator, propagatee]),
887 Ok([&TestValue(1), &TestValue(1)])
888 );
889 }
890
891 #[test]
892 fn test_reparent_respects_stop() {
893 let mut app = App::new();
894 app.add_schedule(Schedule::new(Update));
895 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
896
897 let mut query = app.world_mut().query::<&TestValue>();
898
899 let propagator = app
900 .world_mut()
901 .spawn((
902 Propagate(TestValue(1)),
903 PropagateStop::<TestValue>::default(),
904 ))
905 .id();
906 let propagatee = app.world_mut().spawn(TestValue(2)).id();
907
908 app.update();
909
910 assert_eq!(
911 query.get_many(app.world(), [propagator, propagatee]),
912 Ok([&TestValue(1), &TestValue(2)])
913 );
914
915 app.world_mut()
916 .commands()
917 .entity(propagatee)
918 .insert(ChildOf(propagator));
919 app.update();
920
921 assert_eq!(
922 query.get_many(app.world(), [propagator, propagatee]),
923 Ok([&TestValue(1), &TestValue(2)])
924 );
925 }
926}