diff --git a/_release-content/migration-guides/observer_event_matching.md b/_release-content/migration-guides/observer_event_matching.md new file mode 100644 index 0000000000000..ff2b3c69f8536 --- /dev/null +++ b/_release-content/migration-guides/observer_event_matching.md @@ -0,0 +1,76 @@ +--- +title: Moved Observer events `B` Bundle generic into the event type +pull_requests: [24013] +--- + +The `B: Bundle` type parameter has been removed from `On` in observer systems. +Lifecycle events (`Add`/`Insert`/`Discard`/`Remove`/`Despawn`) have been updated +to have this `B: Bundle` directly on them. + +```rust +// Bevy 0.19 +world.add_observer(|on: On| { + // ... +}); + +// Bevy 0.20 +world.add_observer(|on: On>| { + // ... +}); +``` + +For custom event types that previously made use of `B: Bundle`, it's recommended to do the following: + +```rust +// Bevy 0.19 + +#[derive(Event)] +pub struct Foo; + +#[derive(Component)] +pub struct Bar; + +world.add_observer(|on: On| { + // ... +}); + +// Bevy 0.20 + +#[derive(Event)] +pub struct FooEvent; + +#[derive(Component)] +pub struct Bar; + +pub struct Foo(PhantomData); + +impl EventPattern for Foo { + type Event = FooEvent; + type Components = B; +} + +world.add_observer(|on: On>| { + // ... +}); +``` + +For lifecycle observers watching dynamic components, you now need to modify +`On` to `On>`: + +```rust +// Bevy 0.19 +world.spawn( + Observer::new(|_: On| { + // ... + }) + .with_component(component_id), +); + +// Bevy 0.20 +world.spawn( + Observer::new(|_: On>| { + // ... + }) + .with_component(component_id), +); +``` diff --git a/benches/benches/bevy_ecs/observers/lifecycle.rs b/benches/benches/bevy_ecs/observers/lifecycle.rs index af1ea15df9f78..365e080c23a39 100644 --- a/benches/benches/bevy_ecs/observers/lifecycle.rs +++ b/benches/benches/bevy_ecs/observers/lifecycle.rs @@ -30,6 +30,6 @@ pub fn observer_lifecycle(criterion: &mut Criterion) { #[derive(Component)] struct A; -fn on_insert(event: On) { +fn on_insert(event: On>) { black_box(event); } diff --git a/crates/bevy_app/src/hierarchy.rs b/crates/bevy_app/src/hierarchy.rs index 4dbb6deb15fda..21441ea537d57 100644 --- a/crates/bevy_app/src/hierarchy.rs +++ b/crates/bevy_app/src/hierarchy.rs @@ -62,7 +62,7 @@ pub struct ValidateParentHasComponentSystems; /// An `Insert` observer that when run, will validate that the parent of a given entity contains /// component `C`. If the parent does not contain `C`, a warning will be logged later in the frame. fn validate_parent_has_component( - event: On, + event: On>, child: Query<&ChildOf>, with_component: Query<(), With>, mut writer: MessageWriter>, diff --git a/crates/bevy_app/src/propagate.rs b/crates/bevy_app/src/propagate.rs index 628e193a3a69e..4ba33e63e9572 100644 --- a/crates/bevy_app/src/propagate.rs +++ b/crates/bevy_app/src/propagate.rs @@ -190,7 +190,7 @@ pub fn on_r_inserted< F: QueryFilter + 'static, R: Relationship, >( - event: On, + event: On>, mut commands: Commands, query: Query<(&R, Has>), (Without>, F)>, relations: Query<&Inherited, Without>>, @@ -207,7 +207,7 @@ pub fn on_r_inserted< /// Remove [`Inherited::`] when an entity loses its `R` relationship pub fn on_r_removed( - event: On, + event: On>, mut commands: Commands, query: Query<(), (With>, Without>, F)>, ) { diff --git a/crates/bevy_dev_tools/src/diagnostics_overlay.rs b/crates/bevy_dev_tools/src/diagnostics_overlay.rs index 336f9dfdc45ed..d668b57628f3e 100644 --- a/crates/bevy_dev_tools/src/diagnostics_overlay.rs +++ b/crates/bevy_dev_tools/src/diagnostics_overlay.rs @@ -335,7 +335,7 @@ fn rebuild_diagnostics_list( } fn build_overlay( - event: On, + event: On>, mut commands: Commands, diagnostics_overlays: Query<&DiagnosticsOverlay>, diagnostics_overlay_plane: Single>, diff --git a/crates/bevy_ecs/src/bundle/insert.rs b/crates/bevy_ecs/src/bundle/insert.rs index 7eb2f64ac9845..53a3cbfb717b2 100644 --- a/crates/bevy_ecs/src/bundle/insert.rs +++ b/crates/bevy_ecs/src/bundle/insert.rs @@ -12,7 +12,7 @@ use crate::{ component::{Components, StorageType}, entity::{Entities, Entity, EntityLocation}, event::EntityComponentsTrigger, - lifecycle::{Add, Discard, Insert, ADD, DISCARD, INSERT}, + lifecycle::{AddEvent, DiscardEvent, InsertEvent, ADD, DISCARD, INSERT}, observer::Observers, query::DebugCheckedUnwrap as _, relationship::RelationshipHookMode, @@ -157,7 +157,7 @@ impl<'w> BundleInserter<'w> { // SAFETY: the DISCARD event_key corresponds to the Discard event's type deferred_world.trigger_raw( DISCARD, - &mut Discard { entity }, + &mut DiscardEvent { entity }, &mut EntityComponentsTrigger { components: archetype_after_insert.existing(), old_archetype: Some(archetype), @@ -412,7 +412,7 @@ impl<'w> BundleInserter<'w> { // SAFETY: the ADD event_key corresponds to the Add event's type deferred_world.trigger_raw( ADD, - &mut Add { entity }, + &mut AddEvent { entity }, &mut EntityComponentsTrigger { components: archetype_after_insert.added(), old_archetype: Some(old_archetype), @@ -435,7 +435,7 @@ impl<'w> BundleInserter<'w> { // SAFETY: the INSERT event_key corresponds to the Insert event's type deferred_world.trigger_raw( INSERT, - &mut Insert { entity }, + &mut InsertEvent { entity }, &mut EntityComponentsTrigger { components: archetype_after_insert.inserted(), old_archetype: Some(old_archetype), @@ -459,7 +459,7 @@ impl<'w> BundleInserter<'w> { // SAFETY: the INSERT event_key corresponds to the Insert event's type deferred_world.trigger_raw( INSERT, - &mut Insert { entity }, + &mut InsertEvent { entity }, &mut EntityComponentsTrigger { components: archetype_after_insert.added(), old_archetype: Some(old_archetype), diff --git a/crates/bevy_ecs/src/bundle/remove.rs b/crates/bevy_ecs/src/bundle/remove.rs index 6b9f59186a835..c1c26f393123d 100644 --- a/crates/bevy_ecs/src/bundle/remove.rs +++ b/crates/bevy_ecs/src/bundle/remove.rs @@ -9,7 +9,7 @@ use crate::{ component::{ComponentId, Components, StorageType}, entity::{Entity, EntityLocation}, event::EntityComponentsTrigger, - lifecycle::{Discard, Remove, DISCARD, REMOVE}, + lifecycle::{DiscardEvent, RemoveEvent, DISCARD, REMOVE}, observer::Observers, relationship::RelationshipHookMode, storage::{SparseSets, Storages, Table, TableId}, @@ -150,7 +150,7 @@ impl<'w> BundleRemover<'w> { // SAFETY: the DISCARD event_key corresponds to the Discard event's type deferred_world.trigger_raw( DISCARD, - &mut Discard { entity }, + &mut DiscardEvent { entity }, &mut EntityComponentsTrigger { components: &components, old_archetype: Some(self.old_archetype.as_ref()), @@ -171,7 +171,7 @@ impl<'w> BundleRemover<'w> { // SAFETY: the REMOVE event_key corresponds to the Remove event's type deferred_world.trigger_raw( REMOVE, - &mut Remove { entity }, + &mut RemoveEvent { entity }, &mut EntityComponentsTrigger { components: &components, old_archetype: Some(self.old_archetype.as_ref()), diff --git a/crates/bevy_ecs/src/bundle/spawner.rs b/crates/bevy_ecs/src/bundle/spawner.rs index 8a43899bb28a2..a79b7e12b6452 100644 --- a/crates/bevy_ecs/src/bundle/spawner.rs +++ b/crates/bevy_ecs/src/bundle/spawner.rs @@ -8,7 +8,7 @@ use crate::{ change_detection::{MaybeLocation, Tick}, entity::{Entity, EntityAllocator, EntityLocation}, event::EntityComponentsTrigger, - lifecycle::{Add, Insert, ADD, INSERT}, + lifecycle::{AddEvent, InsertEvent, ADD, INSERT}, relationship::RelationshipHookMode, storage::Table, world::{unsafe_world_cell::UnsafeWorldCell, World}, @@ -141,7 +141,7 @@ impl<'w> BundleSpawner<'w> { // SAFETY: the ADD event_key corresponds to the Add event's type deferred_world.trigger_raw( ADD, - &mut Add { entity }, + &mut AddEvent { entity }, &mut EntityComponentsTrigger { components: bundle_info.contributed_components(), old_archetype: None, @@ -161,7 +161,7 @@ impl<'w> BundleSpawner<'w> { // SAFETY: the INSERT event_key corresponds to the Insert event's type deferred_world.trigger_raw( INSERT, - &mut Insert { entity }, + &mut InsertEvent { entity }, &mut EntityComponentsTrigger { components: bundle_info.contributed_components(), old_archetype: None, diff --git a/crates/bevy_ecs/src/event/mod.rs b/crates/bevy_ecs/src/event/mod.rs index 2bad38049368e..3da87df513760 100644 --- a/crates/bevy_ecs/src/event/mod.rs +++ b/crates/bevy_ecs/src/event/mod.rs @@ -5,6 +5,7 @@ pub use bevy_ecs_macros::{EntityEvent, Event}; pub use trigger::*; use crate::{ + bundle::Bundle, component::{Component, ComponentId}, entity::Entity, world::World, @@ -90,6 +91,47 @@ pub trait Event: Send + Sync + Sized + 'static { type Trigger<'a>: Trigger; } +/// Trait for types that can be 'matched' on by [`Observer`]s to register additional +/// metadata for an [`Event`] trigger. All [`Event`]s are also implicitly +/// [`EventPattern`]s, but this trait can be manually implemented. +/// +/// The following are lifecycle [`EventPattern`]s that register components +/// to watch for via their generic [`Bundle`] type parameter: +/// +/// - [`Add`] +/// - [`Insert`] +/// - [`Discard`] +/// - [`Remove`] +/// - [`Despawn`] +/// +/// [`Observer`]: crate::observer::Observer +/// [`Add`]: crate::lifecycle::Add +/// [`Insert`]: crate::lifecycle::Insert +/// [`Discard`]: crate::lifecycle::Discard +/// [`Remove`]: crate::lifecycle::Remove +/// [`Despawn`]: crate::lifecycle::Despawn +#[diagnostic::on_unimplemented( + message = "`{Self}` is not an `Event` or `EventPattern`", + label = "invalid `EventPattern`", + note = "consider annotating `{Self}` with `#[derive(Event)]` or implementing `EventPattern` manually" +)] +pub trait EventPattern: Send + Sync + 'static { + /// The event type being observed. + type Event: Event; + + /// Components to watch for this event. This is used by [`EntityComponentsTrigger`] + /// to determine which entities to run observers for. + /// + /// See [`EntityComponentsTrigger`] for more info. + type Components: Bundle; +} + +// All events are implicitly EventPatterns, with no additional components. +impl EventPattern for E { + type Event = Self; + type Components = (); +} + /// An [`EntityEvent`] is an [`Event`] that is triggered for a specific [`EntityEvent::event_target`] entity: /// /// ``` diff --git a/crates/bevy_ecs/src/event/trigger.rs b/crates/bevy_ecs/src/event/trigger.rs index 83c8624aed4ec..a33cf1c480472 100644 --- a/crates/bevy_ecs/src/event/trigger.rs +++ b/crates/bevy_ecs/src/event/trigger.rs @@ -1,4 +1,4 @@ -use crate::event::SetEntityEventTarget; +use crate::event::{EventPattern, SetEntityEventTarget}; use crate::{ archetype::Archetype, component::ComponentId, @@ -55,6 +55,9 @@ pub unsafe trait Trigger { ); } +/// Shorthand for accessing an [`EventPattern`]s [`Trigger`] via its [`Event`]. +pub type EventPatternTrigger<'a, E> = <::Event as Event>::Trigger<'a>; + /// A [`Trigger`] that runs _every_ "global" [`Observer`](crate::observer::Observer) (ex: registered via [`World::add_observer`](crate::world::World::add_observer)) /// that matches the given [`Event`]. /// @@ -366,7 +369,7 @@ pub struct EntityComponentsTrigger<'a> { /// # let mut world = World::new(); /// # /// fn on_add_disable( - /// on: On, + /// on: On>, /// mut cache: ResMut, /// a_component: ComponentIdFor, /// ) { @@ -405,7 +408,7 @@ pub struct EntityComponentsTrigger<'a> { /// # let mut world = World::new(); /// # /// fn on_remove_disable( - /// on: On, + /// on: On>, /// mut cache: ResMut, /// a_component: ComponentIdFor, /// ) { diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index dee0fc0aa0f24..8dad5e6ac856e 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -75,7 +75,7 @@ pub mod prelude { component::Component, entity::{ContainsEntity, Entity, EntityMapper}, error::{BevyError, Result, ResultSeverityExt, Severity}, - event::{EntityEvent, Event}, + event::{EntityEvent, Event, EventPattern}, hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children}, lifecycle::{Add, Despawn, Discard, Insert, Remove, RemovedComponents}, message::{ diff --git a/crates/bevy_ecs/src/lifecycle.rs b/crates/bevy_ecs/src/lifecycle.rs index 82e73d326c215..2f091ad353950 100644 --- a/crates/bevy_ecs/src/lifecycle.rs +++ b/crates/bevy_ecs/src/lifecycle.rs @@ -50,10 +50,11 @@ //! For example, [`Add`] corresponds to [`ADD`]. //! This is used to skip [`TypeId`](core::any::TypeId) lookups in hot paths. use crate::{ + bundle::Bundle, change_detection::{MaybeLocation, Tick}, component::{Component, ComponentId, ComponentIdFor}, entity::Entity, - event::{EntityComponentsTrigger, EntityEvent, EventKey}, + event::{EntityComponentsTrigger, EntityEvent, EventKey, EventPattern}, message::{ Message, MessageCursor, MessageId, MessageIterator, MessageIteratorWithId, Messages, }, @@ -332,12 +333,26 @@ pub const DESPAWN: EventKey = EventKey(ComponentId::new(crate::component::DESPAW #[entity_event(trigger = EntityComponentsTrigger<'a>)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] -#[doc(alias = "OnAdd")] -pub struct Add { +pub struct AddEvent { /// The entity this component was added to. pub entity: Entity, } +/// [`EventPattern`] for an [`AddEvent`] on a given bundle of components. +/// +/// # Note +/// +/// All components specified in the [`Bundle`] are treated as an `OR` filter +/// **not** an `AND` filter. For example, `Add<(A, B)>` will trigger if either +/// component `A` or component `B` is added to an entity. +#[doc(alias = "OnAdd")] +pub struct Add(PhantomData); + +impl EventPattern for Add { + type Event = AddEvent; + type Components = B; +} + /// Trigger emitted when a component is inserted, regardless of whether or not the entity already /// had that component. Runs after `Add`, if it ran. /// See [`ComponentHooks::on_insert`](`crate::lifecycle::ComponentHooks::on_insert`) for more information. @@ -345,12 +360,26 @@ pub struct Add { #[entity_event(trigger = EntityComponentsTrigger<'a>)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] -#[doc(alias = "OnInsert")] -pub struct Insert { +pub struct InsertEvent { /// The entity this component was inserted into. pub entity: Entity, } +/// [`EventPattern`] for an [`InsertEvent`] on a given bundle of components. +/// +/// # Note +/// +/// All components specified in the [`Bundle`] are treated as an `OR` filter +/// **not** an `AND` filter. For example, `Insert<(A, B)>` will trigger if +/// either component `A` or component `B` is inserted into an entity. +#[doc(alias = "OnInsert")] +pub struct Insert(PhantomData); + +impl EventPattern for Insert { + type Event = InsertEvent; + type Components = B; +} + /// Trigger emitted when a component is removed from an entity, regardless /// of whether or not it is later replaced. /// @@ -360,12 +389,27 @@ pub struct Insert { #[entity_event(trigger = EntityComponentsTrigger<'a>)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] + +pub struct DiscardEvent { + /// The entity that held this component before it was discarded. + pub entity: Entity, +} + +/// [`EventPattern`] for a [`DiscardEvent`] on a given bundle of components. +/// +/// # Note +/// +/// All components specified in the [`Bundle`] are treated as an `OR` filter +/// **not** an `AND` filter. For example, `Discard<(A, B)>` will trigger if +/// either component `A` or component `B` are discarded from an entity. #[doc(alias = "OnDiscard")] #[doc(alias = "OnReplace")] #[doc(alias = "Replace")] -pub struct Discard { - /// The entity that held this component before it was discarded. - pub entity: Entity, +pub struct Discard(PhantomData); + +impl EventPattern for Discard { + type Event = DiscardEvent; + type Components = B; } /// Trigger emitted when a component is removed from an entity, and runs before the component is @@ -375,24 +419,52 @@ pub struct Discard { #[entity_event(trigger = EntityComponentsTrigger<'a>)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] -#[doc(alias = "OnRemove")] -pub struct Remove { +pub struct RemoveEvent { /// The entity this component was removed from. pub entity: Entity, } +/// [`EventPattern`] for a [`RemoveEvent`] on a given bundle of components. +/// +/// # Note +/// +/// All components specified in the [`Bundle`] are treated as an `OR` filter +/// **not** an `AND` filter. For example, `Remove<(A, B)>` will trigger if +/// either component `A` or component `B` are removed from an entity. +#[doc(alias = "OnRemove")] +pub struct Remove(PhantomData); + +impl EventPattern for Remove { + type Event = RemoveEvent; + type Components = B; +} + /// [`EntityEvent`] emitted for each component on an entity when it is despawned. /// See [`ComponentHooks::on_despawn`](`crate::lifecycle::ComponentHooks::on_despawn`) for more information. #[derive(Debug, Clone, EntityEvent)] #[entity_event(trigger = EntityComponentsTrigger<'a>)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] -#[doc(alias = "OnDespawn")] -pub struct Despawn { +pub struct DespawnEvent { /// The entity that held this component before it was despawned. pub entity: Entity, } +/// [`EventPattern`] for a [`DespawnEvent`] on a given bundle of components. +/// +/// # Note +/// +/// All components specified in the [`Bundle`] are treated as an `OR` filter +/// **not** an `AND` filter. For example, `Despawn<(A, B)>` will trigger if +/// either component `A` or component `B` are present on an entity that is despawned. +#[doc(alias = "OnDespawn")] +pub struct Despawn(PhantomData); + +impl EventPattern for Despawn { + type Event = DespawnEvent; + type Components = B; +} + /// Wrapper around [`Entity`] for [`RemovedComponents`]. /// Internally, `RemovedComponents` uses these as an [`Messages`]. #[derive(Message, Debug, Clone, Into)] diff --git a/crates/bevy_ecs/src/observer/condition.rs b/crates/bevy_ecs/src/observer/condition.rs index de77ba78ef328..e82be1b3c356b 100644 --- a/crates/bevy_ecs/src/observer/condition.rs +++ b/crates/bevy_ecs/src/observer/condition.rs @@ -7,8 +7,7 @@ use alloc::{boxed::Box, vec::Vec}; use core::marker::PhantomData; use crate::{ - bundle::Bundle, - event::Event, + event::EventPattern, schedule::{BoxedCondition, SystemCondition}, system::{IntoObserverSystem, IntoSystem}, world::{unsafe_world_cell::UnsafeWorldCell, World}, @@ -52,13 +51,13 @@ pub struct ObserverWithConditionMarker; /// This type is returned by [`ObserverSystemExt::run_if`](super::ObserverSystemExt::run_if) /// and allows `entity.observe(system.run_if(cond))` to work with compile-time /// verification that the event implements [`EntityEvent`](crate::event::EntityEvent). -pub struct ObserverWithCondition> { +pub struct ObserverWithCondition> { pub(crate) system: S, pub(crate) conditions: Vec, - pub(crate) _marker: PhantomData (E, B, M)>, + pub(crate) _marker: PhantomData (E, M)>, } -impl> ObserverWithCondition { +impl> ObserverWithCondition { /// Adds another run condition to this observer. /// /// All conditions must return `true` for the observer to run (AND semantics). diff --git a/crates/bevy_ecs/src/observer/distributed_storage.rs b/crates/bevy_ecs/src/observer/distributed_storage.rs index c7bead140814f..f8610c6017a19 100644 --- a/crates/bevy_ecs/src/observer/distributed_storage.rs +++ b/crates/bevy_ecs/src/observer/distributed_storage.rs @@ -16,7 +16,7 @@ use core::marker::PhantomData; use crate::{ component::{ComponentCloneBehavior, ComponentId, Mutable, StorageType}, error::{ErrorContext, ErrorHandler}, - event::EventKey, + event::{EventKey, EventPattern}, lifecycle::{ComponentHook, HookContext}, observer::{ condition::{ObserverCondition, ObserverWithCondition, ObserverWithConditionMarker}, @@ -221,7 +221,7 @@ impl Observer { /// # Panics /// /// Panics if the given system is an exclusive system. - pub fn new>(system: I) -> Self { + pub fn new>(system: I) -> Self { let system = Box::new(IntoObserverSystem::into_system(system)); assert!( !system.is_exclusive(), @@ -234,9 +234,9 @@ impl Observer { Self { system, descriptor: Default::default(), - hook_on_add: hook_on_add::, + hook_on_add: hook_on_add::, error_handler: None, - runner: observer_system_runner::, + runner: observer_system_runner::, despawned_watched_entities: 0, last_trigger_id: 0, conditions: Vec::new(), @@ -453,15 +453,15 @@ impl ObserverDescriptor { /// The type parameters of this function _must_ match those used to create the [`Observer`]. /// As such, it is recommended to only use this function within the [`Observer::new`] method to /// ensure type parameters match. -fn hook_on_add>( +fn hook_on_add>( mut world: DeferredWorld<'_>, HookContext { entity, .. }: HookContext, ) { world.commands().queue(move |world: &mut World| { - let event_key = world.register_event_key::(); - let components = B::component_ids(&mut world.components_registrator()); + let event_key = world.register_event_key::(); + let components = E::Components::component_ids(&mut world.components_registrator()); - let system_ptr: *mut dyn ObserverSystem = { + let system_ptr: *mut dyn ObserverSystem = { let Some(mut observer) = world.get_mut::(entity) else { return; }; @@ -569,14 +569,14 @@ impl IntoObserver<()> for Observer { } } -impl> IntoObserver<(E, B, M)> for T { +impl> IntoObserver<(E, M)> for T { fn into_observer(self) -> Observer { Observer::new(self) } } -impl> - IntoObserver for ObserverWithCondition +impl> + IntoObserver for ObserverWithCondition { fn into_observer(self) -> Observer { let (system, conditions) = self.take_conditions(); @@ -598,7 +598,7 @@ pub trait IntoEntityObserver: Send + 'static { fn into_observer_for_entity(self, entity: Entity) -> Observer; } -impl> IntoEntityObserver<(E, B, M)> +impl, M, T: IntoObserverSystem> IntoEntityObserver<(E, M)> for T { fn into_observer_for_entity(self, entity: Entity) -> Observer { @@ -606,8 +606,8 @@ impl> IntoEntityObs } } -impl> - IntoEntityObserver for ObserverWithCondition +impl, M: 'static, S: IntoObserverSystem> + IntoEntityObserver for ObserverWithCondition { fn into_observer_for_entity(self, entity: Entity) -> Observer { let (system, conditions) = self.take_conditions(); @@ -618,12 +618,12 @@ impl> } /// Extension trait for adding run conditions to observer systems. -pub trait ObserverSystemExt: IntoObserverSystem + Sized { +pub trait ObserverSystemExt: IntoObserverSystem + Sized { /// Adds a run condition to this observer system. /// /// The observer will only run if the condition returns `true`. /// Multiple conditions can be chained (AND semantics). - fn run_if(self, condition: C) -> ObserverWithCondition + fn run_if(self, condition: C) -> ObserverWithCondition where C: SystemCondition, { @@ -635,4 +635,4 @@ pub trait ObserverSystemExt: IntoObserverSystem } } -impl> ObserverSystemExt for T {} +impl> ObserverSystemExt for T {} diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 2a2189810d074..d5814704b8a53 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -37,10 +37,10 @@ impl World { /// struct A; /// /// # let mut world = World::new(); - /// world.add_observer(|_: On| { + /// world.add_observer(|_: On>| { /// // ... /// }); - /// world.add_observer(|_: On| { + /// world.add_observer(|_: On>| { /// // ... /// }); /// ``` @@ -449,7 +449,7 @@ impl World { #[cfg(test)] mod tests { use alloc::{vec, vec::Vec}; - use core::any::type_name; + use core::{any::type_name, marker::PhantomData}; use bevy_ptr::OwningPtr; @@ -457,8 +457,9 @@ mod tests { archetype::{Archetype, ArchetypeId}, change_detection::MaybeLocation, error::Result, - event::{EntityComponentsTrigger, Event, GlobalTrigger}, + event::{EntityComponentsTrigger, Event, EventPattern, GlobalTrigger}, hierarchy::ChildOf, + lifecycle::RemoveEvent, observer::{Discard, Observer}, prelude::*, world::DeferredWorld, @@ -484,6 +485,13 @@ mod tests { #[entity_event(trigger = EntityComponentsTrigger<'a>)] struct EntityComponentsEvent(Entity); + struct EntityComponents(PhantomData); + + impl EventPattern for EntityComponents { + type Event = EntityComponentsEvent; + type Components = B; + } + #[derive(Event)] struct EventWithData { counter: usize, @@ -508,12 +516,12 @@ mod tests { let mut world = World::new(); world.init_resource::(); - world.add_observer(|_: On, mut res: ResMut| res.observed("add")); - world.add_observer(|_: On, mut res: ResMut| res.observed("insert")); - world.add_observer(|_: On, mut res: ResMut| { + world.add_observer(|_: On>, mut res: ResMut| res.observed("add")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("insert")); + world.add_observer(|_: On>, mut res: ResMut| { res.observed("discard"); }); - world.add_observer(|_: On, mut res: ResMut| res.observed("remove")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("remove")); let entity = world.spawn(A).id(); world.despawn(entity); @@ -528,12 +536,12 @@ mod tests { let mut world = World::new(); world.init_resource::(); - world.add_observer(|_: On, mut res: ResMut| res.observed("add")); - world.add_observer(|_: On, mut res: ResMut| res.observed("insert")); - world.add_observer(|_: On, mut res: ResMut| { + world.add_observer(|_: On>, mut res: ResMut| res.observed("add")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("insert")); + world.add_observer(|_: On>, mut res: ResMut| { res.observed("discard"); }); - world.add_observer(|_: On, mut res: ResMut| res.observed("remove")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("remove")); let mut entity = world.spawn_empty(); entity.insert(A); @@ -550,12 +558,12 @@ mod tests { let mut world = World::new(); world.init_resource::(); - world.add_observer(|_: On, mut res: ResMut| res.observed("add")); - world.add_observer(|_: On, mut res: ResMut| res.observed("insert")); - world.add_observer(|_: On, mut res: ResMut| { + world.add_observer(|_: On>, mut res: ResMut| res.observed("add")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("insert")); + world.add_observer(|_: On>, mut res: ResMut| { res.observed("discard"); }); - world.add_observer(|_: On, mut res: ResMut| res.observed("remove")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("remove")); let mut entity = world.spawn_empty(); entity.insert(S); @@ -574,12 +582,12 @@ mod tests { let entity = world.spawn(A).id(); - world.add_observer(|_: On, mut res: ResMut| res.observed("add")); - world.add_observer(|_: On, mut res: ResMut| res.observed("insert")); - world.add_observer(|_: On, mut res: ResMut| { + world.add_observer(|_: On>, mut res: ResMut| res.observed("add")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("insert")); + world.add_observer(|_: On>, mut res: ResMut| { res.observed("discard"); }); - world.add_observer(|_: On, mut res: ResMut| res.observed("remove")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("remove")); let mut entity = world.entity_mut(entity); entity.insert(A); @@ -592,25 +600,25 @@ mod tests { let mut world = World::new(); world.init_resource::(); world.add_observer( - |add: On, mut res: ResMut, mut commands: Commands| { + |add: On>, mut res: ResMut, mut commands: Commands| { res.observed("add_a"); commands.entity(add.entity).insert(B); }, ); world.add_observer( - |remove: On, mut res: ResMut, mut commands: Commands| { + |remove: On>, mut res: ResMut, mut commands: Commands| { res.observed("remove_a"); commands.entity(remove.entity).remove::(); }, ); world.add_observer( - |add: On, mut res: ResMut, mut commands: Commands| { + |add: On>, mut res: ResMut, mut commands: Commands| { res.observed("add_b"); commands.entity(add.entity).remove::(); }, ); - world.add_observer(|_: On, mut res: ResMut| { + world.add_observer(|_: On>, mut res: ResMut| { res.observed("remove_b"); }); @@ -642,8 +650,8 @@ mod tests { let mut world = World::new(); world.init_resource::(); - world.add_observer(|_: On, mut res: ResMut| res.observed("add_1")); - world.add_observer(|_: On, mut res: ResMut| res.observed("add_2")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("add_1")); + world.add_observer(|_: On>, mut res: ResMut| res.observed("add_2")); world.spawn(A).flush(); assert_eq!(vec!["add_2", "add_1"], world.resource::().0); @@ -656,11 +664,11 @@ mod tests { fn observer_multiple_events() { let mut world = World::new(); world.init_resource::(); - let on_remove = world.register_event_key::(); + let on_remove = world.register_event_key::(); world.spawn( // SAFETY: Add and Remove are both unit types, so this is safe unsafe { - Observer::new(|_: On, mut res: ResMut| { + Observer::new(|_: On>, mut res: ResMut| { res.observed("add/remove"); }) .with_event_key(on_remove) @@ -682,7 +690,7 @@ mod tests { world.register_component::(); world.register_component::(); - world.add_observer(|_: On, mut res: ResMut| { + world.add_observer(|_: On>, mut res: ResMut| { res.observed("add_ab"); }); @@ -695,7 +703,7 @@ mod tests { fn observer_despawn() { let mut world = World::new(); - let system: fn(On) = |_| { + let system: fn(On>) = |_| { panic!("Observer triggered after being despawned."); }; let observer = world.add_observer(system).id(); @@ -711,11 +719,11 @@ mod tests { let entity = world.spawn((A, B)).flush(); - world.add_observer(|_: On, mut res: ResMut| { + world.add_observer(|_: On>, mut res: ResMut| { res.observed("remove_a"); }); - let system: fn(On) = |_: On| { + let system: fn(On>) = |_: On>| { panic!("Observer triggered after being despawned."); }; @@ -732,7 +740,7 @@ mod tests { let mut world = World::new(); world.init_resource::(); - world.add_observer(|_: On, mut res: ResMut| { + world.add_observer(|_: On>, mut res: ResMut| { res.observed("add_ab"); }); @@ -776,29 +784,28 @@ mod tests { // targets (entity_1, A) let entity_1 = world .spawn_empty() - .observe(|_: On, mut res: ResMut| res.0 += 1) + .observe(|_: On>, mut res: ResMut| res.0 += 1) .id(); // targets (entity_2, B) let entity_2 = world .spawn_empty() - .observe(|_: On, mut res: ResMut| res.0 += 10) + .observe(|_: On>, mut res: ResMut| res.0 += 10) .id(); // targets any entity or component world.add_observer(|_: On, mut res: ResMut| res.0 += 100); // targets any entity, and components A or B - world - .add_observer(|_: On, mut res: ResMut| res.0 += 1000); + world.add_observer(|_: On>, mut res: ResMut| res.0 += 1000); // test all tuples world.add_observer( - |_: On, mut res: ResMut| res.0 += 10000, + |_: On>, mut res: ResMut| res.0 += 10000, ); world.add_observer( - |_: On, mut res: ResMut| { + |_: On>, mut res: ResMut| { res.0 += 100000; }, ); world.add_observer( - |_: On, + |_: On>, mut res: ResMut| res.0 += 1000000, ); @@ -866,7 +873,7 @@ mod tests { let component_id = world.register_component::(); world.spawn( - Observer::new(|_: On, mut res: ResMut| res.observed("event_a")) + Observer::new(|_: On>, mut res: ResMut| res.observed("event_a")) .with_component(component_id), ); @@ -1415,7 +1422,7 @@ mod tests { // Originally for https://github.com/bevyengine/bevy/issues/18452 #[test] fn observer_modifies_relationship() { - fn on_add(add: On, mut commands: Commands) { + fn on_add(add: On>, mut commands: Commands) { commands .entity(add.entity) .with_related_entities::(|rsc| { @@ -1435,7 +1442,7 @@ mod tests { let mut world = World::new(); // Observe the removal of A - this will run during despawn - world.add_observer(|_: On, mut cmd: Commands| { + world.add_observer(|_: On>, mut cmd: Commands| { // Spawn a new entity - this reserves a new ID and requires a flush // afterward before Entities::free can be called. cmd.spawn_empty(); @@ -1507,10 +1514,10 @@ mod tests { let caller = MaybeLocation::caller(); let mut world = World::new(); - world.add_observer(move |event: On| { + world.add_observer(move |event: On>| { assert_eq!(event.caller(), caller); }); - world.add_observer(move |event: On| { + world.add_observer(move |event: On>| { assert_eq!(event.caller(), caller); }); world.commands().spawn(Component).clear(); @@ -1798,12 +1805,13 @@ mod tests { let mut world = World::new(); world.init_resource::(); - fn observer Event = EntityComponentsTrigger<'a>>>( - e: On, - mut c: ResMut, - ) { + fn observer(e: On, mut c: ResMut) + where + E: EventPattern, + E::Event: for<'a> Event = EntityComponentsTrigger<'a>>, + { c.0.push(( - type_name::(), + type_name::(), e.trigger().old_archetype.map(Archetype::id), e.trigger().new_archetype.map(Archetype::id), )); @@ -1813,11 +1821,11 @@ mod tests { let a = world.spawn(A).archetype().id(); let ab = world.spawn((A, B)).archetype().id(); - world.add_observer(observer::); - world.add_observer(observer::); - world.add_observer(observer::); - world.add_observer(observer::); - world.add_observer(observer::); + world.add_observer(observer::>); + world.add_observer(observer::>); + world.add_observer(observer::>); + world.add_observer(observer::>); + world.add_observer(observer::>); let mut entity = world.spawn((A, B)); entity.remove::<(A, B)>(); @@ -1828,17 +1836,17 @@ mod tests { assert_eq!( &world.resource_mut::().0, &[ - ("bevy_ecs::lifecycle::Add", None, Some(ab)), - ("bevy_ecs::lifecycle::Insert", None, Some(ab)), - ("bevy_ecs::lifecycle::Discard", Some(ab), Some(empty)), - ("bevy_ecs::lifecycle::Remove", Some(ab), Some(empty)), - ("bevy_ecs::lifecycle::Add", Some(empty), Some(a)), - ("bevy_ecs::lifecycle::Insert", Some(empty), Some(a)), - ("bevy_ecs::lifecycle::Discard", Some(a), Some(a)), - ("bevy_ecs::lifecycle::Insert", Some(a), Some(a)), - ("bevy_ecs::lifecycle::Despawn", Some(a), None), - ("bevy_ecs::lifecycle::Discard", Some(a), None), - ("bevy_ecs::lifecycle::Remove", Some(a), None), + ("bevy_ecs::lifecycle::AddEvent", None, Some(ab)), + ("bevy_ecs::lifecycle::InsertEvent", None, Some(ab)), + ("bevy_ecs::lifecycle::DiscardEvent", Some(ab), Some(empty)), + ("bevy_ecs::lifecycle::RemoveEvent", Some(ab), Some(empty)), + ("bevy_ecs::lifecycle::AddEvent", Some(empty), Some(a)), + ("bevy_ecs::lifecycle::InsertEvent", Some(empty), Some(a)), + ("bevy_ecs::lifecycle::DiscardEvent", Some(a), Some(a)), + ("bevy_ecs::lifecycle::InsertEvent", Some(a), Some(a)), + ("bevy_ecs::lifecycle::DespawnEvent", Some(a), None), + ("bevy_ecs::lifecycle::DiscardEvent", Some(a), None), + ("bevy_ecs::lifecycle::RemoveEvent", Some(a), None), ], ); } diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 90d6bd2add9a4..a5af4d32492e7 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -4,7 +4,7 @@ use core::any::Any; use crate::{ error::ErrorContext, - event::Event, + event::{EventPattern, EventPatternTrigger}, observer::TriggerContext, prelude::*, query::DebugCheckedUnwrap, @@ -32,7 +32,7 @@ pub type ObserverRunner = // NOTE: The way `Trigger` and `On` interact in this implementation is _subtle_ and _easily invalidated_ // from a soundness perspective. Please read and understand the safety comments before making any changes, // either here or in `On`. -pub(super) unsafe fn observer_system_runner>( +pub(super) unsafe fn observer_system_runner>( mut world: DeferredWorld, observer: Entity, trigger_context: &TriggerContext, @@ -74,9 +74,9 @@ pub(super) unsafe fn observer_system_runner in practice. This is why `On<'w, 't>` has the strict constraint that // the 'w lifetime can never be exposed. To do so would make it possible to introduce use-after-free bugs. // See this thread for more details: - let trigger: &mut E::Trigger<'_> = unsafe { trigger_ptr.deref_mut() }; + let trigger: &mut EventPatternTrigger<'_, E> = unsafe { trigger_ptr.deref_mut() }; - let on: On = On::new( + let on: On = On::new( // SAFETY: Caller ensures `ptr` is castable to `&mut E` unsafe { event_ptr.deref_mut() }, observer, @@ -87,7 +87,7 @@ pub(super) unsafe fn observer_system_runner = unsafe { + let system: *mut dyn ObserverSystem = unsafe { let system: &mut dyn Any = state.system.as_mut(); let system = system.downcast_mut::().debug_checked_unwrap(); &mut *system diff --git a/crates/bevy_ecs/src/observer/system_param.rs b/crates/bevy_ecs/src/observer/system_param.rs index c03ffc9a93a3e..5c4c68d001f16 100644 --- a/crates/bevy_ecs/src/observer/system_param.rs +++ b/crates/bevy_ecs/src/observer/system_param.rs @@ -1,16 +1,14 @@ //! System parameters for working with observers. use crate::{ - bundle::Bundle, change_detection::MaybeLocation, - event::{Event, EventKey, PropagateEntityTrigger}, + event::{Event, EventKey, EventPattern, EventPatternTrigger, PropagateEntityTrigger}, prelude::*, traversal::Traversal, }; use bevy_ptr::Ptr; use core::{ fmt::Debug, - marker::PhantomData, ops::{Deref, DerefMut}, }; @@ -20,38 +18,26 @@ use core::{ /// [`Trigger`](crate::event::Trigger), which for things like [`EntityEvent`] with a [`PropagateEntityTrigger`], /// includes control over event propagation. /// -/// The generic `B: Bundle` is used to further specialize the events that this observer is interested in. -/// The entity involved *does not* have to have these components, but the observer will only be -/// triggered if the event matches the components in `B`. -/// -/// This is used to avoid providing a generic argument in your event, as is done for [`Add`] -/// and the other lifecycle events. -/// -/// Providing multiple components in this bundle will cause this event to be triggered by any -/// matching component in the bundle, -/// [rather than requiring all of them to be present](https://github.com/bevyengine/bevy/issues/15325). -/// /// [system parameter]: crate::system::SystemParam // SAFETY WARNING! // this type must _never_ expose anything with the 'w lifetime // See the safety discussion on `Trigger` for more details. -pub struct On<'w, 't, E: Event, B: Bundle = ()> { +pub struct On<'w, 't, E: EventPattern> { observer: Entity, // SAFETY WARNING: never expose this 'w lifetime - event: &'w mut E, + event: &'w mut E::Event, // SAFETY WARNING: never expose this 'w lifetime - trigger: &'w mut E::Trigger<'t>, + trigger: &'w mut EventPatternTrigger<'t, E>, // SAFETY WARNING: never expose this 'w lifetime trigger_context: &'w TriggerContext, - _marker: PhantomData, } -impl<'w, 't, E: Event, B: Bundle> On<'w, 't, E, B> { +impl<'w, 't, E: EventPattern> On<'w, 't, E> { /// Creates a new instance of [`On`] for the given triggered event. pub fn new( - event: &'w mut E, + event: &'w mut E::Event, observer: Entity, - trigger: &'w mut E::Trigger<'t>, + trigger: &'w mut EventPatternTrigger<'t, E>, trigger_context: &'w TriggerContext, ) -> Self { Self { @@ -59,7 +45,6 @@ impl<'w, 't, E: Event, B: Bundle> On<'w, 't, E, B> { observer, trigger, trigger_context, - _marker: PhantomData, } } @@ -69,12 +54,12 @@ impl<'w, 't, E: Event, B: Bundle> On<'w, 't, E, B> { } /// Returns a reference to the triggered event. - pub fn event(&self) -> &E { + pub fn event(&self) -> &E::Event { self.event } /// Returns a mutable reference to the triggered event. - pub fn event_mut(&mut self) -> &mut E { + pub fn event_mut(&mut self) -> &mut E::Event { self.event } @@ -84,12 +69,12 @@ impl<'w, 't, E: Event, B: Bundle> On<'w, 't, E, B> { } /// Returns the [`Trigger`](crate::event::Trigger) context for this event. - pub fn trigger(&self) -> &E::Trigger<'t> { + pub fn trigger(&self) -> &EventPatternTrigger<'t, E> { self.trigger } /// Returns the mutable [`Trigger`](crate::event::Trigger) context for this event. - pub fn trigger_mut(&mut self) -> &mut E::Trigger<'t> { + pub fn trigger_mut(&mut self) -> &mut EventPatternTrigger<'t, E> { self.trigger } @@ -125,14 +110,12 @@ impl<'w, 't, E: Event, B: Bundle> On<'w, 't, E, B> { } } -impl< - 'w, - 't, - const AUTO_PROPAGATE: bool, - E: EntityEvent + for<'a> Event = PropagateEntityTrigger>, - B: Bundle, - T: Traversal, - > On<'w, 't, E, B> +impl<'w, 't, const AUTO_PROPAGATE: bool, E, T> On<'w, 't, E> +where + E: EventPattern< + Event: EntityEvent = PropagateEntityTrigger>, + >, + T: Traversal, { /// Returns the original [`Entity`] that this [`EntityEvent`] targeted via [`EntityEvent::event_target`] when it was _first_ triggered, /// prior to any propagation logic. @@ -165,25 +148,28 @@ impl< } } -impl<'w, 't, E: for<'a> Event: Debug> + Debug, B: Bundle> Debug for On<'w, 't, E, B> { +impl<'w, 't, E> Debug for On<'w, 't, E> +where + E: EventPattern, + E::Event: Event: Debug> + Debug, +{ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("On") .field("event", &self.event) .field("trigger", &self.trigger) - .field("_marker", &self._marker) .finish() } } -impl<'w, 't, E: Event, B: Bundle> Deref for On<'w, 't, E, B> { - type Target = E; +impl<'w, 't, E: EventPattern> Deref for On<'w, 't, E> { + type Target = E::Event; fn deref(&self) -> &Self::Target { self.event } } -impl<'w, 't, E: Event, B: Bundle> DerefMut for On<'w, 't, E, B> { +impl<'w, 't, E: EventPattern> DerefMut for On<'w, 't, E> { fn deref_mut(&mut self) -> &mut Self::Target { self.event } diff --git a/crates/bevy_ecs/src/relationship/related_methods.rs b/crates/bevy_ecs/src/relationship/related_methods.rs index a1559e6e6b965..99fcf27b70018 100644 --- a/crates/bevy_ecs/src/relationship/related_methods.rs +++ b/crates/bevy_ecs/src/relationship/related_methods.rs @@ -916,7 +916,7 @@ mod tests { let result_entity = world.spawn(ObserverResult::default()).id(); world.add_observer( - move |replace: On, + move |replace: On>, has_relationship: Query>, mut results: Query<&mut ObserverResult>| { if has_relationship.get(replace.entity).unwrap_or(false) { diff --git a/crates/bevy_ecs/src/system/input.rs b/crates/bevy_ecs/src/system/input.rs index 48ef42b552c01..eb7b53b184596 100644 --- a/crates/bevy_ecs/src/system/input.rs +++ b/crates/bevy_ecs/src/system/input.rs @@ -2,7 +2,7 @@ use core::ops::{Deref, DerefMut}; use variadics_please::all_tuples; -use crate::{bundle::Bundle, event::Event, prelude::On, system::System}; +use crate::{event::EventPattern, prelude::On, system::System}; /// Trait for types that can be used as input to [`System`]s. /// @@ -247,13 +247,13 @@ impl<'i, T: ?Sized> DerefMut for InMut<'i, T> { /// Used for [`ObserverSystem`]s. /// /// [`ObserverSystem`]: crate::system::ObserverSystem -impl SystemInput for On<'_, '_, E, B> { +impl SystemInput for On<'_, '_, E> { // Note: the fact that we must use a shared lifetime here is // a key piece of the complicated safety story documented above // the `&mut E::Trigger<'_>` cast in `observer_system_runner` and in // the `On` implementation. - type Param<'i> = On<'i, 'i, E, B>; - type Inner<'i> = On<'i, 'i, E, B>; + type Param<'i> = On<'i, 'i, E>; + type Inner<'i> = On<'i, 'i, E>; fn wrap(this: Self::Inner<'_>) -> Self::Param<'_> { this diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 91f7ab26329f5..d2854fe85b1b0 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -1968,14 +1968,14 @@ mod tests { schedule.add_systems(|_query: Query<&Name>| todo!()); schedule.add_systems(|_query: Query<&Name>| -> () { todo!() }); - fn obs(_event: On) { + fn obs(_event: On>) { todo!() } world.add_observer(obs); - world.add_observer(|_event: On| {}); - world.add_observer(|_event: On| todo!()); - world.add_observer(|_event: On| -> () { todo!() }); + world.add_observer(|_event: On>| {}); + world.add_observer(|_event: On>| todo!()); + world.add_observer(|_event: On>| -> () { todo!() }); fn my_command(_world: &mut World) { todo!() diff --git a/crates/bevy_ecs/src/system/observer_system.rs b/crates/bevy_ecs/src/system/observer_system.rs index fe612a3d8bc53..afbdd8aefdd3c 100644 --- a/crates/bevy_ecs/src/system/observer_system.rs +++ b/crates/bevy_ecs/src/system/observer_system.rs @@ -1,19 +1,15 @@ -use crate::{ - event::Event, - prelude::{Bundle, On}, - system::System, -}; +use crate::{event::EventPattern, prelude::On, system::System}; use super::IntoSystem; /// Implemented for [`System`]s that have [`On`] as the first argument. -pub trait ObserverSystem: - System, Out = Out> + Send + 'static +pub trait ObserverSystem: + System, Out = Out> + Send + 'static { } -impl ObserverSystem for T where - T: System, Out = Out> + Send + 'static +impl ObserverSystem for T where + T: System, Out = Out> + Send + 'static { } @@ -29,20 +25,19 @@ impl ObserverSystem for T where label = "the trait `IntoObserverSystem` is not implemented", note = "for function `ObserverSystem`s, ensure the first argument is `On` and any subsequent ones are `SystemParam`" )] -pub trait IntoObserverSystem: Send + 'static { +pub trait IntoObserverSystem: Send + 'static { /// The type of [`System`] that this instance converts into. - type System: ObserverSystem; + type System: ObserverSystem; /// Turns this value into its corresponding [`System`]. fn into_system(this: Self) -> Self::System; } -impl IntoObserverSystem for S +impl IntoObserverSystem for S where - S: IntoSystem, Out, M> + Send + 'static, - S::System: ObserverSystem, + S: IntoSystem, Out, M> + Send + 'static, + S::System: ObserverSystem, E: 'static, - B: Bundle, { type System = S::System; diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index 12e64f9c7611d..f9a9f54ba42b6 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -8,7 +8,7 @@ use crate::{ component::{ComponentId, Mutable}, entity::Entity, event::{EntityComponentsTrigger, Event, EventKey, Trigger}, - lifecycle::{Discard, HookContext, Insert, DISCARD, INSERT}, + lifecycle::{DiscardEvent, HookContext, InsertEvent, DISCARD, INSERT}, message::{Message, MessageId, Messages, WriteBatchIds}, observer::TriggerContext, prelude::{Component, QueryState}, @@ -176,7 +176,7 @@ impl<'w> DeferredWorld<'w> { // SAFETY: the DISCARD event_key corresponds to the Discard event's type self.trigger_raw( DISCARD, - &mut Discard { entity }, + &mut DiscardEvent { entity }, &mut EntityComponentsTrigger { components: &[component_id], old_archetype: Some(archetype), @@ -221,7 +221,7 @@ impl<'w> DeferredWorld<'w> { // SAFETY: the INSERT event_key corresponds to the Insert event's type self.trigger_raw( INSERT, - &mut Insert { entity }, + &mut InsertEvent { entity }, &mut EntityComponentsTrigger { components: &[component_id], old_archetype: Some(archetype), diff --git a/crates/bevy_ecs/src/world/entity_access/mod.rs b/crates/bevy_ecs/src/world/entity_access/mod.rs index a4a63e76d2cf3..33476a35c6690 100644 --- a/crates/bevy_ecs/src/world/entity_access/mod.rs +++ b/crates/bevy_ecs/src/world/entity_access/mod.rs @@ -1168,7 +1168,7 @@ mod tests { #[should_panic] fn location_on_despawned_entity_panics() { let mut world = World::new(); - world.add_observer(|add: On, mut commands: Commands| { + world.add_observer(|add: On>, mut commands: Commands| { commands.entity(add.entity).despawn(); }); let entity = world.spawn_empty().id(); @@ -1188,10 +1188,10 @@ mod tests { fn archetype_modifications_trigger_flush() { let mut world = World::new(); world.insert_resource(TestFlush(0)); - world.add_observer(|_: On, mut commands: Commands| { + world.add_observer(|_: On>, mut commands: Commands| { commands.queue(count_flush); }); - world.add_observer(|_: On, mut commands: Commands| { + world.add_observer(|_: On>, mut commands: Commands| { commands.queue(count_flush); }); @@ -1262,19 +1262,19 @@ mod tests { .push("OrdA hook on_remove"); } - fn ord_a_observer_on_add(_event: On, mut res: ResMut) { + fn ord_a_observer_on_add(_event: On>, mut res: ResMut) { res.0.push("OrdA observer on_add"); } - fn ord_a_observer_on_insert(_event: On, mut res: ResMut) { + fn ord_a_observer_on_insert(_event: On>, mut res: ResMut) { res.0.push("OrdA observer on_insert"); } - fn ord_a_observer_on_discard(_event: On, mut res: ResMut) { + fn ord_a_observer_on_discard(_event: On>, mut res: ResMut) { res.0.push("OrdA observer on_discard"); } - fn ord_a_observer_on_remove(_event: On, mut res: ResMut) { + fn ord_a_observer_on_remove(_event: On>, mut res: ResMut) { res.0.push("OrdA observer on_remove"); } @@ -1313,19 +1313,19 @@ mod tests { .push("OrdB hook on_remove"); } - fn ord_b_observer_on_add(_event: On, mut res: ResMut) { + fn ord_b_observer_on_add(_event: On>, mut res: ResMut) { res.0.push("OrdB observer on_add"); } - fn ord_b_observer_on_insert(_event: On, mut res: ResMut) { + fn ord_b_observer_on_insert(_event: On>, mut res: ResMut) { res.0.push("OrdB observer on_insert"); } - fn ord_b_observer_on_discard(_event: On, mut res: ResMut) { + fn ord_b_observer_on_discard(_event: On>, mut res: ResMut) { res.0.push("OrdB observer on_discard"); } - fn ord_b_observer_on_remove(_event: On, mut res: ResMut) { + fn ord_b_observer_on_remove(_event: On>, mut res: ResMut) { res.0.push("OrdB observer on_remove"); } @@ -1621,10 +1621,10 @@ mod tests { world.insert_resource(Tracker { a: false, b: false }); let entity = world.spawn(A).id(); - world.add_observer(|_: On, mut tracker: ResMut| { + world.add_observer(|_: On>, mut tracker: ResMut| { tracker.a = true; }); - world.add_observer(|_: On, mut tracker: ResMut| { + world.add_observer(|_: On>, mut tracker: ResMut| { tracker.b = true; }); diff --git a/crates/bevy_ecs/src/world/entity_access/world_mut.rs b/crates/bevy_ecs/src/world/entity_access/world_mut.rs index 658cc161482da..b51f8a9e2d8b6 100644 --- a/crates/bevy_ecs/src/world/entity_access/world_mut.rs +++ b/crates/bevy_ecs/src/world/entity_access/world_mut.rs @@ -7,7 +7,7 @@ use crate::{ component::{Component, ComponentId, Components, Mutable, StorageType}, entity::{Entity, EntityCloner, EntityClonerBuilder, EntityLocation, OptIn, OptOut}, event::{EntityComponentsTrigger, EntityEvent}, - lifecycle::{Despawn, Discard, Remove, DESPAWN, DISCARD, REMOVE}, + lifecycle::{DespawnEvent, DiscardEvent, RemoveEvent, DESPAWN, DISCARD, REMOVE}, observer::IntoEntityObserver, query::{ has_conflicts, DebugCheckedUnwrap, QueryAccessError, ReadOnlyQueryData, @@ -1648,7 +1648,7 @@ impl<'w> EntityWorldMut<'w> { // SAFETY: the DESPAWN event_key corresponds to the Despawn event's type deferred_world.trigger_raw( DESPAWN, - &mut Despawn { + &mut DespawnEvent { entity: self.entity, }, &mut EntityComponentsTrigger { @@ -1669,7 +1669,7 @@ impl<'w> EntityWorldMut<'w> { // SAFETY: the DISCARD event_key corresponds to the Discard event's type deferred_world.trigger_raw( DISCARD, - &mut Discard { + &mut DiscardEvent { entity: self.entity, }, &mut EntityComponentsTrigger { @@ -1691,7 +1691,7 @@ impl<'w> EntityWorldMut<'w> { // SAFETY: the REMOVE event_key corresponds to the Remove event's type deferred_world.trigger_raw( REMOVE, - &mut Remove { + &mut RemoveEvent { entity: self.entity, }, &mut EntityComponentsTrigger { diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 89ece4a89b1c9..1f185aecef1ed 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -51,10 +51,12 @@ use crate::{ entity::{Entities, Entity, EntityAllocator, EntityNotSpawnedError, SpawnError}, entity_disabling::DefaultQueryFilters, error::{ErrorHandler, FallbackErrorHandler}, - lifecycle::{ComponentHooks, RemovedComponentMessages, ADD, DESPAWN, DISCARD, INSERT, REMOVE}, + lifecycle::{ + AddEvent, ComponentHooks, DespawnEvent, DiscardEvent, InsertEvent, RemoveEvent, + RemovedComponentMessages, ADD, DESPAWN, DISCARD, INSERT, REMOVE, + }, message::{Message, MessageId, Messages, WriteBatchIds}, observer::Observers, - prelude::{Add, Despawn, Discard, Insert, Remove}, query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState}, relationship::RelationshipHookMode, resource::{IsResource, Resource, ResourceEntities, IS_RESOURCE}, @@ -160,19 +162,19 @@ impl World { #[inline] fn bootstrap(&mut self) { // The order that we register these events is vital to ensure that the constants are correct! - let on_add = self.register_event_key::(); + let on_add = self.register_event_key::(); assert_eq!(ADD, on_add); - let on_insert = self.register_event_key::(); + let on_insert = self.register_event_key::(); assert_eq!(INSERT, on_insert); - let on_discard = self.register_event_key::(); + let on_discard = self.register_event_key::(); assert_eq!(DISCARD, on_discard); - let on_remove = self.register_event_key::(); + let on_remove = self.register_event_key::(); assert_eq!(REMOVE, on_remove); - let on_despawn = self.register_event_key::(); + let on_despawn = self.register_event_key::(); assert_eq!(DESPAWN, on_despawn); let is_resource = self.register_component::(); diff --git a/crates/bevy_feathers/src/alpha_pattern.rs b/crates/bevy_feathers/src/alpha_pattern.rs index 9ea904e67331e..459179423d965 100644 --- a/crates/bevy_feathers/src/alpha_pattern.rs +++ b/crates/bevy_feathers/src/alpha_pattern.rs @@ -43,7 +43,7 @@ pub(crate) struct AlphaPattern; /// Observer to fill in the material handle (since we don't have access to the materials asset /// in the template) fn on_add_alpha_pattern( - add: On, + add: On>, mut q_material_node: Query<&mut MaterialNode>, r_material: Res, ) { diff --git a/crates/bevy_feathers/src/font_styles.rs b/crates/bevy_feathers/src/font_styles.rs index cf2e316c32009..949f9d6e079bd 100644 --- a/crates/bevy_feathers/src/font_styles.rs +++ b/crates/bevy_feathers/src/font_styles.rs @@ -31,7 +31,7 @@ pub struct InheritableFont { /// An observer which looks for changes to the [`InheritableFont`] component on an entity, and /// propagates downward the font to all participating text entities. pub(crate) fn on_changed_font( - insert: On, + insert: On>, font_style: Query<&InheritableFont>, mut commands: Commands, ) { diff --git a/crates/bevy_feathers/src/theme.rs b/crates/bevy_feathers/src/theme.rs index 2dbd11f083493..c7373ab254986 100644 --- a/crates/bevy_feathers/src/theme.rs +++ b/crates/bevy_feathers/src/theme.rs @@ -151,7 +151,7 @@ pub(crate) fn update_theme( } pub(crate) fn on_changed_background( - insert: On, + insert: On>, mut q_background: Query< (&mut BackgroundColor, &ThemeBackgroundColor), Changed, @@ -165,7 +165,7 @@ pub(crate) fn on_changed_background( } pub(crate) fn on_changed_border( - insert: On, + insert: On>, mut q_border: Query<(&mut BorderColor, &ThemeBorderColor), Changed>, theme: Res, ) { @@ -176,7 +176,7 @@ pub(crate) fn on_changed_border( } pub(crate) fn on_changed_text_color( - insert: On, + insert: On>, mut q_span: Query<(&mut TextColor, &ThemeTextColor), Changed>, theme: Res, ) { @@ -189,7 +189,7 @@ pub(crate) fn on_changed_text_color( /// An observer which looks for changes to the [`InheritableThemeTextColor`] component on an entity, /// and propagates downward the text color to all participating text entities. pub(crate) fn on_changed_font_color( - insert: On, + insert: On>, font_color: Query<&InheritableThemeTextColor>, theme: Res, mut commands: Commands, diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index fa35677f0f558..2339531869605 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -878,7 +878,7 @@ pub struct DirectionalLightViewEntities(EntityHashMap>); // TODO: using required component pub(crate) fn add_light_view_entities( - add: On, + add: On>, mut commands: Commands, ) { if let Ok(mut v) = commands.get_entity(add.entity) { @@ -887,7 +887,7 @@ pub(crate) fn add_light_view_entities( } pub(crate) fn remove_light_view_entities( - remove: On, + remove: On>, query: Query<&DirectionalLightViewEntities>, mut commands: Commands, ) { @@ -903,7 +903,7 @@ pub(crate) fn remove_light_view_entities( } pub(crate) fn remove_point_and_spot_light_view_entities( - remove: On, + remove: On>, query: Query<&PointAndSpotLightViewEntities>, mut commands: Commands, ) { diff --git a/crates/bevy_render/src/extract_plugin.rs b/crates/bevy_render/src/extract_plugin.rs index 40b5f801bf982..7763becbf3159 100644 --- a/crates/bevy_render/src/extract_plugin.rs +++ b/crates/bevy_render/src/extract_plugin.rs @@ -184,7 +184,7 @@ mod test { render_app.update_schedule = Some(Render.intern()); render_app.world_mut().add_observer( - |event: On, mut commands: Commands| { + |event: On>, mut commands: Commands| { // Simulate data that's not extracted commands .entity(event.entity) diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index cf92577e53326..3005715586455 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -93,12 +93,12 @@ impl Plugin for SyncWorldPlugin { fn build(&self, app: &mut bevy_app::App) { app.init_resource::(); app.add_observer( - |add: On, mut pending: ResMut| { + |add: On>, mut pending: ResMut| { pending.push(EntityRecord::Added(add.entity)); }, ); app.add_observer( - |remove: On, + |remove: On>, mut pending: ResMut, query: Query<&RenderEntity>| { if let Ok(e) = query.get(remove.entity) { @@ -554,12 +554,12 @@ mod tests { main_world.init_resource::(); main_world.add_observer( - |add: On, mut pending: ResMut| { + |add: On>, mut pending: ResMut| { pending.push(EntityRecord::Added(add.entity)); }, ); main_world.add_observer( - |remove: On, + |remove: On>, mut pending: ResMut, query: Query<&RenderEntity>| { if let Ok(e) = query.get(remove.entity) { diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index c3585bd39e738..01b1fb16f353a 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,10 +1,9 @@ use crate::{CachedSceneError, ResolvedScene, SceneList, ScenePatch}; use bevy_asset::{Asset, AssetPath, AssetServer, Assets}; use bevy_ecs::{ - bundle::Bundle, component::Component, error::Result, - event::EntityEvent, + event::{EntityEvent, EventPattern}, name::Name, relationship::Relationship, system::IntoObserverSystem, @@ -470,10 +469,13 @@ impl SceneList for SceneListScope { /// This is typically initialized using the [`on()`] function, which returns an [`OnTemplate`]. /// /// [`Observer`]: bevy_ecs::observer::Observer -pub struct OnTemplate(pub I, pub PhantomData (E, B, M)>); +pub struct OnTemplate(pub I, pub PhantomData (E, M)>); -impl + Clone, E: EntityEvent, B: Bundle, M: 'static> Template - for OnTemplate +impl Template for OnTemplate +where + I: IntoObserverSystem + Clone, + E: EventPattern, + M: 'static, { type Output = (); @@ -487,12 +489,11 @@ impl + Clone, E: EntityEvent, B: Bundle, M: 'stat } } -impl< - I: IntoObserverSystem + Clone + Send + Sync, - E: EntityEvent, - B: Bundle, - M: 'static, - > Scene for OnTemplate +impl Scene for OnTemplate +where + I: IntoObserverSystem + Clone + Send + Sync, + E: EventPattern, + M: 'static, { fn resolve( self, @@ -507,9 +508,12 @@ impl< /// Returns an [`OnTemplate`] that will create an [`Observer`] of a given [`EntityEvent`] on the current [`Scene`] entity. /// /// [`Observer`]: bevy_ecs::observer::Observer -pub fn on, E: EntityEvent, B: Bundle, M: 'static>( - observer: I, -) -> OnTemplate { +pub fn on(observer: I) -> OnTemplate +where + I: IntoObserverSystem, + E: EventPattern, + M: 'static, +{ OnTemplate(observer, PhantomData) } diff --git a/crates/bevy_scene/src/spawn.rs b/crates/bevy_scene/src/spawn.rs index efb4d94fbc26e..1aabaa7cb1ba2 100644 --- a/crates/bevy_scene/src/spawn.rs +++ b/crates/bevy_scene/src/spawn.rs @@ -680,7 +680,7 @@ pub(crate) struct RelatedSceneListSpawn { /// An [`Observer`] system that queues newly added [`ScenePatchInstance`] entities. pub fn on_add_scene_patch_instance( - add: On, + add: On>, mut queued_scenes: ResMut, ) { queued_scenes.new_scene_entities.push(add.entity); diff --git a/crates/bevy_ui/src/interaction_states.rs b/crates/bevy_ui/src/interaction_states.rs index 3fa2387e2ca23..869ce11745e4a 100644 --- a/crates/bevy_ui/src/interaction_states.rs +++ b/crates/bevy_ui/src/interaction_states.rs @@ -18,7 +18,7 @@ use bevy_ecs::{ #[derive(Component, Debug, Clone, Copy, Default)] pub struct InteractionDisabled; -pub(crate) fn on_add_disabled(add: On, mut world: DeferredWorld) { +pub(crate) fn on_add_disabled(add: On>, mut world: DeferredWorld) { let mut entity = world.entity_mut(add.entity); if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_disabled(); @@ -26,7 +26,7 @@ pub(crate) fn on_add_disabled(add: On, mut world: Defe } pub(crate) fn on_remove_disabled( - remove: On, + remove: On>, mut world: DeferredWorld, ) { let mut entity = world.entity_mut(remove.entity); @@ -48,7 +48,7 @@ pub struct Checkable; #[derive(Component, Debug, Clone, Copy, Default)] pub struct Checked; -pub(crate) fn on_add_checkable(add: On, mut world: DeferredWorld) { +pub(crate) fn on_add_checkable(add: On>, mut world: DeferredWorld) { let mut entity = world.entity_mut(add.entity); let checked = entity.get::().is_some(); if let Some(mut accessibility) = entity.get_mut::() { @@ -59,22 +59,22 @@ pub(crate) fn on_add_checkable(add: On, mut world: DeferredWorld } } -pub(crate) fn on_remove_checkable(add: On, mut world: DeferredWorld) { +pub(crate) fn on_remove_checkable(remove: On>, mut world: DeferredWorld) { // Remove the 'toggled' attribute entirely. - let mut entity = world.entity_mut(add.entity); + let mut entity = world.entity_mut(remove.entity); if let Some(mut accessibility) = entity.get_mut::() { accessibility.clear_toggled(); } } -pub(crate) fn on_add_checked(add: On, mut world: DeferredWorld) { +pub(crate) fn on_add_checked(add: On>, mut world: DeferredWorld) { let mut entity = world.entity_mut(add.entity); if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_toggled(accesskit::Toggled::True); } } -pub(crate) fn on_remove_checked(remove: On, mut world: DeferredWorld) { +pub(crate) fn on_remove_checked(remove: On>, mut world: DeferredWorld) { let mut entity = world.entity_mut(remove.entity); if let Some(mut accessibility) = entity.get_mut::() { accessibility.set_toggled(accesskit::Toggled::False); diff --git a/crates/bevy_ui_widgets/src/observe.rs b/crates/bevy_ui_widgets/src/observe.rs index 944d0c198dd21..1fe917428c5aa 100644 --- a/crates/bevy_ui_widgets/src/observe.rs +++ b/crates/bevy_ui_widgets/src/observe.rs @@ -6,28 +6,31 @@ use core::{marker::PhantomData, mem}; use bevy_ecs::{ bundle::{Bundle, DynamicBundle}, - event::EntityEvent, + event::{EntityEvent, EventPattern}, system::IntoObserverSystem, }; /// Helper struct that adds an observer when inserted as a [`Bundle`]. -pub struct AddObserver> { +pub struct AddObserver +where + E: EventPattern, + I: IntoObserverSystem, +{ observer: I, - marker: PhantomData<(E, B, M)>, + marker: PhantomData<(E, M)>, } // SAFETY: Empty method bodies. -unsafe impl< - E: EntityEvent, - B: Bundle, - M: Send + Sync + 'static, - I: IntoObserverSystem + Send + Sync, - > Bundle for AddObserver +unsafe impl Bundle for AddObserver +where + E: EventPattern, + M: Send + Sync + 'static, + I: IntoObserverSystem + Send + Sync, { #[inline] fn component_ids( _components: &mut bevy_ecs::component::ComponentsRegistrator, - ) -> impl Iterator + use { + ) -> impl Iterator + use { // SAFETY: Empty iterator core::iter::empty() } @@ -41,8 +44,10 @@ unsafe impl< } } -impl> DynamicBundle - for AddObserver +impl DynamicBundle for AddObserver +where + E: EventPattern, + I: IntoObserverSystem, { type Effect = Self; @@ -74,9 +79,11 @@ impl> DynamicBundle } /// Adds an observer as a bundle effect. -pub fn observe>( - observer: I, -) -> AddObserver { +pub fn observe(observer: I) -> AddObserver +where + E: EventPattern, + I: IntoObserverSystem, +{ AddObserver { observer, marker: PhantomData, diff --git a/crates/bevy_ui_widgets/src/slider.rs b/crates/bevy_ui_widgets/src/slider.rs index 5fa9a7ab29529..0ae55be319289 100644 --- a/crates/bevy_ui_widgets/src/slider.rs +++ b/crates/bevy_ui_widgets/src/slider.rs @@ -591,7 +591,7 @@ fn slider_on_key_input( } } -pub(crate) fn slider_on_insert(insert: On, mut world: DeferredWorld) { +pub(crate) fn slider_on_insert(insert: On>, mut world: DeferredWorld) { let mut entity = world.entity_mut(insert.entity); let orientation = entity .get::() @@ -606,7 +606,7 @@ pub(crate) fn slider_on_insert(insert: On, mut world: DeferredWo } } -pub(crate) fn slider_on_insert_value(insert: On, mut world: DeferredWorld) { +pub(crate) fn slider_on_insert_value(insert: On>, mut world: DeferredWorld) { let mut entity = world.entity_mut(insert.entity); let value = entity.get::().unwrap().0; if let Some(mut accessibility) = entity.get_mut::() { @@ -614,7 +614,7 @@ pub(crate) fn slider_on_insert_value(insert: On, mut world: } } -pub(crate) fn slider_on_insert_range(insert: On, mut world: DeferredWorld) { +pub(crate) fn slider_on_insert_range(insert: On>, mut world: DeferredWorld) { let mut entity = world.entity_mut(insert.entity); let range = *entity.get::().unwrap(); if let Some(mut accessibility) = entity.get_mut::() { @@ -623,7 +623,7 @@ pub(crate) fn slider_on_insert_range(insert: On, mut world: } } -pub(crate) fn slider_on_insert_step(insert: On, mut world: DeferredWorld) { +pub(crate) fn slider_on_insert_step(insert: On>, mut world: DeferredWorld) { let mut entity = world.entity_mut(insert.entity); let step = entity.get::().unwrap().0; if let Some(mut accessibility) = entity.get_mut::() { diff --git a/crates/bevy_winit/src/cursor/mod.rs b/crates/bevy_winit/src/cursor/mod.rs index 85c56f6ff1114..cdf5093169e13 100644 --- a/crates/bevy_winit/src/cursor/mod.rs +++ b/crates/bevy_winit/src/cursor/mod.rs @@ -221,7 +221,7 @@ fn update_cursors( } /// Resets the cursor to the default icon when `CursorIcon` is removed. -fn on_remove_cursor_icon(remove: On, mut commands: Commands) { +fn on_remove_cursor_icon(remove: On>, mut commands: Commands) { // Use `try_insert` to avoid panic if the window is being destroyed. commands .entity(remove.entity) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index faa10523355a0..60ddf19357df7 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -148,7 +148,7 @@ impl Plugin for WinitPlugin { app.add_plugins(cursor::WinitCursorPlugin); app.add_observer( - |_window: On, event_loop_proxy: Res| -> Result { + |_window: On>, event_loop_proxy: Res| -> Result { event_loop_proxy.send_event(WinitUserEvent::WindowAdded)?; Ok(()) diff --git a/examples/ecs/observers.rs b/examples/ecs/observers.rs index 2f828a2e5792a..ba60d25601780 100644 --- a/examples/ecs/observers.rs +++ b/examples/ecs/observers.rs @@ -139,7 +139,7 @@ fn setup(mut commands: Commands) { commands.spawn(observer); } -fn on_add_mine(add: On, query: Query<&Mine>, mut index: ResMut) { +fn on_add_mine(add: On>, query: Query<&Mine>, mut index: ResMut) { let mine = query.get(add.entity).unwrap(); let tile = ( (mine.pos.x / CELL_SIZE).floor() as i32, @@ -149,7 +149,7 @@ fn on_add_mine(add: On, query: Query<&Mine>, mut index: ResMut, query: Query<&Mine>, mut index: ResMut) { +fn on_remove_mine(remove: On>, query: Query<&Mine>, mut index: ResMut) { let mine = query.get(remove.entity).unwrap(); let tile = ( (mine.pos.x / CELL_SIZE).floor() as i32, diff --git a/examples/ecs/removal_detection.rs b/examples/ecs/removal_detection.rs index 1216c53df37cb..fe65502a1ddd1 100644 --- a/examples/ecs/removal_detection.rs +++ b/examples/ecs/removal_detection.rs @@ -48,7 +48,7 @@ fn remove_component( } } -fn react_on_removal(remove: On, mut query: Query<&mut Sprite>) { +fn react_on_removal(remove: On>, mut query: Query<&mut Sprite>) { // The `Remove` event was automatically triggered for the `Entity` that had its `MyComponent` removed. if let Ok(mut sprite) = query.get_mut(remove.entity) { sprite.color = Color::srgb(0.5, 1., 1.); diff --git a/examples/ui/widgets/standard_widgets_observers.rs b/examples/ui/widgets/standard_widgets_observers.rs index 56eb42b9336a4..6fb1ee198bc56 100644 --- a/examples/ui/widgets/standard_widgets_observers.rs +++ b/examples/ui/widgets/standard_widgets_observers.rs @@ -6,6 +6,7 @@ use bevy::{ color::palettes::basic::*, + ecs::{event::EventPattern, lifecycle::RemoveEvent}, input_focus::tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, picking::hover::Hovered, prelude::*, @@ -22,21 +23,21 @@ fn main() { .add_plugins((DefaultPlugins, TabNavigationPlugin)) .insert_resource(DemoWidgetStates { slider_value: 50.0 }) .add_systems(Startup, setup) - .add_observer(button_on_interaction::) - .add_observer(button_on_interaction::) - .add_observer(button_on_interaction::) - .add_observer(button_on_interaction::) - .add_observer(button_on_interaction::) - .add_observer(slider_on_interaction::) - .add_observer(slider_on_interaction::) - .add_observer(slider_on_interaction::) + .add_observer(button_on_interaction::>) + .add_observer(button_on_interaction::>) + .add_observer(button_on_interaction::>) + .add_observer(button_on_interaction::>) + .add_observer(button_on_interaction::>) + .add_observer(slider_on_interaction::>) + .add_observer(slider_on_interaction::>) + .add_observer(slider_on_interaction::>) .add_observer(slider_on_change_value::) .add_observer(slider_on_change_value::) - .add_observer(checkbox_on_interaction::) - .add_observer(checkbox_on_interaction::) - .add_observer(checkbox_on_interaction::) - .add_observer(checkbox_on_interaction::) - .add_observer(checkbox_on_interaction::) + .add_observer(checkbox_on_interaction::>) + .add_observer(checkbox_on_interaction::>) + .add_observer(checkbox_on_interaction::>) + .add_observer(checkbox_on_interaction::>) + .add_observer(checkbox_on_interaction::>) .add_systems(Update, (update_widget_values, toggle_disabled)) .run(); } @@ -150,8 +151,8 @@ fn button(asset_server: &AssetServer) -> impl Bundle { ) } -fn button_on_interaction( - event: On, +fn button_on_interaction>( + event: On, mut buttons: Query< ( &Hovered, @@ -177,8 +178,9 @@ fn button_on_interaction( let hovered = hovered.get(); // These "removal event checks" exist because the `Remove` event is triggered _before_ the component is actually // removed, meaning it still shows up in the query. We're investigating the best way to improve this scenario. - let pressed = pressed && !(E::is::() && C::is::()); - let disabled = disabled && !(E::is::() && C::is::()); + let pressed = pressed && !(E::Event::is::() && E::Components::is::()); + let disabled = disabled + && !(E::Event::is::() && E::Components::is::()); match (disabled, hovered, pressed) { // Disabled button (true, _, _) => { @@ -276,8 +278,8 @@ fn slider(min: f32, max: f32, value: f32) -> impl Bundle { ) } -fn slider_on_interaction( - event: On, +fn slider_on_interaction>( + event: On, sliders: Query<(Entity, &Hovered, Has), With>, children: Query<&Children>, mut thumbs: Query<(&mut BackgroundColor, Has), Without>, @@ -285,7 +287,8 @@ fn slider_on_interaction( if let Ok((slider_ent, hovered, disabled)) = sliders.get(event.event_target()) { // These "removal event checks" exist because the `Remove` event is triggered _before_ the component is actually // removed, meaning it still shows up in the query. We're investigating the best way to improve this scenario. - let disabled = disabled && !(E::is::() && C::is::()); + let disabled = disabled + && !(E::Event::is::() && E::Components::is::()); for child in children.iter_descendants(slider_ent) { if let Ok((mut thumb_bg, is_thumb)) = thumbs.get_mut(child) && is_thumb @@ -297,7 +300,7 @@ fn slider_on_interaction( } fn slider_on_change_value( - insert: On, + insert: On>, sliders: Query<(Entity, &SliderValue, &SliderRange), With>, children: Query<&Children>, mut thumbs: Query<(&mut Node, Has), Without>, @@ -380,8 +383,8 @@ fn checkbox(asset_server: &AssetServer, caption: &str) -> impl Bundle { ) } -fn checkbox_on_interaction( - event: On, +fn checkbox_on_interaction>( + event: On, checkboxes: Query< (&Hovered, Has, Has, &Children), With, @@ -393,8 +396,9 @@ fn checkbox_on_interaction( let hovered = hovered.get(); // These "removal event checks" exist because the `Remove` event is triggered _before_ the component is actually // removed, meaning it still shows up in the query. We're investigating the best way to improve this scenario. - let checked = checked && !(E::is::() && C::is::()); - let disabled = disabled && !(E::is::() && C::is::()); + let checked = checked && !(E::Event::is::() && E::Components::is::()); + let disabled = disabled + && !(E::Event::is::() && E::Components::is::()); let Some(border_id) = children.first() else { return;