use crate::ecs::graphics::Visual;
use crate::ecs::graphics::clip::ClipShape;
use crate::ecs::graphics::components::VisualGraphics;
use crate::ecs::graphics::wuc_resource::WucGraphicsResource;
use crate::ecs::layout::{Arrangement, GlobalArrangement};
use bevy_ecs::prelude::*;
use tracing::{debug, error, trace, warn};
use windows::UI::Composition::{CompositionClip, Compositor};
use windows::core::Interface;
pub fn clip_sync_system(
wuc_resource: Option<Res<WucGraphicsResource>>,
query: Query<
(
Entity,
&Arrangement,
&GlobalArrangement,
&Visual,
&VisualGraphics,
),
Or<(
Changed<Arrangement>,
Changed<GlobalArrangement>,
Changed<Visual>,
)>,
>,
) {
let Some(wuc_resource) = wuc_resource else {
return;
};
let Some(compositor) = wuc_resource.compositor() else {
return;
};
for (entity, arrangement, ga, visual, vg) in query.iter() {
let Some(visual_com) = vg.visual() else {
continue;
};
let size = &arrangement.size;
match &visual.clip {
Some(clip_shape) if size.width > 0.0 && size.height > 0.0 => {
let scale_x = ga.scale_x();
let scale_y = ga.scale_y();
let physical_width = size.width * scale_x;
let physical_height = size.height * scale_y;
let clip_res = build_clip(
compositor,
clip_shape,
physical_width,
physical_height,
scale_x,
);
let clip = match clip_res {
Ok(c) => c,
Err(e) => {
error!(
entity = ?entity,
error = ?e,
"[clip_sync] Failed to build WUC clip"
);
continue;
}
};
if let Err(e) = visual_com.SetClip(&clip) {
error!(
entity = ?entity,
error = ?e,
"[clip_sync] SetClip failed"
);
} else {
debug!(
entity = ?entity,
clip_shape = ?clip_shape,
physical_width,
physical_height,
"[clip_sync] Clip applied"
);
}
}
_ => {
if let Err(e) = visual_com.SetClip(None::<&CompositionClip>) {
error!(
entity = ?entity,
error = ?e,
"[clip_sync] SetClip(None) failed"
);
} else {
trace!(
entity = ?entity,
"[clip_sync] Clip cleared"
);
}
}
}
}
}
fn build_clip(
compositor: &Compositor,
clip_shape: &ClipShape,
physical_width: f32,
physical_height: f32,
scale_x: f32,
) -> windows::core::Result<CompositionClip> {
match clip_shape {
ClipShape::Rectangle => {
build_rounded_geometric_clip(compositor, physical_width, physical_height, 0.0)
}
ClipShape::RoundedRectangle { radius } => {
let physical_radius = radius * scale_x;
build_rounded_geometric_clip(
compositor,
physical_width,
physical_height,
physical_radius,
)
}
ClipShape::RoundedRectangleIndividual {
top_left,
top_right,
bottom_left,
bottom_right,
} => {
let tl = top_left * scale_x;
let tr = top_right * scale_x;
let bl = bottom_left * scale_x;
let br = bottom_right * scale_x;
let uniform = tl.max(tr).max(bl).max(br);
let all_equal = tl == tr && tr == bl && bl == br;
if !all_equal {
warn!(
top_left = tl,
top_right = tr,
bottom_left = bl,
bottom_right = br,
uniform,
"[clip_sync] RoundedRectangleIndividual with non-uniform radii approximated by \
uniform max radius (WUC lacks per-corner clip geometry without Win2D)"
);
}
build_rounded_geometric_clip(compositor, physical_width, physical_height, uniform)
}
}
}
fn build_rounded_geometric_clip(
compositor: &Compositor,
physical_width: f32,
physical_height: f32,
physical_radius: f32,
) -> windows::core::Result<CompositionClip> {
let geometry = compositor.CreateRoundedRectangleGeometry()?;
geometry.SetSize(windows_numerics::Vector2 {
X: physical_width,
Y: physical_height,
})?;
geometry.SetCornerRadius(windows_numerics::Vector2 {
X: physical_radius,
Y: physical_radius,
})?;
let clip = compositor.CreateGeometricClipWithGeometry(&geometry)?;
clip.cast::<CompositionClip>()
}