use super::init::format_entity_name;
use crate::ecs::graphics::{Visual, VisualGraphics};
use crate::ecs::layout::{Arrangement, GlobalArrangement};
use crate::ecs::window::Window;
use bevy_ecs::hierarchy::{ChildOf, Children};
use bevy_ecs::name::Name;
use bevy_ecs::prelude::*;
use tracing::{debug, error};
use windows::core::Interface;
pub fn visual_hierarchy_sync_system(
mut vg_queries: ParamSet<(
// ChildOf + VisualGraphics を持つ全Entity
Query<(Entity, &ChildOf, &mut VisualGraphics, Option<&Name>)>,
// 親エンティティクエリ
Query<(&VisualGraphics, Option<&Name>)>,
)>,
child_of_query: Query<&ChildOf>,
children_query: Query<&Children>,
) {
let mut affected_parents = std::collections::HashSet::new();
let mut parent_depths: std::collections::HashMap<Entity, usize> =
std::collections::HashMap::new();
{
let child_query = vg_queries.p0();
for (_entity, child_of, child_vg, _child_name) in child_query.iter() {
if child_vg.parent_visual().is_none() {
let parent_entity = child_of.parent();
affected_parents.insert(parent_entity);
if !parent_depths.contains_key(&parent_entity) {
let mut depth = 0;
let mut current = parent_entity;
while let Ok(co) = child_of_query.get(current) {
depth += 1;
current = co.parent();
}
parent_depths.insert(parent_entity, depth);
}
}
}
}
if affected_parents.is_empty() {
return;
}
let mut sorted_parents: Vec<Entity> = affected_parents.into_iter().collect();
sorted_parents.sort_by_key(|e| parent_depths.get(e).copied().unwrap_or(0));
debug!(
count = sorted_parents.len(),
"[visual_hierarchy_sync] Processing affected parents (sorted by depth)"
);
for parent_entity in sorted_parents {
let parent_depth = parent_depths.get(&parent_entity).copied().unwrap_or(0);
let (parent_visual, parent_name) = {
let parent_query = vg_queries.p1();
match parent_query.get(parent_entity) {
Ok((pv, name)) => (pv.visual().cloned(), name.map(|n| n.to_string())),
Err(_) => (None, None),
}
};
let parent_fallback = format!("Entity({:?})", parent_entity);
let parent_display = parent_name.as_deref().unwrap_or(&parent_fallback);
if let Some(ref parent_visual) = parent_visual {
let parent_children = match parent_visual
.cast::<windows::UI::Composition::ContainerVisual>()
.and_then(|cv| cv.Children())
{
Ok(c) => c,
Err(e) => {
error!(
parent = %parent_display,
error = ?e,
"[visual_hierarchy_sync] Children() failed"
);
continue;
}
};
let _ = parent_children.RemoveAll();
if let Ok(children) = children_query.get(parent_entity) {
for child_entity in children.iter() {
let mut child_query = vg_queries.p0();
if let Ok((_, _, mut child_vg, child_name)) = child_query.get_mut(child_entity)
{
let child_visual = match child_vg.visual() {
Some(v) => v.clone(),
None => continue,
};
let child_name_str = child_name.map(|n| n.to_string());
let child_fallback = format!("Entity({:?})", child_entity);
let child_display = child_name_str.as_deref().unwrap_or(&child_fallback);
if let Err(e) = parent_children.InsertAtBottom(&child_visual) {
error!(
child = %child_display,
depth = parent_depth + 1,
parent = %parent_display,
error = ?e,
"[visual_hierarchy_sync] InsertAtBottom failed"
);
} else {
debug!(
child = %child_display,
depth = parent_depth + 1,
parent = %parent_display,
"[visual_hierarchy_sync] InsertAtBottom success"
);
}
child_vg.set_parent_visual(Some(parent_visual.clone()));
}
}
}
} else {
if let Ok(children) = children_query.get(parent_entity) {
for child_entity in children.iter() {
let mut child_query = vg_queries.p0();
if let Ok((_, _, mut child_vg, child_name)) = child_query.get_mut(child_entity)
{
if child_vg.parent_visual().is_some() {
continue; }
let child_visual = match child_vg.visual() {
Some(v) => v.clone(),
None => continue,
};
let child_name_str = child_name.map(|n| n.to_string());
let child_fallback = format!("Entity({:?})", child_entity);
let child_display = child_name_str.as_deref().unwrap_or(&child_fallback);
debug!(
name = %child_display,
depth = parent_depth + 1,
"[visual_hierarchy_sync] Visual hierarchy root"
);
child_vg.set_parent_visual(Some(child_visual.clone()));
}
}
}
}
}
}
pub fn visual_property_sync_system(
changed_entities: Query<
(
Entity,
&Arrangement,
&GlobalArrangement,
&Visual,
&VisualGraphics,
Option<&Name>,
Has<Window>,
),
Or<(
Changed<Arrangement>,
Changed<GlobalArrangement>,
Changed<Visual>,
)>,
>,
) {
for (entity, arrangement, global_arrangement, visual_component, vg, name, is_window) in
changed_entities.iter()
{
let Some(visual_com) = vg.visual() else {
continue;
};
let entity_name = format_entity_name(entity, name);
if !is_window {
let scale_x = global_arrangement.scale_x();
let scale_y = global_arrangement.scale_y();
let offset_x = arrangement.offset.x * scale_x;
let offset_y = arrangement.offset.y * scale_y;
let offset = windows_numerics::Vector3 {
X: offset_x,
Y: offset_y,
Z: 0.0,
};
if let Err(e) = visual_com.SetOffset(offset) {
error!(
entity = %entity_name,
error = ?e,
"[visual_property_sync] SetOffset failed"
);
}
}
let opacity_value = if !visual_component.is_visible {
0.0
} else {
visual_component.clamped_opacity()
};
if let Err(e) = visual_com.SetOpacity(opacity_value) {
error!(
entity = %entity_name,
error = ?e,
"[visual_property_sync] SetOpacity failed"
);
}
}
}