guise/input/
checkboxgroup.rs1use std::rc::Rc;
7
8use gpui::prelude::*;
9use gpui::{div, px, App, IntoElement, SharedString, Window};
10
11use super::Checkbox;
12use crate::devtools::Probed;
13use crate::reactive::Binding;
14use crate::theme::{theme, ColorName, Size};
15
16type GroupHandler = Rc<dyn Fn(Vec<usize>, &mut Window, &mut App) + 'static>;
17
18#[derive(IntoElement)]
20pub struct CheckboxGroup {
21 options: Vec<SharedString>,
22 value: Vec<usize>,
23 color: ColorName,
24 size: Size,
25 label: Option<SharedString>,
26 binding: Option<Binding<Vec<usize>>>,
27 on_change: Option<GroupHandler>,
28}
29
30impl CheckboxGroup {
31 pub fn new() -> Self {
32 CheckboxGroup {
33 options: Vec::new(),
34 value: Vec::new(),
35 color: ColorName::Blue,
36 size: Size::Sm,
37 label: None,
38 binding: None,
39 on_change: None,
40 }
41 }
42
43 pub fn options<I, S>(mut self, options: I) -> Self
44 where
45 I: IntoIterator<Item = S>,
46 S: Into<SharedString>,
47 {
48 self.options = options.into_iter().map(Into::into).collect();
49 self
50 }
51
52 pub fn value(mut self, value: impl IntoIterator<Item = usize>) -> Self {
54 self.value = value.into_iter().collect();
55 self
56 }
57
58 pub fn color(mut self, color: ColorName) -> Self {
59 self.color = color;
60 self
61 }
62
63 pub fn size(mut self, size: Size) -> Self {
64 self.size = size;
65 self
66 }
67
68 pub fn label(mut self, label: impl Into<SharedString>) -> Self {
69 self.label = Some(label.into());
70 self
71 }
72
73 pub fn bind(mut self, binding: Binding<Vec<usize>>) -> Self {
77 self.binding = Some(binding);
78 self
79 }
80
81 pub fn on_change(
83 mut self,
84 handler: impl Fn(Vec<usize>, &mut Window, &mut App) + 'static,
85 ) -> Self {
86 self.on_change = Some(Rc::new(handler));
87 self
88 }
89}
90
91impl Default for CheckboxGroup {
92 fn default() -> Self {
93 CheckboxGroup::new()
94 }
95}
96
97impl RenderOnce for CheckboxGroup {
98 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
99 let t = theme(cx);
100 let gap = t.spacing(Size::Xs);
101 let text = t.text().hsla();
102 let font = t.font_size(Size::Sm);
103 let value = self
104 .binding
105 .as_ref()
106 .map_or_else(|| self.value.clone(), |b| b.get(cx));
107
108 let mut column = div().flex().flex_col().gap(px(gap));
109 if let Some(label) = self.label.clone() {
110 column = column.child(div().text_size(px(font)).text_color(text).child(label));
111 }
112
113 let current = Rc::new(value.clone());
114 for (i, option) in self.options.iter().enumerate() {
115 let mut checkbox = Checkbox::new(("guise-checkboxgroup", i))
116 .label(option.clone())
117 .checked(value.contains(&i))
118 .color(self.color)
119 .size(self.size);
120 if self.binding.is_some() || self.on_change.is_some() {
121 let binding = self.binding.clone();
122 let handler = self.on_change.clone();
123 let current = current.clone();
124 checkbox = checkbox.on_change(move |_ev, window, cx| {
125 let mut next = (*current).clone();
126 if let Some(pos) = next.iter().position(|x| *x == i) {
127 next.remove(pos);
128 } else {
129 next.push(i);
130 next.sort_unstable();
131 }
132 if let Some(binding) = &binding {
133 binding.set(cx, next.clone());
134 }
135 if let Some(handler) = &handler {
136 handler(next, window, cx);
137 }
138 });
139 }
140 column = column.child(checkbox);
141 }
142 column.probe("CheckboxGroup")
143 }
144}