cranpose_ui/widgets/
layout.rs1#![allow(non_snake_case)]
4
5use super::nodes::LayoutNode;
6use super::scopes::BoxWithConstraintsScopeImpl;
7use crate::composable;
8use crate::modifier::Modifier;
9use crate::subcompose_layout::{
10 Constraints, MeasurePolicy as SubcomposeMeasurePolicy, MeasureResult, SubcomposeLayoutNode,
11 SubcomposeLayoutScope, SubcomposeMeasureScope, SubcomposeMeasureScopeImpl,
12};
13use cranpose_core::{NodeId, SlotId};
14use cranpose_ui_layout::{MeasurePolicy, Placement};
15use std::cell::RefCell;
16use std::rc::Rc;
17
18#[composable]
19pub fn Layout<F, P>(modifier: Modifier, measure_policy: P, content: F) -> NodeId
20where
21 F: FnMut() + 'static,
22 P: MeasurePolicy + Clone + PartialEq + 'static,
23{
24 let policy: Rc<dyn MeasurePolicy> = Rc::new(measure_policy);
25 let id = cranpose_core::with_current_composer(|composer| {
26 composer.emit_node(|| LayoutNode::new(modifier.clone(), Rc::clone(&policy)))
27 });
28 if let Err(err) = cranpose_core::with_node_mut(id, |node: &mut LayoutNode| {
29 node.set_modifier(modifier.clone());
30 node.set_measure_policy(Rc::clone(&policy));
31 }) {
32 debug_assert!(false, "failed to update Layout node: {err}");
33 }
34 cranpose_core::push_parent(id);
35 content();
36 cranpose_core::pop_parent();
37 id
38}
39
40#[composable]
41pub fn SubcomposeLayout(
42 modifier: Modifier,
43 measure_policy: impl for<'scope> Fn(&mut SubcomposeMeasureScopeImpl<'scope>, Constraints) -> MeasureResult
44 + 'static,
45) -> NodeId {
46 let policy: Rc<SubcomposeMeasurePolicy> = Rc::new(measure_policy);
47 let id = cranpose_core::with_current_composer(|composer| {
48 composer.emit_node(|| SubcomposeLayoutNode::new(modifier.clone(), Rc::clone(&policy)))
49 });
50 if let Err(err) = cranpose_core::with_node_mut(id, |node: &mut SubcomposeLayoutNode| {
51 node.set_modifier(modifier.clone());
52 node.set_measure_policy(Rc::clone(&policy));
53 }) {
54 debug_assert!(false, "failed to update SubcomposeLayout node: {err}");
55 }
56 id
57}
58
59#[composable(no_skip)]
60pub fn BoxWithConstraints<F>(modifier: Modifier, content: F) -> NodeId
61where
62 F: FnMut(BoxWithConstraintsScopeImpl) + 'static,
63{
64 let content_ref: Rc<RefCell<F>> = Rc::new(RefCell::new(content));
65 SubcomposeLayout(modifier, move |scope, constraints| {
66 let scope_impl = BoxWithConstraintsScopeImpl::new(constraints);
67 let scope_for_content = scope_impl;
68 let measurables = {
69 let content_ref = Rc::clone(&content_ref);
70 scope.subcompose(SlotId::new(0), move || {
71 let mut content = content_ref.borrow_mut();
72 content(scope_for_content);
73 })
74 };
75 let child_constraints = Constraints {
76 min_width: 0.0,
77 max_width: constraints.max_width,
78 min_height: 0.0,
79 max_height: constraints.max_height,
80 };
81
82 let mut width = 0.0_f32;
83 let mut height = 0.0_f32;
84 let mut placements = Vec::with_capacity(measurables.len());
85
86 for measurable in measurables {
87 let placeable = scope.measure(measurable, child_constraints);
88 width = width.max(placeable.width());
89 height = height.max(placeable.height());
90 placeable.place(0.0, 0.0);
91 placements.push(Placement::new(placeable.node_id(), 0.0, 0.0, 0));
92 }
93
94 width = width.clamp(constraints.min_width, constraints.max_width);
95 height = height.clamp(constraints.min_height, constraints.max_height);
96 scope.layout(width, height, placements)
97 })
98}