mod components;mod page_data;mod pages; use anyhow::Result;use editor::{Editor, EditorEvent};use fuzzy::StringMatchCandidate;use gpui::{ Action, App, ClipboardItem, DEFAULT_ADDITIONAL_WINDOW_SIZE, Div, Entity, FocusHandle, Focusable, Global, KeyContext, ListState, ReadGlobal as _, ScrollHandle, Stateful, Subscription, Task, TitlebarOptions, UniformListScrollHandle, Window, WindowBounds, WindowHandle, WindowOptions, actions, div, list, point, prelude::*, px, uniform_list,};use project::{Project, WorktreeId};use release_channel::ReleaseChannel;use schemars::JsonSchema;use serde::Deserialize;use settings::{Settings, SettingsContent, SettingsStore, initial_project_settings_content};use std::{ any::{Any, TypeId, type_name}, cell::RefCell, collections::{HashMap, HashSet}, num::{NonZero, NonZeroU32}, ops::Range, rc::Rc, sync::{Arc, LazyLock, RwLock}, time::Duration,};use theme::ThemeSettings;use title_bar::platform_title_bar::PlatformTitleBar;use ui::{ Banner, ContextMenu, Divider, DropdownMenu, DropdownStyle, IconButtonShape, KeyBinding, KeybindingHint, PopoverMenu, Switch, Tooltip, TreeViewItem, WithScrollbar, prelude::*,};use ui_input::{NumberField, NumberFieldMode, NumberFieldType};use util::{ResultExt as _, paths::PathStyle, rel_path::RelPath};use workspace::{AppState, OpenOptions, OpenVisible, Workspace, client_side_decorations};use zed_actions::{OpenProjectSettings, OpenSettings, OpenSettingsAt}; use crate::components::{ EnumVariantDropdown, SettingsInputField, SettingsSectionHeader, font_picker, icon_theme_picker, theme_picker,}; const NAVBAR_CONTAINER_TAB_INDEX: isize = 0;const NAVBAR_GROUP_TAB_INDEX: isize = 1; const HEADER_CONTAINER_TAB_INDEX: isize = 2;const HEADER_GROUP_TAB_INDEX: isize = 3; const CONTENT_CONTAINER_TAB_INDEX: isize = 4;const CONTENT_GROUP_TAB_INDEX: isize = 5; actions!( settings_editor, [ /// Minimizes the settings UI window. Minimize, /// Toggles focus between the navbar and the main content. ToggleFocusNav, /// Expands the navigation entry. ExpandNavEntry, /// Collapses the navigation entry. CollapseNavEntry, /// Focuses the next file in the file list. FocusNextFile, /// Focuses the previous file in the file list. FocusPreviousFile, /// Opens an editor for the current file OpenCurrentFile, /// Focuses the previous root navigation entry. FocusPreviousRootNavEntry, /// Focuses the next root navigation entry. FocusNextRootNavEntry, /// Focuses the first navigation entry. FocusFirstNavEntry, /// Focuses the last navigation entry. FocusLastNavEntry, /// Focuses and opens the next navigation entry without moving focus to content. FocusNextNavEntry, /// Focuses and opens the previous navigation entry without moving focus to content. FocusPreviousNavEntry ]); #[derive(Action, PartialEq, Eq, Clone, Copy, Debug, JsonSchema, Deserialize)]#[action(namespace = settings_editor)]struct FocusFile(pub u32); struct SettingField<T: 'static> { pick: fn(&SettingsContent) -> Option<&T>, write: fn(&mut SettingsContent, Option<T>), /// A json-path-like string that gives a unique-ish string that identifies /// where in the JSON the setting is defined. /// /// The syntax is `jq`-like, but modified slightly to be URL-safe (and /// without the leading dot), e.g. `foo.bar`. /// /// They are URL-safe (this is important since links are the main use-case /// for these paths). /// /// There are a couple of special cases: /// - discrimminants are represented with a trailing `$`, for example /// `terminal.working_directory$`. This is to distinguish the discrimminant /// setting (i.e. the setting that changes whether the value is a string or /// an object) from the setting in the case that it is a string. /// - language-specific settings begin `languages.$(language)`. Links /// targeting these settings should take the form `languages/Rust/...`, for /// example, but are not currently supported. json_path: Option<&'static str>,} impl<T: 'static> Clone for SettingField<T> { fn clone(&self) -> Self { *self }} // manual impl because derive puts a Copy bound on T, which is inaccurate in our caseimpl<T: 'static> Copy for SettingField<T> {} /// Helper for unimplemented settings, used in combination with `SettingField::unimplemented`/// to keep the setting around in the UI with valid pick and write implementations, but don't actually try to render it./// TODO(settings_ui): In non-dev builds (`#[cfg(not(debug_assertions))]`) make this render as edit-in-json#[derive(Clone, Copy)]struct UnimplementedSettingField; impl PartialEq for UnimplementedSettingField { fn eq(&self, _other: &Self) -> bool { true }} impl<T: 'static> SettingField<T> { /// Helper for settings with types that are not yet implemented. #[allow(unused)] fn unimplemented(self) -> SettingField<UnimplementedSettingField> { SettingField { pick: |_| Some(&UnimplementedSettingField), write: |_, _| unreachable!(), json_path: self.json_path, } }} trait AnySettingField { fn as_any(&self) -> &dyn Any; fn type_name(&self) -> &'static str; fn type_id(&self) -> TypeId; // Returns the file this value was set in and true, or File::Default and false to indicate it was not found in any file (missing default) fn file_set_in(&self, file: SettingsUiFile, cx: &App) -> (settings::SettingsFile, bool); fn reset_to_default_fn( &self, current_file: &SettingsUiFile, file_set_in: &settings::SettingsFile, cx: &App, ) -> Option<Box<dyn Fn(&mut App)>>; fn json_path(&self) -> Option<&'static str>;} impl<T: PartialEq + Clone + Send + Sync + 'static> AnySettingField for SettingField<T> { fn as_any(&self) -> &dyn Any { self } fn type_name(&self) -> &'static str { type_name::<T>() } fn type_id(&self) -> TypeId { TypeId::of::<T>() } fn file_set_in(&self, file: SettingsUiFile, cx: &App) -> (settings::SettingsFile, bool) { let (file, value) = cx .global::<SettingsStore>() .get_value_from_file(file.to_settings(), self.pick); return (file, value.is_some()); } fn reset_to_default_fn( &self, current_file: &SettingsUiFile, file_set_in: &settings::SettingsFile, cx: &App, ) -> Option<Box<dyn Fn(&mut App)>> { if file_set_in == &settings::SettingsFile::Default { return None; } if file_set_in != ¤t_file.to_settings() { return None; } let this = *self; let store = SettingsStore::global(cx); let default_value = (this.pick)(store.raw_default_settings()); let is_default = store .get_content_for_file(file_set_in.clone()) .map_or(None, this.pick) == default_value; if is_default { return None; } let current_file = current_file.clone(); return Some(Box::new(move |cx| { let store = SettingsStore::global(cx); let default_value = (this.pick)(store.raw_default_settings()); let is_set_somewhere_other_than_default = store .get_value_up_to_file(current_file.to_settings(), this.pick) .0 != settings::SettingsFile::Default; let value_to_set = if is_set_somewhere_other_than_default { default_value.cloned() } else { None }; update_settings_file(current_file.clone(), None, cx, move |settings, _| { (this.write)(settings, value_to_set); }) // todo(settings_ui): Don't log err .log_err(); })); } fn json_path(&self) -> Option<&'static str> { self.json_path }} #[derive(Default, Clone)]struct SettingFieldRenderer { renderers: Rc< RefCell< HashMap< TypeId, Box< dyn Fn( &SettingsWindow, &SettingItem, SettingsUiFile, Option<&SettingsFieldMetadata>, bool, &mut Window, &mut Context<SettingsWindow>, ) -> Stateful<Div>, >, >, >, >,} impl Global for SettingFieldRenderer {} impl SettingFieldRenderer { fn add_basic_renderer<T: 'static>( &mut self, render_control: impl Fn( SettingField<T>, SettingsUiFile, Option<&SettingsFieldMetadata>, &mut Window, &mut App, ) -> AnyElement + 'static, ) -> &mut Self { self.add_renderer( move |settings_window: &SettingsWindow, item: &SettingItem, field: SettingField<T>, settings_file: SettingsUiFile, metadata: Option<&SettingsFieldMetadata>, sub_field: bool, window: &mut Window, cx: &mut Context<SettingsWindow>| { render_settings_item( settings_window, item, settings_file.clone(), render_control(field, settings_file, metadata, window, cx), sub_field, cx, ) }, ) } fn add_renderer<T: 'static>( &mut self, renderer: impl Fn( &SettingsWindow, &SettingItem, SettingField<T>, SettingsUiFile, Option<&SettingsFieldMetadata>, bool, &mut Window, &mut Context<SettingsWindow>, ) -> Stateful<Div> + 'static, ) -> &mut Self { let key = TypeId::of::<T>(); let renderer = Box::new( move |settings_window: &SettingsWindow, item: &SettingItem, settings_file: SettingsUiFile, metadata: Option<&SettingsFieldMetadata>, sub_field: bool, window: &mut Window, cx: &mut Context<SettingsWindow>| { let field = *item .field .as_ref() .as_any() .downcast_ref::<SettingField<T>>() .unwrap(); renderer( settings_window, item, field, settings_file, metadata, sub_field, window, cx, ) }, ); self.renderers.borrow_mut().insert(key, renderer); self }} struct NonFocusableHandle { handle: FocusHandle, _subscription: Subscription,} impl NonFocusableHandle { fn new(tab_index: isize, tab_stop: bool, window: &mut Window, cx: &mut App) -> Entity<Self> { let handle = cx.focus_handle().tab_index(tab_index).tab_stop(tab_stop); Self::from_handle(handle, window, cx) } fn from_handle(handle: FocusHandle, window: &mut Window, cx: &mut App) -> Entity<Self> { cx.new(|cx| { let _subscription = cx.on_focus(&handle, window, { move |_, window, cx| { window.focus_next(cx); } }); Self { handle, _subscription, } }) }} impl Focusable for NonFocusableHandle { fn focus_handle(&self, _: &App) -> FocusHandle { self.handle.clone() }} #[derive(Default)]struct SettingsFieldMetadata { placeholder: Option<&'static str>, should_do_titlecase: Option<bool>,} pub fn init(cx: &mut App) { init_renderers(cx); cx.observe_new(|workspace: &mut workspace::Workspace, _, _| { workspace .register_action( |workspace, OpenSettingsAt { path }: &OpenSettingsAt, window, cx| { let window_handle = window .window_handle() .downcast::<Workspace>() .expect("Workspaces are root Windows"); open_settings_editor(workspace, Some(&path), false, window_handle, cx); }, ) .register_action(|workspace, _: &OpenSettings, window, cx| { let window_handle = window .window_handle() .downcast::<Workspace>() .expect("Workspaces are root Windows"); open_settings_editor(workspace, None, false, window_handle, cx); }) .register_action(|workspace, _: &OpenProjectSettings, window, cx| { let window_handle = window .window_handle() .downcast::<Workspace>() .expect("Workspaces are root Windows"); open_settings_editor(workspace, None, true, window_handle, cx); }); }) .detach();} fn init_renderers(cx: &mut App) { cx.default_global::<SettingFieldRenderer>() .add_renderer::<UnimplementedSettingField>( |settings_window, item, _, settings_file, _, sub_field, _, cx| { render_settings_item( settings_window, item, settings_file, Button::new("open-in-settings-file", "Edit in settings.json") .style(ButtonStyle::Outlined) .size(ButtonSize::Medium) .tab_index(0_isize) .tooltip(Tooltip::for_action_title_in( "Edit in settings.json", &OpenCurrentFile, &settings_window.focus_handle, )) .on_click(cx.listener(|this, _, window, cx| { this.open_current_settings_file(window, cx); })) .into_any_element(), sub_field, cx, ) }, ) .add_basic_renderer::<bool>(render_toggle_button) .add_basic_renderer::<String>(render_text_field) .add_basic_renderer::<SharedString>(render_text_field) .add_basic_renderer::<settings::SaturatingBool>(render_toggle_button) .add_basic_renderer::<settings::CursorShape>(render_dropdown) .add_basic_renderer::<settings::RestoreOnStartupBehavior>(render_dropdown) .add_basic_renderer::<settings::BottomDockLayout>(render_dropdown) .add_basic_renderer::<settings::OnLastWindowClosed>(render_dropdown) .add_basic_renderer::<settings::CloseWindowWhenNoItems>(render_dropdown) .add_basic_renderer::<settings::TextRenderingMode>(render_dropdown) .add_basic_renderer::<settings::FontFamilyName>(render_font_picker) .add_basic_renderer::<settings::BaseKeymapContent>(render_dropdown) .add_basic_renderer::<settings::MultiCursorModifier>(render_dropdown) .add_basic_renderer::<settings::HideMouseMode>(render_dropdown) .add_basic_renderer::<settings::CurrentLineHighlight>(render_dropdown) .add_basic_renderer::<settings::ShowWhitespaceSetting>(render_dropdown) .add_basic_renderer::<settings::SoftWrap>(render_dropdown) .add_basic_renderer::<settings::ScrollBeyondLastLine>(render_dropdown) .add_basic_renderer::<settings::SnippetSortOrder>(render_dropdown) .add_basic_renderer::<settings::ClosePosition>(render_dropdown) .add_basic_renderer::<settings::DockSide>(render_dropdown) .add_basic_renderer::<settings::TerminalDockPosition>(render_dropdown) .add_basic_renderer::<settings::DockPosition>(render_dropdown) .add_basic_renderer::<settings::GitGutterSetting>(render_dropdown) .add_basic_renderer::<settings::GitHunkStyleSetting>(render_dropdown) .add_basic_renderer::<settings::GitPathStyle>(render_dropdown) .add_basic_renderer::<settings::DiagnosticSeverityContent>(render_dropdown) .add_basic_renderer::<settings::SeedQuerySetting>(render_dropdown) .add_basic_renderer::<settings::DoubleClickInMultibuffer>(render_dropdown) .add_basic_renderer::<settings::GoToDefinitionFallback>(render_dropdown) .add_basic_renderer::<settings::ActivateOnClose>(render_dropdown) .add_basic_renderer::<settings::ShowDiagnostics>(render_dropdown) .add_basic_renderer::<settings::ShowCloseButton>(render_dropdown) .add_basic_renderer::<settings::ProjectPanelEntrySpacing>(render_dropdown) .add_basic_renderer::<settings::ProjectPanelSortMode>(render_dropdown) .add_basic_renderer::<settings::RewrapBehavior>(render_dropdown) .add_basic_renderer::<settings::FormatOnSave>(render_dropdown) .add_basic_renderer::<settings::IndentGuideColoring>(render_dropdown) .add_basic_renderer::<settings::IndentGuideBackgroundColoring>(render_dropdown) .add_basic_renderer::<settings::FileFinderWidthContent>(render_dropdown) .add_basic_renderer::<settings::ShowDiagnostics>(render_dropdown) .add_basic_renderer::<settings::WordsCompletionMode>(render_dropdown) .add_basic_renderer::<settings::LspInsertMode>(render_dropdown) .add_basic_renderer::<settings::CompletionDetailAlignment>(render_dropdown) .add_basic_renderer::<settings::AlternateScroll>(render_dropdown) .add_basic_renderer::<settings::TerminalBlink>(render_dropdown) .add_basic_renderer::<settings::CursorShapeContent>(render_dropdown) .add_basic_renderer::<f32>(render_number_field) .add_basic_renderer::<u32>(render_number_field) .add_basic_renderer::<u64>(render_number_field) .add_basic_renderer::<usize>(render_number_field) .add_basic_renderer::<NonZero<usize>>(render_number_field) .add_basic_renderer::<NonZeroU32>(render_number_field) .add_basic_renderer::<settings::CodeFade>(render_number_field) .add_basic_renderer::<settings::DelayMs>(render_number_field) .add_basic_renderer::<gpui::FontWeight>(render_number_field) .add_basic_renderer::<settings::CenteredPaddingSettings>(render_number_field) .add_basic_renderer::<settings::InactiveOpacity>(render_number_field) .add_basic_renderer::<settings::MinimumContrast>(render_number_field) .add_basic_renderer::<settings::ShowScrollbar>(render_dropdown) .add_basic_renderer::<settings::ScrollbarDiagnostics>(render_dropdown) .add_basic_renderer::<settings::ShowMinimap>(render_dropdown) .add_basic_renderer::<settings::DisplayIn>(render_dropdown) .add_basic_renderer::<settings::MinimapThumb>(render_dropdown) .add_basic_renderer::<settings::MinimapThumbBorder>(render_dropdown) .add_basic_renderer::<settings::SteppingGranularity>(render_dropdown) .add_basic_renderer::<settings::NotifyWhenAgentWaiting>(render_dropdown) .add_basic_renderer::<settings::NotifyWhenAgentWaiting>(render_dropdown) .add_basic_renderer::<settings::ImageFileSizeUnit>(render_dropdown) .add_basic_renderer::<settings::StatusStyle>(render_dropdown)Lines 1–500 of 4243. Load more
mod components; mod page_data; mod pages; use anyhow::Result; use editor::{Editor, EditorEvent}; use fuzzy::StringMatchCandidate; use gpui::{ Action, App, ClipboardItem, DEFAULT_ADDITIONAL_WINDOW_SIZE, Div, Entity, FocusHandle, Focusable, Global, KeyContext, ListState, ReadGlobal as _, ScrollHandle, Stateful, Subscription, Task, TitlebarOptions, UniformListScrollHandle, Window, WindowBounds, WindowHandle, WindowOptions, actions, div, list, point, prelude::*, px, uniform_list, }; use project::{Project, WorktreeId}; use release_channel::ReleaseChannel; use schemars::JsonSchema; use serde::Deserialize; use settings::{Settings, SettingsContent, SettingsStore, initial_project_settings_content}; use std::{ any::{Any, TypeId, type_name}, cell::RefCell, collections::{HashMap, HashSet}, num::{NonZero, NonZeroU32}, ops::Range, rc::Rc, sync::{Arc, LazyLock, RwLock}, time::Duration, }; use theme::ThemeSettings; use title_bar::platform_title_bar::PlatformTitleBar; use ui::{ Banner, ContextMenu, Divider, DropdownMenu, DropdownStyle, IconButtonShape, KeyBinding, KeybindingHint, PopoverMenu, Switch, Tooltip, TreeViewItem, WithScrollbar, prelude::*, }; use ui_input::{NumberField, NumberFieldMode, NumberFieldType}; use util::{ResultExt as _, paths::PathStyle, rel_path::RelPath}; use workspace::{AppState, OpenOptions, OpenVisible, Workspace, client_side_decorations}; use zed_actions::{OpenProjectSettings, OpenSettings, OpenSettingsAt}; use crate::components::{ EnumVariantDropdown, SettingsInputField, SettingsSectionHeader, font_picker, icon_theme_picker, theme_picker, }; const NAVBAR_CONTAINER_TAB_INDEX: isize = 0; const NAVBAR_GROUP_TAB_INDEX: isize = 1; const HEADER_CONTAINER_TAB_INDEX: isize = 2; const HEADER_GROUP_TAB_INDEX: isize = 3; const CONTENT_CONTAINER_TAB_INDEX: isize = 4; const CONTENT_GROUP_TAB_INDEX: isize = 5; actions!( settings_editor, [ /// Minimizes the settings UI window. Minimize, /// Toggles focus between the navbar and the main content. ToggleFocusNav, /// Expands the navigation entry. ExpandNavEntry, /// Collapses the navigation entry. CollapseNavEntry, /// Focuses the next file in the file list. FocusNextFile, /// Focuses the previous file in the file list. FocusPreviousFile, /// Opens an editor for the current file OpenCurrentFile, /// Focuses the previous root navigation entry. FocusPreviousRootNavEntry, /// Focuses the next root navigation entry. FocusNextRootNavEntry, /// Focuses the first navigation entry. FocusFirstNavEntry, /// Focuses the last navigation entry. FocusLastNavEntry, /// Focuses and opens the next navigation entry without moving focus to content. FocusNextNavEntry, /// Focuses and opens the previous navigation entry without moving focus to content. FocusPreviousNavEntry ] ); #[derive(Action, PartialEq, Eq, Clone, Copy, Debug, JsonSchema, Deserialize)] #[action(namespace = settings_editor)] struct FocusFile(pub u32); struct SettingField<T: 'static> { pick: fn(&SettingsContent) -> Option<&T>, write: fn(&mut SettingsContent, Option<T>), /// A json-path-like string that gives a unique-ish string that identifies /// where in the JSON the setting is defined. /// /// The syntax is `jq`-like, but modified slightly to be URL-safe (and /// without the leading dot), e.g. `foo.bar`. /// /// They are URL-safe (this is important since links are the main use-case /// for these paths). /// /// There are a couple of special cases: /// - discrimminants are represented with a trailing `$`, for example /// `terminal.working_directory$`. This is to distinguish the discrimminant /// setting (i.e. the setting that changes whether the value is a string or /// an object) from the setting in the case that it is a string. /// - language-specific settings begin `languages.$(language)`. Links /// targeting these settings should take the form `languages/Rust/...`, for /// example, but are not currently supported. json_path: Option<&'static str>, } impl<T: 'static> Clone for SettingField<T> { fn clone(&self) -> Self { *self } } // manual impl because derive puts a Copy bound on T, which is inaccurate in our case impl<T: 'static> Copy for SettingField<T> {} /// Helper for unimplemented settings, used in combination with `SettingField::unimplemented` /// to keep the setting around in the UI with valid pick and write implementations, but don't actually try to render it. /// TODO(settings_ui): In non-dev builds (`#[cfg(not(debug_assertions))]`) make this render as edit-in-json #[derive(Clone, Copy)] struct UnimplementedSettingField; impl PartialEq for UnimplementedSettingField { fn eq(&self, _other: &Self) -> bool { true } } impl<T: 'static> SettingField<T> { /// Helper for settings with types that are not yet implemented. #[allow(unused)] fn unimplemented(self) -> SettingField<UnimplementedSettingField> { SettingField { pick: |_| Some(&UnimplementedSettingField), write: |_, _| unreachable!(), json_path: self.json_path, } } } trait AnySettingField { fn as_any(&self) -> &dyn Any; fn type_name(&self) -> &'static str; fn type_id(&self) -> TypeId; // Returns the file this value was set in and true, or File::Default and false to indicate it was not found in any file (missing default) fn file_set_in(&self, file: SettingsUiFile, cx: &App) -> (settings::SettingsFile, bool); fn reset_to_default_fn( &self, current_file: &SettingsUiFile, file_set_in: &settings::SettingsFile, cx: &App, ) -> Option<Box<dyn Fn(&mut App)>>; fn json_path(&self) -> Option<&'static str>; } impl<T: PartialEq + Clone + Send + Sync + 'static> AnySettingField for SettingField<T> { fn as_any(&self) -> &dyn Any { self } fn type_name(&self) -> &'static str { type_name::<T>() } fn type_id(&self) -> TypeId { TypeId::of::<T>() } fn file_set_in(&self, file: SettingsUiFile, cx: &App) -> (settings::SettingsFile, bool) { let (file, value) = cx .global::<SettingsStore>() .get_value_from_file(file.to_settings(), self.pick); return (file, value.is_some()); } fn reset_to_default_fn( &self, current_file: &SettingsUiFile, file_set_in: &settings::SettingsFile, cx: &App, ) -> Option<Box<dyn Fn(&mut App)>> { if file_set_in == &settings::SettingsFile::Default { return None; } if file_set_in != ¤t_file.to_settings() { return None; } let this = *self; let store = SettingsStore::global(cx); let default_value = (this.pick)(store.raw_default_settings()); let is_default = store .get_content_for_file(file_set_in.clone()) .map_or(None, this.pick) == default_value; if is_default { return None; } let current_file = current_file.clone(); return Some(Box::new(move |cx| { let store = SettingsStore::global(cx); let default_value = (this.pick)(store.raw_default_settings()); let is_set_somewhere_other_than_default = store .get_value_up_to_file(current_file.to_settings(), this.pick) .0 != settings::SettingsFile::Default; let value_to_set = if is_set_somewhere_other_than_default { default_value.cloned() } else { None }; update_settings_file(current_file.clone(), None, cx, move |settings, _| { (this.write)(settings, value_to_set); }) // todo(settings_ui): Don't log err .log_err(); })); } fn json_path(&self) -> Option<&'static str> { self.json_path } } #[derive(Default, Clone)] struct SettingFieldRenderer { renderers: Rc< RefCell< HashMap< TypeId, Box< dyn Fn( &SettingsWindow, &SettingItem, SettingsUiFile, Option<&SettingsFieldMetadata>, bool, &mut Window, &mut Context<SettingsWindow>, ) -> Stateful<Div>, >, >, >, >, } impl Global for SettingFieldRenderer {} impl SettingFieldRenderer { fn add_basic_renderer<T: 'static>( &mut self, render_control: impl Fn( SettingField<T>, SettingsUiFile, Option<&SettingsFieldMetadata>, &mut Window, &mut App, ) -> AnyElement + 'static, ) -> &mut Self { self.add_renderer( move |settings_window: &SettingsWindow, item: &SettingItem, field: SettingField<T>, settings_file: SettingsUiFile, metadata: Option<&SettingsFieldMetadata>, sub_field: bool, window: &mut Window, cx: &mut Context<SettingsWindow>| { render_settings_item( settings_window, item, settings_file.clone(), render_control(field, settings_file, metadata, window, cx), sub_field, cx, ) }, ) } fn add_renderer<T: 'static>( &mut self, renderer: impl Fn( &SettingsWindow, &SettingItem, SettingField<T>, SettingsUiFile, Option<&SettingsFieldMetadata>, bool, &mut Window, &mut Context<SettingsWindow>, ) -> Stateful<Div> + 'static, ) -> &mut Self { let key = TypeId::of::<T>(); let renderer = Box::new( move |settings_window: &SettingsWindow, item: &SettingItem, settings_file: SettingsUiFile, metadata: Option<&SettingsFieldMetadata>, sub_field: bool, window: &mut Window, cx: &mut Context<SettingsWindow>| { let field = *item .field .as_ref() .as_any() .downcast_ref::<SettingField<T>>() .unwrap(); renderer( settings_window, item, field, settings_file, metadata, sub_field, window, cx, ) }, ); self.renderers.borrow_mut().insert(key, renderer); self } } struct NonFocusableHandle { handle: FocusHandle, _subscription: Subscription, } impl NonFocusableHandle { fn new(tab_index: isize, tab_stop: bool, window: &mut Window, cx: &mut App) -> Entity<Self> { let handle = cx.focus_handle().tab_index(tab_index).tab_stop(tab_stop); Self::from_handle(handle, window, cx) } fn from_handle(handle: FocusHandle, window: &mut Window, cx: &mut App) -> Entity<Self> { cx.new(|cx| { let _subscription = cx.on_focus(&handle, window, { move |_, window, cx| { window.focus_next(cx); } }); Self { handle, _subscription, } }) } } impl Focusable for NonFocusableHandle { fn focus_handle(&self, _: &App) -> FocusHandle { self.handle.clone() } } #[derive(Default)] struct SettingsFieldMetadata { placeholder: Option<&'static str>, should_do_titlecase: Option<bool>, } pub fn init(cx: &mut App) { init_renderers(cx); cx.observe_new(|workspace: &mut workspace::Workspace, _, _| { workspace .register_action( |workspace, OpenSettingsAt { path }: &OpenSettingsAt, window, cx| { let window_handle = window .window_handle() .downcast::<Workspace>() .expect("Workspaces are root Windows"); open_settings_editor(workspace, Some(&path), false, window_handle, cx); }, ) .register_action(|workspace, _: &OpenSettings, window, cx| { let window_handle = window .window_handle() .downcast::<Workspace>() .expect("Workspaces are root Windows"); open_settings_editor(workspace, None, false, window_handle, cx); }) .register_action(|workspace, _: &OpenProjectSettings, window, cx| { let window_handle = window .window_handle() .downcast::<Workspace>() .expect("Workspaces are root Windows"); open_settings_editor(workspace, None, true, window_handle, cx); }); }) .detach(); } fn init_renderers(cx: &mut App) { cx.default_global::<SettingFieldRenderer>() .add_renderer::<UnimplementedSettingField>( |settings_window, item, _, settings_file, _, sub_field, _, cx| { render_settings_item( settings_window, item, settings_file, Button::new("open-in-settings-file", "Edit in settings.json") .style(ButtonStyle::Outlined) .size(ButtonSize::Medium) .tab_index(0_isize) .tooltip(Tooltip::for_action_title_in( "Edit in settings.json", &OpenCurrentFile, &settings_window.focus_handle, )) .on_click(cx.listener(|this, _, window, cx| { this.open_current_settings_file(window, cx); })) .into_any_element(), sub_field, cx, ) }, ) .add_basic_renderer::<bool>(render_toggle_button) .add_basic_renderer::<String>(render_text_field) .add_basic_renderer::<SharedString>(render_text_field) .add_basic_renderer::<settings::SaturatingBool>(render_toggle_button) .add_basic_renderer::<settings::CursorShape>(render_dropdown) .add_basic_renderer::<settings::RestoreOnStartupBehavior>(render_dropdown) .add_basic_renderer::<settings::BottomDockLayout>(render_dropdown) .add_basic_renderer::<settings::OnLastWindowClosed>(render_dropdown) .add_basic_renderer::<settings::CloseWindowWhenNoItems>(render_dropdown) .add_basic_renderer::<settings::TextRenderingMode>(render_dropdown) .add_basic_renderer::<settings::FontFamilyName>(render_font_picker) .add_basic_renderer::<settings::BaseKeymapContent>(render_dropdown) .add_basic_renderer::<settings::MultiCursorModifier>(render_dropdown) .add_basic_renderer::<settings::HideMouseMode>(render_dropdown) .add_basic_renderer::<settings::CurrentLineHighlight>(render_dropdown) .add_basic_renderer::<settings::ShowWhitespaceSetting>(render_dropdown) .add_basic_renderer::<settings::SoftWrap>(render_dropdown) .add_basic_renderer::<settings::ScrollBeyondLastLine>(render_dropdown) .add_basic_renderer::<settings::SnippetSortOrder>(render_dropdown) .add_basic_renderer::<settings::ClosePosition>(render_dropdown) .add_basic_renderer::<settings::DockSide>(render_dropdown) .add_basic_renderer::<settings::TerminalDockPosition>(render_dropdown) .add_basic_renderer::<settings::DockPosition>(render_dropdown) .add_basic_renderer::<settings::GitGutterSetting>(render_dropdown) .add_basic_renderer::<settings::GitHunkStyleSetting>(render_dropdown) .add_basic_renderer::<settings::GitPathStyle>(render_dropdown) .add_basic_renderer::<settings::DiagnosticSeverityContent>(render_dropdown) .add_basic_renderer::<settings::SeedQuerySetting>(render_dropdown) .add_basic_renderer::<settings::DoubleClickInMultibuffer>(render_dropdown) .add_basic_renderer::<settings::GoToDefinitionFallback>(render_dropdown) .add_basic_renderer::<settings::ActivateOnClose>(render_dropdown) .add_basic_renderer::<settings::ShowDiagnostics>(render_dropdown) .add_basic_renderer::<settings::ShowCloseButton>(render_dropdown) .add_basic_renderer::<settings::ProjectPanelEntrySpacing>(render_dropdown) .add_basic_renderer::<settings::ProjectPanelSortMode>(render_dropdown) .add_basic_renderer::<settings::RewrapBehavior>(render_dropdown) .add_basic_renderer::<settings::FormatOnSave>(render_dropdown) .add_basic_renderer::<settings::IndentGuideColoring>(render_dropdown) .add_basic_renderer::<settings::IndentGuideBackgroundColoring>(render_dropdown) .add_basic_renderer::<settings::FileFinderWidthContent>(render_dropdown) .add_basic_renderer::<settings::ShowDiagnostics>(render_dropdown) .add_basic_renderer::<settings::WordsCompletionMode>(render_dropdown) .add_basic_renderer::<settings::LspInsertMode>(render_dropdown) .add_basic_renderer::<settings::CompletionDetailAlignment>(render_dropdown) .add_basic_renderer::<settings::AlternateScroll>(render_dropdown) .add_basic_renderer::<settings::TerminalBlink>(render_dropdown) .add_basic_renderer::<settings::CursorShapeContent>(render_dropdown) .add_basic_renderer::<f32>(render_number_field) .add_basic_renderer::<u32>(render_number_field) .add_basic_renderer::<u64>(render_number_field) .add_basic_renderer::<usize>(render_number_field) .add_basic_renderer::<NonZero<usize>>(render_number_field) .add_basic_renderer::<NonZeroU32>(render_number_field) .add_basic_renderer::<settings::CodeFade>(render_number_field) .add_basic_renderer::<settings::DelayMs>(render_number_field) .add_basic_renderer::<gpui::FontWeight>(render_number_field) .add_basic_renderer::<settings::CenteredPaddingSettings>(render_number_field) .add_basic_renderer::<settings::InactiveOpacity>(render_number_field) .add_basic_renderer::<settings::MinimumContrast>(render_number_field) .add_basic_renderer::<settings::ShowScrollbar>(render_dropdown) .add_basic_renderer::<settings::ScrollbarDiagnostics>(render_dropdown) .add_basic_renderer::<settings::ShowMinimap>(render_dropdown) .add_basic_renderer::<settings::DisplayIn>(render_dropdown) .add_basic_renderer::<settings::MinimapThumb>(render_dropdown) .add_basic_renderer::<settings::MinimapThumbBorder>(render_dropdown) .add_basic_renderer::<settings::SteppingGranularity>(render_dropdown) .add_basic_renderer::<settings::NotifyWhenAgentWaiting>(render_dropdown) .add_basic_renderer::<settings::NotifyWhenAgentWaiting>(render_dropdown) .add_basic_renderer::<settings::ImageFileSizeUnit>(render_dropdown) .add_basic_renderer::<settings::StatusStyle>(render_dropdown)