use gpui::{ App, AppContext, Axis, Context, Element, Entity, FocusHandle, Focusable, Global, IntoElement, ParentElement as _, Render, SharedString, Styled, Window, }; use gpui_component::{ ActiveTheme, Icon, IconName, Sizable, Size, Theme, ThemeMode, button::Button, group_box::GroupBoxVariant, h_flex, label::Label, setting::{NumberFieldOptions, SettingField, SettingGroup, SettingItem, SettingPage, Settings}, text::TextView, v_flex, }; struct AppSettings { auto_switch_theme: bool, cli_path: SharedString, font_family: SharedString, font_size: f64, notifications_enabled: bool, auto_update: bool, resettable: bool, } impl Default for AppSettings { fn default() -> Self { Self { auto_switch_theme: false, cli_path: "/usr/local/bin/bash".into(), font_family: "Arial".into(), font_size: 14.0, notifications_enabled: true, auto_update: true, resettable: true, } } } impl Global for AppSettings {} impl AppSettings { fn global(cx: &App) -> &AppSettings { cx.global::() } pub fn global_mut(cx: &mut App) -> &mut AppSettings { cx.global_mut::() } } pub struct SettingsStory { focus_handle: FocusHandle, group_variant: GroupBoxVariant, size: Size, } impl super::Story for SettingsStory { fn title() -> &'static str { "Settings" } fn description() -> &'static str { "A collection of settings groups and items for the application." } fn new_view(window: &mut Window, cx: &mut App) -> Entity { Self::view(window, cx) } } impl SettingsStory { pub fn view(window: &mut Window, cx: &mut App) -> Entity { cx.new(|cx| Self::new(window, cx)) } fn new(_: &mut Window, cx: &mut Context) -> Self { cx.set_global::(AppSettings::default()); Self { focus_handle: cx.focus_handle(), group_variant: GroupBoxVariant::Outline, size: Size::default(), } } fn setting_pages(&self, window: &mut Window, cx: &mut Context) -> Vec { let view = cx.entity(); let default_settings = AppSettings::default(); let resettable = AppSettings::global(cx).resettable; vec![ SettingPage::new("General") .resettable(resettable) .default_open(true) .groups(vec![ SettingGroup::new().title("Appearance").items(vec![ SettingItem::new( "Dark Mode", SettingField::switch( |cx: &App| cx.theme().mode.is_dark(), |val: bool, cx: &mut App| { let mode = if val { ThemeMode::Dark } else { ThemeMode::Light }; Theme::global_mut(cx).mode = mode; Theme::change(mode, None, cx); }, ) .default_value(false), ) .description("Switch between light and dark themes."), SettingItem::new( "Auto Switch Theme", SettingField::checkbox( |cx: &App| AppSettings::global(cx).auto_switch_theme, |val: bool, cx: &mut App| { AppSettings::global_mut(cx).auto_switch_theme = val; }, ) .default_value(default_settings.auto_switch_theme), ) .description("Automatically switch theme based on system settings."), SettingItem::new( "resettable", SettingField::switch( |cx: &App| AppSettings::global(cx).resettable, |checked: bool, cx: &mut App| { AppSettings::global_mut(cx).resettable = checked }, ), ) .description("Enable/Disable reset button for settings."), SettingItem::new( "Group Variant", SettingField::dropdown( vec![ (GroupBoxVariant::Normal.as_str().into(), "Normal".into()), (GroupBoxVariant::Outline.as_str().into(), "Outline".into()), (GroupBoxVariant::Fill.as_str().into(), "Fill".into()), ], { let view = view.clone(); move |cx: &App| { SharedString::from( view.read(cx).group_variant.as_str().to_string(), ) } }, { let view = view.clone(); move |val: SharedString, cx: &mut App| { view.update(cx, |view, cx| { view.group_variant = GroupBoxVariant::from_str(val.as_str()); cx.notify(); }); } }, ) .default_value(GroupBoxVariant::Outline.as_str().to_string()), ) .description("Select the variant for setting groups."), SettingItem::new( "Group Size", SettingField::dropdown( vec![ (Size::Medium.as_str().into(), "Medium".into()), (Size::Small.as_str().into(), "Small".into()), (Size::XSmall.as_str().into(), "XSmall".into()), ], { let view = view.clone(); move |cx: &App| { SharedString::from(view.read(cx).size.as_str().to_string()) } }, { let view = view.clone(); move |val: SharedString, cx: &mut App| { view.update(cx, |view, cx| { view.size = Size::from_str(val.as_str()); cx.notify(); }); } }, ) .default_value(Size::default().as_str().to_string()), ) .description("Select the size for the setting group."), ]), SettingGroup::new() .title("Font") .item( SettingItem::new( "Font Family", SettingField::dropdown( vec![ ("Arial".into(), "Arial".into()), ("Helvetica".into(), "Helvetica".into()), ("Times New Roman".into(), "Times New Roman".into()), ("Courier New".into(), "Courier New".into()), ], |cx: &App| AppSettings::global(cx).font_family.clone(), |val: SharedString, cx: &mut App| { AppSettings::global_mut(cx).font_family = val; }, ) .default_value(default_settings.font_family), ) .description("Select the font family for the story."), ) .item( SettingItem::new( "Font Size", SettingField::number_input( NumberFieldOptions { min: 8.0, max: 72.0, ..Default::default() }, |cx: &App| AppSettings::global(cx).font_size, |val: f64, cx: &mut App| { AppSettings::global_mut(cx).font_size = val; }, ) .default_value(default_settings.font_size), ) .description("Adjust the font size for better readability."), ), SettingGroup::new().title("Other").items(vec![ SettingItem::element(|options, _, _| { h_flex() .w_full() .justify_between() .flex_wrap() .gap_3() .child("This is a custom element item by use SettingItem::element.") .child( Button::new("action") .icon(IconName::Globe) .label("Repository...") .outline() .with_size(options.size) .on_click(|_, _, cx| { cx.open_url( "https://github.com/longbridge/gpui-component", ); }), ) .into_any_element() }), SettingItem::new( "CLI Path", SettingField::input( |cx: &App| AppSettings::global(cx).cli_path.clone(), |val: SharedString, cx: &mut App| { println!("cli-path set value: {}", val); AppSettings::global_mut(cx).cli_path = val; }, ) .default_value(default_settings.cli_path), ) .layout(Axis::Vertical) .description( "Path to the CLI executable. \n\ This item uses Vertical layout. The title,\ description, and field are all aligned vertically with width 100%.", ), ]), ]), SettingPage::new("Software Update") .resettable(resettable) .groups(vec![SettingGroup::new().title("Updates").items(vec![ SettingItem::new( "Enable Notifications", SettingField::switch( |cx: &App| AppSettings::global(cx).notifications_enabled, |val: bool, cx: &mut App| { AppSettings::global_mut(cx).notifications_enabled = val; }, ) .default_value(default_settings.notifications_enabled), ) .description("Receive notifications about updates and news."), SettingItem::new( "Auto Update", SettingField::switch( |cx: &App| AppSettings::global(cx).auto_update, |val: bool, cx: &mut App| { AppSettings::global_mut(cx).auto_update = val; }, ) .default_value(default_settings.auto_update), ) .description("Automatically download and install updates."), ])]), SettingPage::new("About") .resettable(resettable) .group( SettingGroup::new().item(SettingItem::element(|_options, _, cx| { v_flex() .gap_3() .w_full() .items_center() .justify_center() .child(Icon::new(IconName::GalleryVerticalEnd).size_16()) .child("GPUI Component") .child( Label::new( "Rust GUI components for building fantastic cross-platform \ desktop application by using GPUI.", ) .text_sm() .text_color(cx.theme().muted_foreground), ) .into_any() })), ) .group(SettingGroup::new().title("Links").items(vec![ SettingItem::new( "GitHub Repository", SettingField::element(|options, _window, _cx| { Button::new("open-url") .outline() .label("Repository...") .with_size(options.size) .on_click(|_, _window, cx| { cx.open_url("https://github.com/longbridge/gpui-component"); }) }), ) .description("Open the GitHub repository in your default browser."), SettingItem::new( "Documentation", SettingField::element(|options, _window, _cx| { Button::new("open-url") .outline() .label("Rust Docs...") .with_size(options.size) .on_click(|_, _window, cx| { cx.open_url("https://docs.rs/gpui-component"); }) }), ) .description(TextView::markdown( "desc", "Rust doc for the `gpui-component` crate.", window, cx, )), SettingItem::new( "Website", SettingField::element(|options, _window, _cx| { Button::new("open-url") .outline() .label("Website...") .with_size(options.size) .on_click(|_, _window, cx| { cx.open_url("https://longbridge.github.io/gpui-component/"); }) }), ) .description("Official website and documentation for the GPUI Component."), ])), ] } } impl Focusable for SettingsStory { fn focus_handle(&self, _: &gpui::App) -> gpui::FocusHandle { self.focus_handle.clone() } } impl Render for SettingsStory { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { Settings::new("app-settings") .with_size(self.size) .with_group_variant(self.group_variant) .pages(self.setting_pages(window, cx)) } }