Skip to main content

fission_core/ui/widgets/
column.rs

1use crate::{Lower, LoweringContext, Node, NodeBuilder};
2use fission_ir::{FlexDirection, LayoutOp, NodeId, Op, Semantics};
3use fission_ir::op::{FlexWrap, AlignItems, JustifyContent};
4use serde::{Deserialize, Serialize};
5
6/// A vertical flex container that lays out children in a column.
7///
8/// Children are arranged top-to-bottom. Use `align_items` to control
9/// cross-axis (horizontal) alignment and `justify_content` for main-axis
10/// (vertical) distribution.
11///
12/// # Example
13///
14/// ```rust,ignore
15/// Column {
16///     children: vec![
17///         Text::new("Title").size(24.0).into_node().into(),
18///         Text::new("Subtitle").size(14.0).into_node().into(),
19///     ],
20///     gap: Some(4.0),
21///     align_items: AlignItems::Stretch,
22///     ..Default::default()
23/// }
24/// ```
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct Column {
27    /// Explicit node identity.
28    pub id: Option<NodeId>,
29    /// The child widgets laid out top-to-bottom.
30    pub children: Vec<Node>,
31    /// Custom semantics for accessibility.
32    pub semantics: Option<Semantics>,
33    /// Flex grow factor.
34    pub flex_grow: f32,
35    /// Flex shrink factor.
36    pub flex_shrink: f32,
37    /// Spacing between children in layout points.
38    pub gap: Option<f32>,
39    /// Whether children wrap when they overflow.
40    pub wrap: FlexWrap,
41    /// Cross-axis (horizontal) alignment (default: `Stretch`).
42    pub align_items: AlignItems,
43    /// Main-axis (vertical) distribution (default: `Start`).
44    pub justify_content: JustifyContent,
45}
46
47impl Default for Column {
48    fn default() -> Self {
49        Self {
50            id: None,
51            children: Vec::new(),
52            gap: None,
53            flex_grow: 0.0,
54            flex_shrink: 1.0,
55            semantics: None,
56            wrap: FlexWrap::NoWrap,
57            align_items: AlignItems::Stretch,
58            justify_content: JustifyContent::Start,
59        }
60    }
61}
62
63impl Column {
64    pub fn children(mut self, children: Vec<Node>) -> Self {
65        self.children = children;
66        self
67    }
68
69    pub fn flex_grow(mut self, flex_grow: f32) -> Self {
70        self.flex_grow = flex_grow;
71        self
72    }
73
74    pub fn gap(mut self, gap: Option<f32>) -> Self {
75        self.gap = gap;
76        self
77    }
78
79    pub fn align_items(mut self, align: AlignItems) -> Self {
80        self.align_items = align;
81        self
82    }
83
84    pub fn justify_content(mut self, justify: JustifyContent) -> Self {
85        self.justify_content = justify;
86        self
87    }
88
89    pub fn into_node(self) -> Node {
90        Node::Column(self)
91    }
92}
93
94impl Lower for Column {
95    fn lower(&self, cx: &mut LoweringContext) -> NodeId {
96        let layout_id = self.id.unwrap_or_else(|| cx.next_node_id());
97        
98        cx.push_scope(layout_id);
99        
100        let mut builder = NodeBuilder::new(
101            layout_id,
102            Op::Layout(LayoutOp::Flex {
103                direction: FlexDirection::Column,
104                wrap: self.wrap,
105                flex_grow: self.flex_grow,
106                flex_shrink: self.flex_shrink,
107                padding: [0.0; 4],
108                gap: self.gap,
109                align_items: self.align_items,
110                justify_content: self.justify_content,
111            }),
112        );
113        for child in &self.children {
114            builder.add_child(child.lower(cx));
115        }
116        
117        cx.pop_scope();
118        
119        let layout_id = builder.build(cx);
120
121        if let Some(s) = &self.semantics {
122            let mut semantics_builder =
123                NodeBuilder::new(cx.next_node_id(), Op::Semantics(s.clone()));
124            semantics_builder.add_child(layout_id);
125            return semantics_builder.build(cx);
126        }
127
128        layout_id
129    }
130}