theme_settings_ui.rs

  1use fs::Fs;
  2use gpui::AppContext;
  3use project::project_settings::{InlineBlameSettings, ProjectSettings};
  4use settings::{update_settings_file, Settings};
  5use theme::ThemeSettings;
  6use ui::{prelude::*, CheckboxWithLabel, NumericStepper};
  7
  8pub trait EditableSetting: RenderOnce {
  9    /// The type of the setting value.
 10    type Value: Send;
 11
 12    /// The settings type to which this setting belongs.
 13    type Settings: Settings;
 14
 15    /// Returns the name of this setting.
 16    fn name(&self) -> SharedString;
 17
 18    /// Returns the icon to be displayed in place of the setting name.
 19    fn icon(&self) -> Option<IconName> {
 20        None
 21    }
 22
 23    /// Returns a new instance of this setting.
 24    fn new(cx: &AppContext) -> Self;
 25
 26    /// Applies the given setting file to the settings file contents.
 27    ///
 28    /// This will be called when writing the setting value back to the settings file.
 29    fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value);
 30
 31    /// Writes the given setting value to the settings files.
 32    fn write(value: Self::Value, cx: &AppContext) {
 33        let fs = <dyn Fs>::global(cx);
 34
 35        update_settings_file::<Self::Settings>(fs, cx, move |settings, _cx| {
 36            Self::apply(settings, value);
 37        });
 38    }
 39}
 40
 41#[derive(IntoElement)]
 42pub struct UiFontSizeSetting(Pixels);
 43
 44impl EditableSetting for UiFontSizeSetting {
 45    type Value = Pixels;
 46    type Settings = ThemeSettings;
 47
 48    fn name(&self) -> SharedString {
 49        "UI Font Size".into()
 50    }
 51
 52    fn icon(&self) -> Option<IconName> {
 53        Some(IconName::FontSize)
 54    }
 55
 56    fn new(cx: &AppContext) -> Self {
 57        let settings = ThemeSettings::get_global(cx);
 58
 59        Self(settings.ui_font_size)
 60    }
 61
 62    fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value) {
 63        settings.ui_font_size = Some(value.into());
 64    }
 65}
 66
 67impl RenderOnce for UiFontSizeSetting {
 68    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
 69        let value = self.0;
 70
 71        h_flex()
 72            .gap_2()
 73            .map(|el| {
 74                if let Some(icon) = self.icon() {
 75                    el.child(Icon::new(icon))
 76                } else {
 77                    el.child(Label::new(self.name()))
 78                }
 79            })
 80            .child(NumericStepper::new(
 81                self.0.to_string(),
 82                move |_, cx| {
 83                    Self::write(value - px(1.), cx);
 84                },
 85                move |_, cx| {
 86                    Self::write(value + px(1.), cx);
 87                },
 88            ))
 89    }
 90}
 91
 92#[derive(IntoElement)]
 93pub struct BufferFontSizeSetting(Pixels);
 94
 95impl EditableSetting for BufferFontSizeSetting {
 96    type Value = Pixels;
 97    type Settings = ThemeSettings;
 98
 99    fn name(&self) -> SharedString {
100        "Buffer Font Size".into()
101    }
102
103    fn icon(&self) -> Option<IconName> {
104        Some(IconName::FontSize)
105    }
106
107    fn new(cx: &AppContext) -> Self {
108        let settings = ThemeSettings::get_global(cx);
109
110        Self(settings.buffer_font_size)
111    }
112
113    fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value) {
114        settings.buffer_font_size = Some(value.into());
115    }
116}
117
118impl RenderOnce for BufferFontSizeSetting {
119    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
120        let value = self.0;
121
122        h_flex()
123            .gap_2()
124            .map(|el| {
125                if let Some(icon) = self.icon() {
126                    el.child(Icon::new(icon))
127                } else {
128                    el.child(Label::new(self.name()))
129                }
130            })
131            .child(NumericStepper::new(
132                self.0.to_string(),
133                move |_, cx| {
134                    Self::write(value - px(1.), cx);
135                },
136                move |_, cx| {
137                    Self::write(value + px(1.), cx);
138                },
139            ))
140    }
141}
142
143#[derive(IntoElement)]
144pub struct InlineGitBlameSetting(bool);
145
146impl EditableSetting for InlineGitBlameSetting {
147    type Value = bool;
148    type Settings = ProjectSettings;
149
150    fn name(&self) -> SharedString {
151        "Inline Git Blame".into()
152    }
153
154    fn new(cx: &AppContext) -> Self {
155        let settings = ProjectSettings::get_global(cx);
156        Self(settings.git.inline_blame_enabled())
157    }
158
159    fn apply(settings: &mut <Self::Settings as Settings>::FileContent, value: Self::Value) {
160        if let Some(inline_blame) = settings.git.inline_blame.as_mut() {
161            inline_blame.enabled = value;
162        } else {
163            settings.git.inline_blame = Some(InlineBlameSettings {
164                enabled: false,
165                ..Default::default()
166            });
167        }
168    }
169}
170
171impl RenderOnce for InlineGitBlameSetting {
172    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
173        let value = self.0;
174
175        CheckboxWithLabel::new(
176            "inline-git-blame",
177            Label::new(self.name()),
178            value.into(),
179            |selection, cx| {
180                Self::write(
181                    match selection {
182                        Selection::Selected => true,
183                        Selection::Unselected | Selection::Indeterminate => false,
184                    },
185                    cx,
186                );
187            },
188        )
189    }
190}