use bevy::asset::Assets;
use bevy::ecs::schedule::SystemSetConfigs;
use bevy::ecs::{prelude::*, system::SystemParam};
use bevy::log::trace;
use bevy::prelude::Vec2;
use bevy::text::{Font, Text, TextMeasureInfo};
use bevy::ui::widget::UiImageSize;
use cuicui_layout::content_sized::{
ComputeContentParam, ComputeContentSize, ContentSizedComputeSystem,
};
use cuicui_layout::{require_layout_recompute, Node, Size};
#[derive(SystemParam)]
pub(crate) struct UiContentSize<'w> {
fonts: Res<'w, Assets<Font>>,
}
impl ComputeContentParam for UiContentSize<'static> {
type Components = AnyOf<(&'static Text, &'static UiImageSize)>;
fn condition(label: ContentSizedComputeSystem<Self>) -> SystemSetConfigs {
use bevy::ecs::schedule::common_conditions as cond;
let cond = cond::resource_changed::<Assets<Font>>()
.or_else(|c: Query<(), (Changed<UiImageSize>, With<Node>)>| !c.is_empty())
.or_else(|c: Query<(), (Changed<Text>, With<Node>)>| !c.is_empty());
label.run_if(require_layout_recompute.or_else(cond))
}
}
impl UiContentSize<'_> {
fn bounds(&self, text: &Text, bounds: Vec2) -> Vec2 {
trace!("Recomputing text sizes");
let measure = TextMeasureInfo::from_text(text, &self.fonts, 1.01);
measure.map_or(Vec2::ZERO, |m| m.compute_size(bounds))
}
}
fn compute_image_size(size: Vec2, set_size: Size<Option<f32>>) -> Vec2 {
let size = match (set_size.width, set_size.height) {
(None, None) => size,
(Some(width), None) => Vec2::new(width, width * size.y / size.x),
(None, Some(height)) => Vec2::new(height * size.x / size.y, height),
(Some(_), Some(_)) => unreachable!(
"This is a bug in cuicui_layout, the API promises that \
compute_content is never called with two set values."
),
};
Vec2::select(size.is_nan_mask(), Vec2::ZERO, size)
}
impl ComputeContentSize for UiContentSize<'_> {
type Components = AnyOf<(&'static Text, &'static UiImageSize)>;
fn compute_content(
&self,
components: (Option<&Text>, Option<&UiImageSize>),
set_size: Size<Option<f32>>,
) -> Size<f32> {
let inf = f32::INFINITY;
let size_vec = Vec2::new(
set_size.width.unwrap_or(inf),
set_size.height.unwrap_or(inf),
);
let bevy_ui = match components {
(Some(text), _) => self.bounds(text, size_vec),
(None, Some(image)) => compute_image_size(image.size(), set_size),
(None, None) => {
unreachable!("This is a bevy bug: AnyOf should at least have one element")
}
};
bevy_ui.into()
}
}