editor_settings_controls.rs

  1use gpui::{AppContext, FontWeight};
  2use project::project_settings::{InlineBlameSettings, ProjectSettings};
  3use settings::{EditableSettingControl, Settings};
  4use theme::{FontFamilyCache, ThemeSettings};
  5use ui::{
  6    prelude::*, CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer,
  7    SettingsGroup,
  8};
  9
 10#[derive(IntoElement)]
 11pub struct EditorSettingsControls {}
 12
 13impl EditorSettingsControls {
 14    pub fn new() -> Self {
 15        Self {}
 16    }
 17}
 18
 19impl RenderOnce for EditorSettingsControls {
 20    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
 21        SettingsContainer::new()
 22            .child(
 23                SettingsGroup::new("Font")
 24                    .child(
 25                        h_flex()
 26                            .gap_2()
 27                            .justify_between()
 28                            .child(BufferFontFamilyControl)
 29                            .child(BufferFontWeightControl),
 30                    )
 31                    .child(BufferFontSizeControl),
 32            )
 33            .child(SettingsGroup::new("Editor").child(InlineGitBlameControl))
 34    }
 35}
 36
 37#[derive(IntoElement)]
 38struct BufferFontFamilyControl;
 39
 40impl EditableSettingControl for BufferFontFamilyControl {
 41    type Value = SharedString;
 42    type Settings = ThemeSettings;
 43
 44    fn name(&self) -> SharedString {
 45        "Buffer Font Family".into()
 46    }
 47
 48    fn read(cx: &AppContext) -> Self::Value {
 49        let settings = ThemeSettings::get_global(cx);
 50        settings.buffer_font.family.clone()
 51    }
 52
 53    fn apply(
 54        settings: &mut <Self::Settings as Settings>::FileContent,
 55        value: Self::Value,
 56        _cx: &AppContext,
 57    ) {
 58        settings.buffer_font_family = Some(value.to_string());
 59    }
 60}
 61
 62impl RenderOnce for BufferFontFamilyControl {
 63    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
 64        let value = Self::read(cx);
 65
 66        h_flex()
 67            .gap_2()
 68            .child(Icon::new(IconName::Font))
 69            .child(DropdownMenu::new(
 70                "buffer-font-family",
 71                value.clone(),
 72                ContextMenu::build(cx, |mut menu, cx| {
 73                    let font_family_cache = FontFamilyCache::global(cx);
 74
 75                    for font_name in font_family_cache.list_font_families(cx) {
 76                        menu = menu.custom_entry(
 77                            {
 78                                let font_name = font_name.clone();
 79                                move |_cx| Label::new(font_name.clone()).into_any_element()
 80                            },
 81                            {
 82                                let font_name = font_name.clone();
 83                                move |cx| {
 84                                    Self::write(font_name.clone(), cx);
 85                                }
 86                            },
 87                        )
 88                    }
 89
 90                    menu
 91                }),
 92            ))
 93    }
 94}
 95
 96#[derive(IntoElement)]
 97struct BufferFontSizeControl;
 98
 99impl EditableSettingControl for BufferFontSizeControl {
100    type Value = Pixels;
101    type Settings = ThemeSettings;
102
103    fn name(&self) -> SharedString {
104        "Buffer Font Size".into()
105    }
106
107    fn read(cx: &AppContext) -> Self::Value {
108        let settings = ThemeSettings::get_global(cx);
109        settings.buffer_font_size
110    }
111
112    fn apply(
113        settings: &mut <Self::Settings as Settings>::FileContent,
114        value: Self::Value,
115        _cx: &AppContext,
116    ) {
117        settings.buffer_font_size = Some(value.into());
118    }
119}
120
121impl RenderOnce for BufferFontSizeControl {
122    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
123        let value = Self::read(cx);
124
125        h_flex()
126            .gap_2()
127            .child(Icon::new(IconName::FontSize))
128            .child(NumericStepper::new(
129                value.to_string(),
130                move |_, cx| {
131                    Self::write(value - px(1.), cx);
132                },
133                move |_, cx| {
134                    Self::write(value + px(1.), cx);
135                },
136            ))
137    }
138}
139
140#[derive(IntoElement)]
141struct BufferFontWeightControl;
142
143impl EditableSettingControl for BufferFontWeightControl {
144    type Value = FontWeight;
145    type Settings = ThemeSettings;
146
147    fn name(&self) -> SharedString {
148        "Buffer Font Weight".into()
149    }
150
151    fn read(cx: &AppContext) -> Self::Value {
152        let settings = ThemeSettings::get_global(cx);
153        settings.buffer_font.weight
154    }
155
156    fn apply(
157        settings: &mut <Self::Settings as Settings>::FileContent,
158        value: Self::Value,
159        _cx: &AppContext,
160    ) {
161        settings.buffer_font_weight = Some(value.0);
162    }
163}
164
165impl RenderOnce for BufferFontWeightControl {
166    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
167        let value = Self::read(cx);
168
169        h_flex()
170            .gap_2()
171            .child(Icon::new(IconName::FontWeight))
172            .child(DropdownMenu::new(
173                "buffer-font-weight",
174                value.0.to_string(),
175                ContextMenu::build(cx, |mut menu, _cx| {
176                    for weight in FontWeight::ALL {
177                        menu = menu.custom_entry(
178                            move |_cx| Label::new(weight.0.to_string()).into_any_element(),
179                            {
180                                move |cx| {
181                                    Self::write(weight, cx);
182                                }
183                            },
184                        )
185                    }
186
187                    menu
188                }),
189            ))
190    }
191}
192
193#[derive(IntoElement)]
194struct InlineGitBlameControl;
195
196impl EditableSettingControl for InlineGitBlameControl {
197    type Value = bool;
198    type Settings = ProjectSettings;
199
200    fn name(&self) -> SharedString {
201        "Inline Git Blame".into()
202    }
203
204    fn read(cx: &AppContext) -> Self::Value {
205        let settings = ProjectSettings::get_global(cx);
206        settings.git.inline_blame_enabled()
207    }
208
209    fn apply(
210        settings: &mut <Self::Settings as Settings>::FileContent,
211        value: Self::Value,
212        _cx: &AppContext,
213    ) {
214        if let Some(inline_blame) = settings.git.inline_blame.as_mut() {
215            inline_blame.enabled = value;
216        } else {
217            settings.git.inline_blame = Some(InlineBlameSettings {
218                enabled: false,
219                ..Default::default()
220            });
221        }
222    }
223}
224
225impl RenderOnce for InlineGitBlameControl {
226    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
227        let value = Self::read(cx);
228
229        CheckboxWithLabel::new(
230            "inline-git-blame",
231            Label::new(self.name()),
232            value.into(),
233            |selection, cx| {
234                Self::write(
235                    match selection {
236                        Selection::Selected => true,
237                        Selection::Unselected | Selection::Indeterminate => false,
238                    },
239                    cx,
240                );
241            },
242        )
243    }
244}