setting: Use precise subscripts for use_keyed_state (#1735)

This commit is contained in:
Ylin 2025-12-03 21:51:57 +08:00 committed by GitHub
parent a50b1e9341
commit 514aea00be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 121 additions and 65 deletions

View file

@ -22,6 +22,7 @@ struct AppSettings {
cli_path: SharedString,
font_family: SharedString,
font_size: f64,
line_height: f64,
notifications_enabled: bool,
auto_update: bool,
resettable: bool,
@ -34,6 +35,7 @@ impl Default for AppSettings {
cli_path: "/usr/local/bin/bash".into(),
font_family: "Arial".into(),
font_size: 14.0,
line_height: 12.0,
notifications_enabled: true,
auto_update: true,
resettable: true,
@ -265,6 +267,24 @@ impl SettingsStory {
.default_value(default_settings.font_size),
)
.description("Adjust the font size for better readability."),
)
.item(
SettingItem::new(
"Line Height",
SettingField::number_input(
NumberFieldOptions {
min: 8.0,
max: 32.0,
..Default::default()
},
|cx: &App| AppSettings::global(cx).line_height,
|val: f64, cx: &mut App| {
AppSettings::global_mut(cx).line_height = val;
},
)
.default_value(default_settings.line_height),
)
.description("Adjust the line height for better readability."),
),
SettingGroup::new().title("Other").items(vec![
SettingItem::render(|options, _, _| {

View file

@ -1,17 +1,17 @@
use std::rc::Rc;
use gpui::{
prelude::FluentBuilder as _, AnyElement, App, AppContext as _, Entity, IntoElement,
SharedString, StyleRefinement, Styled, Window,
AnyElement, App, AppContext as _, Entity, IntoElement, SharedString, StyleRefinement, Styled,
Window, prelude::FluentBuilder as _,
};
use crate::{
AxisExt, Sizable, StyledExt,
input::{InputState, NumberInput, NumberInputEvent},
setting::{
fields::{get_value, set_value, SettingFieldRender},
AnySettingField, RenderOptions,
fields::{SettingFieldRender, get_value, set_value},
},
AxisExt, Sizable, StyledExt,
};
#[derive(Clone, Debug)]
@ -65,35 +65,43 @@ impl SettingFieldRender for NumberField {
let num_options = self.options.clone();
let state = window
.use_keyed_state("number-state", cx, |window, cx| {
let input =
cx.new(|cx| InputState::new(window, cx).default_value(value.to_string()));
let _subscription = cx.subscribe_in(&input, window, {
move |_, input, event: &NumberInputEvent, window, cx| match event {
NumberInputEvent::Step(action) => input.update(cx, |input, cx| {
let value = input.value();
if let Ok(value) = value.parse::<f64>() {
let new_value = if *action == crate::input::StepAction::Increment {
(value + num_options.step).min(num_options.max)
} else {
(value - num_options.step).max(num_options.min)
};
set_value(new_value, cx);
input.set_value(
SharedString::from(new_value.to_string()),
window,
cx,
);
}
}),
}
});
.use_keyed_state(
SharedString::from(format!(
"number-state-{}-{}-{}",
options.page_ix, options.group_ix, options.item_ix
)),
cx,
|window, cx| {
let input =
cx.new(|cx| InputState::new(window, cx).default_value(value.to_string()));
let _subscription = cx.subscribe_in(&input, window, {
move |_, input, event: &NumberInputEvent, window, cx| match event {
NumberInputEvent::Step(action) => input.update(cx, |input, cx| {
let value = input.value();
if let Ok(value) = value.parse::<f64>() {
let new_value =
if *action == crate::input::StepAction::Increment {
(value + num_options.step).min(num_options.max)
} else {
(value - num_options.step).max(num_options.min)
};
set_value(new_value, cx);
input.set_value(
SharedString::from(new_value.to_string()),
window,
cx,
);
}
}),
}
});
State {
input,
_subscription,
}
})
State {
input,
_subscription,
}
},
)
.read(cx);
NumberInput::new(&state.input)

View file

@ -1,17 +1,17 @@
use std::rc::Rc;
use gpui::{
prelude::FluentBuilder as _, AnyElement, App, AppContext as _, Entity, IntoElement,
SharedString, StyleRefinement, Styled, Window,
AnyElement, App, AppContext as _, Entity, IntoElement, SharedString, StyleRefinement, Styled,
Window, prelude::FluentBuilder as _,
};
use crate::{
AxisExt as _, Sizable, StyledExt,
input::{Input, InputEvent, InputState},
setting::{
fields::{get_value, set_value, SettingFieldRender},
AnySettingField, RenderOptions,
fields::{SettingFieldRender, get_value, set_value},
},
AxisExt as _, Sizable, StyledExt,
};
pub(crate) struct StringField<T> {
@ -47,23 +47,30 @@ where
let set_value = set_value::<T>(&field, cx);
let state = window
.use_keyed_state("string-state", cx, |window, cx| {
let input = cx.new(|cx| InputState::new(window, cx).default_value(value));
let _subscription = cx.subscribe(&input, {
move |_, input, event: &InputEvent, cx| match event {
InputEvent::Change => {
let value = input.read(cx).value();
set_value(value.into(), cx);
.use_keyed_state(
SharedString::from(format!(
"string-state-{}-{}-{}",
options.page_ix, options.group_ix, options.item_ix
)),
cx,
|window, cx| {
let input = cx.new(|cx| InputState::new(window, cx).default_value(value));
let _subscription = cx.subscribe(&input, {
move |_, input, event: &InputEvent, cx| match event {
InputEvent::Change => {
let value = input.read(cx).value();
set_value(value.into(), cx);
}
_ => {}
}
_ => {}
}
});
});
State {
input,
_subscription,
}
})
State {
input,
_subscription,
}
},
)
.read(cx);
Input::new(&state.input)

View file

@ -1,13 +1,14 @@
use gpui::{
prelude::FluentBuilder as _, App, IntoElement, ParentElement as _, SharedString,
StyleRefinement, Styled, Window,
App, IntoElement, ParentElement as _, SharedString, StyleRefinement, Styled, Window,
prelude::FluentBuilder as _,
};
use crate::{
ActiveTheme, StyledExt,
group_box::{GroupBox, GroupBoxVariants},
label::Label,
setting::{RenderOptions, SettingItem},
v_flex, ActiveTheme, StyledExt,
v_flex,
};
/// A setting group that can contain multiple setting items.
@ -75,14 +76,13 @@ impl SettingGroup {
pub(crate) fn render(
self,
group_ix: usize,
query: &str,
options: &RenderOptions,
window: &mut Window,
cx: &mut App,
) -> impl IntoElement {
GroupBox::new()
.id(SharedString::from(format!("group-{}", group_ix)))
.id(SharedString::from(format!("group-{}", options.group_ix)))
.with_variant(options.group_variant)
.when_some(self.title.clone(), |this, title| {
this.title(v_flex().gap_1().child(title).when_some(
@ -99,7 +99,14 @@ impl SettingGroup {
.gap_4()
.children(self.items.iter().enumerate().filter_map(|(item_ix, item)| {
if item.is_match(&query) {
Some(item.clone().render_item(item_ix, options, window, cx))
Some(item.clone().render_item(
&RenderOptions {
item_ix,
..*options
},
window,
cx,
))
} else {
None
}

View file

@ -150,13 +150,12 @@ impl SettingItem {
pub(super) fn render_item(
self,
ix: usize,
options: &RenderOptions,
window: &mut Window,
cx: &mut App,
) -> Stateful<Div> {
div()
.id(SharedString::from(format!("item-{}", ix)))
.id(SharedString::from(format!("item-{}", options.item_ix)))
.w_full()
.child(match self {
SettingItem::Item {

View file

@ -167,11 +167,20 @@ impl SettingPage {
list(list_state.clone(), {
let query = query.clone();
let options = *options;
move |ix, window, cx| {
let group = groups[ix].clone();
move |group_ix, window, cx| {
let group = groups[group_ix].clone();
group
.py_4()
.render(ix, &query, &options, window, cx)
.render(
&query,
&RenderOptions {
page_ix: ix,
group_ix,
..options
},
window,
cx,
)
.into_any_element()
}
})

View file

@ -1,14 +1,14 @@
use crate::{
IconName, Sizable, Size, StyledExt,
group_box::GroupBoxVariant,
input::{Input, InputState},
resizable::{h_resizable, resizable_panel},
setting::{SettingGroup, SettingPage},
sidebar::{Sidebar, SidebarMenu, SidebarMenuItem},
IconName, Sizable, Size, StyledExt,
};
use gpui::{
div, prelude::FluentBuilder as _, px, relative, App, AppContext as _, Axis, ElementId, Entity,
IntoElement, ParentElement as _, Pixels, RenderOnce, StyleRefinement, Styled, Window,
App, AppContext as _, Axis, ElementId, Entity, IntoElement, ParentElement as _, Pixels,
RenderOnce, StyleRefinement, Styled, Window, div, prelude::FluentBuilder as _, px, relative,
};
use rust_i18n::t;
@ -221,6 +221,9 @@ pub(super) struct SettingsState {
/// Options for rendering setting item.
#[derive(Clone, Copy)]
pub struct RenderOptions {
pub page_ix: usize,
pub group_ix: usize,
pub item_ix: usize,
pub size: Size,
pub group_variant: GroupBoxVariant,
pub layout: Axis,
@ -251,6 +254,9 @@ impl RenderOnce for Settings {
let query = state.read(cx).search_input.read(cx).value();
let filtered_pages = self.filtered_pages(&query);
let options = RenderOptions {
page_ix: 0,
group_ix: 0,
item_ix: 0,
size: self.size,
group_variant: self.group_variant,
layout: Axis::Horizontal,