bevy_ui_builders/panel/
builder.rs

1//! PanelBuilder implementation
2
3use bevy::prelude::*;
4use crate::{dimensions, label::{LabelBuilder, LabelStyle}};
5use super::types::*;
6
7/// Builder for creating panels with consistent styling
8pub struct PanelBuilder {
9    style: PanelStyle,
10    width: Val,
11    height: Val,
12    min_width: Val,
13    min_height: Val,
14    max_width: Val,
15    max_height: Val,
16    padding: UiRect,
17    margin: UiRect,
18    flex_direction: FlexDirection,
19    justify_content: JustifyContent,
20    align_items: AlignItems,
21    position_type: PositionType,
22    display: Display,
23    overflow: Overflow,
24    custom_background: Option<Color>,
25    title: Option<String>,
26    column_gap: Val,
27    row_gap: Val,
28    flex_basis: Val,
29    flex_grow: f32,
30    custom_border: Option<UiRect>,
31}
32
33impl PanelBuilder {
34    pub fn new() -> Self {
35        Self {
36            style: PanelStyle::Default,
37            width: Val::Auto,
38            height: Val::Auto,
39            min_width: Val::Auto,
40            min_height: Val::Auto,
41            max_width: Val::Auto,
42            max_height: Val::Auto,
43            padding: UiRect::all(Val::Px(dimensions::PANEL_PADDING)),
44            margin: UiRect::all(Val::Px(0.0)),
45            flex_direction: FlexDirection::Column,
46            justify_content: JustifyContent::Start,
47            align_items: AlignItems::Stretch,
48            position_type: PositionType::Relative,
49            display: Display::Flex,
50            overflow: Overflow::visible(),
51            custom_background: None,
52            title: None,
53            column_gap: Val::Px(0.0),
54            row_gap: Val::Px(0.0),
55            flex_basis: Val::Auto,
56            flex_grow: 0.0,
57            custom_border: None,
58        }
59    }
60
61    pub fn style(mut self, style: PanelStyle) -> Self {
62        self.style = style;
63        self
64    }
65
66    pub fn width(mut self, width: Val) -> Self {
67        self.width = width;
68        self
69    }
70
71    pub fn height(mut self, height: Val) -> Self {
72        self.height = height;
73        self
74    }
75
76    pub fn min_width(mut self, min_width: Val) -> Self {
77        self.min_width = min_width;
78        self
79    }
80
81    pub fn min_height(mut self, min_height: Val) -> Self {
82        self.min_height = min_height;
83        self
84    }
85
86    pub fn max_width(mut self, max_width: Val) -> Self {
87        self.max_width = max_width;
88        self
89    }
90
91    pub fn max_height(mut self, max_height: Val) -> Self {
92        self.max_height = max_height;
93        self
94    }
95
96    pub fn overflow(mut self, overflow: Overflow) -> Self {
97        self.overflow = overflow;
98        self
99    }
100
101    pub fn padding(mut self, padding: UiRect) -> Self {
102        self.padding = padding;
103        self
104    }
105
106    pub fn margin(mut self, margin: UiRect) -> Self {
107        self.margin = margin;
108        self
109    }
110
111    pub fn flex_direction(mut self, direction: FlexDirection) -> Self {
112        self.flex_direction = direction;
113        self
114    }
115
116    pub fn justify_content(mut self, justify: JustifyContent) -> Self {
117        self.justify_content = justify;
118        self
119    }
120
121    pub fn align_items(mut self, align: AlignItems) -> Self {
122        self.align_items = align;
123        self
124    }
125
126    pub fn position_type(mut self, position: PositionType) -> Self {
127        self.position_type = position;
128        self
129    }
130
131    pub fn display(mut self, display: Display) -> Self {
132        self.display = display;
133        self
134    }
135
136    /// Set a custom background color
137    pub fn custom_background(mut self, color: Color) -> Self {
138        self.custom_background = Some(color);
139        self
140    }
141
142    /// Set a title for the panel
143    pub fn with_title(mut self, title: impl Into<String>) -> Self {
144        self.title = Some(title.into());
145        self
146    }
147
148    /// Set column gap for flex layout
149    pub fn column_gap(mut self, gap: Val) -> Self {
150        self.column_gap = gap;
151        self
152    }
153
154    /// Set row gap for flex layout
155    pub fn row_gap(mut self, gap: Val) -> Self {
156        self.row_gap = gap;
157        self
158    }
159
160    /// Set flex basis
161    pub fn flex_basis(mut self, basis: Val) -> Self {
162        self.flex_basis = basis;
163        self
164    }
165
166    /// Set flex grow
167    pub fn flex_grow(mut self, grow: f32) -> Self {
168        self.flex_grow = grow;
169        self
170    }
171
172    /// Set background color (alias for custom_background)
173    pub fn background_color(mut self, color: Color) -> Self {
174        self.custom_background = Some(color);
175        self
176    }
177
178    /// Set custom border
179    pub fn border(mut self, border: UiRect) -> Self {
180        self.custom_border = Some(border);
181        self
182    }
183
184    pub fn build(self, parent: &mut ChildSpawnerCommands) -> Entity {
185        let background_color = self
186            .custom_background
187            .unwrap_or_else(|| self.style.background_color());
188
189        let border = self
190            .custom_border
191            .unwrap_or_else(|| UiRect::all(self.style.border_width()));
192
193        let mut panel_entity = parent.spawn((
194            Node {
195                width: self.width,
196                height: self.height,
197                min_width: self.min_width,
198                min_height: self.min_height,
199                max_width: self.max_width,
200                max_height: self.max_height,
201                padding: self.padding,
202                margin: self.margin,
203                border,
204                flex_direction: self.flex_direction,
205                justify_content: self.justify_content,
206                align_items: self.align_items,
207                position_type: self.position_type,
208                display: self.display,
209                overflow: self.overflow,
210                column_gap: self.column_gap,
211                row_gap: self.row_gap,
212                flex_basis: self.flex_basis,
213                flex_grow: self.flex_grow,
214                ..default()
215            },
216            BackgroundColor(background_color),
217            BorderColor(self.style.border_color()),
218            Panel { style: self.style },
219        ));
220
221        // Add title if provided
222        if let Some(title) = self.title {
223            panel_entity.with_children(|parent| {
224                LabelBuilder::new(title)
225                    .style(LabelStyle::Title)
226                    .margin(UiRect::bottom(Val::Px(dimensions::MARGIN_SMALL)))
227                    .build(parent);
228            });
229        }
230
231        panel_entity.id()
232    }
233
234    pub fn build_with_children<F>(
235        self,
236        parent: &mut ChildSpawnerCommands,
237        children: F,
238    ) -> Entity
239    where
240        F: FnOnce(&mut ChildSpawnerCommands),
241    {
242        let background_color = self
243            .custom_background
244            .unwrap_or_else(|| self.style.background_color());
245
246        let border = self
247            .custom_border
248            .unwrap_or_else(|| UiRect::all(self.style.border_width()));
249        let title = self.title.clone(); // Clone title for use in closure
250
251        parent
252            .spawn((
253                Node {
254                    width: self.width,
255                    height: self.height,
256                    min_width: self.min_width,
257                    min_height: self.min_height,
258                    max_width: self.max_width,
259                    max_height: self.max_height,
260                    padding: self.padding,
261                    margin: self.margin,
262                    border,
263                    flex_direction: self.flex_direction,
264                    justify_content: self.justify_content,
265                    align_items: self.align_items,
266                    position_type: self.position_type,
267                    display: self.display,
268                    overflow: self.overflow,
269                    column_gap: self.column_gap,
270                    row_gap: self.row_gap,
271                    flex_basis: self.flex_basis,
272                    flex_grow: self.flex_grow,
273                    ..default()
274                },
275                BackgroundColor(background_color),
276                BorderColor(self.style.border_color()),
277                Panel { style: self.style },
278            ))
279            .with_children(|parent| {
280                // Add title first if provided
281                if let Some(title_text) = title {
282                    LabelBuilder::new(title_text)
283                        .style(LabelStyle::Title)
284                        .margin(UiRect::bottom(Val::Px(dimensions::MARGIN_SMALL)))
285                        .build(parent);
286                }
287
288                // Then add user-provided children
289                children(parent);
290            })
291            .id()
292    }
293}
294
295/// Convenience function to create a panel builder
296pub fn panel() -> PanelBuilder {
297    PanelBuilder::new()
298}