386 lines · 11.1 KB · Rust
RawDownload
use std::sync::Arc; use gpui::{AppContext, FontFeatures, FontWeight};use settings::{EditableSettingControl, Settings};use theme::{FontFamilyCache, SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings};use ui::{    prelude::*, CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer,    SettingsGroup, ToggleButton,}; #[derive(IntoElement)]pub struct AppearanceSettingsControls {} impl AppearanceSettingsControls {    pub fn new() -> Self {        Self {}    }} impl RenderOnce for AppearanceSettingsControls {    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {        SettingsContainer::new()            .child(                SettingsGroup::new("Theme").child(                    h_flex()                        .gap_2()                        .justify_between()                        .child(ThemeControl)                        .child(ThemeModeControl),                ),            )            .child(                SettingsGroup::new("Font")                    .child(                        h_flex()                            .gap_2()                            .justify_between()                            .child(UiFontFamilyControl)                            .child(UiFontWeightControl),                    )                    .child(UiFontSizeControl)                    .child(UiFontLigaturesControl),            )    }} #[derive(IntoElement)]struct ThemeControl; impl EditableSettingControl for ThemeControl {    type Value = String;    type Settings = ThemeSettings;     fn name(&self) -> SharedString {        "Theme".into()    }     fn read(cx: &AppContext) -> Self::Value {        let settings = ThemeSettings::get_global(cx);        let appearance = SystemAppearance::global(cx);        settings            .theme_selection            .as_ref()            .map(|selection| selection.theme(appearance.0).to_string())            .unwrap_or_else(|| ThemeSettings::default_theme(*appearance).to_string())    }     fn apply(        settings: &mut <Self::Settings as Settings>::FileContent,        value: Self::Value,        cx: &AppContext,    ) {        let appearance = SystemAppearance::global(cx);        settings.set_theme(value, appearance.0);    }} impl RenderOnce for ThemeControl {    fn render(self, cx: &mut WindowContext) -> impl IntoElement {        let value = Self::read(cx);         DropdownMenu::new(            "theme",            value.clone(),            ContextMenu::build(cx, |mut menu, cx| {                let theme_registry = ThemeRegistry::global(cx);                 for theme in theme_registry.list_names() {                    menu = menu.custom_entry(                        {                            let theme = theme.clone();                            move |_cx| Label::new(theme.clone()).into_any_element()                        },                        {                            let theme = theme.clone();                            move |cx| {                                Self::write(theme.to_string(), cx);                            }                        },                    )                }                 menu            }),        )        .full_width(true)    }} #[derive(IntoElement)]struct ThemeModeControl; impl EditableSettingControl for ThemeModeControl {    type Value = ThemeMode;    type Settings = ThemeSettings;     fn name(&self) -> SharedString {        "Theme Mode".into()    }     fn read(cx: &AppContext) -> Self::Value {        let settings = ThemeSettings::get_global(cx);        settings            .theme_selection            .as_ref()            .and_then(|selection| selection.mode())            .unwrap_or_default()    }     fn apply(        settings: &mut <Self::Settings as Settings>::FileContent,        value: Self::Value,        _cx: &AppContext,    ) {        settings.set_mode(value);    }} impl RenderOnce for ThemeModeControl {    fn render(self, cx: &mut WindowContext) -> impl IntoElement {        let value = Self::read(cx);         h_flex()            .child(                ToggleButton::new("light", "Light")                    .style(ButtonStyle::Filled)                    .size(ButtonSize::Large)                    .toggle_state(value == ThemeMode::Light)                    .on_click(|_, cx| Self::write(ThemeMode::Light, cx))                    .first(),            )            .child(                ToggleButton::new("system", "System")                    .style(ButtonStyle::Filled)                    .size(ButtonSize::Large)                    .toggle_state(value == ThemeMode::System)                    .on_click(|_, cx| Self::write(ThemeMode::System, cx))                    .middle(),            )            .child(                ToggleButton::new("dark", "Dark")                    .style(ButtonStyle::Filled)                    .size(ButtonSize::Large)                    .toggle_state(value == ThemeMode::Dark)                    .on_click(|_, cx| Self::write(ThemeMode::Dark, cx))                    .last(),            )    }} #[derive(IntoElement)]struct UiFontFamilyControl; impl EditableSettingControl for UiFontFamilyControl {    type Value = SharedString;    type Settings = ThemeSettings;     fn name(&self) -> SharedString {        "UI Font Family".into()    }     fn read(cx: &AppContext) -> Self::Value {        let settings = ThemeSettings::get_global(cx);        settings.ui_font.family.clone()    }     fn apply(        settings: &mut <Self::Settings as Settings>::FileContent,        value: Self::Value,        _cx: &AppContext,    ) {        settings.ui_font_family = Some(value.to_string());    }} impl RenderOnce for UiFontFamilyControl {    fn render(self, cx: &mut WindowContext) -> impl IntoElement {        let value = Self::read(cx);         h_flex()            .gap_2()            .child(Icon::new(IconName::Font))            .child(DropdownMenu::new(                "ui-font-family",                value.clone(),                ContextMenu::build(cx, |mut menu, cx| {                    let font_family_cache = FontFamilyCache::global(cx);                     for font_name in font_family_cache.list_font_families(cx) {                        menu = menu.custom_entry(                            {                                let font_name = font_name.clone();                                move |_cx| Label::new(font_name.clone()).into_any_element()                            },                            {                                let font_name = font_name.clone();                                move |cx| {                                    Self::write(font_name.clone(), cx);                                }                            },                        )                    }                     menu                }),            ))    }} #[derive(IntoElement)]struct UiFontSizeControl; impl EditableSettingControl for UiFontSizeControl {    type Value = Pixels;    type Settings = ThemeSettings;     fn name(&self) -> SharedString {        "UI Font Size".into()    }     fn read(cx: &AppContext) -> Self::Value {        let settings = ThemeSettings::get_global(cx);        settings.ui_font_size    }     fn apply(        settings: &mut <Self::Settings as Settings>::FileContent,        value: Self::Value,        _cx: &AppContext,    ) {        settings.ui_font_size = Some(value.into());    }} impl RenderOnce for UiFontSizeControl {    fn render(self, cx: &mut WindowContext) -> impl IntoElement {        let value = Self::read(cx);         h_flex()            .gap_2()            .child(Icon::new(IconName::FontSize))            .child(NumericStepper::new(                "ui-font-size",                value.to_string(),                move |_, cx| {                    Self::write(value - px(1.), cx);                },                move |_, cx| {                    Self::write(value + px(1.), cx);                },            ))    }} #[derive(IntoElement)]struct UiFontWeightControl; impl EditableSettingControl for UiFontWeightControl {    type Value = FontWeight;    type Settings = ThemeSettings;     fn name(&self) -> SharedString {        "UI Font Weight".into()    }     fn read(cx: &AppContext) -> Self::Value {        let settings = ThemeSettings::get_global(cx);        settings.ui_font.weight    }     fn apply(        settings: &mut <Self::Settings as Settings>::FileContent,        value: Self::Value,        _cx: &AppContext,    ) {        settings.ui_font_weight = Some(value.0);    }} impl RenderOnce for UiFontWeightControl {    fn render(self, cx: &mut WindowContext) -> impl IntoElement {        let value = Self::read(cx);         h_flex()            .gap_2()            .child(Icon::new(IconName::FontWeight))            .child(DropdownMenu::new(                "ui-font-weight",                value.0.to_string(),                ContextMenu::build(cx, |mut menu, _cx| {                    for weight in FontWeight::ALL {                        menu = menu.custom_entry(                            move |_cx| Label::new(weight.0.to_string()).into_any_element(),                            {                                move |cx| {                                    Self::write(weight, cx);                                }                            },                        )                    }                     menu                }),            ))    }} #[derive(IntoElement)]struct UiFontLigaturesControl; impl EditableSettingControl for UiFontLigaturesControl {    type Value = bool;    type Settings = ThemeSettings;     fn name(&self) -> SharedString {        "UI Font Ligatures".into()    }     fn read(cx: &AppContext) -> Self::Value {        let settings = ThemeSettings::get_global(cx);        settings.ui_font.features.is_calt_enabled().unwrap_or(true)    }     fn apply(        settings: &mut <Self::Settings as Settings>::FileContent,        value: Self::Value,        _cx: &AppContext,    ) {        let value = if value { 1 } else { 0 };         let mut features = settings            .ui_font_features            .as_ref()            .map(|features| features.tag_value_list().to_vec())            .unwrap_or_default();         if let Some(calt_index) = features.iter().position(|(tag, _)| tag == "calt") {            features[calt_index].1 = value;        } else {            features.push(("calt".into(), value));        }         settings.ui_font_features = Some(FontFeatures(Arc::new(features)));    }} impl RenderOnce for UiFontLigaturesControl {    fn render(self, cx: &mut WindowContext) -> impl IntoElement {        let value = Self::read(cx);         CheckboxWithLabel::new(            "ui-font-ligatures",            Label::new(self.name()),            value.into(),            |selection, cx| {                Self::write(                    match selection {                        ToggleState::Selected => true,                        ToggleState::Unselected | ToggleState::Indeterminate => false,                    },                    cx,                );            },        )    }}