Skip to main content

bevy_ui/
measurement.rs

1use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
2use bevy_math::Vec2;
3use bevy_reflect::{std_traits::ReflectDefault, Reflect};
4use bevy_text::FontCx;
5use core::fmt::Formatter;
6pub use taffy::style::AvailableSpace;
7use taffy::MaybeMath;
8use taffy::MaybeResolve;
9
10use crate::widget::ImageMeasure;
11
12use crate::widget::TextMeasure;
13
14impl core::fmt::Debug for ContentSize {
15    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
16        f.debug_struct("ContentSize").finish()
17    }
18}
19
20/// Inputs provided to [`Measure::measure`].
21pub struct MeasureArgs<'a> {
22    /// The known width from Taffy if this size is definite.
23    pub known_width: Option<f32>,
24    /// The known height from Taffy if this size is definite.
25    pub known_height: Option<f32>,
26    /// The horizontal space available for this UI node.
27    pub available_width: AvailableSpace,
28    /// The vertical space available for this UI node.
29    pub available_height: AvailableSpace,
30    /// Parley font database, needed for text measurement.
31    pub font_system: &'a mut FontCx,
32    /// Text layout buffer used to compute intrinsic text size when required.
33    pub buffer: Option<&'a mut bevy_text::ComputedTextBlock>,
34    /// Resolved style for this node in physical pixels.
35    pub style: &'a taffy::Style,
36}
37
38#[derive(#[automatically_derived]impl ::core::marker::Copy for ResolvedAxis { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for ResolvedAxis {
}
#[automatically_derived]impl ::core::clone::Clone for ResolvedAxis {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Option<f32>>;
        let _: ::core::clone::AssertParamIsClone<Option<f32>>;
        let _: ::core::clone::AssertParamIsClone<Option<f32>>;
        let _: ::core::clone::AssertParamIsClone<Option<f32>>;
        *self
    }
}Clone)]
39/// Resolved values for per-axis size constraints.
40pub struct ResolvedAxis {
41    /// Resolved minimum size along this axis.
42    pub min: Option<f32>,
43    /// Resolved preferred size along this axis.
44    pub preferred: Option<f32>,
45    /// Resolved maximum size along this axis.
46    pub max: Option<f32>,
47    /// Effective size along this axis after applying known size and min/max clamping.
48    pub effective: Option<f32>,
49}
50
51fn resolve_axis(
52    known_size: Option<f32>,
53    available_space: AvailableSpace,
54    min_dim: taffy::style::LengthPercentageAuto,
55    size_dim: taffy::style::Dimension,
56    max_dim: taffy::style::LengthPercentageAuto,
57) -> ResolvedAxis {
58    let calc = |_, _| 0.;
59    let available = available_space.into_option();
60    let min = min_dim.maybe_resolve(available, calc);
61    let preferred = size_dim.maybe_resolve(available, calc);
62    let max = max_dim.maybe_resolve(available, calc);
63    ResolvedAxis {
64        min,
65        preferred,
66        max,
67        effective: known_size.or(preferred.or(min).maybe_clamp(min, max)),
68    }
69}
70
71impl MeasureArgs<'_> {
72    /// Resolve the node's width constraints and the effective width.
73    pub fn resolve_width(&self) -> ResolvedAxis {
74        resolve_axis(
75            self.known_width,
76            self.available_width,
77            self.style.min_size.width,
78            self.style.size.width,
79            self.style.max_size.width,
80        )
81    }
82
83    /// Resolve the node's height constraints and the effective height.
84    pub fn resolve_height(&self) -> ResolvedAxis {
85        resolve_axis(
86            self.known_height,
87            self.available_height,
88            self.style.min_size.height,
89            self.style.size.height,
90            self.style.max_size.height,
91        )
92    }
93}
94
95/// A `Measure` is used to compute the size of a UI node
96/// when the size of that node is based on its content.
97pub trait Measure: Send + Sync + 'static {
98    /// Calculate the size of the node given the constraints.
99    fn measure(&mut self, measure_args: MeasureArgs<'_>) -> Vec2;
100}
101
102/// A type to serve as Taffy's node context (which allows the content size of leaf nodes to be computed)
103///
104/// It has specific variants for common built-in types to avoid making them opaque and needing to box them
105/// by wrapping them in a closure and a Custom variant that allows arbitrary measurement closures if required.
106pub enum NodeMeasure {
107    Fixed(FixedMeasure),
108    Text(TextMeasure),
109    Image(ImageMeasure),
110    Custom(Box<dyn Measure>),
111}
112
113impl Measure for NodeMeasure {
114    fn measure(&mut self, measure_args: MeasureArgs) -> Vec2 {
115        match self {
116            NodeMeasure::Fixed(fixed) => fixed.measure(measure_args),
117            NodeMeasure::Text(text) => text.measure(measure_args),
118            NodeMeasure::Image(image) => image.measure(measure_args),
119            NodeMeasure::Custom(custom) => custom.measure(measure_args),
120        }
121    }
122}
123
124/// A `FixedMeasure` is a `Measure` that ignores all constraints and
125/// always returns the same size.
126#[derive(#[automatically_derived]impl ::core::default::Default for FixedMeasure {
    #[inline]
    fn default() -> Self {
        FixedMeasure { size: ::core::default::Default::default() }
    }
}Default, #[automatically_derived]impl ::core::clone::Clone for FixedMeasure {
    #[inline]
    fn clone(&self) -> Self {
        FixedMeasure { size: ::core::clone::Clone::clone(&self.size) }
    }
}Clone)]
127pub struct FixedMeasure {
128    pub size: Vec2,
129}
130
131impl Measure for FixedMeasure {
132    fn measure(&mut self, _: MeasureArgs) -> Vec2 {
133        self.size
134    }
135}
136
137/// A node with a `ContentSize` component is a node where its size
138/// is based on its content.
139#[derive(impl bevy_ecs::component::Component for ContentSize where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {
    const STORAGE_TYPE: bevy_ecs::component::StorageType =
        bevy_ecs::component::StorageType::Table;
    type Mutability = bevy_ecs::component::Mutable;
    fn register_required_components(_requiree:
            bevy_ecs::component::ComponentId,
        required_components:
            &mut bevy_ecs::component::RequiredComponentsRegistrator) {}
    fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
        use bevy_ecs::component::{
            DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
        };
        (&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
    }
    fn relationship_accessor()
        ->
            ::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
        ::core::option::Option::None
    }
}Component, const _: () =
    {
        impl bevy_reflect::GetTypeRegistration for ContentSize where  {
            fn get_type_registration() -> bevy_reflect::TypeRegistration {
                let mut registration =
                    bevy_reflect::TypeRegistration::of::<Self>();
                registration.insert(<bevy_reflect::ReflectFromPtr as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.insert(<bevy_reflect::ReflectFromReflect as
                            bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
                registration.register_type_data_with::<ReflectComponent, Self,
                    _>(());
                registration.register_type_data_with::<ReflectDefault, Self,
                    _>(());
                registration
            }
            #[inline(never)]
            fn register_type_dependencies(registry:
                    &mut bevy_reflect::TypeRegistry) {}
        }
        impl bevy_reflect::Typed for ContentSize where  {
            #[inline(never)]
            fn type_info() -> &'static bevy_reflect::TypeInfo {
                static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
                    bevy_reflect::utility::NonGenericTypeInfoCell::new();
                CELL.get_or_set(#[inline(never)] ||
                        {
                            bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[]))
                        })
            }
        }
        #[allow(deprecated, reason =
        "derives on a deprecated type shouldn't be considered a usage")]
        impl bevy_reflect::TypePath for ContentSize where  {
            fn type_path() -> &'static str {
                "bevy_ui::measurement::ContentSize"
            }
            fn short_type_path() -> &'static str { "ContentSize" }
            fn type_ident() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("ContentSize")
            }
            fn crate_name() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui::measurement".split(':').next().unwrap())
            }
            fn module_path() -> ::core::option::Option<&'static str> {
                ::core::option::Option::Some("bevy_ui::measurement")
            }
        }
        impl bevy_reflect::Reflect for ContentSize where  {
            #[inline]
            fn into_any(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
                self
            }
            #[inline]
            fn as_any(&self) -> &dyn ::core::any::Any { self }
            #[inline]
            fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
            #[inline]
            fn into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
                self
            }
            #[inline]
            fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
            #[inline]
            fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
                self
            }
            #[inline]
            fn set(&mut self,
                value:
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
                ->
                    ::core::result::Result<(),
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
                *self = <dyn bevy_reflect::Reflect>::take(value)?;
                ::core::result::Result::Ok(())
            }
        }
        #[allow(non_upper_case_globals)]
        const _: () =
            {
                static __INVENTORY: ::inventory::Node =
                    ::inventory::Node {
                        value: &{
                                bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<ContentSize
                                        as
                                        bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
                            },
                        next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
                    };
                #[link_section = ".text.startup"]
                unsafe extern "C" fn __ctor() {
                    unsafe {
                        ::inventory::ErasedNode::submit(__INVENTORY.value,
                            &__INVENTORY)
                    }
                }
                #[used]
                #[link_section = ".init_array"]
                static __CTOR: unsafe extern "C" fn() = __ctor;
            };
        impl bevy_reflect::structs::Struct for ContentSize where  {
            fn field(&self, name: &str)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match name { _ => ::core::option::Option::None, }
            }
            fn field_mut(&mut self, name: &str)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match name { _ => ::core::option::Option::None, }
            }
            fn field_at(&self, index: usize)
                -> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
                match index { _ => ::core::option::Option::None, }
            }
            fn field_at_mut(&mut self, index: usize)
                ->
                    ::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
                match index { _ => ::core::option::Option::None, }
            }
            fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
                match index { _ => ::core::option::Option::None, }
            }
            fn index_of_name(&self, name: &str)
                -> ::core::option::Option<usize> {
                match name { _ => ::core::option::Option::None, }
            }
            fn field_len(&self) -> usize { 0usize }
            fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
                bevy_reflect::structs::FieldIter::new(self)
            }
            fn to_dynamic_struct(&self)
                ->
                    ::core::result::Result<bevy_reflect::structs::DynamicStruct,
                    bevy_reflect::ReflectCloneError> {
                let mut dynamic: bevy_reflect::structs::DynamicStruct =
                    ::core::default::Default::default();
                dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
                ::core::result::Result::Ok(dynamic)
            }
        }
        impl bevy_reflect::PartialReflect for ContentSize where  {
            #[inline]
            fn get_represented_type_info(&self)
                -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
                ::core::option::Option::Some(<Self as
                            bevy_reflect::Typed>::type_info())
            }
            #[inline]
            fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
                -> ::core::result::Result<(), bevy_reflect::ApplyError> {
                if let bevy_reflect::ReflectRef::Struct(struct_value) =
                        bevy_reflect::PartialReflect::reflect_ref(value) {
                    for (name, value) in
                        bevy_reflect::structs::Struct::iter_fields(struct_value) {
                        if let ::core::option::Option::Some(v) =
                                bevy_reflect::structs::Struct::field_mut(self, name) {
                            bevy_reflect::PartialReflect::try_apply(v, value)?;
                        }
                    }
                } else {
                    return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
                                from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
                                to_kind: bevy_reflect::ReflectKind::Struct,
                            });
                }
                ::core::result::Result::Ok(())
            }
            #[inline]
            fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
                bevy_reflect::ReflectKind::Struct
            }
            #[inline]
            fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
                bevy_reflect::ReflectRef::Struct(self)
            }
            #[inline]
            fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
                bevy_reflect::ReflectMut::Struct(self)
            }
            #[inline]
            fn reflect_owned(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                -> bevy_reflect::ReflectOwned {
                bevy_reflect::ReflectOwned::Struct(self)
            }
            #[inline]
            fn try_into_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
                ::core::result::Result::Ok(self)
            }
            #[inline]
            fn try_as_reflect(&self)
                -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn try_as_reflect_mut(&mut self)
                -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
                ::core::option::Option::Some(self)
            }
            #[inline]
            fn into_partial_reflect(self:
                    bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
                ->
                    bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
                self
            }
            #[inline]
            fn as_partial_reflect(&self)
                -> &dyn bevy_reflect::PartialReflect {
                self
            }
            #[inline]
            fn as_partial_reflect_mut(&mut self)
                -> &mut dyn bevy_reflect::PartialReflect {
                self
            }
            fn reflect_partial_eq(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<bool> {
                (bevy_reflect::structs::struct_partial_eq)(self, value)
            }
            fn reflect_partial_cmp(&self,
                value: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<::core::cmp::Ordering> {
                (bevy_reflect::structs::struct_partial_cmp)(self, value)
            }
            #[inline]
            #[allow(unreachable_code, reason =
            "Ignored fields without a `clone` attribute will early-return with an error")]
            fn reflect_clone(&self)
                ->
                    ::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
                    bevy_reflect::ReflectCloneError> {
                ::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(Self {
                            measure: return ::core::result::Result::Err(bevy_reflect::ReflectCloneError::FieldNotCloneable {
                                        field: bevy_reflect::FieldId::Named(bevy_reflect::__macro_exports::alloc_utils::Cow::Borrowed("measure")),
                                        variant: ::core::option::Option::None,
                                        container_type_path: bevy_reflect::__macro_exports::alloc_utils::Cow::Borrowed(<Self
                                                    as bevy_reflect::TypePath>::type_path()),
                                    }),
                        }))
            }
        }
        impl bevy_reflect::FromReflect for ContentSize where  {
            fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
                -> ::core::option::Option<Self> {
                if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
                        bevy_reflect::PartialReflect::reflect_ref(reflect) {
                    let mut __this =
                        <Self as ::core::default::Default>::default();
                    ::core::option::Option::Some(__this)
                } else { ::core::option::Option::None }
            }
        }
    };Reflect, #[automatically_derived]impl ::core::default::Default for ContentSize {
    #[inline]
    fn default() -> Self {
        ContentSize { measure: ::core::default::Default::default() }
    }
}Default)]
140#[reflect(Component, Default)]
141pub struct ContentSize {
142    /// The `Measure` used to compute the intrinsic size
143    #[reflect(ignore)]
144    pub(crate) measure: Option<NodeMeasure>,
145}
146
147impl ContentSize {
148    /// Set a `Measure` for the UI node entity with this component
149    pub fn set(&mut self, measure: NodeMeasure) {
150        self.measure = Some(measure);
151    }
152
153    /// Clear the current `Measure` for this UI node.
154    pub fn clear(&mut self) {
155        self.measure = None;
156    }
157
158    /// Creates a `ContentSize` with a `Measure` that always returns given `size` argument, regardless of the UI layout's constraints.
159    pub fn fixed_size(size: Vec2) -> ContentSize {
160        let mut content_size = Self::default();
161        content_size.set(NodeMeasure::Fixed(FixedMeasure { size }));
162        content_size
163    }
164}