cranpose_ui/widgets/
layout.rs1#![allow(non_snake_case)]
4
5use super::nodes::LayoutNode;
6use super::scopes::{BoxWithConstraintsScope, 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 width_dp = if scope_impl.max_width().0.is_finite() {
76 scope_impl.max_width()
77 } else {
78 scope_impl.min_width()
79 };
80 let height_dp = if scope_impl.max_height().0.is_finite() {
81 scope_impl.max_height()
82 } else {
83 scope_impl.min_height()
84 };
85 let width = scope_impl.to_px(width_dp);
86 let height = scope_impl.to_px(height_dp);
87 let placements: Vec<Placement> = measurables
88 .into_iter()
89 .map(|measurable| Placement::new(measurable.node_id(), 0.0, 0.0, 0))
90 .collect();
91 scope.layout(width, height, placements)
92 })
93}