use crate::env::{Env, RuntimeState};
use crate::lowering::{LoweringContext, build_layout_tree};
use crate::ui::traits::Lower;
use crate::ui::Node;
use fission_ir::{LayoutOp, Op};
use fission_layout::{LayoutEngine, LayoutSize};
use crate::ui::widgets::{Container, Text};
use crate::ui::widgets::text::TextContent;
use fission_ir::NodeId;
struct SimpleMeasurer;
impl fission_layout::TextMeasurer for SimpleMeasurer {
fn measure(&self, text: &str, _size: f32, avail: Option<f32>) -> (f32, f32) {
let char_width = 10.0;
let line_height = 20.0;
let width = text.len() as f32 * char_width;
if let Some(w) = avail {
if width > w {
let lines = (width / w).ceil();
return (w, lines * line_height);
}
}
(width, line_height)
}
fn hit_test(&self, _: &str, _: f32, _: Option<f32>, _: f32, _: f32) -> usize { 0 }
fn get_line_metrics(&self, _: &str, _: f32, _: Option<f32>) -> Vec<fission_layout::LineMetric> { vec![] }
fn get_caret_position(&self, _: &str, _: f32, _: Option<f32>, _: usize) -> (f32, f32) { (0.0, 0.0) }
fn measure_rich_text(&self, runs: &[fission_ir::op::TextRun], avail: Option<f32>) -> (f32, f32) {
let text: String = runs.iter().map(|r| r.text.clone()).collect();
self.measure(&text, 16.0, avail)
}
}
#[test]
fn test_text_wrapping_in_constrained_flex() {
let env = Env::default();
let runtime_state = RuntimeState::default();
let mut cx = LoweringContext::new(&env, &runtime_state, None, None);
let root_base = NodeId::derived(0xABC, &[0]);
cx.push_scope(root_base);
let root_id = cx.next_node_id();
let row_id = cx.next_node_id();
let text_id = cx.next_node_id();
let mut text_builder = crate::lowering::NodeBuilder::new(text_id, Op::Paint(fission_ir::PaintOp::DrawText {
text: "Hello World".into(),
size: 16.0,
color: fission_ir::op::Color::BLACK,
underline: false,
caret_index: None,
}));
let text_final = text_builder.build(&mut cx);
let mut row_builder = crate::lowering::NodeBuilder::new(row_id, Op::Layout(LayoutOp::Flex {
direction: fission_ir::FlexDirection::Row,
wrap: fission_ir::FlexWrap::NoWrap,
flex_grow: 0.0, flex_shrink: 1.0,
padding: [0.0; 4], gap: None,
align_items: fission_ir::op::AlignItems::Stretch,
justify_content: fission_ir::op::JustifyContent::Start,
}));
row_builder.add_child(text_final);
let row_final = row_builder.build(&mut cx);
let mut root_builder = crate::lowering::NodeBuilder::new(root_id, Op::Layout(LayoutOp::Box {
width: Some(50.0), height: Some(100.0),
min_width: None, max_width: None, min_height: None, max_height: None,
padding: [0.0; 4], flex_grow: 0.0, flex_shrink: 0.0, aspect_ratio: None
}));
root_builder.add_child(row_final);
let root_final = root_builder.build(&mut cx);
cx.ir.root = Some(root_final);
let input_nodes = build_layout_tree(&cx.ir, &env);
let mut engine = LayoutEngine::new().with_measurer(std::sync::Arc::new(SimpleMeasurer));
engine.rebuild(&input_nodes).unwrap();
let snapshot = engine.compute_layout(
&input_nodes,
root_final,
LayoutSize::new(800.0, 600.0),
&|_| 0.0
).unwrap();
let text_geom = snapshot.get_node_geometry(text_final).unwrap();
assert_eq!(text_geom.rect.width(), 50.0);
assert!(text_geom.rect.height() > 20.0, "Text should wrap");
}
#[test]
fn text_parent_max_width_drives_wrapping() {
let env = Env::default();
let runtime_state = RuntimeState::default();
let mut cx = LoweringContext::new(&env, &runtime_state, None, None);
let text = Text {
content: TextContent::Literal("HelloWorld".into()),
max_width: Some(40.0),
..Default::default()
};
let root = Container::new(Node::from(text))
.width(200.0)
.height(200.0)
.into_node();
let root_id = root.lower(&mut cx);
cx.ir.root = Some(root_id);
let input_nodes = build_layout_tree(&cx.ir, &env);
let mut engine = LayoutEngine::new().with_measurer(std::sync::Arc::new(SimpleMeasurer));
engine.rebuild(&input_nodes).unwrap();
let snapshot = engine.compute_layout(
&input_nodes,
root_id,
LayoutSize::new(800.0, 600.0),
&|_| 0.0,
).unwrap();
let text_paint_id = cx.ir.nodes.iter().find_map(|(id, node)| {
match &node.op {
Op::Paint(fission_ir::PaintOp::DrawText { text, .. }) if text == "HelloWorld" => Some(*id),
_ => None,
}
}).expect("expected DrawText node");
let text_geom = snapshot.get_node_geometry(text_paint_id).unwrap();
assert_eq!(text_geom.rect.width(), 40.0);
assert!(text_geom.rect.height() > 20.0, "text should wrap when parent max_width is set");
}