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)]
78#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
79pub struct PropagateOver<C>(PhantomData<fn() -> C>);
80
81#[derive(Component)]
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) {
165 for (entity, source) in &changed {
166 commands
167 .entity(entity)
168 .try_insert(Inherited(source.0.clone()));
169 }
170
171 for removed in removed.read() {
173 if let Ok(mut commands) = commands.get_entity(removed) {
174 if let Some(inherited) = relationship
175 .get(removed)
176 .ok()
177 .and_then(|r| relations.get(r.get()).ok())
178 {
179 commands.insert(inherited.clone());
180 } else {
181 commands.try_remove::<Inherited<C>>();
182 }
183 }
184 }
185}
186
187pub fn on_r_inserted<
189 C: Component + Clone + PartialEq,
190 F: QueryFilter + 'static,
191 R: Relationship,
192>(
193 event: On<Insert, R>,
194 mut commands: Commands,
195 query: Query<(&R, Has<Inherited<C>>), (Without<Propagate<C>>, F)>,
196 relations: Query<&Inherited<C>, Without<PropagateStop<C>>>,
197) {
198 let Ok((relation, has_inherited)) = query.get(event.entity) else {
199 return;
200 };
201 if let Ok(inherited) = relations.get(relation.get()) {
202 commands.entity(event.entity).try_insert(inherited.clone());
203 } else if has_inherited {
204 commands.entity(event.entity).try_remove::<Inherited<C>>();
205 }
206}
207
208pub fn on_r_removed<C: Component + Clone + PartialEq, F: QueryFilter + 'static, R: Relationship>(
210 event: On<Remove, R>,
211 mut commands: Commands,
212 query: Query<(), (With<Inherited<C>>, Without<Propagate<C>>, F)>,
213) {
214 if query.contains(event.entity) {
215 commands.entity(event.entity).try_remove::<Inherited<C>>();
216 }
217}
218
219pub fn update_removed_limit<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
221 mut inherited: Query<&mut Inherited<C>>,
222 mut removed_skip: RemovedComponents<PropagateOver<C>>,
223 mut removed_stop: RemovedComponents<PropagateStop<C>>,
224) {
225 for entity in removed_skip.read() {
226 if let Ok(mut inherited) = inherited.get_mut(entity) {
227 inherited.set_changed();
228 }
229 }
230 for entity in removed_stop.read() {
231 if let Ok(mut inherited) = inherited.get_mut(entity) {
232 inherited.set_changed();
233 }
234 }
235}
236
237pub fn propagate_inherited<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
239 mut commands: Commands,
240 changed: Query<
241 (&Inherited<C>, &R::RelationshipTarget),
242 (Changed<Inherited<C>>, Without<PropagateStop<C>>, F),
243 >,
244 recurse: Query<
245 (
246 Option<&R::RelationshipTarget>,
247 Option<&Inherited<C>>,
248 Option<&PropagateStop<C>>,
249 ),
250 (Without<Propagate<C>>, F),
251 >,
252 mut removed: RemovedComponents<Inherited<C>>,
253 mut to_process: Local<Vec<(Entity, Option<Inherited<C>>)>>,
254) {
255 for (inherited, targets) in &changed {
257 to_process.extend(
258 targets
259 .iter()
260 .map(|target| (target, Some(inherited.clone()))),
261 );
262 }
263
264 for entity in removed.read() {
266 if let Ok((Some(targets), _, _)) = recurse.get(entity) {
267 to_process.extend(targets.iter().map(|target| (target, None)));
268 }
269 }
270
271 while let Some((entity, maybe_inherited)) = (*to_process).pop() {
273 let Ok((maybe_targets, maybe_current, maybe_stop)) = recurse.get(entity) else {
274 continue;
275 };
276
277 if maybe_current == maybe_inherited.as_ref() {
278 continue;
279 }
280
281 if maybe_stop.is_none()
283 && let Some(targets) = maybe_targets
284 {
285 to_process.extend(
286 targets
287 .iter()
288 .map(|target| (target, maybe_inherited.clone())),
289 );
290 }
291
292 if let Some(inherited) = maybe_inherited {
294 commands.entity(entity).try_insert(inherited);
295 } else {
296 commands.entity(entity).try_remove::<Inherited<C>>();
297 }
298 }
299}
300
301pub fn propagate_output<C: Component + Clone + PartialEq, F: QueryFilter>(
303 mut commands: Commands,
304 changed: Query<
305 (Entity, &Inherited<C>, Option<&C>),
306 (Changed<Inherited<C>>, Without<PropagateOver<C>>, F),
307 >,
308 mut inherited_removed: RemovedComponents<Inherited<C>>,
309 without_propagation_components: Query<(), (Without<PropagateOver<C>>, Without<Inherited<C>>)>,
310) {
311 for (entity, inherited, maybe_current) in &changed {
312 if maybe_current.is_some_and(|c| &inherited.0 == c) {
313 continue;
314 }
315
316 commands.entity(entity).try_insert(inherited.0.clone());
317 }
318
319 for inherited_removed in inherited_removed.read() {
320 if without_propagation_components.contains(inherited_removed) {
322 commands.entity(inherited_removed).try_remove::<C>();
323 }
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use bevy_ecs::schedule::Schedule;
330
331 use crate::{App, Update};
332
333 use super::*;
334
335 #[derive(Component, Clone, PartialEq, Debug)]
336 struct TestValue(u32);
337
338 #[test]
339 fn test_simple_propagate() {
340 let mut app = App::new();
341 app.add_schedule(Schedule::new(Update));
342 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
343
344 let mut query = app.world_mut().query::<&TestValue>();
345
346 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
347 let intermediate = app
348 .world_mut()
349 .spawn_empty()
350 .insert(ChildOf(propagator))
351 .id();
352 let propagatee = app
353 .world_mut()
354 .spawn_empty()
355 .insert(ChildOf(intermediate))
356 .id();
357
358 app.update();
359
360 assert_eq!(
361 query.get_many(app.world(), [propagator, intermediate, propagatee]),
362 Ok([&TestValue(1), &TestValue(1), &TestValue(1)])
363 );
364 }
365
366 #[test]
367 fn test_remove_propagate() {
368 let mut app = App::new();
369 app.add_schedule(Schedule::new(Update));
370 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
371
372 let mut query = app.world_mut().query::<&TestValue>();
373
374 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
375 let propagatee = app
376 .world_mut()
377 .spawn_empty()
378 .insert(ChildOf(propagator))
379 .id();
380
381 app.update();
382
383 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
384
385 app.world_mut()
386 .commands()
387 .entity(propagator)
388 .remove::<Propagate<TestValue>>();
389 app.update();
390
391 assert!(query.get(app.world(), propagator).is_err());
392 assert!(query.get(app.world(), propagatee).is_err());
393 }
394
395 #[test]
396 fn test_remove_orphan() {
397 let mut app = App::new();
398 app.add_schedule(Schedule::new(Update));
399 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
400
401 let mut query = app.world_mut().query::<&TestValue>();
402
403 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
404 let propagatee = app
405 .world_mut()
406 .spawn_empty()
407 .insert(ChildOf(propagator))
408 .id();
409
410 app.update();
411
412 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
413
414 app.world_mut()
415 .commands()
416 .entity(propagatee)
417 .remove::<ChildOf>();
418 app.update();
419
420 assert!(query.get(app.world(), propagatee).is_err());
421 }
422
423 #[test]
424 fn test_reparented() {
425 let mut app = App::new();
426 app.add_schedule(Schedule::new(Update));
427 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
428
429 let mut query = app.world_mut().query::<&TestValue>();
430
431 let propagator_one = app.world_mut().spawn(Propagate(TestValue(1))).id();
432 let other_parent = app.world_mut().spawn_empty().id();
433 let propagatee = app
434 .world_mut()
435 .spawn_empty()
436 .insert(ChildOf(propagator_one))
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 .insert(ChildOf(other_parent));
447
448 app.update();
449
450 assert!(query.get(app.world(), propagatee).is_err());
451 }
452
453 #[test]
454 fn test_reparented_with_prior() {
455 let mut app = App::new();
456 app.add_schedule(Schedule::new(Update));
457 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
458
459 let mut query = app.world_mut().query::<&TestValue>();
460
461 let propagator_a = app.world_mut().spawn(Propagate(TestValue(1))).id();
462 let propagator_b = app.world_mut().spawn(Propagate(TestValue(2))).id();
463 let propagatee = app
464 .world_mut()
465 .spawn_empty()
466 .insert(ChildOf(propagator_a))
467 .id();
468
469 app.update();
470
471 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
472
473 app.world_mut()
474 .commands()
475 .entity(propagatee)
476 .insert(ChildOf(propagator_b));
477 app.update();
478
479 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(2)));
480 }
481
482 #[test]
483 fn test_propagate_over() {
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 = app.world_mut().spawn(Propagate(TestValue(1))).id();
491 let propagate_over = app
492 .world_mut()
493 .spawn(TestValue(2))
494 .insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
495 .id();
496 let propagatee = app
497 .world_mut()
498 .spawn_empty()
499 .insert(ChildOf(propagate_over))
500 .id();
501
502 app.update();
503
504 assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
505 assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
506 }
507
508 #[test]
509 fn test_remove_propagate_over() {
510 let mut app = App::new();
511 app.add_schedule(Schedule::new(Update));
512 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
513
514 let mut query = app.world_mut().query::<&TestValue>();
515
516 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
517 let propagate_over = app
518 .world_mut()
519 .spawn(TestValue(2))
520 .insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
521 .id();
522 let propagatee = app
523 .world_mut()
524 .spawn_empty()
525 .insert(ChildOf(propagate_over))
526 .id();
527
528 app.update();
529 assert_eq!(
530 app.world_mut()
531 .query::<&Inherited<TestValue>>()
532 .get(app.world(), propagate_over),
533 Ok(&Inherited(TestValue(1)))
534 );
535 assert_eq!(
536 app.world_mut()
537 .query::<&Inherited<TestValue>>()
538 .get(app.world(), propagatee),
539 Ok(&Inherited(TestValue(1)))
540 );
541 assert_eq!(
542 query.get_many(app.world(), [propagate_over, propagatee]),
543 Ok([&TestValue(2), &TestValue(1)])
544 );
545
546 app.world_mut()
547 .commands()
548 .entity(propagate_over)
549 .remove::<PropagateOver<TestValue>>();
550 app.update();
551
552 assert_eq!(
553 query.get_many(app.world(), [propagate_over, propagatee]),
554 Ok([&TestValue(1), &TestValue(1)])
555 );
556 }
557
558 #[test]
559 fn test_propagate_over_parent_removed() {
560 let mut app = App::new();
561 app.add_schedule(Schedule::new(Update));
562 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
563
564 let mut query = app.world_mut().query::<&TestValue>();
565
566 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
567 let propagate_over = app
568 .world_mut()
569 .spawn(TestValue(2))
570 .insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
571 .id();
572
573 app.update();
574
575 assert_eq!(
576 query.get_many(app.world(), [propagator, propagate_over]),
577 Ok([&TestValue(1), &TestValue(2)])
578 );
579
580 app.world_mut()
581 .commands()
582 .entity(propagator)
583 .remove::<Propagate<TestValue>>();
584 app.update();
585
586 assert!(query.get(app.world(), propagator).is_err(),);
587 assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
588 }
589
590 #[test]
591 fn test_orphaned_propagate_over() {
592 let mut app = App::new();
593 app.add_schedule(Schedule::new(Update));
594 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
595
596 let mut query = app.world_mut().query::<&TestValue>();
597
598 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
599 let propagate_over = app
600 .world_mut()
601 .spawn(TestValue(2))
602 .insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
603 .id();
604 let propagatee = app
605 .world_mut()
606 .spawn_empty()
607 .insert(ChildOf(propagate_over))
608 .id();
609
610 app.update();
611
612 assert_eq!(
613 query.get_many(app.world(), [propagate_over, propagatee]),
614 Ok([&TestValue(2), &TestValue(1)])
615 );
616
617 app.world_mut()
618 .commands()
619 .entity(propagate_over)
620 .remove::<ChildOf>();
621 app.update();
622
623 assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
624 assert!(query.get(app.world(), propagatee).is_err());
625 }
626
627 #[test]
628 fn test_propagate_stop() {
629 let mut app = App::new();
630 app.add_schedule(Schedule::new(Update));
631 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
632
633 let mut query = app.world_mut().query::<&TestValue>();
634
635 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
636 let propagate_stop = app
637 .world_mut()
638 .spawn(PropagateStop::<TestValue>::default())
639 .insert(ChildOf(propagator))
640 .id();
641 let no_propagatee = app
642 .world_mut()
643 .spawn_empty()
644 .insert(ChildOf(propagate_stop))
645 .id();
646
647 app.update();
648
649 assert_eq!(query.get(app.world(), propagate_stop), Ok(&TestValue(1)));
650 assert!(query.get(app.world(), no_propagatee).is_err());
651 }
652
653 #[test]
654 fn test_remove_propagate_stop() {
655 let mut app = App::new();
656 app.add_schedule(Schedule::new(Update));
657 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
658
659 let mut query = app.world_mut().query::<&TestValue>();
660
661 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
662 let propagate_stop = app
663 .world_mut()
664 .spawn(PropagateStop::<TestValue>::default())
665 .insert(ChildOf(propagator))
666 .id();
667 let no_propagatee = app
668 .world_mut()
669 .spawn_empty()
670 .insert(ChildOf(propagate_stop))
671 .id();
672
673 app.update();
674
675 assert_eq!(query.get(app.world(), propagate_stop), Ok(&TestValue(1)));
676 assert!(query.get(app.world(), no_propagatee).is_err());
677
678 app.world_mut()
679 .commands()
680 .entity(propagate_stop)
681 .remove::<PropagateStop<TestValue>>();
682 app.update();
683
684 assert_eq!(
685 query.get_many(app.world(), [propagate_stop, no_propagatee]),
686 Ok([&TestValue(1), &TestValue(1)])
687 );
688 }
689
690 #[test]
691 fn test_intermediate_override() {
692 let mut app = App::new();
693 app.add_schedule(Schedule::new(Update));
694 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
695
696 let mut query = app.world_mut().query::<&TestValue>();
697
698 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
699 let intermediate = app
700 .world_mut()
701 .spawn_empty()
702 .insert(ChildOf(propagator))
703 .id();
704 let propagatee = app
705 .world_mut()
706 .spawn_empty()
707 .insert(ChildOf(intermediate))
708 .id();
709
710 app.update();
711
712 assert_eq!(
713 query.get_many(app.world(), [propagator, intermediate, propagatee]),
714 Ok([&TestValue(1), &TestValue(1), &TestValue(1)])
715 );
716
717 app.world_mut()
718 .entity_mut(intermediate)
719 .insert(Propagate(TestValue(2)));
720 app.update();
721
722 assert_eq!(
723 app.world_mut()
724 .query::<&TestValue>()
725 .get_many(app.world(), [propagator, intermediate, propagatee]),
726 Ok([&TestValue(1), &TestValue(2), &TestValue(2)])
727 );
728 }
729
730 #[test]
731 fn test_filter() {
732 #[derive(Component)]
733 struct Marker;
734
735 let mut app = App::new();
736 app.add_schedule(Schedule::new(Update));
737 app.add_plugins(HierarchyPropagatePlugin::<TestValue, With<Marker>>::new(
738 Update,
739 ));
740
741 let mut query = app.world_mut().query::<&TestValue>();
742
743 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
744 let propagatee = app
745 .world_mut()
746 .spawn_empty()
747 .insert(ChildOf(propagator))
748 .id();
749
750 app.update();
751
752 assert!(query.get(app.world(), propagator).is_err());
753 assert!(query.get(app.world(), propagatee).is_err());
754
755 app.world_mut().entity_mut(propagator).insert(Marker);
757 app.update();
758
759 assert!(query.get(app.world(), propagator).is_err());
760 assert!(query.get(app.world(), propagatee).is_err());
761
762 app.world_mut()
763 .entity_mut(propagator)
764 .insert(Propagate(TestValue(1)));
765 app.update();
766
767 assert_eq!(query.get(app.world(), propagator), Ok(&TestValue(1)));
768 assert!(query.get(app.world(), propagatee).is_err());
769
770 app.world_mut().entity_mut(propagatee).insert(Marker);
771 app.update();
772
773 assert_eq!(query.get(app.world(), propagator), Ok(&TestValue(1)));
774 assert!(query.get(app.world(), propagatee).is_err());
775
776 app.world_mut()
777 .entity_mut(propagator)
778 .insert(Propagate(TestValue(1)));
779 app.update();
780
781 assert_eq!(
782 app.world_mut()
783 .query::<&TestValue>()
784 .get_many(app.world(), [propagator, propagatee]),
785 Ok([&TestValue(1), &TestValue(1)])
786 );
787 }
788
789 #[test]
790 fn test_removed_propagate_still_inherits() {
791 let mut app = App::new();
792 app.add_schedule(Schedule::new(Update));
793 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
794
795 let mut query = app.world_mut().query::<&TestValue>();
796
797 let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
798 let propagatee = app
799 .world_mut()
800 .spawn(Propagate(TestValue(2)))
801 .insert(ChildOf(propagator))
802 .id();
803
804 app.update();
805
806 assert_eq!(
807 query.get_many(app.world(), [propagator, propagatee]),
808 Ok([&TestValue(1), &TestValue(2)])
809 );
810
811 app.world_mut()
812 .commands()
813 .entity(propagatee)
814 .remove::<Propagate<TestValue>>();
815 app.update();
816
817 assert_eq!(
818 query.get_many(app.world(), [propagator, propagatee]),
819 Ok([&TestValue(1), &TestValue(1)])
820 );
821 }
822
823 #[test]
824 fn test_reparent_respects_stop() {
825 let mut app = App::new();
826 app.add_schedule(Schedule::new(Update));
827 app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
828
829 let mut query = app.world_mut().query::<&TestValue>();
830
831 let propagator = app
832 .world_mut()
833 .spawn((
834 Propagate(TestValue(1)),
835 PropagateStop::<TestValue>::default(),
836 ))
837 .id();
838 let propagatee = app.world_mut().spawn(TestValue(2)).id();
839
840 app.update();
841
842 assert_eq!(
843 query.get_many(app.world(), [propagator, propagatee]),
844 Ok([&TestValue(1), &TestValue(2)])
845 );
846
847 app.world_mut()
848 .commands()
849 .entity(propagatee)
850 .insert(ChildOf(propagator));
851 app.update();
852
853 assert_eq!(
854 query.get_many(app.world(), [propagator, propagatee]),
855 Ok([&TestValue(1), &TestValue(2)])
856 );
857 }
858}