1use editor::Editor;
2use gpui::{Focusable, div};
3use ui::{
4 ActiveTheme as _, App, FluentBuilder as _, InteractiveElement as _, IntoElement,
5 ParentElement as _, RenderOnce, Styled as _, Window,
6};
7
8#[derive(IntoElement)]
9pub struct SettingsEditor {
10 initial_text: Option<String>,
11 placeholder: Option<&'static str>,
12 confirm: Option<Box<dyn Fn(Option<String>, &mut App)>>,
13 tab_index: Option<isize>,
14}
15
16impl SettingsEditor {
17 pub fn new() -> Self {
18 Self {
19 initial_text: None,
20 placeholder: None,
21 confirm: None,
22 tab_index: None,
23 }
24 }
25
26 pub fn with_initial_text(mut self, initial_text: String) -> Self {
27 self.initial_text = Some(initial_text);
28 self
29 }
30
31 pub fn with_placeholder(mut self, placeholder: &'static str) -> Self {
32 self.placeholder = Some(placeholder);
33 self
34 }
35
36 pub fn on_confirm(mut self, confirm: impl Fn(Option<String>, &mut App) + 'static) -> Self {
37 self.confirm = Some(Box::new(confirm));
38 self
39 }
40
41 pub(crate) fn tab_index(mut self, arg: isize) -> Self {
42 self.tab_index = Some(arg);
43 self
44 }
45}
46
47impl RenderOnce for SettingsEditor {
48 fn render(self, window: &mut Window, cx: &mut App) -> impl ui::IntoElement {
49 let editor = window.use_state(cx, {
50 move |window, cx| {
51 let mut editor = Editor::single_line(window, cx);
52 if let Some(text) = self.initial_text {
53 editor.set_text(text, window, cx);
54 }
55
56 if let Some(placeholder) = self.placeholder {
57 editor.set_placeholder_text(placeholder, window, cx);
58 }
59 // todo(settings_ui): We should have an observe global use for settings store
60 // so whenever a settings file is updated, the settings ui updates too
61 editor
62 }
63 });
64
65 if let Some(tab_index) = self.tab_index {
66 editor.focus_handle(cx).tab_index(tab_index);
67 }
68
69 let weak_editor = editor.downgrade();
70
71 let theme_colors = cx.theme().colors();
72
73 div()
74 .py_1()
75 .px_2()
76 .min_w_64()
77 .rounded_md()
78 .border_1()
79 .border_color(theme_colors.border)
80 .bg(theme_colors.editor_background)
81 .child(editor)
82 .when_some(self.confirm, |this, confirm| {
83 this.on_action::<menu::Confirm>({
84 move |_, _, cx| {
85 let Some(editor) = weak_editor.upgrade() else {
86 return;
87 };
88 let new_value = editor.read_with(cx, |editor, cx| editor.text(cx));
89 let new_value = (!new_value.is_empty()).then_some(new_value);
90 confirm(new_value, cx);
91 }
92 })
93 })
94 }
95}