gpui_component/setting/
page.rs1use gpui::{
2 App, Entity, InteractiveElement as _, IntoElement, ListAlignment, ListState,
3 ParentElement as _, SharedString, Styled, Window, div, list, prelude::FluentBuilder as _, px,
4};
5use rust_i18n::t;
6
7use crate::{
8 ActiveTheme, IconName, Sizable,
9 button::{Button, ButtonVariants},
10 h_flex,
11 label::Label,
12 scroll::ScrollableElement,
13 setting::{RenderOptions, SettingGroup, settings::SettingsState},
14 v_flex,
15};
16
17#[derive(Clone)]
19pub struct SettingPage {
20 resettable: bool,
21 pub(super) default_open: bool,
22 pub(super) title: SharedString,
23 pub(super) description: Option<SharedString>,
24 pub(super) groups: Vec<SettingGroup>,
25}
26
27impl SettingPage {
28 pub fn new(title: impl Into<SharedString>) -> Self {
29 Self {
30 resettable: true,
31 default_open: false,
32 title: title.into(),
33 description: None,
34 groups: Vec::new(),
35 }
36 }
37
38 pub fn title(mut self, title: impl Into<SharedString>) -> Self {
40 self.title = title.into();
41 self
42 }
43
44 pub fn description(mut self, description: impl Into<SharedString>) -> Self {
46 self.description = Some(description.into());
47 self
48 }
49
50 pub fn default_open(mut self, default_open: bool) -> Self {
52 self.default_open = default_open;
53 self
54 }
55
56 pub fn resettable(mut self, resettable: bool) -> Self {
60 self.resettable = resettable;
61 self
62 }
63
64 pub fn group(mut self, group: SettingGroup) -> Self {
66 self.groups.push(group);
67 self
68 }
69
70 pub fn groups(mut self, groups: impl IntoIterator<Item = SettingGroup>) -> Self {
72 self.groups.extend(groups);
73 self
74 }
75
76 fn is_resettable(&self, cx: &App) -> bool {
77 self.resettable && self.groups.iter().any(|group| group.is_resettable(cx))
78 }
79
80 fn reset_all(&self, window: &mut Window, cx: &mut App) {
81 for group in &self.groups {
82 group.reset(window, cx);
83 }
84 }
85
86 pub(super) fn render(
87 &self,
88 ix: usize,
89 state: &Entity<SettingsState>,
90 options: &RenderOptions,
91 window: &mut Window,
92 cx: &mut App,
93 ) -> impl IntoElement {
94 let search_input = state.read(cx).search_input.clone();
95 let query = search_input.read(cx).value();
96 let groups = self
97 .groups
98 .iter()
99 .filter(|group| group.is_match(&query))
100 .cloned()
101 .collect::<Vec<_>>();
102 let groups_count = groups.len();
103
104 let list_state = window
105 .use_keyed_state(
106 SharedString::from(format!("list-state:{}", ix)),
107 cx,
108 |_, _| ListState::new(groups_count, ListAlignment::Top, px(100.)),
109 )
110 .read(cx)
111 .clone();
112
113 if list_state.item_count() != groups_count {
114 list_state.reset(groups_count);
115 }
116
117 let deferred_scroll_group_ix = state.read(cx).deferred_scroll_group_ix;
118 if let Some(ix) = deferred_scroll_group_ix {
119 state.update(cx, |state, _| {
120 state.deferred_scroll_group_ix = None;
121 });
122 list_state.scroll_to_reveal_item(ix);
123 }
124
125 v_flex()
126 .id(ix)
127 .size_full()
128 .child(
129 v_flex()
130 .p_4()
131 .gap_3()
132 .border_b_1()
133 .border_color(cx.theme().border)
134 .child(h_flex().justify_between().child(self.title.clone()).when(
135 self.is_resettable(cx),
136 |this| {
137 this.child(
138 Button::new("reset")
139 .icon(IconName::Undo2)
140 .ghost()
141 .small()
142 .tooltip(t!("Settings.Reset All"))
143 .on_click({
144 let page = self.clone();
145 move |_, window, cx| {
146 page.reset_all(window, cx);
147 }
148 }),
149 )
150 },
151 ))
152 .when_some(self.description.clone(), |this, description| {
153 this.child(
154 Label::new(description)
155 .text_sm()
156 .text_color(cx.theme().muted_foreground),
157 )
158 }),
159 )
160 .child(
161 div()
162 .px_4()
163 .relative()
164 .flex_1()
165 .w_full()
166 .child(
167 list(list_state.clone(), {
168 let query = query.clone();
169 let options = *options;
170 move |group_ix, window, cx| {
171 let group = groups[group_ix].clone();
172 group
173 .py_4()
174 .render(
175 &query,
176 &RenderOptions {
177 page_ix: ix,
178 group_ix,
179 ..options
180 },
181 window,
182 cx,
183 )
184 .into_any_element()
185 }
186 })
187 .size_full(),
188 )
189 .vertical_scrollbar(&list_state),
190 )
191 }
192}