settings ui: Add text field support to ui layer (#37868)
This is an initial implementation that isn't used for any settings yet, but will be used once `Vec<String>` is implemented. I also updated the window.with_state api to grant access to a `Context<S>` app reference instead of just an App. ## Example <img width="603" height="83" alt="Screenshot 2025-09-09 at 2 15 56 PM" src="https://github.com/user-attachments/assets/7b3fc350-a157-431f-a4bc-80a1806a3147" /> Release Notes: - N/A
This commit is contained in:
Generated
+1
@@ -14871,6 +14871,7 @@ dependencies = [
|
||||
"editor",
|
||||
"feature_flags",
|
||||
"gpui",
|
||||
"menu",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
|
||||
@@ -341,7 +341,7 @@
|
||||
"enter": "agent::Chat",
|
||||
"shift-ctrl-r": "agent::OpenAgentDiff",
|
||||
"ctrl-shift-y": "agent::KeepAll",
|
||||
"ctrl-shift-n": "agent::RejectAll",
|
||||
"ctrl-shift-n": "agent::RejectAll"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ pub enum DebugPanelDockPosition {
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Clone, Copy, SettingsUi, SettingsKey)]
|
||||
#[derive(Serialize, Deserialize, JsonSchema, Clone, SettingsUi, SettingsKey)]
|
||||
#[serde(default)]
|
||||
// todo(settings_ui) @ben: I'm pretty sure not having the fields be optional here is a bug,
|
||||
// it means the defaults will override previously set values if a single key is missing
|
||||
|
||||
@@ -2578,7 +2578,7 @@ impl Window {
|
||||
&mut self,
|
||||
key: impl Into<ElementId>,
|
||||
cx: &mut App,
|
||||
init: impl FnOnce(&mut Self, &mut App) -> S,
|
||||
init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
|
||||
) -> Entity<S> {
|
||||
let current_view = self.current_view();
|
||||
self.with_global_id(key.into(), |global_id, window| {
|
||||
@@ -2611,7 +2611,7 @@ impl Window {
|
||||
pub fn use_state<S: 'static>(
|
||||
&mut self,
|
||||
cx: &mut App,
|
||||
init: impl FnOnce(&mut Self, &mut App) -> S,
|
||||
init: impl FnOnce(&mut Self, &mut Context<S>) -> S,
|
||||
) -> Entity<S> {
|
||||
self.use_keyed_state(
|
||||
ElementId::CodeLocation(*core::panic::Location::caller()),
|
||||
|
||||
@@ -43,6 +43,7 @@ pub struct SettingsUiEntry {
|
||||
#[derive(Clone)]
|
||||
pub enum SettingsUiItemSingle {
|
||||
SwitchField,
|
||||
TextField,
|
||||
/// A numeric stepper for a specific type of number
|
||||
NumericStepper(NumType),
|
||||
ToggleGroup {
|
||||
@@ -150,6 +151,18 @@ impl SettingsUi for Option<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsUi for String {
|
||||
fn settings_ui_item() -> SettingsUiItem {
|
||||
SettingsUiItem::Single(SettingsUiItemSingle::TextField)
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsUi for SettingsUiItem {
|
||||
fn settings_ui_item() -> SettingsUiItem {
|
||||
SettingsUiItem::Single(SettingsUiItemSingle::TextField)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum NumType {
|
||||
|
||||
@@ -21,8 +21,9 @@ command_palette_hooks.workspace = true
|
||||
editor.workspace = true
|
||||
feature_flags.workspace = true
|
||||
gpui.workspace = true
|
||||
serde_json.workspace = true
|
||||
menu.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
theme.workspace = true
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::ops::{Not, Range};
|
||||
|
||||
use anyhow::Context as _;
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
use editor::EditorSettingsControls;
|
||||
use editor::{Editor, EditorSettingsControls};
|
||||
use feature_flags::{FeatureFlag, FeatureFlagViewExt};
|
||||
use gpui::{App, Entity, EventEmitter, FocusHandle, Focusable, ReadGlobal, ScrollHandle, actions};
|
||||
use settings::{
|
||||
@@ -612,6 +612,7 @@ fn render_item_single(
|
||||
SettingsUiItemSingle::DropDown { .. } => {
|
||||
unimplemented!("This")
|
||||
}
|
||||
SettingsUiItemSingle::TextField => render_text_field(settings_value, window, cx),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -798,6 +799,54 @@ fn render_switch_field(
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_text_field(
|
||||
value: SettingsValue<serde_json::Value>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyElement {
|
||||
let value = downcast_any_item::<String>(value);
|
||||
let path = value.path.clone();
|
||||
let editor = window.use_state(cx, {
|
||||
let path = path.clone();
|
||||
move |window, cx| {
|
||||
let mut editor = Editor::single_line(window, cx);
|
||||
|
||||
cx.observe_global_in::<SettingsStore>(window, move |editor, window, cx| {
|
||||
let user_settings = SettingsStore::global(cx).raw_user_settings();
|
||||
if let Some(value) = read_settings_value_from_path(&user_settings, &path).cloned()
|
||||
&& let Some(value) = value.as_str()
|
||||
{
|
||||
editor.set_text(value, window, cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
editor.set_text(value.read().clone(), window, cx);
|
||||
editor
|
||||
}
|
||||
});
|
||||
|
||||
let weak_editor = editor.downgrade();
|
||||
let theme_colors = cx.theme().colors();
|
||||
|
||||
div()
|
||||
.child(editor)
|
||||
.bg(theme_colors.editor_background)
|
||||
.border_1()
|
||||
.rounded_lg()
|
||||
.border_color(theme_colors.border)
|
||||
.on_action::<menu::Confirm>({
|
||||
move |_, _, cx| {
|
||||
let new_value = weak_editor.read_with(cx, |editor, cx| editor.text(cx)).ok();
|
||||
|
||||
if let Some(new_value) = new_value {
|
||||
SettingsValue::write_value(&path, serde_json::Value::String(new_value), cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_toggle_button_group(
|
||||
value: SettingsValue<serde_json::Value>,
|
||||
variants: &'static [&'static str],
|
||||
|
||||
Reference in New Issue
Block a user