use ely_gpui_component::{ forms::{Choice, Combobox, FontPicker}, settings::{ AccentColorPicker, Changed, DensitySelector, DeveloperModeToggle, FeatureFlags, FontSizeControl, ImportExportSettings, KeyboardShortcutsList, NotificationSettings, PrivacySettings, ProxySettings, ResetToDefault, SettingsRow, SettingsSection, Shortcut, StartupSettings, StorageSettings, ThemeSelector, }, theme::{ActiveTheme, HUE_NAMES}, }; use gpui::{AnyElement, App, Entity, IntoElement, ParentElement, Styled, div, px}; use super::window::{Fields, Prefs, SIZE}; use crate::ui::change; /// The open section's page. pub(super) fn page(prefs: &Entity, fields: &Fields, section: &str, cx: &App) -> AnyElement { match section { "appearance" => appearance(prefs, fields, cx), "keyboard" => keyboard(prefs, fields, cx), "network" => network(prefs, cx), "privacy" => privacy(prefs, cx), "notifications" => notifications(prefs, cx), "startup" => startup(prefs, cx), "storage" => storage(prefs, cx), "advanced" => advanced(prefs, fields, cx), other => panic!("settings demo: no page {other}"), } } fn appearance(prefs: &Entity, fields: &Fields, cx: &App) -> AnyElement { let now = prefs.read(cx); let presets = HUE_NAMES .iter() .take(6) .enumerate() .map(|(ix, name)| (*name, cx.theme().colors.hue(ix, "accent presets"))); let [theme, accent, size, density, font] = [(); 5].map(|_| prefs.clone()); SettingsSection::new("Appearance") .description("How the app looks.") .row( SettingsRow::new("Theme").control( ThemeSelector::new("settings-theme", now.appearance).on_change( move |next, _, cx| change(&theme, cx, |prefs| prefs.appearance = next), ), ), ) .row( SettingsRow::new("Accent color") .description("Buttons, links and what is chosen.") .control( AccentColorPicker::new("settings-accent", now.accent, presets).on_change( move |next, _, cx| change(&accent, cx, |prefs| prefs.accent = next), ), ), ) .row( SettingsRow::new("Font size").control( FontSizeControl::new("settings-size", now.size, SIZE, (11.0, 20.0)) .on_change(move |next, _, cx| change(&size, cx, |prefs| prefs.size = next)), ), ) .row( SettingsRow::new("Density") .description("How close things sit.") .control( DensitySelector::new("settings-density", now.density).on_change( move |next, _, cx| change(&density, cx, |prefs| prefs.density = next), ), ), ) .row( SettingsRow::new("Font") .description("FontFamilySelect → forms::FontPicker") .control( div().w(px(220.)).child( FontPicker::new("settings-font", &fields.font) .selected(now.family.clone()) .on_change(move |next, _, cx| { change(&font, cx, |prefs| prefs.family = next.clone()) }), ), ), ) .into_any_element() } fn shortcut(group: &str, name: &str, keys: &str) -> Shortcut { Shortcut { group: group.to_string().into(), name: name.to_string().into(), keys: keys.to_string().into(), } } fn keyboard(prefs: &Entity, fields: &Fields, cx: &App) -> AnyElement { let language = prefs.clone(); SettingsSection::new("Keyboard and language") .description("ShortcutEditor → editor::KeybindingsEditor") .row( SettingsRow::new("Language") .description("LanguageSelector → forms::Combobox::languages") .control( div().w(px(220.)).child( Combobox::languages("settings-language", &fields.language) .selected(prefs.read(cx).language.clone()) .on_change(move |next, _, cx| { change(&language, cx, |prefs| prefs.language = next.clone()) }), ), ), ) .row(KeyboardShortcutsList::new( [ shortcut("General", "Command palette", "cmd-shift-p"), shortcut("General", "Settings", "cmd-,"), shortcut("Editing", "Undo", "cmd-z"), shortcut("Editing", "Find", "cmd-f"), shortcut("Window", "New window", "cmd-shift-n"), ], &fields.shortcuts, )) .into_any_element() } fn network(prefs: &Entity, cx: &App) -> AnyElement { let proxy = prefs.clone(); SettingsSection::new("Network") .row( ProxySettings::new("settings-proxy", prefs.read(cx).proxy.clone()) .on_apply(move |next, _, cx| change(&proxy, cx, |prefs| prefs.proxy = next)), ) .into_any_element() } fn privacy(prefs: &Entity, cx: &App) -> AnyElement { let privacy = prefs.clone(); PrivacySettings::new("settings-privacy", prefs.read(cx).privacy) .on_change(move |next, _, cx| change(&privacy, cx, |prefs| prefs.privacy = next)) .into_any_element() } fn notifications(prefs: &Entity, cx: &App) -> AnyElement { let notices = prefs.clone(); NotificationSettings::new("settings-notices", prefs.read(cx).notices.clone()) .on_change(move |next, _, cx| change(¬ices, cx, |prefs| prefs.notices = next)) .into_any_element() } fn startup(prefs: &Entity, cx: &App) -> AnyElement { let startup = prefs.clone(); let pages = [ Choice::new("last", "Where I left off"), Choice::new("home", "The home page"), Choice::new("blank", "A blank window"), ]; StartupSettings::new("settings-startup", prefs.read(cx).startup.clone(), pages) .on_change(move |next, _, cx| change(&startup, cx, |prefs| prefs.startup = next)) .into_any_element() } fn storage(prefs: &Entity, cx: &App) -> AnyElement { let now = prefs.read(cx); let [cleared, reset, all] = [(); 3].map(|_| prefs.clone()); let changed = (now.size != SIZE).then(|| Changed { key: "size".into(), name: "Font size".into(), now: format!("{} pt", now.size).into(), default: format!("{SIZE} pt").into(), }); div() .flex() .flex_col() .gap_8() .child( SettingsSection::new("Storage") .description("What the app keeps on this device.") .row( StorageSettings::new("settings-storage", now.stores.clone()).on_clear( move |key, _, cx| { change(&cleared, cx, |prefs| { prefs .stores .iter_mut() .filter(|store| store.key == *key) .for_each(|store| store.bytes = 0) }) }, ), ), ) .child( SettingsSection::new("Import and export").row( ImportExportSettings::new("settings-files") .on_export(|_, _| log::info!("gallery: settings exported")) .on_import(|path, _, _| { log::info!("gallery: settings imported from {}", path.display()) }), ), ) .child( SettingsSection::new("Defaults") .description("A larger font size shows here.") .row( ResetToDefault::new("settings-reset", changed) .on_reset(move |_, _, cx| change(&reset, cx, |prefs| prefs.size = SIZE)) .on_reset_all(move |_, cx| change(&all, cx, |prefs| prefs.size = SIZE)), ), ) .into_any_element() } fn advanced(prefs: &Entity, fields: &Fields, cx: &App) -> AnyElement { let now = prefs.read(cx); let [toggled, developer] = [(); 2].map(|_| prefs.clone()); div() .flex() .flex_col() .gap_8() .child( SettingsSection::new("Features") .description("AdvancedSettings / FeatureFlags") .row( FeatureFlags::new("settings-flags", now.flags.clone(), &fields.flags) .on_toggle(move |key, on, _, cx| { change(&toggled, cx, |prefs| { prefs .flags .iter_mut() .filter(|flag| flag.key == *key) .for_each(|flag| flag.on = on) }) }), ), ) .child( DeveloperModeToggle::new("settings-developer", now.developer) .on_change(move |on, _, cx| change(&developer, cx, |prefs| prefs.developer = on)), ) .into_any_element() }