use crate::engine::ecs::ComponentId;
use crate::engine::ecs::component::TransformComponent;
use crate::engine::ecs::{IntentValue, SignalEmitter, World};
use super::block::apply_text_align;
use super::box_model_viz::sync_box_model_viz;
use super::measure::{
MeasuredItem, apply_text_color_for_item, apply_text_font_size_for_item,
apply_text_wrap_for_item, measure_container_items, measure_items,
};
pub fn layout(
world: &mut World,
emit: &mut dyn SignalEmitter,
layout_id: ComponentId,
) -> (f32, f32) {
let (items, avail_w_gu, _avail_h_gu, unit_scale) = measure_items(world, layout_id);
let viz = super::block::layout_root_has_inspect(world, layout_id);
let axis_scales = super::measure::layout_root_axis_scales(world, layout_id);
let (_total_x_gu, total_y_gu) = layout_items(
world,
emit,
&items,
avail_w_gu,
unit_scale,
axis_scales,
0,
0,
viz,
);
(avail_w_gu, total_y_gu)
}
pub(crate) fn layout_items(
world: &mut World,
emit: &mut dyn SignalEmitter,
items: &[MeasuredItem],
avail_w_gu: f32,
unit_scale: f32,
axis_scales: (f32, f32),
depth: i32,
parent_depth: i32,
viz: bool,
) -> (f32, f32) {
let mut cursor_x_gu: f32 = 0.0;
let mut cursor_y_gu: f32 = 0.0;
let mut line_height_gu: f32 = 0.0;
let resolved_z = (depth - parent_depth) as f32 * super::LAYER_DISTANCE;
for original in items {
let item: MeasuredItem = if original.is_auto_width {
let remaining = (avail_w_gu - cursor_x_gu).max(0.0);
super::measure::measure_item(
world,
original.tc_id,
remaining,
Some(original.content_height_gu),
unit_scale,
)
} else {
original.clone()
};
let item = &item;
if cursor_x_gu > 0.0 && cursor_x_gu + item.margin_box_width_gu > avail_w_gu {
cursor_y_gu += line_height_gu;
cursor_x_gu = 0.0;
line_height_gu = 0.0;
}
let content_origin_x_gu = cursor_x_gu + item.margin_left_gu + item.padding_left_gu;
let content_origin_y_gu = cursor_y_gu + item.margin_top_gu + item.padding_top_gu;
let tc_scale = world
.get_component_by_id_as::<TransformComponent>(item.tc_id)
.map(|tc| tc.transform.scale)
.unwrap_or([1.0, 1.0, 1.0]);
let composed_z = resolved_z;
let translation = [
content_origin_x_gu * unit_scale,
-(content_origin_y_gu * unit_scale),
composed_z,
];
emit.push_intent_now(
item.tc_id,
IntentValue::UpdateTransform {
component_id: item.tc_id,
translation,
rotation_quat_xyzw: [0.0, 0.0, 0.0, 1.0],
scale: tc_scale,
},
);
super::block::sync_layout_bounds(world, emit, item, unit_scale);
apply_text_font_size_for_item(world, emit, item.tc_id, unit_scale);
apply_text_wrap_for_item(world, emit, item.tc_id, item.content_width_gu, unit_scale);
apply_text_color_for_item(world, emit, item.tc_id);
super::block::sync_bg_quad(
world,
emit,
item.tc_id,
item.padding_left_gu,
item.padding_top_gu,
item.box_width_gu,
item.box_height_gu,
unit_scale,
);
super::block::sync_auto_text_lift(world, emit, item.tc_id);
sync_box_model_viz(world, emit, item, unit_scale, viz);
apply_text_align(
world,
emit,
item.tc_id,
item.content_width_gu,
item.content_height_gu,
unit_scale,
);
let content_root =
super::block::sync_overflow_topology(world, emit, item.tc_id, item.content_height_gu);
let nested_items =
measure_container_items(world, content_root, item.content_width_gu, None, unit_scale);
if !nested_items.is_empty() {
let child_depth = if super::block::item_owns_layer(world, item.tc_id) {
depth + 1
} else {
depth
};
super::layout_container_items(
world,
emit,
item.tc_id,
&nested_items,
item.content_width_gu,
Some(item.content_height_gu),
unit_scale,
axis_scales,
child_depth,
depth,
viz,
);
}
cursor_x_gu += item.margin_box_width_gu;
if item.margin_box_height_gu > line_height_gu {
line_height_gu = item.margin_box_height_gu;
}
}
(cursor_x_gu, cursor_y_gu + line_height_gu)
}