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