settings_ui.rs

  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}