gpui_component/setting/
group.rs

1use gpui::{
2    App, IntoElement, ParentElement as _, SharedString, StyleRefinement, Styled, Window,
3    prelude::FluentBuilder as _,
4};
5
6use crate::{
7    ActiveTheme, StyledExt,
8    group_box::{GroupBox, GroupBoxVariants},
9    label::Label,
10    setting::{RenderOptions, SettingItem},
11    v_flex,
12};
13
14/// A setting group that can contain multiple setting items.
15#[derive(Clone)]
16pub struct SettingGroup {
17    style: StyleRefinement,
18
19    pub(super) title: Option<SharedString>,
20    pub(super) description: Option<SharedString>,
21    pub(super) items: Vec<SettingItem>,
22}
23
24impl Styled for SettingGroup {
25    fn style(&mut self) -> &mut StyleRefinement {
26        &mut self.style
27    }
28}
29
30impl SettingGroup {
31    /// Create a new setting group.
32    pub fn new() -> Self {
33        Self {
34            style: StyleRefinement::default(),
35            title: None,
36            description: None,
37            items: Vec::new(),
38        }
39    }
40
41    /// Set the label of the setting group, default is None.
42    pub fn title(mut self, title: impl Into<SharedString>) -> Self {
43        self.title = Some(title.into());
44        self
45    }
46
47    /// Set the description of the setting group, default is None.
48    pub fn description(mut self, description: impl Into<SharedString>) -> Self {
49        self.description = Some(description.into());
50        self
51    }
52
53    /// Add a setting item to the group.
54    pub fn item(mut self, item: SettingItem) -> Self {
55        self.items.push(item);
56        self
57    }
58
59    /// Add multiple setting items to the group.
60    pub fn items<I>(mut self, items: I) -> Self
61    where
62        I: IntoIterator<Item = SettingItem>,
63    {
64        self.items.extend(items);
65        self
66    }
67
68    /// Return true if any of the setting items in the group match the given query.
69    pub(super) fn is_match(&self, query: &str) -> bool {
70        self.items.iter().any(|item| item.is_match(query))
71    }
72
73    pub(super) fn is_resettable(&self, cx: &App) -> bool {
74        self.items.iter().any(|item| item.is_resettable(cx))
75    }
76
77    pub(crate) fn render(
78        self,
79        query: &str,
80        options: &RenderOptions,
81        window: &mut Window,
82        cx: &mut App,
83    ) -> impl IntoElement {
84        GroupBox::new()
85            .id(SharedString::from(format!("group-{}", options.group_ix)))
86            .with_variant(options.group_variant)
87            .when_some(self.title.clone(), |this, title| {
88                this.title(v_flex().gap_1().child(title).when_some(
89                    self.description.clone(),
90                    |this, description| {
91                        this.child(
92                            Label::new(description)
93                                .text_sm()
94                                .text_color(cx.theme().muted_foreground),
95                        )
96                    },
97                ))
98            })
99            .gap_4()
100            .children(self.items.iter().enumerate().filter_map(|(item_ix, item)| {
101                if item.is_match(&query) {
102                    Some(item.clone().render_item(
103                        &RenderOptions {
104                            item_ix,
105                            ..*options
106                        },
107                        window,
108                        cx,
109                    ))
110                } else {
111                    None
112                }
113            }))
114            .refine_style(&self.style)
115    }
116
117    pub(crate) fn reset(&self, window: &mut Window, cx: &mut App) {
118        for item in &self.items {
119            item.reset(window, cx);
120        }
121    }
122}