Skip to main content

cranpose_ui/widgets/
flow.rs

1//! `FlowRow` and `FlowColumn`: children that wrap onto the next line.
2
3use cranpose_core::NodeId;
4use cranpose_ui_layout::Axis;
5
6use super::layout::{LayoutEnvironment, compose_layout};
7use crate::{composable, layout::policies::FlowMeasurePolicy, modifier::Modifier};
8
9/// The gaps of a [`FlowRow`] or a [`FlowColumn`].
10#[derive(Clone, Copy, Debug, Default, PartialEq)]
11pub struct FlowSpec {
12    /// Gap between adjacent children on the same line, in dp: horizontal in
13    /// a row, vertical in a column (Compose: the arrangement along the main
14    /// axis, `Arrangement.spacedBy(..)`).
15    pub main_axis_spacing: f32,
16    /// Gap between consecutive lines, in dp: between rows of a row, between
17    /// columns of a column (Compose: the arrangement across them).
18    pub cross_axis_spacing: f32,
19}
20
21impl FlowSpec {
22    /// Creates a spec with no gaps between children or between lines.
23    pub fn new() -> Self {
24        Self::default()
25    }
26
27    /// Sets the gap between adjacent children on one line, in logical pixels.
28    pub fn main_axis_spacing(mut self, spacing: f32) -> Self {
29        self.main_axis_spacing = spacing;
30        self
31    }
32
33    /// Sets the gap between consecutive lines, in logical pixels.
34    pub fn cross_axis_spacing(mut self, spacing: f32) -> Self {
35        self.cross_axis_spacing = spacing;
36        self
37    }
38}
39
40/// Places children across a row and starts a new row when the available width is full.
41/// The spec sets the space between children and between rows.
42///
43/// # Example
44///
45/// ```rust
46/// use cranpose_ui::{
47///     widgets::{FlowRow, FlowSpec},
48///     *,
49/// };
50///
51/// #[composable]
52/// fn Topics() {
53///     FlowRow(
54///         Modifier::empty().fill_max_width(),
55///         FlowSpec::new()
56///             .main_axis_spacing(8.0)
57///             .cross_axis_spacing(4.0),
58///         || {
59///             for label in ["Rust", "Compose", "Android"] {
60///                 Text(label, Modifier::empty().padding(8.0), TextStyle::default());
61///             }
62///         },
63///     );
64/// }
65/// ```
66#[composable(no_skip)]
67pub fn FlowRow<F>(modifier: Modifier, spec: FlowSpec, content: F) -> NodeId
68where
69    F: FnMut() + 'static,
70{
71    flow_layout(modifier, Axis::Horizontal, spec, content)
72}
73
74/// Places children down a column and starts a new column when the
75/// available height is full — Compose's `FlowColumn`. The spec sets the
76/// space between children and between columns. In a right-to-left layout
77/// the first column is at the right.
78#[composable(no_skip)]
79pub fn FlowColumn<F>(modifier: Modifier, spec: FlowSpec, content: F) -> NodeId
80where
81    F: FnMut() + 'static,
82{
83    flow_layout(modifier, Axis::Vertical, spec, content)
84}
85
86fn flow_layout<F>(modifier: Modifier, axis: Axis, spec: FlowSpec, content: F) -> NodeId
87where
88    F: FnMut() + 'static,
89{
90    let environment = LayoutEnvironment::current();
91    let mut policy = FlowMeasurePolicy::new(axis, spec.main_axis_spacing, spec.cross_axis_spacing);
92    policy.layout_direction = environment.direction;
93    compose_layout(modifier, &policy, environment, content)
94}