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