use bevy::prelude::*;
use bevy::ui::widget::ImageNodeSize;
use bevy::ui::ContentSize;
use bevy_cobweb::prelude::*;
use crate::prelude::*;
fn insert_ui_image(
In((entity, img)): In<(Entity, LoadedImageNode)>,
mut commands: Commands,
img_map: Res<ImageMap>,
layout_map: Res<TextureAtlasLayoutMap>,
)
{
let Ok(mut ec) = commands.get_entity(entity) else { return };
let content_size = match img.size {
Some(size) => ContentSize::fixed_size(size),
None => ContentSize::default(),
};
let ui_image = img.to_ui_image(&img_map, &layout_map);
ec.try_insert((ui_image, content_size));
}
#[derive(Reflect, Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LoadedImageNode
{
#[reflect(default)]
pub image: Option<String>,
#[reflect(default)]
pub atlas: Option<TextureAtlasReference>,
#[reflect(default)]
pub mode: Option<LoadedImageMode>,
#[reflect(default = "LoadedImageNode::default_color")]
pub color: Color,
#[reflect(default)]
pub size: Option<Vec2>,
#[reflect(default)]
pub rect: Option<Rect>,
#[reflect(default)]
pub flip_x: bool,
#[reflect(default)]
pub flip_y: bool,
}
impl LoadedImageNode
{
pub fn to_ui_image(self, map: &ImageMap, layout_map: &TextureAtlasLayoutMap) -> ImageNode
{
let texture_atlas = self.atlas.and_then(|a| {
let Some(img) = self.image.as_ref() else {
tracing::warn!("failed setting TextureAtlas in ImageNode when converting LoadedImageNode; the atlas is set but \
the image texture is None");
return None;
};
Some(TextureAtlas {
layout: layout_map.get(img, &a.alias),
index: a.index,
})
});
ImageNode {
color: self.color,
image: self.image.map(|i| map.get(&i)).unwrap_or_default(),
texture_atlas,
flip_x: self.flip_x,
flip_y: self.flip_y,
rect: self.rect,
image_mode: self.mode.unwrap_or_default().into(),
}
}
pub fn default_color() -> Color
{
Color::WHITE
}
}
impl Instruction for LoadedImageNode
{
fn apply(self, entity: Entity, world: &mut World)
{
world.syscall((entity, self), insert_ui_image);
}
fn revert(entity: Entity, world: &mut World)
{
let _ = world.get_entity_mut(entity).map(|mut e| {
e.remove::<(ImageNode, ImageNodeSize, ContentSize)>();
});
}
}
impl StaticAttribute for LoadedImageNode
{
type Value = Self;
fn construct(value: Self::Value) -> Self
{
value
}
}
#[derive(Reflect, Default, Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct ImageNodeColor(pub Color);
impl Instruction for ImageNodeColor
{
fn apply(self, entity: Entity, world: &mut World)
{
let Some(mut img) = world.get_mut::<ImageNode>(entity) else { return };
img.color = self.0;
}
fn revert(entity: Entity, world: &mut World)
{
Instruction::apply(Self(LoadedImageNode::default_color()), entity, world);
}
}
impl StaticAttribute for ImageNodeColor
{
type Value = Color;
fn construct(value: Self::Value) -> Self
{
Self(value)
}
}
impl ResponsiveAttribute for ImageNodeColor {}
impl AnimatedAttribute for ImageNodeColor
{
fn get_value(entity: Entity, world: &World) -> Option<Self::Value>
{
let img = world.get::<ImageNode>(entity)?;
Some(img.color)
}
}
#[derive(Reflect, Default, Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct ImageNodeIndex(pub usize);
impl Instruction for ImageNodeIndex
{
fn apply(self, entity: Entity, world: &mut World)
{
let Some(mut img) = world.get_mut::<ImageNode>(entity) else { return };
img.texture_atlas.as_mut().map(|a| a.index = self.0);
}
fn revert(entity: Entity, world: &mut World)
{
Instruction::apply(Self(0), entity, world);
}
}
impl StaticAttribute for ImageNodeIndex
{
type Value = usize;
fn construct(value: Self::Value) -> Self
{
Self(value)
}
}
impl ResponsiveAttribute for ImageNodeIndex {}
impl AnimatedAttribute for ImageNodeIndex
{
fn get_value(entity: Entity, world: &World) -> Option<Self::Value>
{
let img = world.get::<ImageNode>(entity)?;
let atlas = img.texture_atlas.as_ref()?;
Some(atlas.index)
}
}
pub(crate) struct ImageNodeExtPlugin;
impl Plugin for ImageNodeExtPlugin
{
fn build(&self, app: &mut App)
{
app.register_static::<LoadedImageNode>()
.register_animatable::<ImageNodeColor>()
.register_animatable::<ImageNodeIndex>();
}
}