use super::command_list::GraphicsCommandList;
use crate::com::d2d::{D2D1DeviceContextExt, D2D1DeviceExt};
use crate::com::dcomp::{
DCompositionDesktopDeviceExt, DCompositionDeviceExt, DCompositionSurfaceExt,
DCompositionVisualExt,
};
use crate::ecs::graphics::{
GraphicsCore, HasGraphicsResources, SurfaceCreationStats, SurfaceGraphics,
SurfaceGraphicsDirty, VisualGraphics, WindowGraphics,
};
use crate::ecs::layout::GlobalArrangement;
use crate::ecs::widget::{Brush, BrushInherit, Brushes, DEFAULT_BACKGROUND, DEFAULT_FOREGROUND};
use bevy_ecs::hierarchy::{ChildOf, Children};
use bevy_ecs::name::Name;
use bevy_ecs::prelude::*;
use tracing::{debug, error, info, trace, warn};
use windows::Win32::Foundation::*;
use windows::Win32::Graphics::Dxgi::Common::*;
pub fn format_entity_name(entity: Entity, name: Option<&Name>) -> String {
match name {
Some(n) => n.to_string(),
None => format!("Entity({:?})", entity),
}
}
pub fn calculate_surface_size_from_global_arrangement(
global_arrangement: &GlobalArrangement,
) -> Option<(u32, u32)> {
let width = global_arrangement.bounds.right - global_arrangement.bounds.left;
let height = global_arrangement.bounds.bottom - global_arrangement.bounds.top;
if width <= 0.0 || height <= 0.0 {
return None;
}
let width_px = width.ceil() as u32;
let height_px = height.ceil() as u32;
if width_px == 0 || height_px == 0 {
return None;
}
Some((width_px, height_px))
}
fn create_window_graphics_for_hwnd(
graphics: &GraphicsCore,
hwnd: HWND,
) -> windows::core::Result<WindowGraphics> {
use windows::Win32::Graphics::Direct2D::D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
if !graphics.is_valid() {
return Err(windows::core::Error::from(E_FAIL));
}
let desktop = graphics
.desktop()
.ok_or(windows::core::Error::from(E_FAIL))?;
let target = desktop.create_target_for_hwnd(hwnd, true)?;
let d2d = graphics
.d2d_device()
.ok_or(windows::core::Error::from(E_FAIL))?;
let device_context = d2d.create_device_context(D2D1_DEVICE_CONTEXT_OPTIONS_NONE)?;
Ok(WindowGraphics::new(target, device_context))
}
fn create_surface_for_visual(
graphics: &GraphicsCore,
visual: &VisualGraphics,
width: u32,
height: u32,
) -> windows::core::Result<SurfaceGraphics> {
if !graphics.is_valid() {
return Err(windows::core::Error::from(E_FAIL));
}
let dcomp = graphics.dcomp().ok_or(windows::core::Error::from(E_FAIL))?;
let surface = dcomp.create_surface(
width,
height,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_PREMULTIPLIED,
)?;
let visual_ref = visual.visual().ok_or(windows::core::Error::from(E_FAIL))?;
visual_ref.set_content(&surface)?;
Ok(SurfaceGraphics::new(surface, (width, height)))
}
#[allow(dead_code)]
fn draw_recursive(
entity: Entity,
dc: &windows::Win32::Graphics::Direct2D::ID2D1DeviceContext,
widgets: &Query<(Option<&GlobalArrangement>, Option<&GraphicsCommandList>)>,
hierarchy: &Query<&Children>,
surface_query: &Query<&SurfaceGraphics>,
is_root: bool,
) {
use windows::Win32::Graphics::Direct2D::Common::D2D1_COMPOSITE_MODE_SOURCE_OVER;
use windows::Win32::Graphics::Direct2D::D2D1_INTERPOLATION_MODE_LINEAR;
if !is_root && surface_query.contains(entity) {
return;
}
if let Ok((global_arr, cmd_list)) = widgets.get(entity) {
if let Some(arr) = global_arr {
trace!(
entity = ?entity,
m11 = arr.transform.M11,
m12 = arr.transform.M12,
m31 = arr.transform.M31,
m32 = arr.transform.M32,
bounds_left = arr.bounds.left,
bounds_top = arr.bounds.top,
bounds_right = arr.bounds.right,
bounds_bottom = arr.bounds.bottom,
"Setting transform"
);
dc.set_transform(&arr.transform);
}
if let Some(list) = cmd_list {
if let Some(command_list) = list.command_list() {
unsafe {
dc.DrawImage(
command_list,
None,
None,
D2D1_INTERPOLATION_MODE_LINEAR,
D2D1_COMPOSITE_MODE_SOURCE_OVER,
);
}
}
}
}
if let Ok(children) = hierarchy.get(entity) {
for child in children.iter() {
draw_recursive(child, dc, widgets, hierarchy, surface_query, false);
}
}
}
pub fn render_surface(
surfaces: Query<
(
Entity,
&SurfaceGraphics,
&GlobalArrangement,
Option<&GraphicsCommandList>,
Option<&Name>,
),
Changed<super::components::SurfaceGraphicsDirty>,
>,
_graphics_core: Option<Res<GraphicsCore>>,
_frame_count: Res<crate::ecs::world::FrameCount>,
) {
use windows::Win32::Graphics::Direct2D::Common::D2D1_COLOR_F;
use windows::Win32::Graphics::Direct2D::Common::D2D1_COMPOSITE_MODE_SOURCE_OVER;
use windows::Win32::Graphics::Direct2D::D2D1_INTERPOLATION_MODE_LINEAR;
for (entity, surface, global_arrangement, cmd_list_opt, name) in surfaces.iter() {
let entity_name = format_entity_name(entity, name);
if !surface.is_valid() {
continue;
}
let surface_ref = match surface.surface() {
Some(s) => s,
None => continue,
};
let (dc, offset) = match surface_ref.begin_draw(None) {
Ok(result) => {
result
}
Err(err) => {
error!(
entity = %entity_name,
error = ?err,
hresult = format_args!("0x{:08X}", err.code().0),
"BeginDraw failed"
);
continue;
}
};
unsafe {
let scale_x = global_arrangement.scale_x();
let scale_y = global_arrangement.scale_y();
let transform = windows_numerics::Matrix3x2 {
M11: scale_x,
M12: 0.0,
M21: 0.0,
M22: scale_y,
M31: offset.x as f32,
M32: offset.y as f32,
};
dc.SetTransform(&transform);
}
dc.clear(Some(&D2D1_COLOR_F {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
}));
if let Some(cmd_list) = cmd_list_opt {
if let Some(command_list) = cmd_list.command_list() {
unsafe {
dc.DrawImage(
command_list,
None,
None,
D2D1_INTERPOLATION_MODE_LINEAR,
D2D1_COMPOSITE_MODE_SOURCE_OVER,
);
}
}
}
if let Err(e) = surface_ref.end_draw() {
error!(error = ?e, "EndDraw failed");
}
}
}
pub fn commit_composition(
graphics: Option<Res<GraphicsCore>>,
frame_count: Res<crate::ecs::world::FrameCount>,
) {
let Some(graphics) = graphics else {
warn!(
frame = frame_count.0,
"[commit_composition] GraphicsCore not available"
);
return;
};
if !graphics.is_valid() {
warn!(
frame = frame_count.0,
"[commit_composition] GraphicsCore is invalid"
);
return;
}
let dcomp = match graphics.dcomp() {
Some(d) => d,
None => {
warn!(
frame = frame_count.0,
"[commit_composition] DComp device not available"
);
return;
}
};
match dcomp.commit() {
Ok(()) => {
}
Err(e) => {
error!(
frame = frame_count.0,
error = ?e,
hresult = format!("0x{:08X}", e.code().0),
"[commit_composition] Commit failed"
);
}
}
}
pub fn init_graphics_core(
graphics: Option<ResMut<GraphicsCore>>,
mut query: Query<&mut HasGraphicsResources>,
mut commands: Commands,
frame_count: Res<crate::ecs::world::FrameCount>,
) {
match graphics {
Some(mut gc) => {
if gc.is_valid() {
return;
}
info!(
frame = frame_count.0,
"[init_graphics_core] GraphicsCore re-initialization started"
);
match GraphicsCore::new() {
Ok(new_gc) => {
*gc = new_gc;
info!(
frame = frame_count.0,
"[init_graphics_core] GraphicsCore re-initialization completed"
);
let count = query.iter().count();
debug!(
frame = frame_count.0,
count = count,
"[init_graphics_core] Triggering set_changed() on entities (re-init)"
);
for mut res in query.iter_mut() {
res.set_changed();
}
}
Err(e) => {
error!(
frame = frame_count.0,
error = ?e,
"[init_graphics_core] GraphicsCore re-initialization failed"
);
}
}
}
None => {
info!(
frame = frame_count.0,
"[init_graphics_core] GraphicsCore initialization started"
);
match GraphicsCore::new() {
Ok(gc) => {
info!(
frame = frame_count.0,
"[init_graphics_core] GraphicsCore initialization completed"
);
commands.insert_resource(gc);
let count = query.iter().count();
debug!(
frame = frame_count.0,
count = count,
"[init_graphics_core] Triggering set_changed() on entities (init)"
);
for mut res in query.iter_mut() {
res.set_changed();
}
}
Err(e) => {
error!(
frame = frame_count.0,
error = ?e,
"[init_graphics_core] GraphicsCore initialization failed"
);
}
}
}
}
}
pub fn init_window_graphics(
graphics: Res<GraphicsCore>,
mut query: Query<
(
Entity,
&crate::ecs::window::WindowHandle,
&HasGraphicsResources,
Option<&mut WindowGraphics>,
Option<&Name>,
),
Or<(Without<WindowGraphics>, Changed<HasGraphicsResources>)>,
>,
mut commands: Commands,
frame_count: Res<crate::ecs::world::FrameCount>,
) {
if !graphics.is_valid() {
return;
}
for (entity, handle, _res, window_graphics, name) in query.iter_mut() {
let entity_name = format_entity_name(entity, name);
match window_graphics {
None => {
debug!(
frame = frame_count.0,
entity = %entity_name,
"[init_window_graphics] WindowGraphics creating new"
);
match create_window_graphics_for_hwnd(&graphics, handle.hwnd) {
Ok(wg) => {
debug!(
frame = frame_count.0,
entity = %entity_name,
"[init_window_graphics] WindowGraphics created"
);
commands.entity(entity).insert(wg);
}
Err(e) => {
error!(
frame = frame_count.0,
entity = %entity_name,
error = ?e,
"[init_window_graphics] Error creating WindowGraphics"
);
}
}
}
Some(mut wg) => {
if !wg.is_valid() {
debug!(
frame = frame_count.0,
entity = %entity_name,
"[init_window_graphics] WindowGraphics re-initializing"
);
let old_generation = wg.generation();
match create_window_graphics_for_hwnd(&graphics, handle.hwnd) {
Ok(new_wg) => {
let new_generation = old_generation.wrapping_add(1);
*wg = new_wg;
while wg.generation() < new_generation {
wg.increment_generation();
}
debug!(
frame = frame_count.0,
entity = %entity_name,
old_generation = old_generation,
new_generation = wg.generation(),
"[init_window_graphics] WindowGraphics re-initialized"
);
}
Err(e) => {
error!(
frame = frame_count.0,
entity = %entity_name,
error = ?e,
"[init_window_graphics] Re-initialization error"
);
}
}
}
}
}
}
}
pub fn init_window_visual(
_graphics: Res<GraphicsCore>,
_query: Query<
(
Entity,
&WindowGraphics,
&HasGraphicsResources,
Option<&mut VisualGraphics>,
),
Or<(Without<VisualGraphics>, Changed<HasGraphicsResources>)>,
>,
_commands: Commands,
_frame_count: Res<crate::ecs::world::FrameCount>,
) {
}
#[deprecated(
since = "0.1.0",
note = "Use deferred_surface_creation_system instead. This function will be removed in a future version."
)]
#[allow(dead_code)]
pub fn sync_surface_from_arrangement(
graphics: Option<Res<GraphicsCore>>,
mut query: Query<
(
Entity,
&VisualGraphics,
&crate::ecs::layout::Arrangement,
Option<&mut SurfaceGraphics>,
Option<&Name>,
),
Changed<crate::ecs::layout::Arrangement>,
>,
mut commands: Commands,
frame_count: Res<crate::ecs::world::FrameCount>,
) {
let Some(graphics) = graphics else {
return;
};
if !graphics.is_valid() {
return;
}
let mut _processed_count = 0;
for (entity, visual_graphics, arrangement, surface, name) in query.iter_mut() {
let entity_name = format_entity_name(entity, name);
if !visual_graphics.is_valid() {
warn!(
frame = frame_count.0,
entity = %entity_name,
"[sync_surface_from_arrangement] Entity has invalid VisualGraphics, skipping"
);
continue;
}
let width = arrangement.size.width as u32;
let height = arrangement.size.height as u32;
if width == 0 || height == 0 {
trace!(
frame = frame_count.0,
entity = %entity_name,
width = width,
height = height,
"[sync_surface_from_arrangement] Entity has zero size, skipping"
);
continue;
}
_processed_count += 1;
trace!(
frame = frame_count.0,
entity = %entity_name,
width = width,
height = height,
has_surface = surface.is_some(),
"[sync_surface_from_arrangement] Processing entity"
);
match surface {
Some(mut surf) => {
if surf.size != (width, height) {
trace!(
frame = frame_count.0,
entity = %entity_name,
old_size = ?surf.size,
new_width = width,
new_height = height,
"[sync_surface_from_arrangement] Entity resizing, calling SetContent"
);
match create_surface_for_visual(&graphics, visual_graphics, width, height) {
Ok(new_surface) => {
trace!(
frame = frame_count.0,
entity = %entity_name,
"[sync_surface_from_arrangement] Entity Surface resized successfully"
);
commands.entity(entity).insert(new_surface);
commands
.entity(entity)
.insert(super::components::SurfaceGraphicsDirty::default());
}
Err(e) => {
error!(error = ?e, "[sync_surface_from_arrangement] Error resizing surface");
surf.invalidate();
}
}
}
}
None => {
trace!(
frame = frame_count.0,
entity = %entity_name,
width = width,
height = height,
"[sync_surface_from_arrangement] Entity creating new Surface, calling SetContent"
);
match create_surface_for_visual(&graphics, visual_graphics, width, height) {
Ok(new_surface) => {
trace!(
frame = frame_count.0,
entity = %entity_name,
"[sync_surface_from_arrangement] Entity Surface created successfully"
);
commands.entity(entity).insert(new_surface);
commands
.entity(entity)
.insert(super::components::SurfaceGraphicsDirty::default());
}
Err(e) => {
error!(error = ?e, "[sync_surface_from_arrangement] Error creating new surface");
}
}
}
}
}
}
pub fn apply_window_pos_changes(
mut query: Query<
(
Entity,
&crate::ecs::window::WindowHandle,
&crate::ecs::window::WindowPos,
Option<&Name>,
),
(
Changed<crate::ecs::window::WindowPos>,
With<crate::ecs::window::Window>,
),
>,
) {
for (entity, window_handle, window_pos, name) in query.iter_mut() {
let entity_name = format_entity_name(entity, name);
let position = window_pos.position.unwrap_or_default();
let size = window_pos.size.unwrap_or_default();
if position.x == windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT
|| size.cx == windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT
{
trace!(
entity = %entity_name,
x = position.x,
y = position.y,
cx = size.cx,
cy = size.cy,
"[apply_window_pos_changes] CW_USEDEFAULT detected, skipping"
);
continue;
}
let (x, y, width, height) = match window_pos.to_window_coords(window_handle) {
Ok(coords) => coords,
Err(e) => {
warn!(
entity = %entity_name,
error = %e,
"[apply_window_pos_changes] Failed to transform window coordinates. Using original values."
);
(position.x, position.y, size.cx, size.cy)
}
};
let flags = window_pos.build_flags_for_system();
let hwnd_insert_after = window_pos.get_hwnd_insert_after();
debug!(
entity = %entity_name,
client_x = position.x,
client_y = position.y,
client_cx = size.cx,
client_cy = size.cy,
window_x = x,
window_y = y,
window_cx = width,
window_cy = height,
"[apply_window_pos_changes] Enqueueing SetWindowPosCommand"
);
let cmd = crate::ecs::window::SetWindowPosCommand::new(
window_handle.hwnd,
x,
y,
width,
height,
flags,
hwnd_insert_after,
);
crate::ecs::window::SetWindowPosCommand::enqueue(cmd);
debug!(
entity = %entity_name,
x = position.x,
y = position.y,
cx = size.cx,
cy = size.cy,
"[apply_window_pos_changes] Command enqueued"
);
}
}
pub fn invalidate_dependent_components(
graphics: Option<Res<GraphicsCore>>,
mut window_graphics_query: Query<&mut WindowGraphics>,
mut visual_query: Query<&mut VisualGraphics>,
mut surface_query: Query<&mut SurfaceGraphics>,
mut bitmap_source_query: Query<&mut crate::ecs::widget::bitmap_source::BitmapSourceGraphics>,
) {
if let Some(gc) = graphics {
if !gc.is_valid() {
warn!(
"[invalidate_dependent_components] GraphicsCore invalid - invalidating all dependent components"
);
for mut wg in window_graphics_query.iter_mut() {
wg.invalidate();
}
for mut v in visual_query.iter_mut() {
v.invalidate();
}
for mut s in surface_query.iter_mut() {
s.invalidate();
}
for mut bsg in bitmap_source_query.iter_mut() {
bsg.invalidate();
}
}
}
}
pub fn mark_dirty_surfaces(
mut changed_query: Query<
(
Entity,
&mut super::components::SurfaceGraphicsDirty,
Option<&Name>,
),
(
Or<(
Changed<GraphicsCommandList>,
Changed<SurfaceGraphics>,
Added<SurfaceGraphics>,
Changed<GlobalArrangement>,
)>,
With<SurfaceGraphics>,
),
>,
frame_count: Res<crate::ecs::world::FrameCount>,
) {
let mut count = 0;
for (entity, mut dirty, name) in changed_query.iter_mut() {
dirty.requested_frame = frame_count.0 as u64;
count += 1;
let _entity_name = format_entity_name(entity, name);
}
if count > 0 {
}
}
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>,
) {
use crate::com::dcomp::DCompositionVisualExt;
let mut updates: Vec<(Entity, Entity, Option<String>, Option<String>, usize)> = Vec::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 child_name_str = child_name.map(|n| n.to_string());
let mut depth = 0;
let mut current = entity;
while let Ok(co) = child_of_query.get(current) {
depth += 1;
current = co.parent();
}
updates.push((entity, child_of.parent(), child_name_str, None, depth));
}
}
}
{
let parent_query = vg_queries.p1();
for item in updates.iter_mut() {
let parent_entity = item.1;
if let Ok((_, parent_name)) = parent_query.get(parent_entity) {
item.3 = parent_name.map(|n| n.to_string());
}
}
}
updates.sort_by_key(|item| item.4);
if !updates.is_empty() {
debug!(
count = updates.len(),
"[visual_hierarchy_sync] Processing entities (sorted by depth)"
);
}
for (child_entity, parent_entity, child_name_str, parent_name_str, depth) in updates {
let child_fallback = format!("Entity({:?})", child_entity);
let parent_fallback = format!("Entity({:?})", parent_entity);
let child_display = child_name_str.as_deref().unwrap_or(&child_fallback);
let parent_display = parent_name_str.as_deref().unwrap_or(&parent_fallback);
let parent_visual = {
let parent_query = vg_queries.p1();
parent_query
.get(parent_entity)
.ok()
.and_then(|(pv, _)| pv.visual().cloned())
};
let mut child_query = vg_queries.p0();
if let Ok((_, _, mut child_vg, _)) = child_query.get_mut(child_entity) {
let child_visual = match child_vg.visual() {
Some(v) => v.clone(),
None => continue,
};
if let Some(ref parent_visual) = parent_visual {
if let Err(e) = parent_visual.add_visual(&child_visual, false, None) {
error!(
child = %child_display,
depth = depth,
parent = %parent_display,
error = ?e,
"[visual_hierarchy_sync] AddVisual failed"
);
} else {
debug!(
child = %child_display,
depth = depth,
parent = %parent_display,
"[visual_hierarchy_sync] AddVisual success"
);
}
child_vg.set_parent_visual(Some(parent_visual.clone()));
} else {
debug!(
name = %child_display,
depth = depth,
"[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,
&crate::ecs::layout::Arrangement,
&crate::ecs::layout::GlobalArrangement,
Option<&crate::ecs::layout::Opacity>,
&VisualGraphics,
Option<&Name>,
Has<crate::ecs::window::Window>,
),
Or<(
Changed<crate::ecs::layout::Arrangement>,
Changed<crate::ecs::layout::GlobalArrangement>,
Changed<crate::ecs::layout::Opacity>,
)>,
>,
) {
use crate::com::dcomp::DCompositionVisualExt;
for (entity, arrangement, global_arrangement, opacity_opt, vg, name, is_window) in
changed_entities.iter()
{
let Some(visual) = 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;
if let Err(e) = visual.set_offset_x(offset_x) {
error!(
entity = %entity_name,
error = ?e,
"[visual_property_sync] SetOffsetX failed"
);
}
if let Err(e) = visual.set_offset_y(offset_y) {
error!(
entity = %entity_name,
error = ?e,
"[visual_property_sync] SetOffsetY failed"
);
}
} else {
}
if let Some(opacity) = opacity_opt {
let opacity_value = opacity.clamped();
if let Err(e) = visual.set_opacity(opacity_value) {
error!(
entity = %entity_name,
error = ?e,
"[visual_property_sync] SetOpacity failed"
);
}
}
}
}
pub fn deferred_surface_creation_system(
graphics: Res<GraphicsCore>,
mut query: Query<
(
Entity,
&VisualGraphics,
&GraphicsCommandList,
&GlobalArrangement,
&mut SurfaceGraphics,
&mut SurfaceGraphicsDirty,
Option<&Name>,
),
Or<(Changed<GlobalArrangement>, Changed<GraphicsCommandList>)>,
>,
mut stats: ResMut<SurfaceCreationStats>,
) {
use crate::com::dcomp::DCompositionDeviceExt;
if !graphics.is_valid() {
return;
}
let dcomp = match graphics.dcomp() {
Some(d) => d,
None => return,
};
for (
entity,
visual_graphics,
_cmd_list,
global_arrangement,
mut surface_graphics,
mut dirty,
name,
) in query.iter_mut()
{
let entity_name = format_entity_name(entity, name);
let Some((width, height)) =
calculate_surface_size_from_global_arrangement(global_arrangement)
else {
trace!(
entity = %entity_name,
bounds = ?global_arrangement.bounds,
"[deferred_surface_creation] Entity skipped: invalid size from GlobalArrangement"
);
stats.record_skipped();
continue;
};
if surface_graphics.is_valid() && surface_graphics.size == (width, height) {
continue;
}
let is_new = !surface_graphics.is_valid();
if is_new {
debug!(
entity = %entity_name,
width = width,
height = height,
"[deferred_surface_creation] Creating Surface"
);
} else {
debug!(
entity = %entity_name,
old_size = ?surface_graphics.size,
new_width = width,
new_height = height,
"[deferred_surface_creation] Resizing Surface"
);
}
let surface_res = dcomp.create_surface(
width,
height,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_PREMULTIPLIED,
);
match surface_res {
Ok(surface) => {
if let Some(visual) = visual_graphics.visual() {
trace!(
entity = %entity_name,
"[deferred_surface_creation] SetContent calling"
);
unsafe {
match visual.SetContent(&surface) {
Ok(_) => trace!(
entity = %entity_name,
"[deferred_surface_creation] SetContent SUCCESS"
),
Err(e) => error!(
entity = %entity_name,
error = ?e,
"[deferred_surface_creation] SetContent FAILED"
),
}
}
} else {
warn!(
entity = %entity_name,
"[deferred_surface_creation] NO VISUAL! SetContent skipped"
);
}
surface_graphics.set_surface(surface, (width, height));
dirty.requested_frame = dirty.requested_frame.wrapping_add(1);
if is_new {
stats.record_created();
debug!(
entity = %entity_name,
"[deferred_surface_creation] Surface created successfully"
);
} else {
stats.record_resized();
debug!(
entity = %entity_name,
"[deferred_surface_creation] Surface resized successfully"
);
}
}
Err(e) => {
error!(
entity = %entity_name,
error = ?e,
"[deferred_surface_creation] Failed to create/resize surface"
);
}
}
}
}
pub fn cleanup_surface_on_commandlist_removed(
mut removed: RemovedComponents<GraphicsCommandList>,
mut query: Query<(Entity, &VisualGraphics, &mut SurfaceGraphics, Option<&Name>)>,
mut stats: ResMut<SurfaceCreationStats>,
) {
for entity in removed.read() {
if let Ok((entity, visual_graphics, mut surface_graphics, name)) = query.get_mut(entity) {
if !surface_graphics.is_valid() {
continue;
}
let entity_name = format_entity_name(entity, name);
debug!(
entity = %entity_name,
"[cleanup_surface_on_commandlist_removed] Clearing SurfaceGraphics"
);
if let Some(visual) = visual_graphics.visual() {
unsafe {
let _ = visual.SetContent(None);
}
}
surface_graphics.clear();
stats.record_deleted();
debug!(
entity = %entity_name,
"[cleanup_surface_on_commandlist_removed] SurfaceGraphics cleared"
);
}
}
}
pub fn resolve_inherited_brushes(
mut commands: Commands,
query: Query<(Entity, Option<&Brushes>), With<BrushInherit>>,
parent_query: Query<&ChildOf>,
all_brushes: Query<&Brushes>,
) {
for (entity, brushes_opt) in query.iter() {
let parent_brushes = find_parent_brushes(entity, &parent_query, &all_brushes);
let resolved = match brushes_opt {
Some(brushes) => {
resolve_brush_fields(brushes, parent_brushes.as_ref())
}
None => {
match parent_brushes {
Some(parent) => parent.clone(),
None => Brushes {
foreground: DEFAULT_FOREGROUND,
background: DEFAULT_BACKGROUND,
},
}
}
};
commands
.entity(entity)
.insert(resolved)
.remove::<BrushInherit>();
trace!(
entity = ?entity,
"[resolve_inherited_brushes] Brushes resolved"
);
}
}
fn find_parent_brushes(
entity: Entity,
parent_query: &Query<&ChildOf>,
all_brushes: &Query<&Brushes>,
) -> Option<Brushes> {
let mut current = entity;
while let Ok(child_of) = parent_query.get(current) {
let parent = child_of.parent();
if let Ok(parent_brushes) = all_brushes.get(parent) {
if !parent_brushes.foreground.is_inherit() && !parent_brushes.background.is_inherit() {
return Some(parent_brushes.clone());
}
}
current = parent;
}
None
}
fn resolve_brush_fields(brushes: &Brushes, parent_brushes: Option<&Brushes>) -> Brushes {
let foreground = if brushes.foreground.is_inherit() {
parent_brushes
.and_then(|p| p.foreground.as_color())
.map(Brush::Solid)
.unwrap_or(DEFAULT_FOREGROUND)
} else {
brushes.foreground.clone()
};
let background = if brushes.background.is_inherit() {
parent_brushes
.and_then(|p| p.background.as_color())
.map(Brush::Solid)
.unwrap_or(DEFAULT_BACKGROUND)
} else {
brushes.background.clone()
};
Brushes {
foreground,
background,
}
}