gpui_component/setting/
group.rs

1use gpui::{
2    prelude::FluentBuilder as _, App, IntoElement, ParentElement as _, SharedString,
3    StyleRefinement, Styled, Window,
4};
5
6use crate::{
7    group_box::{GroupBox, GroupBoxVariants},
8    label::Label,
9    setting::{RenderOptions, SettingItem},
10    v_flex, ActiveTheme, StyledExt,
11};
12
13/// A setting group that can contain multiple setting items.
14#[derive(Clone)]
15pub struct SettingGroup {
16    style: StyleRefinement,
17
18    pub(super) title: Option<SharedString>,
19    pub(super) description: Option<SharedString>,
20    pub(super) items: Vec<SettingItem>,
21}
22
23impl Styled for SettingGroup {
24    fn style(&mut self) -> &mut StyleRefinement {
25        &mut self.style
26    }
27}
28
29impl SettingGroup {
30    /// Create a new setting group.
31    pub fn new() -> Self {
32        Self {
33            style: StyleRefinement::default(),
34            title: None,
35            description: None,
36            items: Vec::new(),
37        }
38    }
39
40    /// Set the label of the setting group, default is None.
41    pub fn title(mut self, title: impl Into<SharedString>) -> Self {
42        self.title = Some(title.into());
43        self
44    }
45
46    /// Set the description of the setting group, default is None.
47    pub fn description(mut self, description: impl Into<SharedString>) -> Self {
48        self.description = Some(description.into());
49        self
50    }
51
52    /// Add a setting item to the group.
53    pub fn item(mut self, item: SettingItem) -> Self {
54        self.items.push(item);
55        self
56    }
57
58    /// Add multiple setting items to the group.
59    pub fn items<I>(mut self, items: I) -> Self
60    where
61        I: IntoIterator<Item = SettingItem>,
62    {
63        self.items.extend(items);
64        self
65    }
66
67    /// Return true if any of the setting items in the group match the given query.
68    pub(super) fn is_match(&self, query: &str) -> bool {
69        self.items.iter().any(|item| item.is_match(query))
70    }
71
72    pub(super) fn is_resettable(&self, cx: &App) -> bool {
73        self.items.iter().any(|item| item.is_resettable(cx))
74    }
75
76    pub(crate) fn render(
77        self,
78        group_ix: usize,
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-{}", 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(item_ix, options, window, cx))
103                } else {
104                    None
105                }
106            }))
107            .refine_style(&self.style)
108    }
109
110    pub(crate) fn reset(&self, window: &mut Window, cx: &mut App) {
111        for item in &self.items {
112            item.reset(window, cx);
113        }
114    }
115}