appearance_settings_controls.rs

  1use gpui::{AppContext, FontWeight};
  2use settings::{EditableSettingControl, Settings};
  3use theme::{SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings};
  4use ui::{
  5    prelude::*, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup,
  6    ToggleButton,
  7};
  8
  9#[derive(IntoElement)]
 10pub struct AppearanceSettingsControls {}
 11
 12impl AppearanceSettingsControls {
 13    pub fn new() -> Self {
 14        Self {}
 15    }
 16}
 17
 18impl RenderOnce for AppearanceSettingsControls {
 19    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
 20        SettingsContainer::new()
 21            .child(
 22                SettingsGroup::new("Theme").child(
 23                    h_flex()
 24                        .gap_2()
 25                        .justify_between()
 26                        .child(ThemeControl)
 27                        .child(ThemeModeControl),
 28                ),
 29            )
 30            .child(
 31                SettingsGroup::new("Font")
 32                    .child(UiFontSizeControl)
 33                    .child(UiFontWeightControl),
 34            )
 35    }
 36}
 37
 38#[derive(IntoElement)]
 39struct ThemeControl;
 40
 41impl EditableSettingControl for ThemeControl {
 42    type Value = String;
 43    type Settings = ThemeSettings;
 44
 45    fn name(&self) -> SharedString {
 46        "Theme".into()
 47    }
 48
 49    fn read(cx: &AppContext) -> Self::Value {
 50        let settings = ThemeSettings::get_global(cx);
 51        let appearance = SystemAppearance::global(cx);
 52        settings
 53            .theme_selection
 54            .as_ref()
 55            .map(|selection| selection.theme(appearance.0).to_string())
 56            .unwrap_or_else(|| ThemeSettings::default_theme(*appearance).to_string())
 57    }
 58
 59    fn apply(
 60        settings: &mut <Self::Settings as Settings>::FileContent,
 61        value: Self::Value,
 62        cx: &AppContext,
 63    ) {
 64        let appearance = SystemAppearance::global(cx);
 65        settings.set_theme(value, appearance.0);
 66    }
 67}
 68
 69impl RenderOnce for ThemeControl {
 70    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
 71        let value = Self::read(cx);
 72
 73        DropdownMenu::new(
 74            "theme",
 75            value.clone(),
 76            ContextMenu::build(cx, |mut menu, cx| {
 77                let theme_registry = ThemeRegistry::global(cx);
 78
 79                for theme in theme_registry.list_names(false) {
 80                    menu = menu.custom_entry(
 81                        {
 82                            let theme = theme.clone();
 83                            move |_cx| Label::new(theme.clone()).into_any_element()
 84                        },
 85                        {
 86                            let theme = theme.clone();
 87                            move |cx| {
 88                                Self::write(theme.to_string(), cx);
 89                            }
 90                        },
 91                    )
 92                }
 93
 94                menu
 95            }),
 96        )
 97        .full_width(true)
 98    }
 99}
100
101#[derive(IntoElement)]
102struct ThemeModeControl;
103
104impl EditableSettingControl for ThemeModeControl {
105    type Value = ThemeMode;
106    type Settings = ThemeSettings;
107
108    fn name(&self) -> SharedString {
109        "Theme Mode".into()
110    }
111
112    fn read(cx: &AppContext) -> Self::Value {
113        let settings = ThemeSettings::get_global(cx);
114        settings
115            .theme_selection
116            .as_ref()
117            .and_then(|selection| selection.mode())
118            .unwrap_or_default()
119    }
120
121    fn apply(
122        settings: &mut <Self::Settings as Settings>::FileContent,
123        value: Self::Value,
124        _cx: &AppContext,
125    ) {
126        settings.set_mode(value);
127    }
128}
129
130impl RenderOnce for ThemeModeControl {
131    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
132        let value = Self::read(cx);
133
134        h_flex()
135            .child(
136                ToggleButton::new("light", "Light")
137                    .style(ButtonStyle::Filled)
138                    .size(ButtonSize::Large)
139                    .selected(value == ThemeMode::Light)
140                    .on_click(|_, cx| Self::write(ThemeMode::Light, cx))
141                    .first(),
142            )
143            .child(
144                ToggleButton::new("system", "System")
145                    .style(ButtonStyle::Filled)
146                    .size(ButtonSize::Large)
147                    .selected(value == ThemeMode::System)
148                    .on_click(|_, cx| Self::write(ThemeMode::System, cx))
149                    .middle(),
150            )
151            .child(
152                ToggleButton::new("dark", "Dark")
153                    .style(ButtonStyle::Filled)
154                    .size(ButtonSize::Large)
155                    .selected(value == ThemeMode::Dark)
156                    .on_click(|_, cx| Self::write(ThemeMode::Dark, cx))
157                    .last(),
158            )
159    }
160}
161
162#[derive(IntoElement)]
163struct UiFontSizeControl;
164
165impl EditableSettingControl for UiFontSizeControl {
166    type Value = Pixels;
167    type Settings = ThemeSettings;
168
169    fn name(&self) -> SharedString {
170        "UI Font Size".into()
171    }
172
173    fn read(cx: &AppContext) -> Self::Value {
174        let settings = ThemeSettings::get_global(cx);
175        settings.ui_font_size
176    }
177
178    fn apply(
179        settings: &mut <Self::Settings as Settings>::FileContent,
180        value: Self::Value,
181        _cx: &AppContext,
182    ) {
183        settings.ui_font_size = Some(value.into());
184    }
185}
186
187impl RenderOnce for UiFontSizeControl {
188    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
189        let value = Self::read(cx);
190
191        h_flex()
192            .gap_2()
193            .child(Icon::new(IconName::FontSize))
194            .child(NumericStepper::new(
195                value.to_string(),
196                move |_, cx| {
197                    Self::write(value - px(1.), cx);
198                },
199                move |_, cx| {
200                    Self::write(value + px(1.), cx);
201                },
202            ))
203    }
204}
205
206#[derive(IntoElement)]
207struct UiFontWeightControl;
208
209impl EditableSettingControl for UiFontWeightControl {
210    type Value = FontWeight;
211    type Settings = ThemeSettings;
212
213    fn name(&self) -> SharedString {
214        "UI Font Weight".into()
215    }
216
217    fn read(cx: &AppContext) -> Self::Value {
218        let settings = ThemeSettings::get_global(cx);
219        settings.ui_font.weight
220    }
221
222    fn apply(
223        settings: &mut <Self::Settings as Settings>::FileContent,
224        value: Self::Value,
225        _cx: &AppContext,
226    ) {
227        settings.ui_font_weight = Some(value.0);
228    }
229}
230
231impl RenderOnce for UiFontWeightControl {
232    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
233        let value = Self::read(cx);
234
235        h_flex()
236            .gap_2()
237            .child(Icon::new(IconName::FontWeight))
238            .child(DropdownMenu::new(
239                "ui-font-weight",
240                value.0.to_string(),
241                ContextMenu::build(cx, |mut menu, _cx| {
242                    for weight in FontWeight::ALL {
243                        menu = menu.custom_entry(
244                            move |_cx| Label::new(weight.0.to_string()).into_any_element(),
245                            {
246                                move |cx| {
247                                    Self::write(weight, cx);
248                                }
249                            },
250                        )
251                    }
252
253                    menu
254                }),
255            ))
256    }
257}