From ba532e4a372b812cb47542e66b2dab697d702ccf Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 1 May 2023 16:40:53 +0100 Subject: [PATCH] `MeasureFunc` improvements (#8402) # Objective fixes #8516 * Give `CalculatedSize` a more specific and intuitive name. * `MeasureFunc`s should only be updated when their `CalculatedSize` is modified by the systems managing their content. For example, suppose that you have a UI displaying an image using an `ImageNode`. When the window is resized, the node's `MeasureFunc` will be updated even though the dimensions of the texture contained by the node are unchanged. * Fix the `CalculatedSize` API so that it no longer requires the extra boxing and the `dyn_clone` method. ## Solution * Rename `CalculatedSize` to `ContentSize` * Only update `MeasureFunc`s on `CalculatedSize` changes. * Remove the `dyn_clone` method from `Measure` and move the `Measure` from the `ContentSize` component rather than cloning it. * Change the measure_func field of `ContentSize` to type `Option`. Add a `set` method that wraps the given measure appropriately. --- ## Changelog * Renamed `CalculatedSize` to `ContentSize`. * Replaced `upsert_leaf` with a function `update_measure` that only updates the node's `MeasureFunc`. * `MeasureFunc`s are only updated when the `ContentSize` changes and not when the layout changes. * Scale factor is no longer applied to the size values passed to the `MeasureFunc`. * Remove the `ContentSize` scaling in `text_system`. * The `dyn_clone` method has been removed from the `Measure` trait. * `Measure`s are moved from the `ContentSize` component instead of cloning them. * Added `set` method to `ContentSize` that replaces the `new` function. ## Migration Guide * `CalculatedSize` has been renamed to `ContentSize`. * The `upsert_leaf` function has been removed from `UiSurface` and replaced with `update_measure` which updates the `MeasureFunc` without node insertion. * The `dyn_clone` method has been removed from the `Measure` trait. * The new function of `CalculatedSize` has been replaced with the method `set`. --- crates/bevy_ui/src/layout/mod.rs | 99 ++++++++++-------------------- crates/bevy_ui/src/lib.rs | 4 +- crates/bevy_ui/src/measurement.rs | 51 ++++++++------- crates/bevy_ui/src/node_bundles.rs | 10 +-- crates/bevy_ui/src/widget/image.rs | 18 +++--- crates/bevy_ui/src/widget/text.rs | 12 ++-- 6 files changed, 79 insertions(+), 115 deletions(-) diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index 8c808be06d..75dc688f8b 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -1,13 +1,14 @@ mod convert; -use crate::{CalculatedSize, Node, Style, UiScale}; +use crate::{ContentSize, Node, Style, UiScale}; use bevy_ecs::{ change_detection::DetectChanges, entity::Entity, event::EventReader, - query::{Changed, Or, With, Without}, + query::{Changed, With, Without}, removal_detection::RemovedComponents, system::{Query, Res, ResMut, Resource}, + world::Ref, }; use bevy_hierarchy::{Children, Parent}; use bevy_log::warn; @@ -16,11 +17,7 @@ use bevy_transform::components::Transform; use bevy_utils::HashMap; use bevy_window::{PrimaryWindow, Window, WindowResolution, WindowScaleFactorChanged}; use std::fmt; -use taffy::{ - prelude::{AvailableSpace, Size}, - style_helpers::TaffyMaxContent, - Taffy, -}; +use taffy::{prelude::Size, style_helpers::TaffyMaxContent, Taffy}; pub struct LayoutContext { pub scale_factor: f64, @@ -75,6 +72,8 @@ impl Default for UiSurface { } impl UiSurface { + /// Retrieves the taffy node corresponding to given entity exists, or inserts a new taffy node into the layout if no corresponding node exists. + /// Then convert the given `Style` and use it update the taffy node's style. pub fn upsert_node(&mut self, entity: Entity, style: &Style, context: &LayoutContext) { let mut added = false; let taffy = &mut self.taffy; @@ -90,43 +89,13 @@ impl UiSurface { } } - pub fn upsert_leaf( - &mut self, - entity: Entity, - style: &Style, - calculated_size: &CalculatedSize, - context: &LayoutContext, - ) { - let taffy = &mut self.taffy; - let taffy_style = convert::from_style(context, style); - let measure = calculated_size.measure.dyn_clone(); - let measure_func = taffy::node::MeasureFunc::Boxed(Box::new( - move |constraints: Size>, available: Size| { - let size = measure.measure( - constraints.width, - constraints.height, - available.width, - available.height, - ); - taffy::geometry::Size { - width: size.x, - height: size.y, - } - }, - )); - if let Some(taffy_node) = self.entity_to_taffy.get(&entity) { - self.taffy.set_style(*taffy_node, taffy_style).unwrap(); - self.taffy - .set_measure(*taffy_node, Some(measure_func)) - .unwrap(); - } else { - let taffy_node = taffy - .new_leaf_with_measure(taffy_style, measure_func) - .unwrap(); - self.entity_to_taffy.insert(entity, taffy_node); - } + /// Update the `MeasureFunc` of the taffy node corresponding to the given [`Entity`]. + pub fn update_measure(&mut self, entity: Entity, measure_func: taffy::node::MeasureFunc) { + let taffy_node = self.entity_to_taffy.get(&entity).unwrap(); + 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 { @@ -160,6 +129,7 @@ without UI components as a child of an entity with UI components, results may be } } + /// Retrieve or insert the root layout node and update its size to match the size of the window. pub fn update_window(&mut self, window: Entity, window_resolution: &WindowResolution) { let taffy = &mut self.taffy; let node = self @@ -185,6 +155,7 @@ without UI components as a child of an entity with UI components, results may be .unwrap(); } + /// Set the ui node entities without a [`Parent`] as children to the root node in the taffy layout. pub fn set_window_children( &mut self, parent_window: Entity, @@ -197,6 +168,7 @@ without UI components as a child of an entity with UI components, results may be self.taffy.set_children(*taffy_node, &child_nodes).unwrap(); } + /// Compute the layout for each window entity's corresponding root node in the layout. pub fn compute_window_layouts(&mut self) { for window_node in self.window_nodes.values() { self.taffy @@ -214,6 +186,8 @@ without UI components as a child of an entity with UI components, results may be } } + /// 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 @@ -235,6 +209,7 @@ pub enum LayoutError { TaffyError(taffy::error::TaffyError), } +/// Updates the UI's layout tree, computes the new layout geometry and then updates the sizes and transforms of all the UI nodes. #[allow(clippy::too_many_arguments)] pub fn ui_layout_system( primary_window: Query<(Entity, &Window), With>, @@ -244,18 +219,11 @@ pub fn ui_layout_system( mut resize_events: EventReader, mut ui_surface: ResMut, root_node_query: Query, Without)>, - full_node_query: Query<(Entity, &Style, Option<&CalculatedSize>), With>, - changed_style_query: Query< - (Entity, &Style), - (With, Without, Changed