ui: feed computed image size into bevy_ui flex

This commit is contained in:
Carter Anderson 2020-07-28 00:37:25 -07:00
parent cf9501a50e
commit 4a8c6c335a
6 changed files with 124 additions and 39 deletions

View File

@ -1,7 +1,7 @@
use super::Node; use super::Node;
use crate::{ use crate::{
render::UI_PIPELINE_HANDLE, render::UI_PIPELINE_HANDLE,
widget::{Button, Text}, widget::{Button, Text, Image},
Click, FocusPolicy, Hover, Style, CalculatedSize, Click, FocusPolicy, Hover, Style, CalculatedSize,
}; };
use bevy_asset::Handle; use bevy_asset::Handle;
@ -62,6 +62,55 @@ impl Default for NodeComponents {
} }
} }
#[derive(Bundle)]
pub struct ImageComponents {
pub node: Node,
pub style: Style,
pub image: Image,
pub calculated_size: CalculatedSize,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
pub material: Handle<ColorMaterial>,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
pub local_transform: LocalTransform,
}
impl Default for ImageComponents {
fn default() -> Self {
ImageComponents {
mesh: QUAD_HANDLE,
render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized(
UI_PIPELINE_HANDLE,
PipelineSpecialization {
dynamic_bindings: vec![
// Transform
DynamicBinding {
bind_group: 1,
binding: 0,
},
// Node_size
DynamicBinding {
bind_group: 1,
binding: 1,
},
],
..Default::default()
},
)]),
node: Default::default(),
image: Default::default(),
calculated_size: Default::default(),
style: Default::default(),
material: Default::default(),
draw: Default::default(),
transform: Default::default(),
local_transform: Default::default(),
}
}
}
#[derive(Bundle)] #[derive(Bundle)]
pub struct TextComponents { pub struct TextComponents {
pub node: Node, pub node: Node,

View File

@ -6,7 +6,7 @@ use bevy_math::Vec2;
use bevy_transform::prelude::{Children, LocalTransform, Parent}; use bevy_transform::prelude::{Children, LocalTransform, Parent};
use bevy_window::{Window, WindowId, Windows}; use bevy_window::{Window, WindowId, Windows};
use std::collections::HashMap; use std::collections::HashMap;
use stretch::Stretch; use stretch::{number::Number, Stretch};
pub struct FlexSurface { pub struct FlexSurface {
entity_to_stretch: HashMap<Entity, stretch::node::Node>, entity_to_stretch: HashMap<Entity, stretch::node::Node>,
@ -43,40 +43,41 @@ impl FlexSurface {
} }
pub fn upsert_leaf(&mut self, entity: Entity, style: &Style, calculated_size: CalculatedSize) { pub fn upsert_leaf(&mut self, entity: Entity, style: &Style, calculated_size: CalculatedSize) {
let mut added = false;
let stretch = &mut self.stretch; let stretch = &mut self.stretch;
let stretch_style = style.into(); let stretch_style = style.into();
let stretch_node = self.entity_to_stretch.entry(entity).or_insert_with(|| { let measure = Box::new(move |constraints: stretch::geometry::Size<Number>| {
added = true; let mut size = stretch::geometry::Size {
let stretch_node = stretch width: calculated_size.size.width,
.new_leaf( height: calculated_size.size.height,
stretch_style, };
Box::new(move |_| { match (constraints.width, constraints.height) {
Ok(stretch::geometry::Size { (Number::Undefined, Number::Undefined) => {}
width: calculated_size.size.width, (Number::Defined(width), Number::Undefined) => {
height: calculated_size.size.height, size.height = width * size.height / size.width;
}) size.width = width;
}), }
) (Number::Undefined, Number::Defined(height)) => {
.unwrap(); size.width = height * size.width / size.height;
stretch_node size.height = height;
}
(Number::Defined(width), Number::Defined(height)) => {
size.width = width;
size.height = height;
}
}
Ok(size)
}); });
if !added { if let Some(stretch_node) = self.entity_to_stretch.get(&entity) {
self.stretch self.stretch
.set_style(*stretch_node, stretch_style) .set_style(*stretch_node, stretch_style)
.unwrap(); .unwrap();
self.stretch self.stretch
.set_measure( .set_measure(*stretch_node, Some(measure))
*stretch_node,
Some(Box::new(move |_| {
Ok(stretch::geometry::Size {
width: calculated_size.size.width,
height: calculated_size.size.height,
})
})),
)
.unwrap(); .unwrap();
} else {
let stretch_node = stretch.new_leaf(stretch_style, measure).unwrap();
self.entity_to_stretch.insert(entity, stretch_node);
} }
} }

View File

@ -40,6 +40,7 @@ impl AppPlugin for UiPlugin {
.add_system_to_stage_front(stage::POST_UPDATE, flex_node_system.system()) .add_system_to_stage_front(stage::POST_UPDATE, flex_node_system.system())
.add_system_to_stage_front(stage::POST_UPDATE, ui_z_system.system()) .add_system_to_stage_front(stage::POST_UPDATE, ui_z_system.system())
.add_system_to_stage_front(stage::POST_UPDATE, widget::text_system.system()) .add_system_to_stage_front(stage::POST_UPDATE, widget::text_system.system())
.add_system_to_stage_front(stage::POST_UPDATE, widget::image_node_system.system())
.add_system_to_stage(bevy_render::stage::DRAW, widget::draw_text_system.system()); .add_system_to_stage(bevy_render::stage::DRAW, widget::draw_text_system.system());
let resources = app.resources(); let resources = app.resources();

View File

@ -0,0 +1,35 @@
use crate::CalculatedSize;
use bevy_asset::{Assets, Handle};
use bevy_ecs::{Query, Res};
use bevy_math::Size;
use bevy_render::texture::Texture;
use bevy_sprite::ColorMaterial;
pub enum Image {
KeepAspect,
}
impl Default for Image {
fn default() -> Self {
Image::KeepAspect
}
}
pub fn image_node_system(
materials: Res<Assets<ColorMaterial>>,
textures: Res<Assets<Texture>>,
mut query: Query<(&Image, &mut CalculatedSize, &Handle<ColorMaterial>)>,
) {
for (_image, mut calculated_size, material_handle) in &mut query.iter() {
materials
.get(material_handle)
.and_then(|material| material.texture)
.and_then(|texture_handle| textures.get(&texture_handle))
.map(|texture| {
calculated_size.size = Size {
width: texture.size.x(),
height: texture.size.y(),
};
});
}
}

View File

@ -1,5 +1,7 @@
mod button; mod button;
mod text; mod text;
mod image;
pub use button::*; pub use button::*;
pub use text::*; pub use text::*;
pub use image::*;

View File

@ -10,16 +10,8 @@ fn main() {
fn setup( fn setup(
mut commands: Commands, mut commands: Commands,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
mut textures: ResMut<Assets<Texture>>,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
) { ) {
let texture_handle = asset_server
.load_sync(&mut textures, "assets/branding/bevy_logo_dark_big.png")
.unwrap();
let texture = textures.get(&texture_handle).unwrap();
let aspect = texture.aspect();
commands commands
// ui camera // ui camera
.spawn(UiCameraComponents::default()) .spawn(UiCameraComponents::default())
@ -87,7 +79,7 @@ fn setup(
material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()), material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()),
..Default::default() ..Default::default()
}) })
// Absolute positioning // absolute positioning
.spawn(NodeComponents { .spawn(NodeComponents {
style: Style { style: Style {
size: Size::new(Val::Px(200.0), Val::Px(200.0)), size: Size::new(Val::Px(200.0), Val::Px(200.0)),
@ -222,12 +214,17 @@ fn setup(
}) })
.with_children(|parent| { .with_children(|parent| {
// bevy logo (image) // bevy logo (image)
parent.spawn(NodeComponents { parent.spawn(ImageComponents {
style: Style { style: Style {
min_size: Size::new(Val::Px(500.0), Val::Px(500.0 * aspect)), size: Size::new(Val::Px(500.0), Val::Auto),
..Default::default() ..Default::default()
}, },
material: materials.add(ColorMaterial::texture(texture_handle)), material: materials.add(
asset_server
.load("assets/branding/bevy_logo_dark_big.png")
.unwrap()
.into(),
),
draw: Draw { draw: Draw {
is_transparent: true, is_transparent: true,
..Default::default() ..Default::default()