Skip to main content

cranpose_ui_layout/
core.rs

1//! Core layout traits and types shared by Compose UI widgets.
2
3use crate::constraints::Constraints;
4use cranpose_core::NodeId;
5use cranpose_ui_graphics::Size;
6
7/// Parent data for flex layouts (Row/Column weights and alignment).
8#[derive(Clone, Copy, Debug, Default)]
9pub struct FlexParentData {
10    /// Weight for distributing remaining space in the main axis.
11    /// If > 0.0, this child participates in weighted distribution.
12    pub weight: f32,
13
14    /// Whether to fill the allocated space when using weight.
15    /// If true, child gets tight constraints; if false, child gets loose constraints.
16    pub fill: bool,
17}
18
19impl FlexParentData {
20    pub fn new(weight: f32, fill: bool) -> Self {
21        Self { weight, fill }
22    }
23
24    pub fn has_weight(&self) -> bool {
25        self.weight > 0.0
26    }
27}
28
29/// Object capable of measuring a layout child and exposing intrinsic sizes.
30pub trait Measurable {
31    /// Measures the child with the provided constraints, returning a [`Placeable`].
32    fn measure(&self, constraints: Constraints) -> Box<dyn Placeable>;
33
34    /// Returns the minimum width achievable for the given height.
35    fn min_intrinsic_width(&self, height: f32) -> f32;
36
37    /// Returns the maximum width achievable for the given height.
38    fn max_intrinsic_width(&self, height: f32) -> f32;
39
40    /// Returns the minimum height achievable for the given width.
41    fn min_intrinsic_height(&self, width: f32) -> f32;
42
43    /// Returns the maximum height achievable for the given width.
44    fn max_intrinsic_height(&self, width: f32) -> f32;
45
46    /// Returns flex parent data if this measurable has weight/fill properties.
47    /// Default implementation returns None (no weight).
48    fn flex_parent_data(&self) -> Option<FlexParentData> {
49        None
50    }
51}
52
53/// Result of running a measurement pass for a single child.
54pub trait Placeable {
55    /// Places the child at the provided coordinates relative to its parent.
56    fn place(&self, x: f32, y: f32);
57
58    /// Returns the measured width of the child.
59    fn width(&self) -> f32;
60
61    /// Returns the measured height of the child.
62    fn height(&self) -> f32;
63
64    /// Returns the identifier for the underlying layout node.
65    fn node_id(&self) -> NodeId;
66
67    /// Returns the accumulated content offset from the coordinator chain.
68    /// This is used by layout modifiers (like scroll) to report where content
69    /// should be positioned within this placeable's bounds.
70    /// Default is (0.0, 0.0) for no offset.
71    fn content_offset(&self) -> (f32, f32) {
72        (0.0, 0.0)
73    }
74}
75
76/// Scope for measurement operations.
77pub trait MeasureScope {
78    /// Returns the current density for converting Dp to pixels.
79    fn density(&self) -> f32 {
80        1.0
81    }
82
83    /// Returns the current font scale for converting Sp to pixels.
84    fn font_scale(&self) -> f32 {
85        1.0
86    }
87}
88
89/// Policy responsible for measuring and placing children.
90pub trait MeasurePolicy {
91    /// Runs the measurement pass with the provided children and constraints.
92    fn measure(
93        &self,
94        measurables: &[Box<dyn Measurable>],
95        constraints: Constraints,
96    ) -> MeasureResult;
97
98    /// Computes the minimum intrinsic width of this policy.
99    fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32;
100
101    /// Computes the maximum intrinsic width of this policy.
102    fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32;
103
104    /// Computes the minimum intrinsic height of this policy.
105    fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32;
106
107    /// Computes the maximum intrinsic height of this policy.
108    fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32;
109}
110
111/// Result of a measurement operation.
112#[derive(Clone, Debug)]
113pub struct MeasureResult {
114    pub size: Size,
115    pub placements: Vec<Placement>,
116}
117
118impl MeasureResult {
119    pub fn new(size: Size, placements: Vec<Placement>) -> Self {
120        Self { size, placements }
121    }
122}
123
124/// Placement information for a measured child.
125#[derive(Clone, Copy, Debug)]
126pub struct Placement {
127    pub node_id: NodeId,
128    pub x: f32,
129    pub y: f32,
130    pub z_index: i32,
131}
132
133impl Placement {
134    pub fn new(node_id: NodeId, x: f32, y: f32, z_index: i32) -> Self {
135        Self {
136            node_id,
137            x,
138            y,
139            z_index,
140        }
141    }
142}
143
144/// Result of a layout modifier measurement operation.
145///
146/// Unlike `MeasureResult` which is for `MeasurePolicy` (multiple children),
147/// this type is specifically for layout modifiers which wrap a single piece
148/// of content and need to specify where that wrapped content should be placed.
149#[derive(Clone, Copy, Debug)]
150pub struct LayoutModifierMeasureResult {
151    /// The size this modifier will occupy.
152    pub size: Size,
153    /// The offset at which to place the wrapped content relative to
154    /// the top-left corner of this modifier's bounds.
155    /// For example, PaddingNode returns (padding.left, padding.top) here
156    /// to offset the child by the padding amount.
157    pub placement_offset_x: f32,
158    pub placement_offset_y: f32,
159}
160
161impl LayoutModifierMeasureResult {
162    pub fn new(size: Size, placement_offset_x: f32, placement_offset_y: f32) -> Self {
163        Self {
164            size,
165            placement_offset_x,
166            placement_offset_y,
167        }
168    }
169
170    /// Creates a result with zero placement offset (wrapped content placed at 0,0).
171    pub fn with_size(size: Size) -> Self {
172        Self {
173            size,
174            placement_offset_x: 0.0,
175            placement_offset_y: 0.0,
176        }
177    }
178}