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