Skip to content

Commit 1e21936

Browse files
committed
Owned callbacks.
1 parent 09ccedd commit 1e21936

File tree

3 files changed

+154
-3
lines changed

3 files changed

+154
-3
lines changed

crates/bevy_core_widgets/src/callback.rs

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use bevy_ecs::system::{Commands, SystemId, SystemInput};
2-
use bevy_ecs::world::{DeferredWorld, World};
1+
use bevy_ecs::component::Component;
2+
use bevy_ecs::lifecycle::HookContext;
3+
use bevy_ecs::system::{Commands, EntityCommands, IntoSystem, SystemId, SystemInput};
4+
use bevy_ecs::world::{DeferredWorld, EntityWorldMut, World};
35

46
/// A callback defines how we want to be notified when a widget changes state. Unlike an event
57
/// or observer, callbacks are intended for "point-to-point" communication that cuts across the
@@ -111,3 +113,100 @@ impl Notify for DeferredWorld<'_> {
111113
}
112114
}
113115
}
116+
117+
/// A component that hangs on to a registered one-shot system, and unregisters it when the
118+
/// component is despawned.
119+
#[derive(Component)]
120+
#[component(on_remove = on_despawn_callback_owner::<I>, storage = "SparseSet")]
121+
pub struct OwnedCallbackSystem<I: SystemInput + Send>(SystemId<I, ()>);
122+
123+
fn on_despawn_callback_owner<I: SystemInput + Send + 'static>(
124+
mut world: DeferredWorld,
125+
context: HookContext,
126+
) {
127+
let system_id = world
128+
.entity(context.entity)
129+
.get::<OwnedCallbackSystem<I>>()
130+
.unwrap()
131+
.0;
132+
world.commands().unregister_system(system_id);
133+
}
134+
135+
/// Methods for registering scoped callbacks.
136+
pub trait RegisterOwnedCallback {
137+
/// Registers a scoped one-shot system, with no input, that will be removed when the parent
138+
/// entity is despawned.
139+
fn register_owned_callback<M, I: IntoSystem<(), (), M> + 'static>(
140+
&mut self,
141+
callback: I,
142+
) -> Callback;
143+
144+
/// Registers a scoped one-shot systemm, with input, that will be removed when the
145+
/// parent entity is despawned.
146+
fn register_owned_callback_with<
147+
M,
148+
A: SystemInput + Send + 'static,
149+
I: IntoSystem<A, (), M> + 'static,
150+
>(
151+
&mut self,
152+
callback: I,
153+
) -> Callback<A>;
154+
}
155+
156+
impl RegisterOwnedCallback for EntityCommands<'_> {
157+
fn register_owned_callback<M, I: IntoSystem<(), (), M> + 'static>(
158+
&mut self,
159+
callback: I,
160+
) -> Callback {
161+
let system_id = self.commands().register_system(callback);
162+
let owner = self.id();
163+
self.commands()
164+
.spawn((OwnedCallbackSystem(system_id), crate::owner::OwnedBy(owner)));
165+
Callback::System(system_id)
166+
}
167+
168+
fn register_owned_callback_with<
169+
M,
170+
A: SystemInput + Send + 'static,
171+
I: IntoSystem<A, (), M> + 'static,
172+
>(
173+
&mut self,
174+
callback: I,
175+
) -> Callback<A> {
176+
let owner = self.id();
177+
let system_id = self.commands().register_system(callback);
178+
self.commands()
179+
.spawn((OwnedCallbackSystem(system_id), crate::owner::OwnedBy(owner)));
180+
Callback::System(system_id)
181+
}
182+
}
183+
184+
impl RegisterOwnedCallback for EntityWorldMut<'_> {
185+
fn register_owned_callback<M, I: IntoSystem<(), (), M> + 'static>(
186+
&mut self,
187+
callback: I,
188+
) -> Callback {
189+
let owner = self.id();
190+
let system_id = self.world_scope(|world| world.register_system(callback));
191+
self.world_scope(|world| {
192+
world.spawn((OwnedCallbackSystem(system_id), crate::owner::OwnedBy(owner)));
193+
});
194+
Callback::System(system_id)
195+
}
196+
197+
fn register_owned_callback_with<
198+
M,
199+
A: SystemInput + Send + 'static,
200+
I: IntoSystem<A, (), M> + 'static,
201+
>(
202+
&mut self,
203+
callback: I,
204+
) -> Callback<A> {
205+
let owner = self.id();
206+
let system_id = self.world_scope(|world| world.register_system(callback));
207+
self.world_scope(|world| {
208+
world.spawn((OwnedCallbackSystem(system_id), crate::owner::OwnedBy(owner)));
209+
});
210+
Callback::System(system_id)
211+
}
212+
}

crates/bevy_core_widgets/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ mod core_checkbox;
2020
mod core_radio;
2121
mod core_scrollbar;
2222
mod core_slider;
23+
pub mod owner;
2324

2425
use bevy_app::{App, Plugin};
2526

26-
pub use callback::{Callback, Notify};
27+
pub use callback::{Callback, Notify, RegisterOwnedCallback};
2728
pub use core_button::{CoreButton, CoreButtonPlugin};
2829
pub use core_checkbox::{CoreCheckbox, CoreCheckboxPlugin, SetChecked, ToggleChecked};
2930
pub use core_radio::{CoreRadio, CoreRadioGroup, CoreRadioGroupPlugin};

crates/bevy_core_widgets/src/owner.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//! Defines relationships for ownership of an entity, with no other inherited semantics.
2+
use core::slice;
3+
4+
use bevy_ecs::{component::Component, entity::Entity};
5+
6+
/// A component that represents the owner of an entity. Ownership only determines lifetime,
7+
/// such that the owned entity will be despawned when its owner is despawned. It does not imply
8+
/// any other kind of semantic connection between the two entities.
9+
// TODO: Consider renaming and/or moving this.
10+
#[derive(Component, Clone, PartialEq, Eq, Debug)]
11+
#[relationship(relationship_target = Owned)]
12+
pub struct OwnedBy(pub Entity);
13+
14+
impl OwnedBy {
15+
/// Return the owned entity.
16+
pub fn get(&self) -> Entity {
17+
self.0
18+
}
19+
}
20+
21+
impl Default for OwnedBy {
22+
fn default() -> Self {
23+
OwnedBy(Entity::PLACEHOLDER)
24+
}
25+
}
26+
27+
/// A component that represents a collection of entities that are owned by another entity.
28+
// #[derive(Component, Default, Reflect)]
29+
// #[reflect(Component)]
30+
#[derive(Component, Default)]
31+
#[relationship_target(relationship = OwnedBy, linked_spawn)]
32+
pub struct Owned(Vec<Entity>);
33+
34+
impl<'a> IntoIterator for &'a Owned {
35+
type Item = <Self::IntoIter as Iterator>::Item;
36+
37+
type IntoIter = slice::Iter<'a, Entity>;
38+
39+
#[inline(always)]
40+
fn into_iter(self) -> Self::IntoIter {
41+
self.0.iter()
42+
}
43+
}
44+
45+
impl core::ops::Deref for Owned {
46+
type Target = [Entity];
47+
48+
fn deref(&self) -> &Self::Target {
49+
&self.0
50+
}
51+
}

0 commit comments

Comments
 (0)