diff --git a/crates/story/src/stories/settings_story.rs b/crates/story/src/stories/settings_story.rs index 8b2dc87d29..252605ee75 100644 --- a/crates/story/src/stories/settings_story.rs +++ b/crates/story/src/stories/settings_story.rs @@ -1,10 +1,10 @@ use gpui::{ App, AppContext, Axis, Context, Element, Entity, FocusHandle, Focusable, Global, IntoElement, - ParentElement as _, Render, SharedString, Styled, Window, px, + ParentElement as _, Render, SharedString, Styled, Window, prelude::FluentBuilder, px, }; use gpui_component::{ - ActiveTheme, Icon, IconName, Sizable, Size, Theme, ThemeMode, + ActiveTheme, Disableable, Icon, IconName, Sizable, Size, Theme, ThemeMode, button::Button, group_box::GroupBoxVariant, h_flex, @@ -26,6 +26,7 @@ struct AppSettings { notifications_enabled: bool, auto_update: bool, resettable: bool, + disabled: bool, } impl Default for AppSettings { @@ -39,6 +40,7 @@ impl Default for AppSettings { notifications_enabled: true, auto_update: true, resettable: true, + disabled: false, } } } @@ -126,6 +128,7 @@ impl SettingsStory { let view = cx.entity(); let default_settings = AppSettings::default(); let resettable = AppSettings::global(cx).resettable; + let disabled = AppSettings::global(cx).disabled; vec![ SettingPage::new("General") @@ -150,7 +153,8 @@ impl SettingsStory { ) .default_value(false), ) - .description("Switch between light and dark themes."), + .description("Switch between light and dark themes.") + .disabled(disabled), SettingItem::new( "Auto Switch Theme", SettingField::checkbox( @@ -161,7 +165,8 @@ impl SettingsStory { ) .default_value(default_settings.auto_switch_theme), ) - .description("Automatically switch theme based on system settings."), + .description("Automatically switch theme based on system settings.") + .disabled(disabled), SettingItem::new( "resettable", SettingField::switch( @@ -171,7 +176,8 @@ impl SettingsStory { }, ), ) - .description("Enable/Disable reset button for settings."), + .description("Enable/Disable reset button for settings.") + .disabled(disabled), SettingItem::new( "Group Variant", SettingField::dropdown( @@ -201,7 +207,8 @@ impl SettingsStory { ) .default_value(GroupBoxVariant::Outline.as_str().to_string()), ) - .description("Select the variant for setting groups."), + .description("Select the variant for setting groups.") + .disabled(disabled), SettingItem::new( "Group Size", SettingField::dropdown( @@ -228,7 +235,8 @@ impl SettingsStory { ) .default_value(Size::default().as_str().to_string()), ) - .description("Select the size for the setting group."), + .description("Select the size for the setting group.") + .disabled(disabled), ]), SettingGroup::new() .title("Font") @@ -249,7 +257,8 @@ impl SettingsStory { ) .default_value(default_settings.font_family), ) - .description("Select the font family for the story."), + .description("Select the font family for the story.") + .disabled(disabled), ) .item( SettingItem::new( @@ -269,7 +278,8 @@ impl SettingsStory { ) .description( "Adjust the font size for better readability between 8 and 72.", - ), + ) + .disabled(disabled), ) .item( SettingItem::new( @@ -289,9 +299,21 @@ impl SettingsStory { ) .description( "Adjust the line height for better readability between 8 and 32.", - ), + ) + .disabled(disabled), ), SettingGroup::new().title("Other").items(vec![ + SettingItem::new( + "Disable Settings", + SettingField::switch( + |cx: &App| AppSettings::global(cx).disabled, + |checked: bool, cx: &mut App| { + AppSettings::global_mut(cx).disabled = checked + }, + ) + .default_value(false), + ) + .description("Lock the other settings."), SettingItem::render(|options, _, _| { h_flex() .w_full() @@ -299,12 +321,14 @@ impl SettingsStory { .flex_wrap() .gap_3() .child("This is a custom element item by use SettingItem::element.") + .when(options.disabled, |this| this.opacity(0.5)) .child( Button::new("action") .icon(IconName::Globe) .label("Repository...") .outline() .with_size(options.size) + .disabled(options.disabled) .on_click(|_, _, cx| { cx.open_url( "https://github.com/longbridge/gpui-component", @@ -312,7 +336,8 @@ impl SettingsStory { }), ) .into_any_element() - }), + }) + .disabled(disabled), SettingItem::new( "CLI Path", SettingField::input( @@ -329,7 +354,8 @@ impl SettingsStory { "Path to the CLI executable. \n\ This item uses Vertical layout. The title,\ description, and field are all aligned vertically with width 100%.", - ), + ) + .disabled(disabled), ]), ]), SettingPage::new("Software Update") @@ -346,7 +372,8 @@ impl SettingsStory { ) .default_value(default_settings.notifications_enabled), ) - .description("Receive notifications about updates and news."), + .description("Receive notifications about updates and news.") + .disabled(disabled), SettingItem::new( "Auto Update", SettingField::switch( @@ -357,7 +384,8 @@ impl SettingsStory { ) .default_value(default_settings.auto_update), ) - .description("Automatically download and install updates."), + .description("Automatically download and install updates.") + .disabled(disabled), ])]), SettingPage::new("About") .resettable(resettable) diff --git a/crates/ui/src/setting/fields/bool.rs b/crates/ui/src/setting/fields/bool.rs index 83920c1b72..e2a205f405 100644 --- a/crates/ui/src/setting/fields/bool.rs +++ b/crates/ui/src/setting/fields/bool.rs @@ -7,7 +7,7 @@ use crate::{ AnySettingField, RenderOptions, }, switch::Switch, - Sizable, StyledExt, + Disableable, Sizable, StyledExt, }; use gpui::{div, AnyElement, App, IntoElement, ParentElement as _, StyleRefinement, Window}; @@ -38,6 +38,7 @@ impl SettingFieldRender for BoolField { .child(if self.use_switch { Switch::new("check") .checked(checked) + .disabled(options.disabled) .with_size(options.size) .on_click(move |checked: &bool, _, cx: &mut App| { set_value(*checked, cx); @@ -46,6 +47,7 @@ impl SettingFieldRender for BoolField { } else { Checkbox::new("check") .checked(checked) + .disabled(options.disabled) .with_size(options.size) .on_click(move |checked: &bool, _, cx: &mut App| { set_value(*checked, cx); diff --git a/crates/ui/src/setting/fields/dropdown.rs b/crates/ui/src/setting/fields/dropdown.rs index db7278d004..b5a61d573b 100644 --- a/crates/ui/src/setting/fields/dropdown.rs +++ b/crates/ui/src/setting/fields/dropdown.rs @@ -6,7 +6,7 @@ use gpui::{ }; use crate::{ - AxisExt, Sizable, StyledExt, + AxisExt, Disableable, Sizable, StyledExt, button::Button, menu::{DropdownMenu, PopupMenuItem}, setting::{ @@ -62,6 +62,7 @@ where .label(old_label) .dropdown_caret(true) .outline() + .disabled(options.disabled) .with_size(options.size) .refine_style(style) .dropdown_menu_with_anchor(Anchor::TopRight, move |menu, _, _| { diff --git a/crates/ui/src/setting/fields/number.rs b/crates/ui/src/setting/fields/number.rs index b44712c9df..527b0da910 100644 --- a/crates/ui/src/setting/fields/number.rs +++ b/crates/ui/src/setting/fields/number.rs @@ -6,7 +6,7 @@ use gpui::{ }; use crate::{ - AxisExt, Sizable, StyledExt, + AxisExt, Disableable, Sizable, StyledExt, input::{InputEvent, InputState, NumberInput, NumberInputEvent, StepAction}, setting::{ AnySettingField, RenderOptions, @@ -137,6 +137,7 @@ impl SettingFieldRender for NumberField { .read(cx); NumberInput::new(&state.input) + .disabled(options.disabled) .with_size(options.size) .map(|this| { if options.layout.is_horizontal() { diff --git a/crates/ui/src/setting/fields/string.rs b/crates/ui/src/setting/fields/string.rs index dd96805f4b..b5be4dde9f 100644 --- a/crates/ui/src/setting/fields/string.rs +++ b/crates/ui/src/setting/fields/string.rs @@ -74,6 +74,7 @@ where .read(cx); Input::new(&state.input) + .disabled(options.disabled) .with_size(options.size) .map(|this| { if options.layout.is_horizontal() { diff --git a/crates/ui/src/setting/item.rs b/crates/ui/src/setting/item.rs index 6993a1028d..ee157880f8 100644 --- a/crates/ui/src/setting/item.rs +++ b/crates/ui/src/setting/item.rs @@ -23,10 +23,12 @@ pub enum SettingItem { title: SharedString, description: Option, layout: Axis, + disabled: bool, field: Rc, }, /// A full custom element to render. Element { + disabled: bool, render: Rc AnyElement + 'static>, }, } @@ -41,6 +43,7 @@ impl SettingItem { title: title.into(), description: None, layout: Axis::Horizontal, + disabled: false, field: Rc::new(field), } } @@ -52,12 +55,28 @@ impl SettingItem { R: Fn(&RenderOptions, &mut Window, &mut App) -> E + 'static, { SettingItem::Element { + disabled: false, render: Rc::new(move |options, window, cx| { render(options, window, cx).into_any_element() }), } } + /// Set whether the setting item is disabled, default is false. + /// + /// A disabled item is rendered with reduced opacity. For + /// [`SettingItem::Item`] the underlying field is also rendered in a + /// non-interactive state. For [`SettingItem::Element`] the `disabled` flag + /// is forwarded via [`RenderOptions::disabled`] so the custom renderer can + /// disable its interactive controls. + pub fn disabled(mut self, disabled: bool) -> Self { + match &mut self { + SettingItem::Item { disabled: d, .. } => *d = disabled, + SettingItem::Element { disabled: d, .. } => *d = disabled, + } + self + } + /// Set the description of the setting item. /// /// Only applies to [`SettingItem::Item`]. @@ -170,10 +189,12 @@ impl SettingItem { title, description, layout, + disabled, field, } => div() .w_full() .overflow_hidden() + .when(disabled, |this| this.opacity(0.5)) .map(|this| { if layout.is_horizontal() { this.h_flex().justify_between().items_start() @@ -205,14 +226,27 @@ impl SettingItem { ) .child(div().id("field").child(Self::render_field( field, - RenderOptions { layout, ..*options }, + RenderOptions { + layout, + disabled, + ..*options + }, window, cx, ))) .into_any_element(), - SettingItem::Element { render } => { - (render)(&options, window, cx).into_any_element() - } + SettingItem::Element { disabled, render } => div() + .w_full() + .when(disabled, |this| this.opacity(0.5)) + .child((render)( + &RenderOptions { + disabled, + ..*options + }, + window, + cx, + )) + .into_any_element(), }) } } diff --git a/crates/ui/src/setting/settings.rs b/crates/ui/src/setting/settings.rs index 4dd43f263f..6ec2496bfc 100644 --- a/crates/ui/src/setting/settings.rs +++ b/crates/ui/src/setting/settings.rs @@ -247,6 +247,7 @@ pub struct RenderOptions { pub size: Size, pub group_variant: GroupBoxVariant, pub layout: Axis, + pub disabled: bool, } #[derive(Clone, Copy, Default)] @@ -280,6 +281,7 @@ impl RenderOnce for Settings { size: self.size, group_variant: self.group_variant, layout: Axis::Horizontal, + disabled: false, }; h_resizable(self.id.clone()) diff --git a/docs/docs/components/settings.md b/docs/docs/components/settings.md index f75cd6935d..b2a2f11f7a 100644 --- a/docs/docs/components/settings.md +++ b/docs/docs/components/settings.md @@ -226,6 +226,40 @@ SettingItem::new( .description(markdown("Rust doc for the `gpui-component` crate.")) ``` +### Disabled + +Use `disabled(true)` to render a setting item in a non-interactive state. The +whole row is dimmed and the built-in field (Switch, Checkbox, Input, Dropdown, +NumberInput) is automatically disabled. + +```rust +SettingItem::new( + "Dark Mode", + SettingField::switch(...) +) +.description("Switch between light and dark themes.") +.disabled(true) +``` + +For [SettingItem::render] custom items, the row is still dimmed automatically, +but the renderer is responsible for honoring the disabled state on any +interactive controls inside it via `options.disabled`: + +```rust +SettingItem::render(|options, _, _| { + h_flex() + .child("Custom content") + .child( + Button::new("action") + .label("Action") + .with_size(options.size) + .disabled(options.disabled) + ) + .into_any_element() +}) +.disabled(true) +``` + ## Setting Fields The [SettingField] enum provides different field types for various input needs. diff --git a/docs/zh-CN/docs/components/settings.md b/docs/zh-CN/docs/components/settings.md index 21ad314b2e..0fe30eab25 100644 --- a/docs/zh-CN/docs/components/settings.md +++ b/docs/zh-CN/docs/components/settings.md @@ -221,6 +221,38 @@ SettingItem::new( .description(markdown("Rust doc for the `gpui-component` crate.")) ``` +### 禁用状态 + +通过 `disabled(true)` 可以将设置项置为不可交互状态:整行会变暗,内置字段 +(Switch、Checkbox、Input、Dropdown、NumberInput)也会被自动禁用。 + +```rust +SettingItem::new( + "Dark Mode", + SettingField::switch(...) +) +.description("Switch between light and dark themes.") +.disabled(true) +``` + +对于 [SettingItem::render] 自定义项,整行同样会自动变暗,但其中可交互的控件 +需要渲染闭包通过 `options.disabled` 自行处理: + +```rust +SettingItem::render(|options, _, _| { + h_flex() + .child("Custom content") + .child( + Button::new("action") + .label("Action") + .with_size(options.size) + .disabled(options.disabled) + ) + .into_any_element() +}) +.disabled(true) +``` + ## Setting Fields [SettingField] 枚举提供了多种常见字段类型。