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