gpui_ui_kit/
checkbox.rs

1//! Checkbox component
2//!
3//! A checkbox input with optional label.
4
5use gpui::prelude::*;
6use gpui::*;
7
8/// Checkbox size variants
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum CheckboxSize {
11    /// Small (14px)
12    Sm,
13    /// Medium (18px, default)
14    #[default]
15    Md,
16    /// Large (22px)
17    Lg,
18}
19
20impl CheckboxSize {
21    fn size(&self) -> Pixels {
22        match self {
23            CheckboxSize::Sm => px(14.0),
24            CheckboxSize::Md => px(18.0),
25            CheckboxSize::Lg => px(22.0),
26        }
27    }
28}
29
30/// A checkbox component
31pub struct Checkbox {
32    id: ElementId,
33    checked: bool,
34    indeterminate: bool,
35    label: Option<SharedString>,
36    size: CheckboxSize,
37    disabled: bool,
38    on_change: Option<Box<dyn Fn(bool, &mut Window, &mut App) + 'static>>,
39}
40
41impl Checkbox {
42    /// Create a new checkbox
43    pub fn new(id: impl Into<ElementId>) -> Self {
44        Self {
45            id: id.into(),
46            checked: false,
47            indeterminate: false,
48            label: None,
49            size: CheckboxSize::default(),
50            disabled: false,
51            on_change: None,
52        }
53    }
54
55    /// Set checked state
56    pub fn checked(mut self, checked: bool) -> Self {
57        self.checked = checked;
58        self
59    }
60
61    /// Set indeterminate state
62    pub fn indeterminate(mut self, indeterminate: bool) -> Self {
63        self.indeterminate = indeterminate;
64        self
65    }
66
67    /// Set label
68    pub fn label(mut self, label: impl Into<SharedString>) -> Self {
69        self.label = Some(label.into());
70        self
71    }
72
73    /// Set size
74    pub fn size(mut self, size: CheckboxSize) -> Self {
75        self.size = size;
76        self
77    }
78
79    /// Set disabled state
80    pub fn disabled(mut self, disabled: bool) -> Self {
81        self.disabled = disabled;
82        self
83    }
84
85    /// Set change handler
86    pub fn on_change(mut self, handler: impl Fn(bool, &mut Window, &mut App) + 'static) -> Self {
87        self.on_change = Some(Box::new(handler));
88        self
89    }
90
91    /// Build into element
92    pub fn build(self) -> Stateful<Div> {
93        let size = self.size.size();
94        let checked = self.checked;
95        let indeterminate = self.indeterminate;
96
97        let (bg, border_color) = if checked || indeterminate {
98            (rgb(0x007acc), rgb(0x007acc))
99        } else {
100            (rgba(0x00000000), rgb(0x555555))
101        };
102
103        let mut container = div()
104            .id(self.id)
105            .flex()
106            .items_center()
107            .gap_2()
108            .cursor_pointer();
109
110        if self.disabled {
111            container = container.opacity(0.5).cursor_not_allowed();
112        }
113
114        // Checkbox box
115        let mut checkbox = div()
116            .flex()
117            .items_center()
118            .justify_center()
119            .w(size)
120            .h(size)
121            .rounded(px(3.0))
122            .border_1()
123            .border_color(border_color)
124            .bg(bg);
125
126        // Check mark or indeterminate line
127        if indeterminate {
128            checkbox = checkbox.child(
129                div()
130                    .w(size - px(6.0))
131                    .h(px(2.0))
132                    .bg(rgb(0xffffff))
133                    .rounded(px(1.0)),
134            );
135        } else if checked {
136            checkbox = checkbox.child(
137                div()
138                    .text_color(rgb(0xffffff))
139                    .text_xs()
140                    .font_weight(FontWeight::BOLD)
141                    .child("✓"),
142            );
143        }
144
145        if !self.disabled {
146            checkbox = checkbox.hover(|s| s.border_color(rgb(0x007acc)));
147        }
148
149        container = container.child(checkbox);
150
151        // Label
152        if let Some(label) = self.label {
153            let label_el = match self.size {
154                CheckboxSize::Sm => div().text_xs(),
155                CheckboxSize::Md => div().text_sm(),
156                CheckboxSize::Lg => div(),
157            };
158            container = container.child(label_el.text_color(rgb(0xcccccc)).child(label));
159        }
160
161        // Click handler
162        if !self.disabled {
163            if let Some(handler) = self.on_change {
164                let new_checked = !checked;
165                container = container.on_mouse_up(MouseButton::Left, move |_event, window, cx| {
166                    handler(new_checked, window, cx);
167                });
168            }
169        }
170
171        container
172    }
173}
174
175impl IntoElement for Checkbox {
176    type Element = Stateful<Div>;
177
178    fn into_element(self) -> Self::Element {
179        self.build()
180    }
181}