gpui_component/form/
form.rs

1use gpui::{
2    px, App, Axis, IntoElement, ParentElement, Pixels, Rems, RenderOnce, StyleRefinement, Styled,
3    Window,
4};
5
6use crate::{
7    form::{Field, FieldProps},
8    v_flex, Sizable, Size,
9};
10
11/// A form element that contains multiple form fields.
12#[derive(IntoElement)]
13pub struct Form {
14    style: StyleRefinement,
15    fields: Vec<Field>,
16    props: FieldProps,
17}
18
19impl Form {
20    fn new() -> Self {
21        Self {
22            style: StyleRefinement::default(),
23            props: FieldProps::default(),
24            fields: Vec::new(),
25        }
26    }
27
28    /// Creates a new form with a horizontal layout.
29    pub fn horizontal() -> Self {
30        Self::new().layout(Axis::Horizontal)
31    }
32
33    /// Creates a new form with a vertical layout.
34    pub fn vertical() -> Self {
35        Self::new().layout(Axis::Vertical)
36    }
37
38    /// Set the layout for the form, default is `Axis::Vertical`.
39    pub fn layout(mut self, layout: Axis) -> Self {
40        self.props.layout = layout;
41        self
42    }
43
44    /// Set the width of the labels in the form. Default is `px(100.)`.
45    pub fn label_width(mut self, width: Pixels) -> Self {
46        self.props.label_width = Some(width);
47        self
48    }
49
50    /// Set the text size of the labels in the form. Default is `None`.
51    pub fn label_text_size(mut self, size: Rems) -> Self {
52        self.props.label_text_size = Some(size);
53        self
54    }
55
56    /// Add a child to the form.
57    pub fn child(mut self, field: impl Into<Field>) -> Self {
58        self.fields.push(field.into());
59        self
60    }
61
62    /// Add multiple children to the form.
63    pub fn children(mut self, fields: impl IntoIterator<Item = Field>) -> Self {
64        self.fields.extend(fields);
65        self
66    }
67
68    /// Set the column count for the form.
69    ///
70    /// Default is 1.
71    pub fn columns(mut self, columns: usize) -> Self {
72        self.props.columns = columns;
73        self
74    }
75}
76
77impl Styled for Form {
78    fn style(&mut self) -> &mut StyleRefinement {
79        &mut self.style
80    }
81}
82
83impl Sizable for Form {
84    fn with_size(mut self, size: impl Into<Size>) -> Self {
85        self.props.size = size.into();
86        self
87    }
88}
89
90impl RenderOnce for Form {
91    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
92        let props = self.props;
93
94        let gap = match props.size {
95            Size::XSmall | Size::Small => px(6.),
96            Size::Large => px(12.),
97            _ => px(8.),
98        };
99
100        v_flex()
101            .w_full()
102            .gap_x(gap * 3.)
103            .gap_y(gap)
104            .grid()
105            .grid_cols(props.columns as u16)
106            .children(
107                self.fields
108                    .into_iter()
109                    .enumerate()
110                    .map(|(ix, field)| field.props(ix, props)),
111            )
112    }
113}