use std::any::type_name;
use bevy::ecs::entity::Entities;
use bevy::prelude::*;
use bevy_cobweb::prelude::*;
use smallvec::SmallVec;
use smol_str::SmolStr;
use crate::prelude::*;
use crate::sickle::*;
fn revert_attributes(
In(entity): In<Entity>,
mut c: Commands,
parents: Query<&ChildOf>,
entities: &Entities,
mut control_maps: Query<&mut ControlMap>,
)
{
if !entities.contains(entity) {
return;
}
if let Ok(mut control_map) = control_maps.get_mut(entity) {
if control_map.is_anonymous() {
c.entity(entity).remove::<ControlMap>();
} else {
control_map.remove(entity);
}
return;
}
for ancestor in parents.iter_ancestors(entity) {
let Ok(mut control_map) = control_maps.get_mut(ancestor) else { continue };
if control_map.is_anonymous() {
continue;
}
control_map.remove(entity);
return;
}
}
impl<T> StaticAttribute for Splat<T>
where
T: Splattable + StaticAttribute,
{
type Value = T::Splat;
fn construct(value: Self::Value) -> Self
{
Self(value)
}
}
impl<T> ResponsiveAttribute for Splat<T> where T: Splattable + ResponsiveAttribute {}
impl<T> AnimatedAttribute for Splat<T>
where
T: Splattable + AnimatedAttribute,
<T as Splattable>::Splat: Lerp,
{
fn get_value(entity: Entity, world: &World) -> Option<T::Splat>
{
let val = T::get_value(entity, world)?;
<T as Splattable>::splat_value(T::construct(val))
}
}
#[derive(Reflect, Default, Debug, Clone, PartialEq)]
pub struct Static<T: StaticAttribute>
{
#[reflect(default)]
pub name: Option<SmolStr>,
#[reflect(default)]
pub state: Option<SmallVec<[PseudoState; 3]>>,
pub value: T::Value,
}
impl<T: StaticAttribute> Instruction for Static<T>
{
fn apply(self, entity: Entity, world: &mut World)
{
let Ok(mut emut) = world.get_entity_mut(entity) else { return };
let attr = NodeAttribute::new_static::<T>(self.name, self.value);
if let Some(mut attrs) = emut.get_mut::<NodeAttributes>() {
if let Some(_) = attrs.insert(self.state, attr) {
tracing::warn!("overwriting attribute {} on {:?}", type_name::<Self>(), entity);
}
} else {
let mut attrs = NodeAttributes::default();
attrs.insert(self.state, attr);
emut.insert(attrs);
}
}
fn revert(entity: Entity, world: &mut World)
{
T::revert(entity, world);
world.syscall(entity, revert_attributes);
let _ = world.get_entity_mut(entity).map(|mut emut| {
emut.remove::<(NodeAttributes, DynamicStyle)>();
});
}
}
#[derive(Reflect, Default, Debug, Clone, PartialEq)]
pub struct Responsive<T: ResponsiveAttribute>
{
#[reflect(default)]
pub name: Option<SmolStr>,
#[reflect(default)]
pub state: Option<SmallVec<[PseudoState; 3]>>,
#[reflect(default)]
pub respond_to: Option<SmolStr>,
pub idle: T::Value,
#[reflect(default)]
pub hover: Option<T::Value>,
#[reflect(default)]
pub press: Option<T::Value>,
#[reflect(default)]
pub cancel: Option<T::Value>,
}
impl<T: ResponsiveAttribute> Instruction for Responsive<T>
{
fn apply(self, entity: Entity, world: &mut World)
{
let ref_vals = ResponsiveVals::<T::Value> {
idle: self.idle,
hover: self.hover,
press: self.press,
cancel: self.cancel,
};
let Ok(mut emut) = world.get_entity_mut(entity) else { return };
let needs_interactive = emut.get::<ControlMember>().map(|m| &m.id) == self.respond_to.as_ref();
let attr = NodeAttribute::new_responsive::<T>(self.name, self.respond_to, ref_vals);
if let Some(mut attrs) = emut.get_mut::<NodeAttributes>() {
if let Some(_) = attrs.insert(self.state, attr) {
tracing::warn!("overwriting attribute {} on {:?}", type_name::<Self>(), entity);
}
} else {
let mut attrs = NodeAttributes::default();
attrs.insert(self.state, attr);
emut.insert(attrs);
}
if needs_interactive {
Interactive.apply(entity, world);
}
}
fn revert(entity: Entity, world: &mut World)
{
T::revert(entity, world);
world.syscall(entity, revert_attributes);
Interactive::revert(entity, world);
let _ = world.get_entity_mut(entity).map(|mut emut| {
emut.remove::<(NodeAttributes, DynamicStyle)>();
});
}
}
#[derive(Reflect, Default, Debug, Clone, PartialEq)]
pub struct Animated<T: AnimatedAttribute>
{
#[reflect(default)]
pub name: Option<SmolStr>,
#[reflect(default)]
pub state: Option<SmallVec<[PseudoState; 3]>>,
#[reflect(default)]
pub respond_to: Option<SmolStr>,
#[reflect(default)]
pub enter_ref_override: Option<T::Value>,
pub idle: T::Value,
#[reflect(default)]
pub enter_idle_with: Option<AnimationConfig>,
#[reflect(default)]
pub disable_with: Option<AnimationConfig>,
#[reflect(default)]
pub hover: Option<T::Value>,
#[reflect(default)]
pub hover_with: Option<AnimationConfig>,
#[reflect(default)]
pub unhover_with: Option<AnimationConfig>,
#[reflect(default)]
pub press: Option<T::Value>,
#[reflect(default)]
pub press_with: Option<AnimationConfig>,
#[reflect(default)]
pub release_with: Option<AnimationConfig>,
#[reflect(default)]
pub cancel: Option<T::Value>,
#[reflect(default)]
pub cancel_with: Option<AnimationConfig>,
#[reflect(default)]
pub cancel_end_with: Option<AnimationConfig>,
#[reflect(default)]
pub idle_secondary: Option<T::Value>,
#[reflect(default)]
pub idle_loop: Option<LoopedAnimationConfig>,
#[reflect(default)]
pub hover_secondary: Option<T::Value>,
#[reflect(default)]
pub hover_loop: Option<LoopedAnimationConfig>,
#[reflect(default)]
pub press_secondary: Option<T::Value>,
#[reflect(default)]
pub press_loop: Option<LoopedAnimationConfig>,
#[reflect(default)]
pub delete_on_entered: bool,
}
impl<T: AnimatedAttribute> Instruction for Animated<T>
{
fn apply(self, entity: Entity, world: &mut World)
{
let ref_vals = AnimatedVals::<T::Value> {
enter_ref: self.enter_ref_override,
idle: self.idle,
hover: self.hover,
press: self.press,
cancel: self.cancel,
idle_secondary: self.idle_secondary,
hover_secondary: self.hover_secondary,
press_secondary: self.press_secondary,
};
let settings = AnimationSettings {
enter_idle_with: self.enter_idle_with,
hover_with: self.hover_with,
unhover_with: self.unhover_with,
press_with: self.press_with,
release_with: self.release_with,
cancel_with: self.cancel_with,
cancel_end_with: self.cancel_end_with,
disable_with: self.disable_with,
idle_loop: self.idle_loop,
hover_loop: self.hover_loop,
press_loop: self.press_loop,
delete_on_entered: self.delete_on_entered,
};
let Ok(mut emut) = world.get_entity_mut(entity) else { return };
let needs_interactive = emut.get::<ControlMember>().map(|m| &m.id) == self.respond_to.as_ref();
let attr = NodeAttribute::new_animated::<T>(self.name, self.respond_to, ref_vals, settings);
if let Some(mut attrs) = emut.get_mut::<NodeAttributes>() {
if let Some(_) = attrs.insert(self.state, attr) {
tracing::warn!("overwriting attribute {} on {:?}", type_name::<Self>(), entity);
}
} else {
let mut attrs = NodeAttributes::default();
attrs.insert(self.state, attr);
emut.insert(attrs);
}
if needs_interactive {
Interactive.apply(entity, world);
}
}
fn revert(entity: Entity, world: &mut World)
{
T::revert(entity, world);
world.syscall(entity, revert_attributes);
Interactive::revert(entity, world);
let _ = world.get_entity_mut(entity).map(|mut emut| {
emut.remove::<(NodeAttributes, DynamicStyle)>();
});
}
}