1use anyhow::Context as _;
2use fs::Fs;
3use gpui::{AnyElement, App, AppContext as _, ReadGlobal as _, Window};
4use smallvec::SmallVec;
5
6use crate::SettingsStore;
7
8pub trait SettingsUi {
9 fn settings_ui_item() -> SettingsUiItem {
10 SettingsUiItem::None
11 }
12 fn settings_ui_entry() -> SettingsUiEntry;
13}
14
15pub struct SettingsUiEntry {
16 // todo(settings_ui): move this back here once there isn't a None variant
17 // pub path: &'static str,
18 // pub title: &'static str,
19 pub item: SettingsUiEntryVariant,
20}
21
22pub enum SettingsUiEntryVariant {
23 Group {
24 path: &'static str,
25 title: &'static str,
26 items: Vec<SettingsUiEntry>,
27 },
28 Item {
29 path: &'static str,
30 item: SettingsUiItemSingle,
31 },
32 Dynamic {
33 path: &'static str,
34 options: Vec<SettingsUiEntry>,
35 determine_option: fn(&serde_json::Value, &mut App) -> usize,
36 },
37 // todo(settings_ui): remove
38 None,
39}
40
41pub enum SettingsUiItemSingle {
42 SwitchField,
43 NumericStepper,
44 ToggleGroup(&'static [&'static str]),
45 /// This should be used when toggle group size > 6
46 DropDown(&'static [&'static str]),
47 Custom(Box<dyn Fn(SettingsValue<serde_json::Value>, &mut Window, &mut App) -> AnyElement>),
48}
49
50pub struct SettingsValue<T> {
51 pub title: &'static str,
52 pub path: SmallVec<[&'static str; 1]>,
53 pub value: Option<T>,
54 pub default_value: T,
55}
56
57impl<T> SettingsValue<T> {
58 pub fn read(&self) -> &T {
59 match &self.value {
60 Some(value) => value,
61 None => &self.default_value,
62 }
63 }
64}
65
66impl SettingsValue<serde_json::Value> {
67 pub fn write_value(path: &SmallVec<[&'static str; 1]>, value: serde_json::Value, cx: &mut App) {
68 let settings_store = SettingsStore::global(cx);
69 let fs = <dyn Fs>::global(cx);
70
71 let rx = settings_store.update_settings_file_at_path(fs.clone(), path.as_slice(), value);
72 let path = path.clone();
73 cx.background_spawn(async move {
74 rx.await?
75 .with_context(|| format!("Failed to update setting at path `{:?}`", path.join(".")))
76 })
77 .detach_and_log_err(cx);
78 }
79}
80
81impl<T: serde::Serialize> SettingsValue<T> {
82 pub fn write(
83 path: &SmallVec<[&'static str; 1]>,
84 value: T,
85 cx: &mut App,
86 ) -> Result<(), serde_json::Error> {
87 SettingsValue::write_value(path, serde_json::to_value(value)?, cx);
88 Ok(())
89 }
90}
91
92pub enum SettingsUiItem {
93 Group {
94 title: &'static str,
95 items: Vec<SettingsUiEntry>,
96 },
97 Single(SettingsUiItemSingle),
98 Dynamic {
99 options: Vec<SettingsUiEntry>,
100 determine_option: fn(&serde_json::Value, &mut App) -> usize,
101 },
102 None,
103}
104
105impl SettingsUi for bool {
106 fn settings_ui_item() -> SettingsUiItem {
107 SettingsUiItem::Single(SettingsUiItemSingle::SwitchField)
108 }
109
110 fn settings_ui_entry() -> SettingsUiEntry {
111 SettingsUiEntry {
112 item: SettingsUiEntryVariant::None,
113 }
114 }
115}
116
117impl SettingsUi for u64 {
118 fn settings_ui_item() -> SettingsUiItem {
119 SettingsUiItem::Single(SettingsUiItemSingle::NumericStepper)
120 }
121
122 fn settings_ui_entry() -> SettingsUiEntry {
123 SettingsUiEntry {
124 item: SettingsUiEntryVariant::None,
125 }
126 }
127}