bevy_cobweb/react/
react_component.rs

1//local shortcuts
2use crate::prelude::*;
3
4//third-party shortcuts
5use bevy::prelude::*;
6use bevy::ecs::system::SystemParam;
7
8//standard shortcuts
9use core::ops::Deref;
10use std::any::type_name;
11
12//-------------------------------------------------------------------------------------------------------------------
13
14/// Tag trait for reactive components.
15///
16/// It is not recommended to add `ReactComponent` and `Component` to the same struct, as it will likely cause confusion.
17pub trait ReactComponent: Send + Sync + 'static {}
18
19//-------------------------------------------------------------------------------------------------------------------
20
21/// Component wrapper that enables reacting to component mutations.
22/// - WARNING: It is possible to remove a `React` from one entity and manually insert it to another entity. That WILL
23///            break the react framework. Instead use `react_commands.insert(new_entity, react_component.take());`.
24#[derive(Component)]
25pub struct React<C: ReactComponent>
26{
27    pub(crate) entity    : Entity,
28    pub(crate) component : C,
29}
30
31impl<C: ReactComponent> React<C>
32{
33    /// Constructs the component without setting a valid entity or triggering on-insert reactions.
34    pub fn new_unsafe(component: C) -> Self
35    {
36        Self{ entity: Entity::PLACEHOLDER, component }
37    }
38
39    /// Immutably accesses the component.
40    pub fn get(&self) -> &C
41    {
42        &self.component
43    }
44
45    /// Mutably accesses the component and triggers reactions.
46    pub fn get_mut<'a>(&'a mut self, c: &mut Commands) -> &'a mut C
47    {
48        c.syscall(self.entity, ReactCache::schedule_mutation_reaction::<C>);
49        &mut self.component
50    }
51
52    /// Allows manually triggering mutation reactions when in an exclusive context.
53    pub fn trigger_mutation(entity: Entity, world: &mut World)
54    {
55        world.syscall(entity, ReactCache::schedule_mutation_reaction::<C>);
56    }
57
58    /// Mutably accesses the component without triggering reactions.
59    pub fn get_noreact(&mut self) -> &mut C
60    {
61        &mut self.component
62    }
63
64    /// Sets the component value and triggers mutations only if the value will change.
65    ///
66    /// Returns the previous value if it changed.
67    pub fn set_if_neq(&mut self, c: &mut Commands, new: C) -> Option<C>
68    where
69        C: PartialEq
70    {
71        if new == self.component { return None; }
72
73        c.syscall(self.entity, ReactCache::schedule_mutation_reaction::<C>);
74        let old = std::mem::replace(&mut self.component, new);
75        Some(old)
76    }
77
78    /// Unwrap the `React`.
79    pub fn take(self) -> C
80    {
81        self.component
82    }
83}
84
85impl<C: ReactComponent> Deref for React<C>
86{
87    type Target = C;
88
89    fn deref(&self) -> &C
90    {
91        &self.component
92    }
93}
94
95//-------------------------------------------------------------------------------------------------------------------
96
97/// System parameter for accessing [`React<T>`] components immutably.
98///
99/// See [`ReactiveMut`] for the mutable version.
100#[derive(SystemParam)]
101pub struct Reactive<'w, 's, T: ReactComponent>
102{
103    components: Query<'w, 's, (Entity, &'static React<T>)>,
104}
105
106impl<'w, 's, T: ReactComponent> Reactive<'w, 's, T>
107{
108    /// Reads `T` on `entity`.
109    ///
110    /// Does not trigger reactions.
111    pub fn get(&self, entity: Entity) -> Result<&T, CobwebReactError>
112    {
113        let t = type_name::<T>();
114        self.components.get(entity).map(|(_, c)| c.get()).map_err(|_| CobwebReactError::Reactive(entity, t))
115    }
116
117    /// Reads `T` on a single entity.
118    ///
119    /// Does not trigger reactions.
120    ///
121    /// Panics if the inner query doesn't have exactly one entity.
122    pub fn single(&self) -> (Entity, &T)
123    {
124        let (e, x) = self.components.single().unwrap();
125        (e, x.get())
126    }
127}
128
129//-------------------------------------------------------------------------------------------------------------------
130
131/// System parameter for accessing [`React<T>`] components mutably.
132///
133/// See [`Reactive`] for the immutable version.
134#[derive(SystemParam)]
135pub struct ReactiveMut<'w, 's, T: ReactComponent>
136{
137    components: Query<'w, 's, (Entity, &'static mut React<T>)>,
138}
139
140impl<'w, 's, T: ReactComponent> ReactiveMut<'w, 's, T>
141{
142    /// Reads `T` on `entity`.
143    ///
144    /// Does not trigger reactions.
145    pub fn get(&self, entity: Entity) -> Result<&T, CobwebReactError>
146    {
147        let t = type_name::<T>();
148        self.components.get(entity).map(|(_, c)| c.get()).map_err(|_| CobwebReactError::ReactiveMut(entity, t))
149    }
150
151    /// Reads `T` on a single entity.
152    ///
153    /// Does not trigger reactions.
154    ///
155    /// Panics if the inner query doesn't have exactly one entity.
156    pub fn single(&self) -> (Entity, &T)
157    {
158        let (e, x) = self.components.single().unwrap();
159        (e, x.get())
160    }
161
162    /// Gets a mutable reference to `T` on `entity`.
163    ///
164    /// Triggers mutation reactions.
165    pub fn get_mut(&mut self, c: &mut Commands, entity: Entity) -> Result<&mut T, CobwebReactError>
166    {
167        let t = type_name::<T>();
168        let (_, x) = self.components.get_mut(entity).map_err(|_| CobwebReactError::ReactiveMut(entity, t))?;
169        Ok(x.into_inner().get_mut(c))
170    }
171
172    /// Gets a mutable reference to `T` on a single entity.
173    ///
174    /// Triggers mutation reactions.
175    ///
176    /// Panics if the inner query doesn't have exactly one entity.
177    pub fn single_mut(&mut self, c: &mut Commands) -> (Entity, &mut T)
178    {
179        let (e, x) = self.components.single_mut().unwrap();
180        (e, x.into_inner().get_mut(c))
181    }
182
183    /// Gets a mutable reference to `T` on `entity`.
184    ///
185    /// Does not trigger reactions.
186    pub fn get_noreact(&mut self, entity: Entity) -> Result<&mut T, CobwebReactError>
187    {
188        let t = type_name::<T>();
189        let (_, x) = self.components.get_mut(entity).map_err(|_| CobwebReactError::ReactiveMut(entity, t))?;
190        Ok(x.into_inner().get_noreact())
191    }
192
193    /// Gets a mutable reference to `T` on a single entity
194    ///
195    /// Does not trigger reactions.
196    ///
197    /// Panics if the inner query doesn't have exactly one entity.
198    pub fn single_noreact(&mut self) -> (Entity, &mut T)
199    {
200        let (e, x) = self.components.single_mut().unwrap();
201        (e, x.into_inner().get_noreact())
202    }
203
204    /// Sets a new value on the specified entity if it would change.
205    ///
206    /// Returns the previous value if changed.
207    pub fn set_if_neq(&mut self, c: &mut Commands, entity: Entity, new: T) -> Option<T>
208    where
209        T: PartialEq
210    {
211        let (_, mut x) = self.components.get_mut(entity).ok()?;
212        (*x).set_if_neq(c, new)
213    }
214
215    /// Sets a new value on a single entity if it would change.
216    ///
217    /// Returns the previous value if changed.
218    ///
219    /// Panics if the inner query doesn't have exactly one entity.
220    pub fn set_single_if_not_eq(&mut self, c: &mut Commands, new: T) -> (Entity, Option<T>)
221    where
222        T: PartialEq
223    {
224        let (e, mut x) = self.components.single_mut().unwrap();
225        (e, (*x).set_if_neq(c, new))
226    }
227}
228
229//-------------------------------------------------------------------------------------------------------------------