 ed2b8e0f35
			
		
	
	
		ed2b8e0f35
		
			
		
	
	
	
	
		
			
			# Objective
Add basic bubbling to observers, modeled off `bevy_eventlistener`.
## Solution
- Introduce a new `Traversal` trait for components which point to other
entities.
- Provide a default `TraverseNone: Traversal` component which cannot be
constructed.
- Implement `Traversal` for `Parent`.
- The `Event` trait now has an associated `Traversal` which defaults to
`TraverseNone`.
- Added a field `bubbling: &mut bool` to `Trigger` which can be used to
instruct the runner to bubble the event to the entity specified by the
event's traversal type.
- Added an associated constant `SHOULD_BUBBLE` to `Event` which
configures the default bubbling state.
- Added logic to wire this all up correctly.
Introducing the new associated information directly on `Event` (instead
of a new `BubblingEvent` trait) lets us dispatch both bubbling and
non-bubbling events through the same api.
## Testing
I have added several unit tests to cover the common bugs I identified
during development. Running the unit tests should be enough to validate
correctness. The changes effect unsafe portions of the code, but should
not change any of the safety assertions.
## Changelog
Observers can now bubble up the entity hierarchy! To create a bubbling
event, change your `Derive(Event)` to something like the following:
```rust
#[derive(Component)]
struct MyEvent;
impl Event for MyEvent {
    type Traverse = Parent; // This event will propagate up from child to parent.
    const AUTO_PROPAGATE: bool = true; // This event will propagate by default.
}
```
You can dispatch a bubbling event using the normal
`world.trigger_targets(MyEvent, entity)`.
Halting an event mid-bubble can be done using
`trigger.propagate(false)`. Events with `AUTO_PROPAGATE = false` will
not propagate by default, but you can enable it using
`trigger.propagate(true)`.
If there are multiple observers attached to a target, they will all be
triggered by bubbling. They all share a bubbling state, which can be
accessed mutably using `trigger.propagation_mut()` (`trigger.propagate`
is just sugar for this).
You can choose to implement `Traversal` for your own types, if you want
to bubble along a different structure than provided by `bevy_hierarchy`.
Implementers must be careful never to produce loops, because this will
cause bevy to hang.
## Migration Guide
+ Manual implementations of `Event` should add associated type `Traverse
= TraverseNone` and associated constant `AUTO_PROPAGATE = false`;
+ `Trigger::new` has new field `propagation: &mut Propagation` which
provides the bubbling state.
+ `ObserverRunner` now takes the same `&mut Propagation` as a final
parameter.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Torstein Grindvik <52322338+torsteingrindvik@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
		
	
			
		
			
				
	
	
		
			44 lines
		
	
	
		
			1.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			44 lines
		
	
	
		
			1.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! A trait for components that let you traverse the ECS.
 | |
| 
 | |
| use crate::{
 | |
|     component::{Component, StorageType},
 | |
|     entity::Entity,
 | |
| };
 | |
| 
 | |
| /// A component that can point to another entity, and which can be used to define a path through the ECS.
 | |
| ///
 | |
| /// Traversals are used to [specify the direction] of [event propagation] in [observers]. By default,
 | |
| /// events use the [`TraverseNone`] placeholder component, which cannot actually be created or added to
 | |
| /// an entity and so never causes traversal.
 | |
| ///
 | |
| /// Infinite loops are possible, and are not checked for. While looping can be desirable in some contexts
 | |
| /// (for example, an observer that triggers itself multiple times before stopping), following an infinite
 | |
| /// traversal loop without an eventual exit will can your application to hang. Each implementer of `Traversal`
 | |
| /// for documenting possible looping behavior, and consumers of those implementations are responsible for
 | |
| /// avoiding infinite loops in their code.
 | |
| ///
 | |
| /// [specify the direction]: crate::event::Event::Traversal
 | |
| /// [event propagation]: crate::observer::Trigger::propagate
 | |
| /// [observers]: crate::observer::Observer
 | |
| pub trait Traversal: Component {
 | |
|     /// Returns the next entity to visit.
 | |
|     fn traverse(&self) -> Option<Entity>;
 | |
| }
 | |
| 
 | |
| /// A traversal component that doesn't traverse anything. Used to provide a default traversal
 | |
| /// implementation for events.
 | |
| ///
 | |
| /// It is not possible to actually construct an instance of this component.
 | |
| pub enum TraverseNone {}
 | |
| 
 | |
| impl Traversal for TraverseNone {
 | |
|     #[inline(always)]
 | |
|     fn traverse(&self) -> Option<Entity> {
 | |
|         None
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Component for TraverseNone {
 | |
|     const STORAGE_TYPE: StorageType = StorageType::Table;
 | |
| }
 |