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}