use bevy::prelude::*;
use bevy::render::Extract;
use bevy::render::RenderApp;
use bevy::render::RenderStage;
use bevy::ui::widget::ImageMode;
use bevy::ui::ExtractedUiNode;
use bevy::ui::ExtractedUiNodes;
use bevy::ui::FocusPolicy;
use bevy::ui::RenderUiSystem;
use bevy::ui::UiStack;
use bevy::ui::UiSystem;
use bevy::window::WindowId;
#[derive(Component, Clone, Debug, Default, Reflect)]
#[reflect(Component, Default)]
pub struct UiAtlasImage {
pub atlas: Handle<TextureAtlas>,
pub index: usize,
}
#[derive(Bundle, Clone, Debug, Default)]
pub struct AtlasImageBundle {
pub node: Node,
pub style: Style,
pub image_mode: ImageMode,
pub calculated_size: CalculatedSize,
pub color: BackgroundColor,
pub atlas_image: UiAtlasImage,
pub focus_policy: FocusPolicy,
pub transform: Transform,
pub global_transform: GlobalTransform,
pub visibility: Visibility,
pub computed_visibility: ComputedVisibility,
pub z_index: ZIndex,
}
fn texture_atlas_image_node_system(
texture_atlases: Res<Assets<TextureAtlas>>,
mut query: Query<(&mut CalculatedSize, &UiAtlasImage), With<ImageMode>>,
) {
for (mut calculated_size, atlas_image) in &mut query {
if let Some(atlas) = texture_atlases.get(&atlas_image.atlas) {
let rect_size = atlas.textures[atlas_image.index].size();
let size = Size::new(Val::Px(rect_size.x), Val::Px(rect_size.y));
if size != calculated_size.size {
calculated_size.size = size;
}
}
}
}
#[allow(clippy::type_complexity)]
fn extract_texture_atlas_image_uinodes(
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
images: Extract<Res<Assets<Image>>>,
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
ui_stack: Extract<Res<UiStack>>,
windows: Extract<Res<Windows>>,
uinode_query: Extract<
Query<(
&Node,
&GlobalTransform,
&BackgroundColor,
&UiAtlasImage,
&ComputedVisibility,
Option<&CalculatedClip>,
)>,
>,
) {
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() {
if let Ok((uinode, global_transform, color, atlas_image, visibility, clip)) =
uinode_query.get(*entity)
{
if !visibility.is_visible() || uinode.size() == Vec2::ZERO {
continue;
}
if let Some(texture_atlas) = texture_atlases.get(&atlas_image.atlas) {
let image = texture_atlas.texture.clone_weak();
if !images.contains(&image) || color.0.a() == 0.0 {
continue;
}
let rect = texture_atlas.textures[atlas_image.index];
let scale = uinode.size() / rect.size();
let transform =
global_transform.compute_matrix() * Mat4::from_scale(scale.extend(1.0));
extracted_uinodes.uinodes.push(ExtractedUiNode {
stack_index,
transform,
background_color: color.0,
rect,
image,
atlas_size: Some(texture_atlas.size),
clip: clip.map(|clip| clip.clip),
scale_factor,
});
}
}
}
}
pub struct UiAtlasImagePlugin;
impl Plugin for UiAtlasImagePlugin {
fn build(&self, app: &mut App) {
app.register_type::<UiAtlasImage>().add_system_to_stage(
CoreStage::PostUpdate,
texture_atlas_image_node_system.before(UiSystem::Flex),
);
let render_app = match app.get_sub_app_mut(RenderApp) {
Ok(render_app) => render_app,
Err(_) => return,
};
render_app.add_system_to_stage(
RenderStage::Extract,
extract_texture_atlas_image_uinodes.after(RenderUiSystem::ExtractNode),
);
}
}