use bevy_ecs::hierarchy::ChildOf;
use bevy_ecs::lifecycle::HookContext;
use bevy_ecs::prelude::*;
use bevy_ecs::world::DeferredWorld;
use tracing::warn;
use super::clip::ClipShape;
use super::components::{SurfaceGraphics, SurfaceGraphicsDirty, VisualGraphics};
#[derive(Component, Debug, Clone, PartialEq)]
#[component(on_add = on_visual_add)]
pub struct Visual {
pub is_visible: bool,
pub opacity: f32,
pub transform_origin: crate::ecs::PointF,
pub clip: Option<ClipShape>,
}
pub fn owner_window_exists(world: &DeferredWorld, entity: Entity) -> bool {
if world.get::<crate::ecs::window::Window>(entity).is_some() {
return true;
}
let mut current = entity;
loop {
let parent = world.get::<ChildOf>(current).map(|c| c.parent());
match parent {
Some(p) => {
if world.get::<crate::ecs::window::Window>(p).is_some() {
return true;
}
current = p;
}
None => return false,
}
}
}
fn on_visual_add(mut world: DeferredWorld, context: HookContext) {
let entity = context.entity;
let needs_arrangement = world
.get::<crate::ecs::layout::Arrangement>(entity)
.is_none();
let needs_brush_inherit = world
.get::<crate::ecs::widget::BrushInherit>(entity)
.is_none();
let has_owner_window = owner_window_exists(&world, entity);
let needs_visual_graphics = has_owner_window && world.get::<VisualGraphics>(entity).is_none();
let needs_surface_graphics = has_owner_window && world.get::<SurfaceGraphics>(entity).is_none();
let needs_surface_dirty =
has_owner_window && world.get::<SurfaceGraphicsDirty>(entity).is_none();
let mut cmds = world.commands();
let mut entity_cmds = cmds.entity(entity);
if needs_arrangement {
entity_cmds.insert(crate::ecs::layout::Arrangement::default());
}
if needs_visual_graphics {
entity_cmds.insert(VisualGraphics::default());
}
if needs_surface_graphics {
entity_cmds.insert(SurfaceGraphics::default());
}
if needs_surface_dirty {
entity_cmds.insert(SurfaceGraphicsDirty::default());
}
if needs_brush_inherit {
entity_cmds.insert(crate::ecs::widget::BrushInherit);
}
}
impl Default for Visual {
fn default() -> Self {
Self {
is_visible: true,
opacity: 1.0,
transform_origin: crate::ecs::PointF::default(),
clip: None,
}
}
}
impl Visual {
pub fn set_opacity(&mut self, value: f32) {
if value < 0.0 || value > 1.0 {
warn!(
value = value,
"Opacity value is outside valid range [0.0, 1.0]"
);
}
self.opacity = value.clamp(0.0, 1.0);
}
pub fn clamped_opacity(&self) -> f32 {
self.opacity.clamp(0.0, 1.0)
}
pub fn set_visible(&mut self, visible: bool) {
self.is_visible = visible;
}
pub fn set_clip(&mut self, clip: Option<ClipShape>) {
self.clip = clip;
}
}