gpui_component/setting/
group.rs1use std::rc::Rc;
2
3use gpui::{
4 AnyElement, App, IntoElement, ParentElement as _, SharedString, StyleRefinement, Styled,
5 Window, prelude::FluentBuilder as _,
6};
7
8use crate::{
9 ActiveTheme, StyledExt,
10 group_box::{GroupBox, GroupBoxVariants},
11 label::Label,
12 setting::{RenderOptions, SettingItem},
13 v_flex,
14};
15
16#[derive(Clone)]
18pub struct SettingGroup {
19 style: StyleRefinement,
20 footer: Option<Rc<dyn Fn(&mut Window, &mut App) -> AnyElement>>,
21
22 pub(super) title: Option<SharedString>,
23 pub(super) description: Option<SharedString>,
24 pub(super) items: Vec<SettingItem>,
25}
26
27impl Styled for SettingGroup {
28 fn style(&mut self) -> &mut StyleRefinement {
29 &mut self.style
30 }
31}
32
33impl SettingGroup {
34 pub fn new() -> Self {
36 Self {
37 style: StyleRefinement::default(),
38 footer: None,
39 title: None,
40 description: None,
41 items: Vec::new(),
42 }
43 }
44
45 pub fn title(mut self, title: impl Into<SharedString>) -> Self {
47 self.title = Some(title.into());
48 self
49 }
50
51 pub fn description(mut self, description: impl Into<SharedString>) -> Self {
53 self.description = Some(description.into());
54 self
55 }
56
57 pub fn footer<F, E>(mut self, footer: F) -> Self
64 where
65 E: IntoElement,
66 F: Fn(&mut Window, &mut App) -> E + 'static,
67 {
68 self.footer = Some(Rc::new(move |window, cx| {
69 footer(window, cx).into_any_element()
70 }));
71 self
72 }
73
74 pub fn item(mut self, item: SettingItem) -> Self {
76 self.items.push(item);
77 self
78 }
79
80 pub fn items<I>(mut self, items: I) -> Self
82 where
83 I: IntoIterator<Item = SettingItem>,
84 {
85 self.items.extend(items);
86 self
87 }
88
89 pub(super) fn is_match(&self, query: &str, cx: &App) -> bool {
91 self.items.iter().any(|item| item.is_match(query, cx))
92 }
93
94 pub(super) fn is_resettable(&self, query: &str, cx: &App) -> bool {
95 self.items
96 .iter()
97 .any(|item| item.is_match(query, cx) && item.is_resettable(cx))
98 }
99
100 pub(crate) fn render(
101 self,
102 query: &str,
103 options: &RenderOptions,
104 window: &mut Window,
105 cx: &mut App,
106 ) -> impl IntoElement {
107 GroupBox::new()
108 .id(SharedString::from(format!("group-{}", options.group_ix())))
109 .with_variant(options.group_variant())
110 .when_some(self.title.clone(), |this, title| {
111 this.title(v_flex().gap_1().child(title).when_some(
112 self.description.clone(),
113 |this, description| {
114 this.child(
115 Label::new(description)
116 .text_sm()
117 .text_color(cx.theme().muted_foreground),
118 )
119 },
120 ))
121 })
122 .gap_4()
123 .children(self.items.iter().enumerate().filter_map(|(item_ix, item)| {
124 if item.is_match(&query, cx) {
125 Some(
126 item.clone()
127 .render_item(&options.with_item_ix(item_ix), window, cx),
128 )
129 } else {
130 None
131 }
132 }))
133 .when_some(self.footer, |this, footer| this.footer(footer(window, cx)))
134 .refine_style(&self.style)
135 }
136
137 pub(crate) fn reset(&self, query: &str, window: &mut Window, cx: &mut App) {
138 for item in &self.items {
139 if item.is_match(query, cx) {
140 item.reset(window, cx);
141 }
142 }
143 }
144}