use crate::com::d2d::{D2D1CommandListExt, D2D1DeviceContextExt};
use crate::ecs::graphics::{format_entity_name, GraphicsCommandList, GraphicsCore};
use crate::ecs::layout::Arrangement;
use crate::ecs::widget::brushes::{Brushes, DEFAULT_FOREGROUND};
use crate::ecs::Visual;
use bevy_ecs::component::Component;
use bevy_ecs::lifecycle::HookContext;
use bevy_ecs::name::Name;
use bevy_ecs::prelude::*;
use bevy_ecs::world::DeferredWorld;
use tracing::{trace, warn};
use windows::Win32::Graphics::Direct2D::Common::{D2D1_COLOR_F, D2D_RECT_F};
pub type Color = D2D1_COLOR_F;
#[deprecated(since = "0.1.0", note = "Use Brush::XXX constants instead")]
pub mod colors {
use super::Color;
pub const TRANSPARENT: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
};
pub const BLACK: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
pub const WHITE: Color = Color {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
};
pub const RED: Color = Color {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
pub const GREEN: Color = Color {
r: 0.0,
g: 1.0,
b: 0.0,
a: 1.0,
};
pub const BLUE: Color = Color {
r: 0.0,
g: 0.0,
b: 1.0,
a: 1.0,
};
}
const TRANSPARENT_COLOR: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
};
#[derive(Component, Debug, Clone, Default)]
#[component(on_add = on_rectangle_add, on_remove = on_rectangle_remove)]
pub struct Rectangle;
impl Rectangle {
pub fn new() -> Self {
Self
}
}
fn on_rectangle_add(mut world: DeferredWorld, hook: HookContext) {
if world.get::<Visual>(hook.entity).is_some() {
return;
}
world
.commands()
.entity(hook.entity)
.insert(Visual::default());
}
fn on_rectangle_remove(mut world: DeferredWorld, hook: HookContext) {
let entity = hook.entity;
if let Some(mut cmd_list) = world.get_mut::<GraphicsCommandList>(entity) {
cmd_list.set_if_neq(GraphicsCommandList::empty());
}
}
pub fn draw_rectangles(
mut commands: Commands,
query: Query<
(
Entity,
&Rectangle,
&Arrangement,
&Brushes,
Option<&GraphicsCommandList>,
Option<&Name>,
),
Or<(Changed<Rectangle>, Changed<Arrangement>, Changed<Brushes>)>,
>,
graphics_core: Option<Res<GraphicsCore>>,
) {
let Some(graphics_core) = graphics_core else {
warn!("GraphicsCore not available, skipping draw_rectangles");
return;
};
for (entity, _rectangle, arrangement, brushes, cmd_list_opt, name) in query.iter() {
let entity_name = format_entity_name(entity, name);
trace!(
entity = %entity_name,
width = arrangement.size.width,
height = arrangement.size.height,
"Drawing rectangle"
);
let color = brushes
.foreground
.as_color()
.unwrap_or_else(|| DEFAULT_FOREGROUND.as_color().unwrap());
let dc = match graphics_core.device_context() {
Some(dc) => dc,
None => {
warn!(
entity = %entity_name,
"DeviceContext not available"
);
continue;
}
};
let command_list = match unsafe { dc.CreateCommandList() } {
Ok(cl) => cl,
Err(err) => {
warn!(
entity = %entity_name,
error = ?err,
"Failed to create CommandList"
);
continue;
}
};
unsafe {
dc.SetTarget(&command_list);
}
unsafe {
dc.BeginDraw();
dc.clear(Some(&TRANSPARENT_COLOR));
let rect = D2D_RECT_F {
left: 0.0,
top: 0.0,
right: arrangement.size.width,
bottom: arrangement.size.height,
};
trace!(
entity = %entity_name,
left = rect.left,
top = rect.top,
right = rect.right,
bottom = rect.bottom,
r = format_args!("{:.2}", color.r),
g = format_args!("{:.2}", color.g),
b = format_args!("{:.2}", color.b),
a = format_args!("{:.2}", color.a),
"Rectangle details"
);
let brush = match dc.create_solid_color_brush(&color, None) {
Ok(b) => b,
Err(err) => {
warn!(
entity = %entity_name,
error = ?err,
"Failed to create brush"
);
let _ = dc.EndDraw(None, None);
continue;
}
};
dc.fill_rectangle(&rect, &brush);
if let Err(err) = dc.EndDraw(None, None) {
warn!(
entity = %entity_name,
error = ?err,
"EndDraw failed"
);
continue;
}
}
if let Err(err) = command_list.close() {
warn!(
entity = %entity_name,
error = ?err,
"Failed to close CommandList"
);
continue;
}
let new_cmd_list = GraphicsCommandList::new(command_list);
match cmd_list_opt {
Some(existing) if *existing != new_cmd_list => {
commands.entity(entity).insert(new_cmd_list);
trace!(
entity = %entity_name,
"CommandList updated"
);
}
None => {
commands.entity(entity).insert(new_cmd_list);
trace!(
entity = %entity_name,
"CommandList created"
);
}
_ => {
trace!(
entity = %entity_name,
"CommandList unchanged"
);
}
}
}
}