Compare commits

...

7 Commits

Author SHA1 Message Date
Ben Kunkle
4d46eca9d3 machete --fix 2025-12-19 17:31:34 -05:00
Ben Kunkle
f92403a6dd cache opened buffers to reduce I/O 2025-12-18 15:05:50 -05:00
Ben Kunkle
7adb4fb73d clean 2025-12-18 14:26:55 -05:00
Ben Kunkle
cedca47a82 use buffers to update project setting files 2025-12-18 14:25:10 -05:00
Ben Kunkle
ef3065abe7 pipe window through to update settings file 2025-12-18 14:11:43 -05:00
Ben Kunkle
8bfa78d34c use original window 2025-12-18 14:00:43 -05:00
Ben Kunkle
115431d917 wip - try using window
Co-Authored-By: Mikayla <mikayla@zed.dev>
2025-12-18 13:40:58 -05:00
11 changed files with 206 additions and 156 deletions

1
Cargo.lock generated
View File

@@ -12477,7 +12477,6 @@ dependencies = [
"dap",
"dap_adapters",
"db",
"encoding_rs",
"extension",
"fancy-regex",
"fs",

View File

@@ -40,7 +40,6 @@ clock.workspace = true
collections.workspace = true
context_server.workspace = true
dap.workspace = true
encoding_rs.workspace = true
extension.workspace = true
fancy-regex.workspace = true
fs.workspace = true

View File

@@ -65,7 +65,7 @@ use debugger::{
dap_store::{DapStore, DapStoreEvent},
session::Session,
};
use encoding_rs;
pub use environment::ProjectEnvironment;
#[cfg(test)]
use futures::future::join_all;
@@ -5444,48 +5444,6 @@ impl Project {
worktree.read(cx).entry_for_path(rel_path).is_some()
})
}
pub fn update_local_settings_file(
&self,
worktree_id: WorktreeId,
rel_path: Arc<RelPath>,
cx: &mut App,
update: impl 'static + Send + FnOnce(&mut settings::SettingsContent, &App),
) {
let Some(worktree) = self.worktree_for_id(worktree_id, cx) else {
// todo(settings_ui) error?
return;
};
cx.spawn(async move |cx| {
let file = worktree
.update(cx, |worktree, cx| worktree.load_file(&rel_path, cx))?
.await
.context("Failed to load settings file")?;
let has_bom = file.has_bom;
let new_text = cx.read_global::<SettingsStore, _>(|store, cx| {
store.new_text_for_update(file.text, move |settings| update(settings, cx))
})?;
worktree
.update(cx, |worktree, cx| {
let line_ending = text::LineEnding::detect(&new_text);
worktree.write_file(
rel_path.clone(),
new_text.into(),
line_ending,
encoding_rs::UTF_8,
has_bom,
cx,
)
})?
.await
.context("Failed to write settings file")?;
anyhow::Ok(())
})
.detach_and_log_err(cx);
}
}
pub struct PathMatchCandidateSet {

View File

@@ -27,6 +27,7 @@ fs.workspace = true
fuzzy.workspace = true
gpui.workspace = true
heck.workspace = true
language.workspace = true
log.workspace = true
menu.workspace = true
paths.workspace = true

View File

@@ -17,7 +17,7 @@ where
labels: &'static [&'static str],
should_do_title_case: bool,
tab_index: Option<isize>,
on_change: Rc<dyn Fn(T, &mut App) + 'static>,
on_change: Rc<dyn Fn(T, &mut ui::Window, &mut App) + 'static>,
}
impl<T> EnumVariantDropdown<T>
@@ -29,7 +29,7 @@ where
current_value: T,
variants: &'static [T],
labels: &'static [&'static str],
on_change: impl Fn(T, &mut App) + 'static,
on_change: impl Fn(T, &mut ui::Window, &mut App) + 'static,
) -> Self {
Self {
id: id.into(),
@@ -78,8 +78,8 @@ where
value == current_value,
IconPosition::End,
None,
move |_, cx| {
on_change(value, cx);
move |window, cx| {
on_change(value, window, cx);
},
);
}

View File

@@ -13,13 +13,13 @@ pub struct FontPickerDelegate {
filtered_fonts: Vec<StringMatch>,
selected_index: usize,
current_font: SharedString,
on_font_changed: Arc<dyn Fn(SharedString, &mut App) + 'static>,
on_font_changed: Arc<dyn Fn(SharedString, &mut Window, &mut App) + 'static>,
}
impl FontPickerDelegate {
fn new(
current_font: SharedString,
on_font_changed: impl Fn(SharedString, &mut App) + 'static,
on_font_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
cx: &mut Context<FontPicker>,
) -> Self {
let font_family_cache = FontFamilyCache::global(cx);
@@ -132,10 +132,10 @@ impl PickerDelegate for FontPickerDelegate {
Task::ready(())
}
fn confirm(&mut self, _secondary: bool, _window: &mut Window, cx: &mut Context<FontPicker>) {
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<FontPicker>) {
if let Some(font_match) = self.filtered_fonts.get(self.selected_index) {
let font = font_match.string.clone();
(self.on_font_changed)(font.into(), cx);
(self.on_font_changed)(font.into(), window, cx);
}
}
@@ -168,7 +168,7 @@ impl PickerDelegate for FontPickerDelegate {
pub fn font_picker(
current_font: SharedString,
on_font_changed: impl Fn(SharedString, &mut App) + 'static,
on_font_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
window: &mut Window,
cx: &mut Context<FontPicker>,
) -> FontPicker {

View File

@@ -13,13 +13,13 @@ pub struct IconThemePickerDelegate {
filtered_themes: Vec<StringMatch>,
selected_index: usize,
current_theme: SharedString,
on_theme_changed: Arc<dyn Fn(SharedString, &mut App) + 'static>,
on_theme_changed: Arc<dyn Fn(SharedString, &mut Window, &mut App) + 'static>,
}
impl IconThemePickerDelegate {
fn new(
current_theme: SharedString,
on_theme_changed: impl Fn(SharedString, &mut App) + 'static,
on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
cx: &mut Context<IconThemePicker>,
) -> Self {
let theme_registry = ThemeRegistry::global(cx);
@@ -32,15 +32,15 @@ impl IconThemePickerDelegate {
let selected_index = icon_themes
.iter()
.position(|icon_themes| *icon_themes == current_theme)
.position(|icon_theme| *icon_theme == current_theme)
.unwrap_or(0);
let filtered_themes = icon_themes
.iter()
.enumerate()
.map(|(index, icon_themes)| StringMatch {
.map(|(index, theme)| StringMatch {
candidate_id: index,
string: icon_themes.to_string(),
string: theme.to_string(),
positions: Vec::new(),
score: 0.0,
})
@@ -67,13 +67,18 @@ impl PickerDelegate for IconThemePickerDelegate {
self.selected_index
}
fn set_selected_index(&mut self, ix: usize, _: &mut Window, cx: &mut Context<IconThemePicker>) {
self.selected_index = ix.min(self.filtered_themes.len().saturating_sub(1));
fn set_selected_index(
&mut self,
index: usize,
_window: &mut Window,
cx: &mut Context<IconThemePicker>,
) {
self.selected_index = index.min(self.filtered_themes.len().saturating_sub(1));
cx.notify();
}
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Search icon theme…".into()
"Search icon themes".into()
}
fn update_matches(
@@ -89,9 +94,9 @@ impl PickerDelegate for IconThemePickerDelegate {
icon_themes
.iter()
.enumerate()
.map(|(index, icon_theme)| StringMatch {
.map(|(index, theme)| StringMatch {
candidate_id: index,
string: icon_theme.to_string(),
string: theme.to_string(),
positions: Vec::new(),
score: 0.0,
})
@@ -100,16 +105,16 @@ impl PickerDelegate for IconThemePickerDelegate {
let _candidates: Vec<StringMatchCandidate> = icon_themes
.iter()
.enumerate()
.map(|(id, icon_theme)| StringMatchCandidate::new(id, icon_theme.as_ref()))
.map(|(id, theme)| StringMatchCandidate::new(id, theme.as_ref()))
.collect();
icon_themes
.iter()
.enumerate()
.filter(|(_, icon_theme)| icon_theme.to_lowercase().contains(&query.to_lowercase()))
.map(|(index, icon_theme)| StringMatch {
.filter(|(_, theme)| theme.to_lowercase().contains(&query.to_lowercase()))
.map(|(index, theme)| StringMatch {
candidate_id: index,
string: icon_theme.to_string(),
string: theme.to_string(),
positions: Vec::new(),
score: 0.0,
})
@@ -119,7 +124,7 @@ impl PickerDelegate for IconThemePickerDelegate {
let selected_index = if query.is_empty() {
icon_themes
.iter()
.position(|icon_theme| *icon_theme == current_theme)
.position(|theme| *theme == current_theme)
.unwrap_or(0)
} else {
matches
@@ -138,12 +143,12 @@ impl PickerDelegate for IconThemePickerDelegate {
fn confirm(
&mut self,
_secondary: bool,
_window: &mut Window,
window: &mut Window,
cx: &mut Context<IconThemePicker>,
) {
if let Some(theme_match) = self.filtered_themes.get(self.selected_index) {
let theme = theme_match.string.clone();
(self.on_theme_changed)(theme.into(), cx);
(self.on_theme_changed)(theme.into(), window, cx);
}
}
@@ -156,15 +161,15 @@ impl PickerDelegate for IconThemePickerDelegate {
fn render_match(
&self,
ix: usize,
index: usize,
selected: bool,
_window: &mut Window,
_cx: &mut Context<IconThemePicker>,
) -> Option<Self::ListItem> {
let theme_match = self.filtered_themes.get(ix)?;
let theme_match = self.filtered_themes.get(index)?;
Some(
ListItem::new(ix)
ListItem::new(index)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
@@ -176,7 +181,7 @@ impl PickerDelegate for IconThemePickerDelegate {
pub fn icon_theme_picker(
current_theme: SharedString,
on_theme_changed: impl Fn(SharedString, &mut App) + 'static,
on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
window: &mut Window,
cx: &mut Context<IconThemePicker>,
) -> IconThemePicker {

View File

@@ -9,7 +9,7 @@ use ui::{
pub struct SettingsInputField {
initial_text: Option<String>,
placeholder: Option<&'static str>,
confirm: Option<Box<dyn Fn(Option<String>, &mut App)>>,
confirm: Option<Box<dyn Fn(Option<String>, &mut Window, &mut App)>>,
tab_index: Option<isize>,
}
@@ -34,7 +34,10 @@ impl SettingsInputField {
self
}
pub fn on_confirm(mut self, confirm: impl Fn(Option<String>, &mut App) + 'static) -> Self {
pub fn on_confirm(
mut self,
confirm: impl Fn(Option<String>, &mut Window, &mut App) + 'static,
) -> Self {
self.confirm = Some(Box::new(confirm));
self
}
@@ -83,13 +86,13 @@ impl RenderOnce for SettingsInputField {
.child(editor)
.when_some(self.confirm, |this, confirm| {
this.on_action::<menu::Confirm>({
move |_, _, cx| {
move |_, window, cx| {
let Some(editor) = weak_editor.upgrade() else {
return;
};
let new_value = editor.read_with(cx, |editor, cx| editor.text(cx));
let new_value = (!new_value.is_empty()).then_some(new_value);
confirm(new_value, cx);
confirm(new_value, window, cx);
}
})
})

View File

@@ -13,13 +13,13 @@ pub struct ThemePickerDelegate {
filtered_themes: Vec<StringMatch>,
selected_index: usize,
current_theme: SharedString,
on_theme_changed: Arc<dyn Fn(SharedString, &mut App) + 'static>,
on_theme_changed: Arc<dyn Fn(SharedString, &mut Window, &mut App) + 'static>,
}
impl ThemePickerDelegate {
fn new(
current_theme: SharedString,
on_theme_changed: impl Fn(SharedString, &mut App) + 'static,
on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
cx: &mut Context<ThemePicker>,
) -> Self {
let theme_registry = ThemeRegistry::global(cx);
@@ -130,10 +130,10 @@ impl PickerDelegate for ThemePickerDelegate {
Task::ready(())
}
fn confirm(&mut self, _secondary: bool, _window: &mut Window, cx: &mut Context<ThemePicker>) {
fn confirm(&mut self, _secondary: bool, window: &mut Window, cx: &mut Context<ThemePicker>) {
if let Some(theme_match) = self.filtered_themes.get(self.selected_index) {
let theme = theme_match.string.clone();
(self.on_theme_changed)(theme.into(), cx);
(self.on_theme_changed)(theme.into(), window, cx);
}
}
@@ -166,7 +166,7 @@ impl PickerDelegate for ThemePickerDelegate {
pub fn theme_picker(
current_theme: SharedString,
on_theme_changed: impl Fn(SharedString, &mut App) + 'static,
on_theme_changed: impl Fn(SharedString, &mut Window, &mut App) + 'static,
window: &mut Window,
cx: &mut Context<ThemePicker>,
) -> ThemePicker {

View File

@@ -217,7 +217,7 @@ fn render_api_key_provider(
SettingsInputField::new()
.tab_index(0)
.with_placeholder("xxxxxxxxxxxxxxxxxxxx")
.on_confirm(move |api_key, cx| {
.on_confirm(move |api_key, _window, cx| {
write_key(api_key.filter(|key| !key.is_empty()), cx);
}),
),

View File

@@ -2,7 +2,7 @@ mod components;
mod page_data;
mod pages;
use anyhow::Result;
use anyhow::{Context as _, Result};
use editor::{Editor, EditorEvent};
use fuzzy::StringMatchCandidate;
use gpui::{
@@ -11,7 +11,9 @@ use gpui::{
Subscription, Task, TitlebarOptions, UniformListScrollHandle, Window, WindowBounds,
WindowHandle, WindowOptions, actions, div, list, point, prelude::*, px, uniform_list,
};
use project::{Project, WorktreeId};
use language::Buffer;
use project::{Project, ProjectPath, WorktreeId};
use release_channel::ReleaseChannel;
use schemars::JsonSchema;
use serde::Deserialize;
@@ -154,7 +156,7 @@ trait AnySettingField {
current_file: &SettingsUiFile,
file_set_in: &settings::SettingsFile,
cx: &App,
) -> Option<Box<dyn Fn(&mut App)>>;
) -> Option<Box<dyn Fn(&mut Window, &mut App)>>;
fn json_path(&self) -> Option<&'static str>;
}
@@ -184,7 +186,7 @@ impl<T: PartialEq + Clone + Send + Sync + 'static> AnySettingField for SettingFi
current_file: &SettingsUiFile,
file_set_in: &settings::SettingsFile,
cx: &App,
) -> Option<Box<dyn Fn(&mut App)>> {
) -> Option<Box<dyn Fn(&mut Window, &mut App)>> {
if file_set_in == &settings::SettingsFile::Default {
return None;
}
@@ -203,7 +205,7 @@ impl<T: PartialEq + Clone + Send + Sync + 'static> AnySettingField for SettingFi
}
let current_file = current_file.clone();
return Some(Box::new(move |cx| {
return Some(Box::new(move |window, cx| {
let store = SettingsStore::global(cx);
let default_value = (this.pick)(store.raw_default_settings());
let is_set_somewhere_other_than_default = store
@@ -215,9 +217,15 @@ impl<T: PartialEq + Clone + Send + Sync + 'static> AnySettingField for SettingFi
} else {
None
};
update_settings_file(current_file.clone(), None, cx, move |settings, _| {
(this.write)(settings, value_to_set);
})
update_settings_file(
current_file.clone(),
None,
window,
cx,
move |settings, _| {
(this.write)(settings, value_to_set);
},
)
// todo(settings_ui): Don't log err
.log_err();
}));
@@ -575,7 +583,6 @@ pub fn open_settings_editor(
}
// We have to defer this to get the workspace off the stack.
let path = path.map(ToOwned::to_owned);
cx.defer(move |cx| {
let current_rem_size: f32 = theme::ThemeSettings::get_global(cx).ui_font_size(cx).into();
@@ -671,6 +678,8 @@ pub struct SettingsWindow {
pages: Vec<SettingsPage>,
search_bar: Entity<Editor>,
search_task: Option<Task<()>>,
/// Cached settings file buffers to avoid repeated disk I/O on each settings change
project_setting_file_buffers: HashMap<ProjectPath, Entity<Buffer>>,
/// Index into navbar_entries
navbar_entry: usize,
navbar_entries: Vec<NavBarEntry>,
@@ -1069,8 +1078,8 @@ fn render_settings_item(
.icon_size(IconSize::Small)
.tooltip(Tooltip::text("Reset to Default"))
.on_click({
move |_, _, cx| {
reset_to_default(cx);
move |_, window, cx| {
reset_to_default(window, cx);
}
}),
)
@@ -1499,6 +1508,7 @@ impl SettingsWindow {
files: vec![],
current_file: current_file,
project_setting_file_buffers: HashMap::default(),
pages: vec![],
navbar_entries: vec![],
navbar_entry: 0,
@@ -2026,14 +2036,9 @@ impl SettingsWindow {
}
if let Some(worktree_id) = settings_ui_file.worktree_id() {
let directory_name = all_projects(cx)
let directory_name = all_projects(self.original_window.as_ref(), cx)
.find_map(|project| project.read(cx).worktree_for_id(worktree_id, cx))
.and_then(|worktree| worktree.read(cx).root_dir())
.and_then(|root_dir| {
root_dir
.file_name()
.map(|os_string| os_string.to_string_lossy().to_string())
});
.map(|worktree| worktree.read(cx).root_name());
let Some(directory_name) = directory_name else {
log::error!(
@@ -2043,7 +2048,8 @@ impl SettingsWindow {
continue;
};
self.worktree_root_dirs.insert(worktree_id, directory_name);
self.worktree_root_dirs
.insert(worktree_id, directory_name.as_unix_str().to_string());
}
let focus_handle = prev_files
@@ -2059,7 +2065,7 @@ impl SettingsWindow {
let mut missing_worktrees = Vec::new();
for worktree in all_projects(cx)
for worktree in all_projects(self.original_window.as_ref(), cx)
.flat_map(|project| project.read(cx).visible_worktrees(cx))
.filter(|tree| !self.worktree_root_dirs.contains_key(&tree.read(cx).id()))
{
@@ -3525,7 +3531,10 @@ impl Render for SettingsWindow {
}
}
fn all_projects(cx: &App) -> impl Iterator<Item = Entity<project::Project>> {
fn all_projects(
window: Option<&WindowHandle<Workspace>>,
cx: &App,
) -> impl Iterator<Item = Entity<project::Project>> {
workspace::AppState::global(cx)
.upgrade()
.map(|app_state| {
@@ -3535,6 +3544,9 @@ fn all_projects(cx: &App) -> impl Iterator<Item = Entity<project::Project>> {
.workspaces()
.iter()
.filter_map(|workspace| Some(workspace.read(cx).ok()?.project().clone()))
.chain(
window.and_then(|workspace| Some(workspace.read(cx).ok()?.project().clone())),
)
})
.into_iter()
.flatten()
@@ -3543,6 +3555,7 @@ fn all_projects(cx: &App) -> impl Iterator<Item = Entity<project::Project>> {
fn update_settings_file(
file: SettingsUiFile,
file_name: Option<&'static str>,
window: &mut Window,
cx: &mut App,
update: impl 'static + Send + FnOnce(&mut SettingsContent, &App),
) -> Result<()> {
@@ -3551,41 +3564,11 @@ fn update_settings_file(
match file {
SettingsUiFile::Project((worktree_id, rel_path)) => {
let rel_path = rel_path.join(paths::local_settings_file_relative_path());
let Some((worktree, project)) = all_projects(cx).find_map(|project| {
project
.read(cx)
.worktree_for_id(worktree_id, cx)
.zip(Some(project))
}) else {
anyhow::bail!("Could not find project with worktree id: {}", worktree_id);
let Some(settings_window) = window.root::<SettingsWindow>().flatten() else {
anyhow::bail!("No settings window found");
};
project.update(cx, |project, cx| {
let task = if project.contains_local_settings_file(worktree_id, &rel_path, cx) {
None
} else {
Some(worktree.update(cx, |worktree, cx| {
worktree.create_entry(rel_path.clone(), false, None, cx)
}))
};
cx.spawn(async move |project, cx| {
if let Some(task) = task
&& task.await.is_err()
{
return;
};
project
.update(cx, |project, cx| {
project.update_local_settings_file(worktree_id, rel_path, cx, update);
})
.ok();
})
.detach();
});
return Ok(());
update_project_setting_file(worktree_id, rel_path, update, settings_window, cx)
}
SettingsUiFile::User => {
// todo(settings_ui) error?
@@ -3596,6 +3579,86 @@ fn update_settings_file(
}
}
fn update_project_setting_file(
worktree_id: WorktreeId,
rel_path: Arc<RelPath>,
update: impl 'static + Send + FnOnce(&mut SettingsContent, &App),
settings_window: Entity<SettingsWindow>,
cx: &mut App,
) -> Result<()> {
let Some((worktree, project)) =
all_projects(settings_window.read(cx).original_window.as_ref(), cx).find_map(|project| {
project
.read(cx)
.worktree_for_id(worktree_id, cx)
.zip(Some(project))
})
else {
anyhow::bail!("Could not find project with worktree id: {}", worktree_id);
};
let project_path = ProjectPath {
worktree_id,
path: rel_path.clone(),
};
let needs_creation = !project
.read(cx)
.contains_local_settings_file(worktree_id, &rel_path, cx);
let create_task = needs_creation.then(|| {
worktree.update(cx, |worktree, cx| {
worktree.create_entry(rel_path.clone(), false, None, cx)
})
});
let buffer_store = project.read(cx).buffer_store().clone();
cx.spawn(async move |cx| {
if let Some(create_task) = create_task {
create_task.await?;
}
let cached_buffer = settings_window.read_with(cx, |settings_window, _| {
settings_window
.project_setting_file_buffers
.get(&project_path)
.cloned()
})?;
let buffer = if let Some(cached_buffer) = cached_buffer {
cached_buffer
} else {
let buffer = buffer_store
.update(cx, |store, cx| store.open_buffer(project_path.clone(), cx))?
.await
.context("Failed to open settings file")?;
settings_window.update(cx, |this, _cx| {
this.project_setting_file_buffers
.insert(project_path, buffer.clone());
})?;
buffer
};
buffer.update(cx, |buffer, cx| {
let current_text = buffer.text();
let new_text = cx
.global::<SettingsStore>()
.new_text_for_update(current_text, |settings| update(settings, cx));
buffer.edit([(0..buffer.len(), new_text)], None, cx);
})?;
buffer_store
.update(cx, |store, cx| store.save_buffer(buffer, cx))?
.await
.context("Failed to save settings file")?;
anyhow::Ok(())
})
.detach();
return anyhow::Ok(());
}
fn render_text_field<T: From<String> + Into<String> + AsRef<str> + Clone>(
field: SettingField<T>,
file: SettingsUiFile,
@@ -3617,10 +3680,16 @@ fn render_text_field<T: From<String> + Into<String> + AsRef<str> + Clone>(
|editor, placeholder| editor.with_placeholder(placeholder),
)
.on_confirm({
move |new_text, cx| {
update_settings_file(file.clone(), field.json_path, cx, move |settings, _cx| {
(field.write)(settings, new_text.map(Into::into));
})
move |new_text, window, cx| {
update_settings_file(
file.clone(),
field.json_path,
window,
cx,
move |settings, _cx| {
(field.write)(settings, new_text.map(Into::into));
},
)
.log_err(); // todo(settings_ui) don't log err
}
})
@@ -3645,11 +3714,11 @@ fn render_toggle_button<B: Into<bool> + From<bool> + Copy>(
Switch::new("toggle_button", toggle_state)
.tab_index(0_isize)
.on_click({
move |state, _window, cx| {
move |state, window, cx| {
telemetry::event!("Settings Change", setting = field.json_path, type = file.setting_type());
let state = *state == ui::ToggleState::Selected;
update_settings_file(file.clone(), field.json_path, cx, move |settings, _cx| {
update_settings_file(file.clone(), field.json_path, window, cx, move |settings, _cx| {
(field.write)(settings, Some(state.into()));
})
.log_err(); // todo(settings_ui) don't log err
@@ -3669,11 +3738,17 @@ fn render_number_field<T: NumberFieldType + Send + Sync>(
let value = value.copied().unwrap_or_else(T::min_value);
NumberField::new("numeric_stepper", value, window, cx)
.on_change({
move |value, _window, cx| {
move |value, window, cx| {
let value = *value;
update_settings_file(file.clone(), field.json_path, cx, move |settings, _cx| {
(field.write)(settings, Some(value));
})
update_settings_file(
file.clone(),
field.json_path,
window,
cx,
move |settings, _cx| {
(field.write)(settings, Some(value));
},
)
.log_err(); // todo(settings_ui) don't log err
}
})
@@ -3701,13 +3776,19 @@ where
let current_value = current_value.copied().unwrap_or(variants()[0]);
EnumVariantDropdown::new("dropdown", current_value, variants(), labels(), {
move |value, cx| {
move |value, window, cx| {
if value == current_value {
return;
}
update_settings_file(file.clone(), field.json_path, cx, move |settings, _cx| {
(field.write)(settings, Some(value));
})
update_settings_file(
file.clone(),
field.json_path,
window,
cx,
move |settings, _cx| {
(field.write)(settings, Some(value));
},
)
.log_err(); // todo(settings_ui) don't log err
}
})
@@ -3752,10 +3833,11 @@ fn render_font_picker(
Some(cx.new(move |cx| {
font_picker(
current_value.clone().into(),
move |font_name, cx| {
move |font_name, window, cx| {
update_settings_file(
file.clone(),
field.json_path,
window,
cx,
move |settings, _cx| {
(field.write)(settings, Some(font_name.into()));
@@ -3801,10 +3883,11 @@ fn render_theme_picker(
let current_value = current_value.clone();
theme_picker(
current_value,
move |theme_name, cx| {
move |theme_name, window, cx| {
update_settings_file(
file.clone(),
field.json_path,
window,
cx,
move |settings, _cx| {
(field.write)(
@@ -3853,10 +3936,11 @@ fn render_icon_theme_picker(
let current_value = current_value.clone();
icon_theme_picker(
current_value,
move |theme_name, cx| {
move |theme_name, window, cx| {
update_settings_file(
file.clone(),
field.json_path,
window,
cx,
move |settings, _cx| {
(field.write)(
@@ -3970,6 +4054,7 @@ pub mod test {
worktree_root_dirs: HashMap::default(),
files: Vec::default(),
current_file: crate::SettingsUiFile::User,
project_setting_file_buffers: HashMap::default(),
pages,
search_bar: cx.new(|cx| Editor::single_line(window, cx)),
navbar_entry: selected_idx.expect("Must have a selected navbar entry"),