Skip to main content

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, cx: &App) -> bool {
70        self.items.iter().any(|item| item.is_match(query, cx))
71    }
72
73    pub(super) fn is_resettable(&self, query: &str, cx: &App) -> bool {
74        self.items
75            .iter()
76            .any(|item| item.is_match(query, cx) && item.is_resettable(cx))
77    }
78
79    pub(crate) fn render(
80        self,
81        query: &str,
82        options: &RenderOptions,
83        window: &mut Window,
84        cx: &mut App,
85    ) -> impl IntoElement {
86        GroupBox::new()
87            .id(SharedString::from(format!("group-{}", options.group_ix())))
88            .with_variant(options.group_variant())
89            .when_some(self.title.clone(), |this, title| {
90                this.title(v_flex().gap_1().child(title).when_some(
91                    self.description.clone(),
92                    |this, description| {
93                        this.child(
94                            Label::new(description)
95                                .text_sm()
96                                .text_color(cx.theme().muted_foreground),
97                        )
98                    },
99                ))
100            })
101            .gap_4()
102            .children(self.items.iter().enumerate().filter_map(|(item_ix, item)| {
103                if item.is_match(&query, cx) {
104                    Some(
105                        item.clone()
106                            .render_item(&options.with_item_ix(item_ix), window, cx),
107                    )
108                } else {
109                    None
110                }
111            }))
112            .refine_style(&self.style)
113    }
114
115    pub(crate) fn reset(&self, query: &str, window: &mut Window, cx: &mut App) {
116        for item in &self.items {
117            if item.is_match(query, cx) {
118                item.reset(window, cx);
119            }
120        }
121    }
122}