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::CosmicFontSystem;
5use core::fmt::Formatter;
6pub use taffy::style::AvailableSpace;
7
8use crate::widget::ImageMeasure;
9
10use crate::widget::TextMeasure;
11
12impl core::fmt::Debug for ContentSize {
13    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
14        f.debug_struct("ContentSize").finish()
15    }
16}
17
18pub struct MeasureArgs<'a> {
19    pub width: Option<f32>,
20    pub height: Option<f32>,
21    pub available_width: AvailableSpace,
22    pub available_height: AvailableSpace,
23    pub font_system: &'a mut CosmicFontSystem,
24    pub buffer: Option<&'a mut bevy_text::ComputedTextBlock>,
25}
26
27/// A `Measure` is used to compute the size of a ui node
28/// when the size of that node is based on its content.
29pub trait Measure: Send + Sync + 'static {
30    /// Calculate the size of the node given the constraints.
31    fn measure(&mut self, measure_args: MeasureArgs<'_>, style: &taffy::Style) -> Vec2;
32}
33
34/// A type to serve as Taffy's node context (which allows the content size of leaf nodes to be computed)
35///
36/// It has specific variants for common built-in types to avoid making them opaque and needing to box them
37/// by wrapping them in a closure and a Custom variant that allows arbitrary measurement closures if required.
38pub enum NodeMeasure {
39    Fixed(FixedMeasure),
40
41    Text(TextMeasure),
42    Image(ImageMeasure),
43    Custom(Box<dyn Measure>),
44}
45
46impl Measure for NodeMeasure {
47    fn measure(&mut self, measure_args: MeasureArgs, style: &taffy::Style) -> Vec2 {
48        match self {
49            NodeMeasure::Fixed(fixed) => fixed.measure(measure_args, style),
50
51            NodeMeasure::Text(text) => text.measure(measure_args, style),
52            NodeMeasure::Image(image) => image.measure(measure_args, style),
53            NodeMeasure::Custom(custom) => custom.measure(measure_args, style),
54        }
55    }
56}
57
58/// A `FixedMeasure` is a `Measure` that ignores all constraints and
59/// always returns the same size.
60#[derive(Default, Clone)]
61pub struct FixedMeasure {
62    pub size: Vec2,
63}
64
65impl Measure for FixedMeasure {
66    fn measure(&mut self, _: MeasureArgs, _: &taffy::Style) -> Vec2 {
67        self.size
68    }
69}
70
71/// A node with a `ContentSize` component is a node where its size
72/// is based on its content.
73#[derive(Component, Reflect, Default)]
74#[reflect(Component, Default)]
75pub struct ContentSize {
76    /// The `Measure` used to compute the intrinsic size
77    #[reflect(ignore)]
78    pub(crate) measure: Option<NodeMeasure>,
79}
80
81impl ContentSize {
82    /// Set a `Measure` for the UI node entity with this component
83    pub fn set(&mut self, measure: NodeMeasure) {
84        self.measure = Some(measure);
85    }
86
87    /// Creates a `ContentSize` with a `Measure` that always returns given `size` argument, regardless of the UI layout's constraints.
88    pub fn fixed_size(size: Vec2) -> ContentSize {
89        let mut content_size = Self::default();
90        content_size.set(NodeMeasure::Fixed(FixedMeasure { size }));
91        content_size
92    }
93}