bevy_ui_builders/checkbox/
builder.rs

1//! CheckboxBuilder for creating interactive checkboxes
2
3use bevy::prelude::*;
4use super::types::*;
5use crate::styles::{colors, dimensions};
6
7/// Builder for creating styled checkboxes
8///
9/// # Examples
10///
11/// ```ignore
12/// use bevy_ui_builders::prelude::*;
13///
14/// fn build_checkbox(parent: &mut ChildSpawnerCommands) {
15///     CheckboxBuilder::new()
16///         .checked(false)
17///         .with_label("Remember me")
18///         .build(parent);
19/// }
20/// ```
21pub struct CheckboxBuilder {
22    checked: bool,
23    style: CheckboxStyle,
24    label: Option<String>,
25    label_on_right: bool,
26    size: f32,
27}
28
29impl CheckboxBuilder {
30    /// Create a new unchecked checkbox
31    pub fn new() -> Self {
32        Self {
33            checked: false,
34            style: CheckboxStyle::Primary,
35            label: None,
36            label_on_right: true,
37            size: 20.0,
38        }
39    }
40
41    /// Set the initial checked state
42    pub fn checked(mut self, checked: bool) -> Self {
43        self.checked = checked;
44        self
45    }
46
47    /// Set the visual style
48    pub fn style(mut self, style: CheckboxStyle) -> Self {
49        self.style = style;
50        self
51    }
52
53    /// Add a text label to the checkbox
54    pub fn with_label(mut self, label: impl Into<String>) -> Self {
55        self.label = Some(label.into());
56        self
57    }
58
59    /// Set whether the label appears on the right (default) or left of the checkbox
60    pub fn label_on_right(mut self, on_right: bool) -> Self {
61        self.label_on_right = on_right;
62        self
63    }
64
65    /// Set the size of the checkbox box (default: 20px)
66    pub fn size(mut self, size: f32) -> Self {
67        self.size = size;
68        self
69    }
70
71    /// Build the checkbox and spawn it
72    pub fn build(self, parent: &mut ChildSpawnerCommands) -> Entity {
73        let state = if self.checked {
74            CheckboxState::Checked
75        } else {
76            CheckboxState::Unchecked
77        };
78
79        // Container for checkbox + label
80        let container_entity = parent.spawn((
81            Node {
82                flex_direction: if self.label_on_right {
83                    FlexDirection::Row
84                } else {
85                    FlexDirection::RowReverse
86                },
87                align_items: AlignItems::Center,
88                column_gap: Val::Px(8.0),
89                ..default()
90            },
91        )).id();
92
93        // Spawn the checkbox box itself
94        parent.commands().entity(container_entity).with_children(|container| {
95            let checkbox_entity = container.spawn((
96                Node {
97                    width: Val::Px(self.size),
98                    height: Val::Px(self.size),
99                    border: UiRect::all(Val::Px(2.0)),
100                    justify_content: JustifyContent::Center,
101                    align_items: AlignItems::Center,
102                    ..default()
103                },
104                BackgroundColor(if state.is_checked() {
105                    self.style.checked_color()
106                } else {
107                    self.style.unchecked_color()
108                }),
109                BorderColor::all(self.style.border_color()),
110                BorderRadius::all(Val::Px(4.0)),
111                Checkbox,
112                state,
113                CheckboxStyleComponent(self.style),
114                Interaction::default(),
115            )).with_children(|checkbox_box| {
116                // Checkmark icon (ASCII X for maximum compatibility)
117                checkbox_box.spawn((
118                    Text::new("X"),
119                    TextFont {
120                        font_size: self.size * 0.6,
121                        ..default()
122                    },
123                    TextColor(Color::WHITE),
124                    Node {
125                        display: if state.is_checked() {
126                            Display::Flex
127                        } else {
128                            Display::None
129                        },
130                        ..default()
131                    },
132                    CheckboxCheckmark,
133                ));
134            }).id();
135
136            // Add label if provided
137            if let Some(label_text) = self.label {
138                container.spawn((
139                    Text::new(label_text),
140                    TextFont {
141                        font_size: dimensions::FONT_SIZE_NORMAL,
142                        ..default()
143                    },
144                    TextColor(colors::TEXT_PRIMARY),
145                    Node {
146                        // Prevent label from interfering with checkbox clicks
147                        ..default()
148                    },
149                ));
150            }
151        });
152
153        container_entity
154    }
155}
156
157impl Default for CheckboxBuilder {
158    fn default() -> Self {
159        Self::new()
160    }
161}