Skip to main content

cranpose_ui/widgets/
column.rs

1//! Column widget implementation
2
3use cranpose_core::NodeId;
4use cranpose_ui_layout::{HorizontalAlignment, LinearArrangement};
5
6use super::layout::{LayoutEnvironment, compose_layout};
7use crate::{composable, layout::policies::FlexMeasurePolicy, modifier::Modifier};
8
9/// Specification for Column layout behavior.
10#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct ColumnSpec {
12    /// How children are spaced along the vertical axis. Defaults to `Start`.
13    pub vertical_arrangement: LinearArrangement,
14    /// How children align on the horizontal axis. Defaults to `Start`.
15    pub horizontal_alignment: HorizontalAlignment,
16}
17
18impl ColumnSpec {
19    /// Creates a spec with start arrangement and start alignment.
20    pub fn new() -> Self {
21        Self::default()
22    }
23
24    /// Sets how children are spaced along the vertical axis.
25    pub fn vertical_arrangement(mut self, arrangement: LinearArrangement) -> Self {
26        self.vertical_arrangement = arrangement;
27        self
28    }
29
30    /// Sets how children align on the horizontal axis.
31    pub fn horizontal_alignment(mut self, alignment: HorizontalAlignment) -> Self {
32        self.horizontal_alignment = alignment;
33        self
34    }
35}
36
37impl Default for ColumnSpec {
38    fn default() -> Self {
39        Self {
40            vertical_arrangement: LinearArrangement::Start,
41            horizontal_alignment: HorizontalAlignment::Start,
42        }
43    }
44}
45
46/// Places children from top to bottom. `spec` sets vertical space and horizontal alignment.
47/// Use [`Row`](crate::Row) for a horizontal sequence.
48/// Like Compose's, it is inline: a state its content reads recomposes the
49/// composable that called it.
50///
51/// # Example
52///
53/// ```rust
54/// use cranpose_ui::*;
55///
56/// #[composable]
57/// fn Details() {
58///     Column(
59///         Modifier::empty().padding(16.0),
60///         ColumnSpec::default().vertical_arrangement(LinearArrangement::spaced_by(8.0)),
61///         || {
62///             Text("Title", Modifier::empty(), TextStyle::default());
63///             Text("Subtitle", Modifier::empty(), TextStyle::default());
64///         },
65///     );
66/// }
67/// ```
68#[composable(no_skip)]
69pub fn Column<F>(modifier: Modifier, spec: ColumnSpec, content: F) -> NodeId
70where
71    F: FnMut() + 'static,
72{
73    let environment = LayoutEnvironment::current();
74    let mut policy = FlexMeasurePolicy::column(
75        spec.vertical_arrangement,
76        spec.horizontal_alignment,
77        environment.density.density(),
78    );
79    policy.layout_direction = environment.direction;
80    compose_layout(modifier, policy, environment, content)
81}