mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 06:30:19 +00:00
[bevy_ui/layout] Extract UiSurface to its own file (#12801)
This is 1 of 5 iterative PR's that affect bevy_ui/layout --- # Objective - Extract `UiSurface` into its own file to make diffs in future PR's easier to digest ## Solution - Moved `UiSurface` to its own file
This commit is contained in:
parent
d0a5ddacd9
commit
cf092d45f9
4 changed files with 253 additions and 231 deletions
|
@ -1,10 +1,13 @@
|
|||
use crate::UiSurface;
|
||||
use bevy_ecs::prelude::Entity;
|
||||
use bevy_utils::HashMap;
|
||||
use std::fmt::Write;
|
||||
|
||||
use taffy::prelude::Node;
|
||||
use taffy::tree::LayoutTree;
|
||||
|
||||
use bevy_ecs::prelude::Entity;
|
||||
use bevy_utils::HashMap;
|
||||
|
||||
use crate::layout::ui_surface::UiSurface;
|
||||
|
||||
/// Prints a debug representation of the computed layout of the UI layout tree for each window.
|
||||
pub fn print_ui_layout_tree(ui_surface: &UiSurface) {
|
||||
let taffy_to_entity: HashMap<Node, Entity> = ui_surface
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
mod convert;
|
||||
pub mod debug;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiScale};
|
||||
use bevy_ecs::{
|
||||
change_detection::{DetectChanges, DetectChangesMut},
|
||||
entity::{Entity, EntityHashMap},
|
||||
entity::Entity,
|
||||
event::EventReader,
|
||||
query::{With, Without},
|
||||
removal_detection::RemovedComponents,
|
||||
system::{Query, Res, ResMut, Resource, SystemParam},
|
||||
system::{Query, Res, ResMut, SystemParam},
|
||||
world::Ref,
|
||||
};
|
||||
use bevy_hierarchy::{Children, Parent};
|
||||
|
@ -16,11 +14,15 @@ use bevy_math::{UVec2, Vec2};
|
|||
use bevy_render::camera::{Camera, NormalizedRenderTarget};
|
||||
use bevy_transform::components::Transform;
|
||||
use bevy_utils::tracing::warn;
|
||||
use bevy_utils::{default, HashMap, HashSet};
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};
|
||||
use std::fmt;
|
||||
use taffy::{tree::LayoutTree, Taffy};
|
||||
use thiserror::Error;
|
||||
use ui_surface::UiSurface;
|
||||
|
||||
use crate::{ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiScale};
|
||||
|
||||
mod convert;
|
||||
pub mod debug;
|
||||
pub(crate) mod ui_surface;
|
||||
|
||||
pub struct LayoutContext {
|
||||
pub scale_factor: f32,
|
||||
|
@ -41,218 +43,6 @@ impl LayoutContext {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct RootNodePair {
|
||||
// The implicit "viewport" node created by Bevy
|
||||
implicit_viewport_node: taffy::node::Node,
|
||||
// The root (parentless) node specified by the user
|
||||
user_root_node: taffy::node::Node,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct UiSurface {
|
||||
entity_to_taffy: EntityHashMap<taffy::node::Node>,
|
||||
camera_entity_to_taffy: EntityHashMap<EntityHashMap<taffy::node::Node>>,
|
||||
camera_roots: EntityHashMap<Vec<RootNodePair>>,
|
||||
taffy: Taffy,
|
||||
}
|
||||
|
||||
fn _assert_send_sync_ui_surface_impl_safe() {
|
||||
fn _assert_send_sync<T: Send + Sync>() {}
|
||||
_assert_send_sync::<EntityHashMap<taffy::node::Node>>();
|
||||
_assert_send_sync::<Taffy>();
|
||||
_assert_send_sync::<UiSurface>();
|
||||
}
|
||||
|
||||
impl fmt::Debug for UiSurface {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("UiSurface")
|
||||
.field("entity_to_taffy", &self.entity_to_taffy)
|
||||
.field("camera_roots", &self.camera_roots)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UiSurface {
|
||||
fn default() -> Self {
|
||||
let mut taffy = Taffy::new();
|
||||
taffy.disable_rounding();
|
||||
Self {
|
||||
entity_to_taffy: Default::default(),
|
||||
camera_entity_to_taffy: Default::default(),
|
||||
camera_roots: Default::default(),
|
||||
taffy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiSurface {
|
||||
/// Retrieves the Taffy node associated with the given UI node entity and updates its style.
|
||||
/// If no associated Taffy node exists a new Taffy node is inserted into the Taffy layout.
|
||||
pub fn upsert_node(&mut self, entity: Entity, style: &Style, context: &LayoutContext) {
|
||||
let mut added = false;
|
||||
let taffy = &mut self.taffy;
|
||||
let taffy_node = self.entity_to_taffy.entry(entity).or_insert_with(|| {
|
||||
added = true;
|
||||
taffy.new_leaf(convert::from_style(context, style)).unwrap()
|
||||
});
|
||||
|
||||
if !added {
|
||||
self.taffy
|
||||
.set_style(*taffy_node, convert::from_style(context, style))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the `MeasureFunc` of the taffy node corresponding to the given [`Entity`] if the node exists.
|
||||
pub fn try_update_measure(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
measure_func: taffy::node::MeasureFunc,
|
||||
) -> Option<()> {
|
||||
let taffy_node = self.entity_to_taffy.get(&entity)?;
|
||||
|
||||
self.taffy.set_measure(*taffy_node, Some(measure_func)).ok()
|
||||
}
|
||||
|
||||
/// Update the children of the taffy node corresponding to the given [`Entity`].
|
||||
pub fn update_children(&mut self, entity: Entity, children: &Children) {
|
||||
let mut taffy_children = Vec::with_capacity(children.len());
|
||||
for child in children {
|
||||
if let Some(taffy_node) = self.entity_to_taffy.get(child) {
|
||||
taffy_children.push(*taffy_node);
|
||||
} else {
|
||||
warn!(
|
||||
"Unstyled child in a UI entity hierarchy. You are using an entity \
|
||||
without UI components as a child of an entity with UI components, results may be unexpected."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let taffy_node = self.entity_to_taffy.get(&entity).unwrap();
|
||||
self.taffy
|
||||
.set_children(*taffy_node, &taffy_children)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Removes children from the entity's taffy node if it exists. Does nothing otherwise.
|
||||
pub fn try_remove_children(&mut self, entity: Entity) {
|
||||
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
|
||||
self.taffy.set_children(*taffy_node, &[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the measure from the entity's taffy node if it exists. Does nothing otherwise.
|
||||
pub fn try_remove_measure(&mut self, entity: Entity) {
|
||||
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
|
||||
self.taffy.set_measure(*taffy_node, None).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the ui node entities without a [`Parent`] as children to the root node in the taffy layout.
|
||||
pub fn set_camera_children(
|
||||
&mut self,
|
||||
camera_id: Entity,
|
||||
children: impl Iterator<Item = Entity>,
|
||||
) {
|
||||
let viewport_style = taffy::style::Style {
|
||||
display: taffy::style::Display::Grid,
|
||||
// Note: Taffy percentages are floats ranging from 0.0 to 1.0.
|
||||
// So this is setting width:100% and height:100%
|
||||
size: taffy::geometry::Size {
|
||||
width: taffy::style::Dimension::Percent(1.0),
|
||||
height: taffy::style::Dimension::Percent(1.0),
|
||||
},
|
||||
align_items: Some(taffy::style::AlignItems::Start),
|
||||
justify_items: Some(taffy::style::JustifyItems::Start),
|
||||
..default()
|
||||
};
|
||||
|
||||
let camera_root_node_map = self.camera_entity_to_taffy.entry(camera_id).or_default();
|
||||
let existing_roots = self.camera_roots.entry(camera_id).or_default();
|
||||
let mut new_roots = Vec::new();
|
||||
for entity in children {
|
||||
let node = *self.entity_to_taffy.get(&entity).unwrap();
|
||||
let root_node = existing_roots
|
||||
.iter()
|
||||
.find(|n| n.user_root_node == node)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
if let Some(previous_parent) = self.taffy.parent(node) {
|
||||
// remove the root node from the previous implicit node's children
|
||||
self.taffy.remove_child(previous_parent, node).unwrap();
|
||||
}
|
||||
|
||||
let viewport_node = *camera_root_node_map
|
||||
.entry(entity)
|
||||
.or_insert_with(|| self.taffy.new_leaf(viewport_style.clone()).unwrap());
|
||||
self.taffy.add_child(viewport_node, node).unwrap();
|
||||
|
||||
RootNodePair {
|
||||
implicit_viewport_node: viewport_node,
|
||||
user_root_node: node,
|
||||
}
|
||||
});
|
||||
new_roots.push(root_node);
|
||||
}
|
||||
|
||||
self.camera_roots.insert(camera_id, new_roots);
|
||||
}
|
||||
|
||||
/// Compute the layout for each window entity's corresponding root node in the layout.
|
||||
pub fn compute_camera_layout(&mut self, camera: Entity, render_target_resolution: UVec2) {
|
||||
let Some(camera_root_nodes) = self.camera_roots.get(&camera) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let available_space = taffy::geometry::Size {
|
||||
width: taffy::style::AvailableSpace::Definite(render_target_resolution.x as f32),
|
||||
height: taffy::style::AvailableSpace::Definite(render_target_resolution.y as f32),
|
||||
};
|
||||
for root_nodes in camera_root_nodes {
|
||||
self.taffy
|
||||
.compute_layout(root_nodes.implicit_viewport_node, available_space)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes each camera entity from the internal map and then removes their associated node from taffy
|
||||
pub fn remove_camera_entities(&mut self, entities: impl IntoIterator<Item = Entity>) {
|
||||
for entity in entities {
|
||||
if let Some(camera_root_node_map) = self.camera_entity_to_taffy.remove(&entity) {
|
||||
for (_, node) in camera_root_node_map.iter() {
|
||||
self.taffy.remove(*node).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes each entity from the internal map and then removes their associated node from taffy
|
||||
pub fn remove_entities(&mut self, entities: impl IntoIterator<Item = Entity>) {
|
||||
for entity in entities {
|
||||
if let Some(node) = self.entity_to_taffy.remove(&entity) {
|
||||
self.taffy.remove(node).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the layout geometry for the taffy node corresponding to the ui node [`Entity`].
|
||||
/// Does not compute the layout geometry, `compute_window_layouts` should be run before using this function.
|
||||
pub fn get_layout(&self, entity: Entity) -> Result<&taffy::layout::Layout, LayoutError> {
|
||||
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
|
||||
self.taffy
|
||||
.layout(*taffy_node)
|
||||
.map_err(LayoutError::TaffyError)
|
||||
} else {
|
||||
warn!(
|
||||
"Styled child in a non-UI entity hierarchy. You are using an entity \
|
||||
with UI components as a child of an entity without UI components, results may be unexpected."
|
||||
);
|
||||
Err(LayoutError::InvalidHierarchy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LayoutError {
|
||||
#[error("Invalid hierarchy")]
|
||||
|
@ -533,12 +323,8 @@ fn round_layout_coords(value: Vec2) -> Vec2 {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::layout::round_layout_coords;
|
||||
use crate::prelude::*;
|
||||
use crate::ui_layout_system;
|
||||
use crate::update::update_target_camera_system;
|
||||
use crate::ContentSize;
|
||||
use crate::UiSurface;
|
||||
use taffy::tree::LayoutTree;
|
||||
|
||||
use bevy_asset::AssetEvent;
|
||||
use bevy_asset::Assets;
|
||||
use bevy_core_pipeline::core_2d::Camera2dBundle;
|
||||
|
@ -566,7 +352,13 @@ mod tests {
|
|||
use bevy_window::WindowResized;
|
||||
use bevy_window::WindowResolution;
|
||||
use bevy_window::WindowScaleFactorChanged;
|
||||
use taffy::tree::LayoutTree;
|
||||
|
||||
use crate::layout::round_layout_coords;
|
||||
use crate::layout::ui_surface::UiSurface;
|
||||
use crate::prelude::*;
|
||||
use crate::ui_layout_system;
|
||||
use crate::update::update_target_camera_system;
|
||||
use crate::ContentSize;
|
||||
|
||||
#[test]
|
||||
fn round_layout_coords_must_round_ties_up() {
|
||||
|
|
226
crates/bevy_ui/src/layout/ui_surface.rs
Normal file
226
crates/bevy_ui/src/layout/ui_surface.rs
Normal file
|
@ -0,0 +1,226 @@
|
|||
use std::fmt;
|
||||
|
||||
use taffy::prelude::LayoutTree;
|
||||
use taffy::Taffy;
|
||||
|
||||
use bevy_ecs::entity::{Entity, EntityHashMap};
|
||||
use bevy_ecs::prelude::Resource;
|
||||
use bevy_hierarchy::Children;
|
||||
use bevy_math::UVec2;
|
||||
use bevy_utils::default;
|
||||
use bevy_utils::tracing::warn;
|
||||
|
||||
use crate::layout::convert;
|
||||
use crate::{LayoutContext, LayoutError, Style};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RootNodePair {
|
||||
// The implicit "viewport" node created by Bevy
|
||||
pub(super) implicit_viewport_node: taffy::node::Node,
|
||||
// The root (parentless) node specified by the user
|
||||
pub(super) user_root_node: taffy::node::Node,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct UiSurface {
|
||||
pub(super) entity_to_taffy: EntityHashMap<taffy::node::Node>,
|
||||
pub(super) camera_entity_to_taffy: EntityHashMap<EntityHashMap<taffy::node::Node>>,
|
||||
pub(super) camera_roots: EntityHashMap<Vec<RootNodePair>>,
|
||||
pub(super) taffy: Taffy,
|
||||
}
|
||||
|
||||
fn _assert_send_sync_ui_surface_impl_safe() {
|
||||
fn _assert_send_sync<T: Send + Sync>() {}
|
||||
_assert_send_sync::<EntityHashMap<taffy::node::Node>>();
|
||||
_assert_send_sync::<Taffy>();
|
||||
_assert_send_sync::<UiSurface>();
|
||||
}
|
||||
|
||||
impl fmt::Debug for UiSurface {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("UiSurface")
|
||||
.field("entity_to_taffy", &self.entity_to_taffy)
|
||||
.field("camera_roots", &self.camera_roots)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UiSurface {
|
||||
fn default() -> Self {
|
||||
let mut taffy = Taffy::new();
|
||||
taffy.disable_rounding();
|
||||
Self {
|
||||
entity_to_taffy: Default::default(),
|
||||
camera_entity_to_taffy: Default::default(),
|
||||
camera_roots: Default::default(),
|
||||
taffy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiSurface {
|
||||
/// Retrieves the Taffy node associated with the given UI node entity and updates its style.
|
||||
/// If no associated Taffy node exists a new Taffy node is inserted into the Taffy layout.
|
||||
pub fn upsert_node(&mut self, entity: Entity, style: &Style, context: &LayoutContext) {
|
||||
let mut added = false;
|
||||
let taffy = &mut self.taffy;
|
||||
let taffy_node = self.entity_to_taffy.entry(entity).or_insert_with(|| {
|
||||
added = true;
|
||||
taffy.new_leaf(convert::from_style(context, style)).unwrap()
|
||||
});
|
||||
|
||||
if !added {
|
||||
self.taffy
|
||||
.set_style(*taffy_node, convert::from_style(context, style))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the `MeasureFunc` of the taffy node corresponding to the given [`Entity`] if the node exists.
|
||||
pub fn try_update_measure(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
measure_func: taffy::node::MeasureFunc,
|
||||
) -> Option<()> {
|
||||
let taffy_node = self.entity_to_taffy.get(&entity)?;
|
||||
|
||||
self.taffy.set_measure(*taffy_node, Some(measure_func)).ok()
|
||||
}
|
||||
|
||||
/// Update the children of the taffy node corresponding to the given [`Entity`].
|
||||
pub fn update_children(&mut self, entity: Entity, children: &Children) {
|
||||
let mut taffy_children = Vec::with_capacity(children.len());
|
||||
for child in children {
|
||||
if let Some(taffy_node) = self.entity_to_taffy.get(child) {
|
||||
taffy_children.push(*taffy_node);
|
||||
} else {
|
||||
warn!(
|
||||
"Unstyled child in a UI entity hierarchy. You are using an entity \
|
||||
without UI components as a child of an entity with UI components, results may be unexpected."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let taffy_node = self.entity_to_taffy.get(&entity).unwrap();
|
||||
self.taffy
|
||||
.set_children(*taffy_node, &taffy_children)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Removes children from the entity's taffy node if it exists. Does nothing otherwise.
|
||||
pub fn try_remove_children(&mut self, entity: Entity) {
|
||||
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
|
||||
self.taffy.set_children(*taffy_node, &[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the measure from the entity's taffy node if it exists. Does nothing otherwise.
|
||||
pub fn try_remove_measure(&mut self, entity: Entity) {
|
||||
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
|
||||
self.taffy.set_measure(*taffy_node, None).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the ui node entities without a [`bevy_hierarchy::Parent`] as children to the root node in the taffy layout.
|
||||
pub fn set_camera_children(
|
||||
&mut self,
|
||||
camera_id: Entity,
|
||||
children: impl Iterator<Item = Entity>,
|
||||
) {
|
||||
let viewport_style = taffy::style::Style {
|
||||
display: taffy::style::Display::Grid,
|
||||
// Note: Taffy percentages are floats ranging from 0.0 to 1.0.
|
||||
// So this is setting width:100% and height:100%
|
||||
size: taffy::geometry::Size {
|
||||
width: taffy::style::Dimension::Percent(1.0),
|
||||
height: taffy::style::Dimension::Percent(1.0),
|
||||
},
|
||||
align_items: Some(taffy::style::AlignItems::Start),
|
||||
justify_items: Some(taffy::style::JustifyItems::Start),
|
||||
..default()
|
||||
};
|
||||
|
||||
let camera_root_node_map = self.camera_entity_to_taffy.entry(camera_id).or_default();
|
||||
let existing_roots = self.camera_roots.entry(camera_id).or_default();
|
||||
let mut new_roots = Vec::new();
|
||||
for entity in children {
|
||||
let node = *self.entity_to_taffy.get(&entity).unwrap();
|
||||
let root_node = existing_roots
|
||||
.iter()
|
||||
.find(|n| n.user_root_node == node)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
if let Some(previous_parent) = self.taffy.parent(node) {
|
||||
// remove the root node from the previous implicit node's children
|
||||
self.taffy.remove_child(previous_parent, node).unwrap();
|
||||
}
|
||||
|
||||
let viewport_node = *camera_root_node_map
|
||||
.entry(entity)
|
||||
.or_insert_with(|| self.taffy.new_leaf(viewport_style.clone()).unwrap());
|
||||
self.taffy.add_child(viewport_node, node).unwrap();
|
||||
|
||||
RootNodePair {
|
||||
implicit_viewport_node: viewport_node,
|
||||
user_root_node: node,
|
||||
}
|
||||
});
|
||||
new_roots.push(root_node);
|
||||
}
|
||||
|
||||
self.camera_roots.insert(camera_id, new_roots);
|
||||
}
|
||||
|
||||
/// Compute the layout for each window entity's corresponding root node in the layout.
|
||||
pub fn compute_camera_layout(&mut self, camera: Entity, render_target_resolution: UVec2) {
|
||||
let Some(camera_root_nodes) = self.camera_roots.get(&camera) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let available_space = taffy::geometry::Size {
|
||||
width: taffy::style::AvailableSpace::Definite(render_target_resolution.x as f32),
|
||||
height: taffy::style::AvailableSpace::Definite(render_target_resolution.y as f32),
|
||||
};
|
||||
for root_nodes in camera_root_nodes {
|
||||
self.taffy
|
||||
.compute_layout(root_nodes.implicit_viewport_node, available_space)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes each camera entity from the internal map and then removes their associated node from taffy
|
||||
pub fn remove_camera_entities(&mut self, entities: impl IntoIterator<Item = Entity>) {
|
||||
for entity in entities {
|
||||
if let Some(camera_root_node_map) = self.camera_entity_to_taffy.remove(&entity) {
|
||||
for (_, node) in camera_root_node_map.iter() {
|
||||
self.taffy.remove(*node).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes each entity from the internal map and then removes their associated node from taffy
|
||||
pub fn remove_entities(&mut self, entities: impl IntoIterator<Item = Entity>) {
|
||||
for entity in entities {
|
||||
if let Some(node) = self.entity_to_taffy.remove(&entity) {
|
||||
self.taffy.remove(node).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the layout geometry for the taffy node corresponding to the ui node [`Entity`].
|
||||
/// Does not compute the layout geometry, `compute_window_layouts` should be run before using this function.
|
||||
pub fn get_layout(&self, entity: Entity) -> Result<&taffy::layout::Layout, LayoutError> {
|
||||
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
|
||||
self.taffy
|
||||
.layout(*taffy_node)
|
||||
.map_err(LayoutError::TaffyError)
|
||||
} else {
|
||||
warn!(
|
||||
"Styled child in a non-UI entity hierarchy. You are using an entity \
|
||||
with UI components as a child of an entity without UI components, results may be unexpected."
|
||||
);
|
||||
Err(LayoutError::InvalidHierarchy)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ use bevy_ecs::prelude::*;
|
|||
use bevy_input::InputSystem;
|
||||
use bevy_render::RenderApp;
|
||||
use bevy_transform::TransformSystem;
|
||||
use layout::ui_surface::UiSurface;
|
||||
use stack::ui_stack_system;
|
||||
pub use stack::UiStack;
|
||||
use update::{update_clipping_system, update_target_camera_system};
|
||||
|
|
Loading…
Reference in a new issue