use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use bevy_cobweb::prelude::*;
use crate::prelude::*;
use crate::sickle::*;
fn enable_reactor(
event: bevy_cobweb::prelude::EntityEvent<Enable>,
mut c: Commands,
fluxes: Query<&FluxInteraction>,
)
{
let entity = event.entity();
let Ok(mut ec) = c.get_entity(entity) else { return };
ec.add_pseudo_state(PseudoState::Enabled);
ec.remove_pseudo_state(PseudoState::Disabled);
if let Ok(prev_flux) = fluxes.get(entity) {
if *prev_flux == FluxInteraction::Disabled {
ec.insert(FluxInteraction::None);
}
}
}
fn disable_reactor(
event: bevy_cobweb::prelude::EntityEvent<Disable>,
mut c: Commands,
fluxes: Query<(), With<FluxInteraction>>,
)
{
let entity = event.entity();
let Ok(mut ec) = c.get_entity(entity) else { return };
ec.add_pseudo_state(PseudoState::Disabled);
ec.remove_pseudo_state(PseudoState::Enabled);
if let Ok(_) = fluxes.get(entity) {
ec.insert(FluxInteraction::Disabled);
}
}
fn select_reactor(event: bevy_cobweb::prelude::EntityEvent<Select>, mut c: Commands)
{
let entity = event.entity();
let _ = c.get_entity(entity).map(|mut ec| {
ec.add_pseudo_state(PseudoState::Selected);
});
}
fn deselect_reactor(event: bevy_cobweb::prelude::EntityEvent<Deselect>, mut c: Commands)
{
let entity = event.entity();
let _ = c.get_entity(entity).map(|mut ec| {
ec.remove_pseudo_state(PseudoState::Selected);
});
}
fn check_reactor(event: bevy_cobweb::prelude::EntityEvent<Check>, mut c: Commands)
{
let entity = event.entity();
let _ = c.get_entity(entity).map(|mut ec| {
ec.add_pseudo_state(PseudoState::Checked);
});
}
fn uncheck_reactor(event: bevy_cobweb::prelude::EntityEvent<Uncheck>, mut c: Commands)
{
let entity = event.entity();
let _ = c.get_entity(entity).map(|mut ec| {
ec.remove_pseudo_state(PseudoState::Checked);
});
}
fn toggle_check_reactor(
event: bevy_cobweb::prelude::EntityEvent<ToggleCheck>,
mut c: Commands,
ps: PseudoStateParam,
)
{
let entity = event.entity();
if !ps.entity_has(entity, PseudoState::Checked) {
c.react().entity_event(entity, Check);
} else {
c.react().entity_event(entity, Uncheck);
}
}
fn open_reactor(event: bevy_cobweb::prelude::EntityEvent<Open>, mut c: Commands)
{
let entity = event.entity();
let _ = c.get_entity(entity).map(|mut ec| {
ec.add_pseudo_state(PseudoState::Open);
ec.remove_pseudo_state(PseudoState::Closed);
});
}
fn close_reactor(event: bevy_cobweb::prelude::EntityEvent<Close>, mut c: Commands)
{
let entity = event.entity();
let _ = c.get_entity(entity).map(|mut ec| {
ec.add_pseudo_state(PseudoState::Closed);
ec.remove_pseudo_state(PseudoState::Open);
});
}
fn fold_reactor(event: bevy_cobweb::prelude::EntityEvent<Fold>, mut c: Commands)
{
let entity = event.entity();
let _ = c.get_entity(entity).map(|mut ec| {
ec.add_pseudo_state(PseudoState::Folded);
});
}
fn unfold_reactor(event: bevy_cobweb::prelude::EntityEvent<Unfold>, mut c: Commands)
{
let entity = event.entity();
let _ = c.get_entity(entity).map(|mut ec| {
ec.remove_pseudo_state(PseudoState::Folded);
});
}
pub struct Enable;
pub struct Disable;
pub struct Select;
pub struct Deselect;
pub struct Check;
pub struct Uncheck;
pub struct ToggleCheck;
pub struct Open;
pub struct Close;
pub struct Fold;
pub struct Unfold;
pub trait PseudoStateExt
{
fn on_enable<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_disable<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_select<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_deselect<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_check<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_uncheck<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_toggle_check<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_open<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_close<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_fold<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn on_unfold<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self;
fn insert_pseudostate_if<T, C, M>(&mut self, state: PseudoState, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static;
fn enable_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static;
fn select_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static;
fn check_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static;
fn open_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static;
fn fold_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static;
}
impl PseudoStateExt for UiBuilder<'_, Entity>
{
fn on_enable<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Enable>().r(callback);
self
}
fn on_disable<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Disable>().r(callback);
self
}
fn on_select<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Select>().r(callback);
self
}
fn on_deselect<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Deselect>().r(callback);
self
}
fn on_check<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Check>().r(callback);
self
}
fn on_uncheck<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Uncheck>().r(callback);
self
}
fn on_toggle_check<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<ToggleCheck>().r(callback);
self
}
fn on_open<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Open>().r(callback);
self
}
fn on_close<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Close>().r(callback);
self
}
fn on_fold<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Fold>().r(callback);
self
}
fn on_unfold<R: CobwebResult, M>(
&mut self,
callback: impl IntoSystem<(), R, M> + Send + Sync + 'static,
) -> &mut Self
{
self.on_event::<Unfold>().r(callback);
self
}
fn insert_pseudostate_if<T, C, M>(&mut self, state: PseudoState, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static,
{
let mut system = RawCallbackSystem::new(callback);
self.update_on(triggers, move |id: TargetId, w: &mut World| {
let r = system.run(w, *id);
w.syscall(
(*id, r, state.clone()),
|In((id, r, state)): In<(Entity, bool, PseudoState)>, mut c: Commands, ps: PseudoStateParam| {
match r {
true => {
ps.try_insert(&mut c, id, state);
}
false => {
ps.try_remove(&mut c, id, state);
}
}
},
);
})
}
fn enable_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static,
{
let mut system = RawCallbackSystem::new(callback);
self.update_on(triggers, move |id: TargetId, w: &mut World| {
let r = system.run(w, *id);
w.syscall(
(*id, r),
|In((id, r)): In<(Entity, bool)>, mut c: Commands, ps: PseudoStateParam| match r {
true => {
ps.try_enable(&mut c, id);
}
false => {
ps.try_disable(&mut c, id);
}
},
);
})
}
fn select_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static,
{
let mut system = RawCallbackSystem::new(callback);
self.update_on(triggers, move |id: TargetId, w: &mut World| {
let r = system.run(w, *id);
w.syscall(
(*id, r),
|In((id, r)): In<(Entity, bool)>, mut c: Commands, ps: PseudoStateParam| match r {
true => {
ps.try_select(&mut c, id);
}
false => {
ps.try_deselect(&mut c, id);
}
},
);
})
}
fn check_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static,
{
let mut system = RawCallbackSystem::new(callback);
self.update_on(triggers, move |id: TargetId, w: &mut World| {
let r = system.run(w, *id);
w.syscall(
(*id, r),
|In((id, r)): In<(Entity, bool)>, mut c: Commands, ps: PseudoStateParam| match r {
true => {
ps.try_check(&mut c, id);
}
false => {
ps.try_uncheck(&mut c, id);
}
},
);
})
}
fn open_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static,
{
let mut system = RawCallbackSystem::new(callback);
self.update_on(triggers, move |id: TargetId, w: &mut World| {
let r = system.run(w, *id);
w.syscall(
(*id, r),
|In((id, r)): In<(Entity, bool)>, mut c: Commands, ps: PseudoStateParam| match r {
true => {
ps.try_open(&mut c, id);
}
false => {
ps.try_close(&mut c, id);
}
},
);
})
}
fn fold_if<T, C, M>(&mut self, triggers: T, callback: C) -> &mut Self
where
T: ReactionTriggerBundle,
C: IntoSystem<TargetId, bool, M> + Send + Sync + 'static,
{
let mut system = RawCallbackSystem::new(callback);
self.update_on(triggers, move |id: TargetId, w: &mut World| {
let r = system.run(w, *id);
w.syscall(
(*id, r),
|In((id, r)): In<(Entity, bool)>, mut c: Commands, ps: PseudoStateParam| match r {
true => {
ps.try_fold(&mut c, id);
}
false => {
ps.try_unfold(&mut c, id);
}
},
);
})
}
}
#[derive(SystemParam)]
pub struct PseudoStateParam<'w, 's>
{
states: Query<'w, 's, &'static PseudoStates>,
}
impl PseudoStateParam<'_, '_>
{
pub fn entity_has(&self, entity: Entity, req: PseudoState) -> bool
{
let Ok(states) = self.states.get(entity) else { return false };
states.has(&req)
}
pub fn entity_has_any<'a>(&'a self, entity: Entity, req: impl IntoIterator<Item = &'a PseudoState>) -> bool
{
let Ok(states) = self.states.get(entity) else { return false };
req.into_iter().any(|s| states.has(s))
}
pub fn entity_has_all<'a>(&'a self, entity: Entity, req: impl IntoIterator<Item = &'a PseudoState>) -> bool
{
let Ok(states) = self.states.get(entity) else { return false };
req.into_iter().any(|s| !states.has(s))
}
pub fn try_insert(&self, c: &mut Commands, entity: Entity, state: PseudoState) -> bool
{
if self
.states
.get(entity)
.map(|s| s.has(&state))
.unwrap_or(false)
{
return false;
}
c.entity(entity).add_pseudo_state(state);
true
}
pub fn try_remove(&self, c: &mut Commands, entity: Entity, state: PseudoState) -> bool
{
if !self
.states
.get(entity)
.map(|s| s.has(&state))
.unwrap_or(false)
{
return false;
}
c.entity(entity).remove_pseudo_state(state);
true
}
pub fn try_enable(&self, c: &mut Commands, entity: Entity) -> bool
{
if self.entity_has(entity, PseudoState::Enabled) {
return false;
}
c.react().entity_event(entity, Enable);
true
}
pub fn try_disable(&self, c: &mut Commands, entity: Entity) -> bool
{
if self.entity_has(entity, PseudoState::Disabled) {
return false;
}
c.react().entity_event(entity, Disable);
true
}
pub fn try_select(&self, c: &mut Commands, entity: Entity) -> bool
{
if self.entity_has(entity, PseudoState::Selected) {
return false;
}
c.react().entity_event(entity, Select);
true
}
pub fn try_deselect(&self, c: &mut Commands, entity: Entity) -> bool
{
if !self.entity_has(entity, PseudoState::Selected) {
return false;
}
c.react().entity_event(entity, Deselect);
true
}
pub fn try_check(&self, c: &mut Commands, entity: Entity) -> bool
{
if self.entity_has(entity, PseudoState::Checked) {
return false;
}
c.react().entity_event(entity, Check);
true
}
pub fn try_uncheck(&self, c: &mut Commands, entity: Entity) -> bool
{
if !self.entity_has(entity, PseudoState::Checked) {
return false;
}
c.react().entity_event(entity, Uncheck);
true
}
pub fn try_open(&self, c: &mut Commands, entity: Entity) -> bool
{
if self.entity_has(entity, PseudoState::Open) {
return false;
}
c.react().entity_event(entity, Open);
true
}
pub fn try_close(&self, c: &mut Commands, entity: Entity) -> bool
{
if self.entity_has(entity, PseudoState::Closed) {
return false;
}
c.react().entity_event(entity, Close);
true
}
pub fn try_fold(&self, c: &mut Commands, entity: Entity) -> bool
{
if self.entity_has(entity, PseudoState::Folded) {
return false;
}
c.react().entity_event(entity, Fold);
true
}
pub fn try_unfold(&self, c: &mut Commands, entity: Entity) -> bool
{
if !self.entity_has(entity, PseudoState::Folded) {
return false;
}
c.react().entity_event(entity, Unfold);
true
}
}
pub(crate) struct PseudoStatesExtPlugin;
impl Plugin for PseudoStatesExtPlugin
{
fn build(&self, app: &mut App)
{
app.add_reactor(any_entity_event::<Enable>(), enable_reactor);
app.add_reactor(any_entity_event::<Disable>(), disable_reactor);
app.add_reactor(any_entity_event::<Select>(), select_reactor);
app.add_reactor(any_entity_event::<Deselect>(), deselect_reactor);
app.add_reactor(any_entity_event::<Check>(), check_reactor);
app.add_reactor(any_entity_event::<Uncheck>(), uncheck_reactor);
app.add_reactor(any_entity_event::<ToggleCheck>(), toggle_check_reactor);
app.add_reactor(any_entity_event::<Open>(), open_reactor);
app.add_reactor(any_entity_event::<Close>(), close_reactor);
app.add_reactor(any_entity_event::<Fold>(), fold_reactor);
app.add_reactor(any_entity_event::<Unfold>(), unfold_reactor);
}
}