use crate::com::d2d::{D2D1CommandListExt, D2D1DeviceContextExt};
use crate::com::dwrite::DWriteFactoryExt;
use crate::ecs::TextLayoutMetrics;
use crate::ecs::graphics::{GraphicsCommandList, GraphicsCore};
use crate::ecs::widget::brushes::{Brushes, DEFAULT_FOREGROUND};
use crate::ecs::widget::text::label::TextDirection;
use crate::ecs::widget::text::{Label, TextLayoutResource};
use bevy_ecs::prelude::*;
use tracing::{debug, warn};
use windows::Win32::Graphics::Direct2D::Common::D2D1_COLOR_F;
use windows::Win32::Graphics::Direct2D::D2D1_DRAW_TEXT_OPTIONS_NONE;
use windows::Win32::Graphics::DirectWrite::*;
use windows_numerics::{Matrix3x2, Vector2};
const TRANSPARENT_COLOR: D2D1_COLOR_F = D2D1_COLOR_F {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
};
pub fn draw_labels(
mut commands: Commands,
query: Query<
(Entity, &Label, &Brushes),
Or<(
Changed<Label>,
Changed<Brushes>,
Without<GraphicsCommandList>,
)>,
>,
graphics_core: Option<Res<GraphicsCore>>,
) {
let Some(graphics_core) = graphics_core else {
warn!("GraphicsCore not available, skipping draw_labels");
return;
};
let Some(dwrite_factory) = graphics_core.dwrite_factory() else {
warn!("DirectWrite factory not available");
return;
};
let Some(dc) = graphics_core.device_context() else {
warn!("DeviceContext not available");
return;
};
for (entity, label, brushes) in query.iter() {
#[cfg(debug_assertions)]
debug!(
entity = ?entity,
text = %label.text,
font = %label.font_family,
size_pt = label.font_size,
"Drawing label"
);
let color = brushes
.foreground
.as_color()
.unwrap_or_else(|| DEFAULT_FOREGROUND.as_color().unwrap());
let font_family_hstring = windows::core::HSTRING::from(&label.font_family);
let locale_hstring = windows::core::HSTRING::from("ja-JP");
let text_format = match dwrite_factory.create_text_format(
&font_family_hstring,
None::<&windows::Win32::Graphics::DirectWrite::IDWriteFontCollection>,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
label.font_size,
&locale_hstring,
) {
Ok(fmt) => fmt,
Err(err) => {
warn!(
entity = ?entity,
error = ?err,
"Failed to create TextFormat"
);
continue;
}
};
unsafe {
match label.direction {
TextDirection::HorizontalLeftToRight => {
let _ = text_format.SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
let _ = text_format.SetFlowDirection(DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM);
}
TextDirection::HorizontalRightToLeft => {
let _ = text_format.SetReadingDirection(DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
let _ = text_format.SetFlowDirection(DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM);
}
TextDirection::VerticalRightToLeft => {
let _ = text_format.SetReadingDirection(DWRITE_READING_DIRECTION_TOP_TO_BOTTOM);
let _ = text_format.SetFlowDirection(DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT);
}
TextDirection::VerticalLeftToRight => {
let _ = text_format.SetReadingDirection(DWRITE_READING_DIRECTION_TOP_TO_BOTTOM);
let _ = text_format.SetFlowDirection(DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT);
}
}
}
let text_hstring = windows::core::HSTRING::from(&label.text);
let text_layout =
match dwrite_factory.create_text_layout(&text_hstring, &text_format, 0.0, 0.0) {
Ok(layout) => layout,
Err(err) => {
warn!(
entity = ?entity,
error = ?err,
"Failed to create TextLayout"
);
continue;
}
}; unsafe {
let _ = text_layout.SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
}
let mut metrics = DWRITE_TEXT_METRICS::default();
unsafe {
let _ = text_layout.GetMetrics(&mut metrics);
}
let command_list = match unsafe { dc.CreateCommandList() } {
Ok(cl) => cl,
Err(err) => {
warn!(
entity = ?entity,
error = ?err,
"Failed to create CommandList"
);
continue;
}
};
unsafe {
dc.SetTarget(&command_list);
}
unsafe {
dc.SetTransform(&Matrix3x2::identity());
dc.BeginDraw();
}
dc.clear(Some(&TRANSPARENT_COLOR));
let brush = match dc.create_solid_color_brush(&color, None) {
Ok(b) => b,
Err(err) => {
warn!(
entity = ?entity,
error = ?err,
"Failed to create brush"
);
unsafe {
let _ = dc.EndDraw(None, None);
}
continue;
}
};
let origin = Vector2 {
X: -metrics.left,
Y: -metrics.top, };
dc.draw_text_layout(origin, &text_layout, &brush, D2D1_DRAW_TEXT_OPTIONS_NONE);
if let Err(err) = unsafe { dc.EndDraw(None, None) } {
warn!(
entity = ?entity,
error = ?err,
"EndDraw failed"
);
continue;
}
if let Err(err) = command_list.close() {
warn!(
entity = ?entity,
error = ?err,
"Failed to close CommandList"
);
continue;
}
commands.entity(entity).insert((
GraphicsCommandList::new(command_list),
TextLayoutResource::new(text_layout),
TextLayoutMetrics {
width: metrics.width,
height: metrics.height,
},
));
}
}