1use gpui::prelude::*;
4use gpui::{div, px, App, ClickEvent, ElementId, FontWeight, IntoElement, SharedString, Window};
5
6use super::{control_box_size, ClickHandler};
7use crate::theme::{theme, ColorName, Size};
8
9#[derive(IntoElement)]
12pub struct Checkbox {
13 id: ElementId,
14 checked: bool,
15 indeterminate: bool,
16 label: Option<SharedString>,
17 size: Size,
18 color: ColorName,
19 disabled: bool,
20 on_change: Option<ClickHandler>,
21}
22
23impl Checkbox {
24 pub fn new(id: impl Into<ElementId>) -> Self {
25 Checkbox {
26 id: id.into(),
27 checked: false,
28 indeterminate: false,
29 label: None,
30 size: Size::Sm,
31 color: ColorName::Blue,
32 disabled: false,
33 on_change: None,
34 }
35 }
36
37 pub fn checked(mut self, checked: bool) -> Self {
38 self.checked = checked;
39 self
40 }
41
42 pub fn indeterminate(mut self, indeterminate: bool) -> Self {
43 self.indeterminate = indeterminate;
44 self
45 }
46
47 pub fn label(mut self, label: impl Into<SharedString>) -> Self {
48 self.label = Some(label.into());
49 self
50 }
51
52 pub fn size(mut self, size: Size) -> Self {
53 self.size = size;
54 self
55 }
56
57 pub fn color(mut self, color: ColorName) -> Self {
58 self.color = color;
59 self
60 }
61
62 pub fn disabled(mut self, disabled: bool) -> Self {
63 self.disabled = disabled;
64 self
65 }
66
67 pub fn on_change(
68 mut self,
69 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
70 ) -> Self {
71 self.on_change = Some(Box::new(handler));
72 self
73 }
74}
75
76impl RenderOnce for Checkbox {
77 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
78 let t = theme(cx);
79 let on = self.checked || self.indeterminate;
80 let accent = t.color(self.color, t.primary_shade());
81 let box_size = control_box_size(self.size);
82
83 let mut check = div()
84 .w(px(box_size))
85 .h(px(box_size))
86 .flex()
87 .items_center()
88 .justify_center()
89 .rounded(px(t.radius(Size::Xs) + 2.0))
90 .text_size(px(box_size * 0.7))
91 .font_weight(FontWeight::BOLD);
92 if on {
93 check = check
94 .bg(accent.hsla())
95 .text_color(accent.contrasting().hsla())
96 .child(SharedString::new_static(if self.indeterminate {
97 "\u{2212}"
98 } else {
99 "\u{2713}"
100 }));
101 } else {
102 check = check
103 .bg(t.surface().hsla())
104 .border_1()
105 .border_color(t.border().hsla());
106 }
107
108 let mut row = div().id(self.id).flex().items_center().gap(px(8.0)).child(check);
109 if let Some(label) = self.label {
110 row = row.child(
111 div()
112 .text_size(px(t.font_size(self.size)))
113 .text_color(t.text().hsla())
114 .child(label),
115 );
116 }
117
118 if self.disabled {
119 row.opacity(0.5)
120 } else {
121 if let Some(handler) = self.on_change {
122 row = row.on_click(handler);
123 }
124 row
125 }
126 }
127}