Skip to content

Rename methods and fields to get entities associated with observers to reduce confusion #19609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
14 changes: 7 additions & 7 deletions crates/bevy_core_widgets/src/core_button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn button_on_key_event(
q_state: Query<(&CoreButton, Has<InteractionDisabled>)>,
mut commands: Commands,
) {
if let Ok((bstate, disabled)) = q_state.get(trigger.target().unwrap()) {
if let Ok((bstate, disabled)) = q_state.get(trigger.entity().unwrap()) {
if !disabled {
let event = &trigger.event().input;
if !event.repeat
Expand All @@ -52,7 +52,7 @@ fn button_on_pointer_click(
mut q_state: Query<(&CoreButton, Has<Pressed>, Has<InteractionDisabled>)>,
mut commands: Commands,
) {
if let Ok((bstate, pressed, disabled)) = q_state.get_mut(trigger.target().unwrap()) {
if let Ok((bstate, pressed, disabled)) = q_state.get_mut(trigger.entity().unwrap()) {
trigger.propagate(false);
if pressed && !disabled {
if let Some(on_click) = bstate.on_click {
Expand All @@ -69,7 +69,7 @@ fn button_on_pointer_down(
focus_visible: Option<ResMut<InputFocusVisible>>,
mut commands: Commands,
) {
if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target().unwrap()) {
if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.entity().unwrap()) {
trigger.propagate(false);
if !disabled {
if !pressed {
Expand All @@ -78,7 +78,7 @@ fn button_on_pointer_down(
// Clicking on a button makes it the focused input,
// and hides the focus ring if it was visible.
if let Some(mut focus) = focus {
focus.0 = trigger.target();
focus.0 = trigger.entity();
}
if let Some(mut focus_visible) = focus_visible {
focus_visible.0 = false;
Expand All @@ -92,7 +92,7 @@ fn button_on_pointer_up(
mut q_state: Query<(Entity, Has<InteractionDisabled>, Has<Pressed>), With<CoreButton>>,
mut commands: Commands,
) {
if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target().unwrap()) {
if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.entity().unwrap()) {
trigger.propagate(false);
if !disabled && pressed {
commands.entity(button).remove::<Pressed>();
Expand All @@ -105,7 +105,7 @@ fn button_on_pointer_drag_end(
mut q_state: Query<(Entity, Has<InteractionDisabled>, Has<Pressed>), With<CoreButton>>,
mut commands: Commands,
) {
if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target().unwrap()) {
if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.entity().unwrap()) {
trigger.propagate(false);
if !disabled && pressed {
commands.entity(button).remove::<Pressed>();
Expand All @@ -118,7 +118,7 @@ fn button_on_pointer_cancel(
mut q_state: Query<(Entity, Has<InteractionDisabled>, Has<Pressed>), With<CoreButton>>,
mut commands: Commands,
) {
if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.target().unwrap()) {
if let Ok((button, disabled, pressed)) = q_state.get_mut(trigger.entity().unwrap()) {
trigger.propagate(false);
if !disabled && pressed {
commands.entity(button).remove::<Pressed>();
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,8 @@ let mut world = World::new();
let entity = world.spawn_empty().id();

world.add_observer(|trigger: On<Explode>, mut commands: Commands| {
println!("Entity {} goes BOOM!", trigger.target().unwrap());
commands.entity(trigger.target().unwrap()).despawn();
println!("Entity {} goes BOOM!", trigger.entity().unwrap());
commands.entity(trigger.entity().unwrap()).despawn();
});

world.flush();
Expand Down
48 changes: 28 additions & 20 deletions crates/bevy_ecs/src/observer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
//! Observers can request other data from the world, such as via a [`Query`] or [`Res`].
//! Commonly, you might want to verify that the entity that the observable event is targeting
//! has a specific component, or meets some other condition. [`Query::get`] or [`Query::contains`]
//! on the [`On::target`] entity is a good way to do this.
//! on the entity returned by [`On::entity`] is a good way to do this.
//!
//! [`Commands`] can also be used inside of observers.
//! This can be particularly useful for triggering other observers!
Expand Down Expand Up @@ -214,8 +214,11 @@ impl<'w, E, B: Bundle> On<'w, E, B> {

/// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. It may
/// be [`None`] if the trigger is not for a particular entity.
pub fn target(&self) -> Option<Entity> {
self.trigger.target
///
/// Note that if event bubbling is enabled for your event type, this may not be the entity that the event was originally targeted at.
/// Instead, this is the entity that the event is *currently* targeted at, which may have changed due to propagation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 helpful

pub fn entity(&self) -> Option<Entity> {
Copy link
Contributor

@Jondolf Jondolf Jun 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the intent is to revisit #18710 after this PR, but these properties are so tightly related that I think we kinda need to consider their naming together. If current_target is entity, would target be something like original_entity?

I see that this was bikeshed in #19263, but personally, I'm also not a huge fan of the name entity.

  • It doesn't indicate what entity it is. In this case, there would be three options: the original target, the entity the event was propagated to, and the observer entity. Judging purely based on the entity name, without looking at docs or the other methods, which one is it? To me, this is not self-evident.
  • I would personally expect the "shorter" name, whether entity or target, to be the entity the event was originally targeting, not the entity the event was propagated to. If I trigger an event on A, and it is propagated to B, I would expect that the target was still A, and the event was just forwarded to be read by another observer. I was surprised to see that this is not the case.
  • Using the short name for the current target is opposite to web conventions where target is the original target and currentTarget is the element the event was bubbled to. I'm aware we don't care too much about following web conventions, but just noting that there is a disconnect, and I personally find the web's semantics to be clearer and more generally useful here.

So my personal preference would be to use target and current_target like proposed in #18710. Or current_target could also be something like observed or observer_target, but I'm not sure if that's clearer.

Aside 1: The lack of an easy way to get the original target is the reason I don't support bubbling collision events to rigid bodies in Avian yet. It's important to know which collider the event originally came from, and that cannot be determined with the current observer API.

Aside 2: It feels a bit weird to use a different naming here than in ObserverTrigger, feels like they should be named the same.

self.trigger.current_target
}

/// Returns the components that triggered the observer, out of the
Expand All @@ -226,7 +229,9 @@ impl<'w, E, B: Bundle> On<'w, E, B> {
}

/// Returns the [`Entity`] that observed the triggered event.
/// This allows you to despawn the observer, ceasing observation.
///
/// This is largely an implementation detail,
/// but this information allows you to despawn the observer, ceasing observation.
///
/// # Examples
///
Expand All @@ -247,7 +252,7 @@ impl<'w, E, B: Bundle> On<'w, E, B> {
/// // ...
/// }
/// ```
pub fn observer(&self) -> Entity {
pub fn observer_entity(&self) -> Entity {
self.trigger.observer
}

Expand Down Expand Up @@ -477,12 +482,15 @@ impl ObserverDescriptor {
pub struct ObserverTrigger {
/// The [`Entity`] of the observer handling the trigger.
pub observer: Entity,
/// The [`Event`] the trigger targeted.
/// The underlying [`Event`] type.
pub event_type: ComponentId,
/// The [`ComponentId`]s the trigger targeted.
/// The [`ComponentId`]s that the event targeted, if any.
components: SmallVec<[ComponentId; 2]>,
/// The entity the trigger targeted.
pub target: Option<Entity>,
/// The current entity that the event targeted, if any.
///
/// Note that this may not be the entity that the event was originally targeted at,
/// as it may have been changed by event bubbling.
pub current_target: Option<Entity>,
/// The location of the source code that triggered the observer.
pub caller: MaybeLocation,
}
Expand Down Expand Up @@ -571,7 +579,7 @@ impl Observers {
pub(crate) fn invoke<T>(
mut world: DeferredWorld,
event_type: ComponentId,
target: Option<Entity>,
current_target: Option<Entity>,
components: impl Iterator<Item = ComponentId> + Clone,
data: &mut T,
propagate: &mut bool,
Expand Down Expand Up @@ -599,7 +607,7 @@ impl Observers {
observer,
event_type,
components: components.clone().collect(),
target,
current_target,
caller,
},
data.into(),
Expand All @@ -610,7 +618,7 @@ impl Observers {
observers.map.iter().for_each(&mut trigger_observer);

// Trigger entity observers listening for this kind of trigger
if let Some(target_entity) = target {
if let Some(target_entity) = current_target {
if let Some(map) = observers.entity_observers.get(&target_entity) {
map.iter().for_each(&mut trigger_observer);
}
Expand All @@ -624,7 +632,7 @@ impl Observers {
.iter()
.for_each(&mut trigger_observer);

if let Some(target_entity) = target {
if let Some(target_entity) = current_target {
if let Some(map) = component_observers.entity_map.get(&target_entity) {
map.iter().for_each(&mut trigger_observer);
}
Expand Down Expand Up @@ -1132,20 +1140,20 @@ mod tests {
world.add_observer(
|obs: On<Add, A>, mut res: ResMut<Order>, mut commands: Commands| {
res.observed("add_a");
commands.entity(obs.target().unwrap()).insert(B);
commands.entity(obs.entity().unwrap()).insert(B);
},
);
world.add_observer(
|obs: On<Remove, A>, mut res: ResMut<Order>, mut commands: Commands| {
res.observed("remove_a");
commands.entity(obs.target().unwrap()).remove::<B>();
commands.entity(obs.entity().unwrap()).remove::<B>();
},
);

world.add_observer(
|obs: On<Add, B>, mut res: ResMut<Order>, mut commands: Commands| {
res.observed("add_b");
commands.entity(obs.target().unwrap()).remove::<A>();
commands.entity(obs.entity().unwrap()).remove::<A>();
},
);
world.add_observer(|_: On<Remove, B>, mut res: ResMut<Order>| {
Expand Down Expand Up @@ -1314,7 +1322,7 @@ mod tests {
};
world.spawn_empty().observe(system);
world.add_observer(move |obs: On<EventA>, mut res: ResMut<Order>| {
assert_eq!(obs.target(), None);
assert_eq!(obs.entity(), None);
res.observed("event_a");
});

Expand All @@ -1341,7 +1349,7 @@ mod tests {
.observe(|_: On<EventA>, mut res: ResMut<Order>| res.observed("a_1"))
.id();
world.add_observer(move |obs: On<EventA>, mut res: ResMut<Order>| {
assert_eq!(obs.target().unwrap(), entity);
assert_eq!(obs.entity().unwrap(), entity);
res.observed("a_2");
});

Expand Down Expand Up @@ -1761,7 +1769,7 @@ mod tests {

world.add_observer(
|trigger: On<EventPropagating>, query: Query<&A>, mut res: ResMut<Order>| {
if query.get(trigger.target().unwrap()).is_ok() {
if query.get(trigger.entity().unwrap()).is_ok() {
res.observed("event");
}
},
Expand All @@ -1784,7 +1792,7 @@ mod tests {
fn observer_modifies_relationship() {
fn on_add(trigger: On<Add, A>, mut commands: Commands) {
commands
.entity(trigger.target().unwrap())
.entity(trigger.entity().unwrap())
.with_related_entities::<crate::hierarchy::ChildOf>(|rsc| {
rsc.spawn_empty();
});
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/observer/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// struct Explode;
///
/// world.add_observer(|trigger: On<Explode>, mut commands: Commands| {
/// println!("Entity {} goes BOOM!", trigger.target().unwrap());
/// commands.entity(trigger.target().unwrap()).despawn();
/// println!("Entity {} goes BOOM!", trigger.entity().unwrap());
/// commands.entity(trigger.entity().unwrap()).despawn();
/// });
///
/// world.flush();
Expand Down Expand Up @@ -157,7 +157,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// # struct Explode;
/// world.entity_mut(e1).observe(|trigger: On<Explode>, mut commands: Commands| {
/// println!("Boom!");
/// commands.entity(trigger.target().unwrap()).despawn();
/// commands.entity(trigger.entity().unwrap()).despawn();
/// });
///
/// world.entity_mut(e2).observe(|trigger: On<Explode>, mut commands: Commands| {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/world/deferred_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,14 +739,14 @@ impl<'w> DeferredWorld<'w> {
pub(crate) unsafe fn trigger_observers(
&mut self,
event: ComponentId,
target: Option<Entity>,
current_target: Option<Entity>,
components: impl Iterator<Item = ComponentId> + Clone,
caller: MaybeLocation,
) {
Observers::invoke::<_>(
self.reborrow(),
event,
target,
current_target,
components,
&mut (),
&mut false,
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5749,7 +5749,7 @@ mod tests {
.spawn_empty()
.observe(|trigger: On<TestEvent>, mut commands: Commands| {
commands
.entity(trigger.target().unwrap())
.entity(trigger.entity().unwrap())
.insert(TestComponent(0));
})
.id();
Expand All @@ -5769,7 +5769,7 @@ mod tests {
fn location_on_despawned_entity_panics() {
let mut world = World::new();
world.add_observer(|trigger: On<Add, TestComponent>, mut commands: Commands| {
commands.entity(trigger.target().unwrap()).despawn();
commands.entity(trigger.entity().unwrap()).despawn();
});
let entity = world.spawn_empty().id();
let mut a = world.entity_mut(entity);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_input_focus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ mod tests {
trigger: On<FocusedInput<KeyboardInput>>,
mut query: Query<&mut GatherKeyboardEvents>,
) {
if let Ok(mut gather) = query.get_mut(trigger.target().unwrap()) {
if let Ok(mut gather) = query.get_mut(trigger.entity().unwrap()) {
if let Key::Character(c) = &trigger.input.logical_key {
gather.0.push_str(c.as_str());
}
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ pub(crate) fn add_light_view_entities(
trigger: On<Add, (ExtractedDirectionalLight, ExtractedPointLight)>,
mut commands: Commands,
) {
if let Ok(mut v) = commands.get_entity(trigger.target().unwrap()) {
if let Ok(mut v) = commands.get_entity(trigger.entity().unwrap()) {
v.insert(LightViewEntities::default());
}
}
Expand All @@ -561,7 +561,7 @@ pub(crate) fn extracted_light_removed(
trigger: On<Remove, (ExtractedDirectionalLight, ExtractedPointLight)>,
mut commands: Commands,
) {
if let Ok(mut v) = commands.get_entity(trigger.target().unwrap()) {
if let Ok(mut v) = commands.get_entity(trigger.entity().unwrap()) {
v.try_remove::<LightViewEntities>();
}
}
Expand All @@ -571,7 +571,7 @@ pub(crate) fn remove_light_view_entities(
query: Query<&LightViewEntities>,
mut commands: Commands,
) {
if let Ok(entities) = query.get(trigger.target().unwrap()) {
if let Ok(entities) = query.get(trigger.entity().unwrap()) {
for v in entities.0.values() {
for e in v.iter().copied() {
if let Ok(mut v) = commands.get_entity(e) {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_picking/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ use crate::{
#[reflect(Component, Debug, Clone)]
pub struct Pointer<E: Debug + Clone + Reflect> {
/// The original target of this picking event, before bubbling
pub target: Entity,
pub original_target: Entity,
/// The pointer that triggered this event
pub pointer_id: PointerId,
/// The location of the pointer during this event
Expand Down Expand Up @@ -136,7 +136,7 @@ impl<E: Debug + Clone + Reflect> Pointer<E> {
/// Construct a new `Pointer<E>` event.
pub fn new(id: PointerId, location: Location, target: Entity, event: E) -> Self {
Self {
target,
original_target: target,
pointer_id: id,
pointer_location: location,
event,
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_picking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@
//! // Spawn your entity here, e.g. a `Mesh3d`.
//! // When dragged, mutate the `Transform` component on the dragged target entity:
//! .observe(|trigger: On<Pointer<Drag>>, mut transforms: Query<&mut Transform>| {
//! let mut transform = transforms.get_mut(trigger.target().unwrap()).unwrap();
//! let mut transform = transforms.get_mut(trigger.entity().unwrap()).unwrap();
//! let drag = trigger.event();
//! transform.rotate_local_y(drag.delta.x / 50.0);
//! })
//! .observe(|trigger: On<Pointer<Click>>, mut commands: Commands| {
//! println!("Entity {} goes BOOM!", trigger.target().unwrap());
//! commands.entity(trigger.target().unwrap()).despawn();
//! println!("Entity {} goes BOOM!", trigger.entity().unwrap());
//! commands.entity(trigger.entity().unwrap()).despawn();
//! })
//! .observe(|trigger: On<Pointer<Over>>, mut events: EventWriter<Greeting>| {
//! events.write(Greeting);
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_render/src/sync_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ impl Plugin for SyncWorldPlugin {
app.init_resource::<PendingSyncEntity>();
app.add_observer(
|trigger: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
pending.push(EntityRecord::Added(trigger.target().unwrap()));
pending.push(EntityRecord::Added(trigger.entity().unwrap()));
},
);
app.add_observer(
|trigger: On<Remove, SyncToRenderWorld>,
mut pending: ResMut<PendingSyncEntity>,
query: Query<&RenderEntity>| {
if let Ok(e) = query.get(trigger.target().unwrap()) {
if let Ok(e) = query.get(trigger.entity().unwrap()) {
pending.push(EntityRecord::Removed(*e));
};
},
Expand Down Expand Up @@ -514,14 +514,14 @@ mod tests {

main_world.add_observer(
|trigger: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
pending.push(EntityRecord::Added(trigger.target().unwrap()));
pending.push(EntityRecord::Added(trigger.entity().unwrap()));
},
);
main_world.add_observer(
|trigger: On<Remove, SyncToRenderWorld>,
mut pending: ResMut<PendingSyncEntity>,
query: Query<&RenderEntity>| {
if let Ok(e) = query.get(trigger.target().unwrap()) {
if let Ok(e) = query.get(trigger.entity().unwrap()) {
pending.push(EntityRecord::Removed(*e));
};
},
Expand Down
Loading
Loading