gpui_component/form/
form.rs1use gpui::{
2 AnyElement, App, Axis, IntoElement, ParentElement, Pixels, Rems, RenderOnce, StyleRefinement,
3 Styled, Window, prelude::FluentBuilder as _, px,
4};
5
6use crate::{
7 Sizable, Size,
8 form::{Field, FieldProps},
9 h_flex, v_flex,
10};
11
12#[derive(IntoElement)]
14pub struct Form {
15 style: StyleRefinement,
16 fields: Vec<Field>,
17 footer: Option<AnyElement>,
18 props: FieldProps,
19}
20
21impl Default for Form {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl Form {
28 pub fn new() -> Self {
30 Self {
31 style: StyleRefinement::default(),
32 props: FieldProps::default(),
33 fields: Vec::new(),
34 footer: None,
35 }
36 }
37
38 pub fn horizontal() -> Self {
40 Self::new().layout(Axis::Horizontal)
41 }
42
43 pub fn vertical() -> Self {
45 Self::new().layout(Axis::Vertical)
46 }
47
48 pub fn layout(self, layout: Axis) -> Self {
52 self.label_layout(layout)
53 }
54
55 pub fn label_layout(mut self, layout: Axis) -> Self {
60 self.props.layout = layout;
61 self
62 }
63
64 pub fn label_width(mut self, width: Pixels) -> Self {
66 self.props.label_width = Some(width);
67 self
68 }
69
70 pub fn label_text_size(mut self, size: Rems) -> Self {
72 self.props.label_text_size = Some(size);
73 self
74 }
75
76 pub fn child(mut self, field: impl Into<Field>) -> Self {
78 self.fields.push(field.into());
79 self
80 }
81
82 pub fn children(mut self, fields: impl IntoIterator<Item = Field>) -> Self {
84 self.fields.extend(fields);
85 self
86 }
87
88 pub fn footer(mut self, footer: impl IntoElement) -> Self {
93 self.footer = Some(footer.into_any_element());
94 self
95 }
96
97 pub fn columns(mut self, columns: usize) -> Self {
101 self.props.columns = columns;
102 self
103 }
104}
105
106impl Styled for Form {
107 fn style(&mut self) -> &mut StyleRefinement {
108 &mut self.style
109 }
110}
111
112impl Sizable for Form {
113 fn with_size(mut self, size: impl Into<Size>) -> Self {
114 self.props.size = size.into();
115 self
116 }
117}
118
119impl RenderOnce for Form {
120 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
121 let props = self.props;
122
123 let gap = match props.size {
124 Size::XSmall | Size::Small => px(6.),
125 Size::Large => px(12.),
126 _ => px(8.),
127 };
128
129 v_flex()
130 .w_full()
131 .gap_x(gap * 3.)
132 .gap_y(gap)
133 .grid()
134 .grid_cols(props.columns as u16)
135 .children(
136 self.fields
137 .into_iter()
138 .enumerate()
139 .map(|(ix, field)| field.props(ix, props)),
140 )
141 .when_some(self.footer, |this, footer| {
142 this.child(
143 h_flex()
144 .col_span_full()
145 .min_w_0()
146 .justify_end()
147 .child(footer),
148 )
149 })
150 }
151}
152
153#[cfg(test)]
154#[path = "tests.rs"]
155mod tests;