settings_ui: Implement reset to default button (#40135)
Closes #ISSUE Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
@@ -484,7 +484,7 @@ impl SettingsStore {
|
||||
files
|
||||
}
|
||||
|
||||
fn get_content_for_file(&self, file: SettingsFile) -> Option<&SettingsContent> {
|
||||
pub fn get_content_for_file(&self, file: SettingsFile) -> Option<&SettingsContent> {
|
||||
match file {
|
||||
SettingsFile::User => self
|
||||
.user_settings
|
||||
@@ -538,6 +538,25 @@ impl SettingsStore {
|
||||
&self,
|
||||
target_file: SettingsFile,
|
||||
pick: fn(&SettingsContent) -> &Option<T>,
|
||||
) -> (SettingsFile, Option<&T>) {
|
||||
self.get_value_from_file_inner(target_file, pick, true)
|
||||
}
|
||||
|
||||
/// Same as `Self::get_value_from_file` except that it does not include the current file.
|
||||
/// Therefore it returns the value that was potentially overloaded by the target file.
|
||||
pub fn get_value_up_to_file<T>(
|
||||
&self,
|
||||
target_file: SettingsFile,
|
||||
pick: fn(&SettingsContent) -> &Option<T>,
|
||||
) -> (SettingsFile, Option<&T>) {
|
||||
self.get_value_from_file_inner(target_file, pick, false)
|
||||
}
|
||||
|
||||
fn get_value_from_file_inner<T>(
|
||||
&self,
|
||||
target_file: SettingsFile,
|
||||
pick: fn(&SettingsContent) -> &Option<T>,
|
||||
include_target_file: bool,
|
||||
) -> (SettingsFile, Option<&T>) {
|
||||
// todo(settings_ui): Add a metadata field for overriding the "overrides" tag, for contextually different settings
|
||||
// e.g. disable AI isn't overridden, or a vec that gets extended instead or some such
|
||||
@@ -547,10 +566,15 @@ impl SettingsStore {
|
||||
let mut found_file = false;
|
||||
|
||||
for file in all_files.into_iter() {
|
||||
if !found_file && file != target_file && file != SettingsFile::Default {
|
||||
continue;
|
||||
if !found_file && file != SettingsFile::Default {
|
||||
if file != target_file {
|
||||
continue;
|
||||
}
|
||||
found_file = true;
|
||||
if !include_target_file {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
found_file = true;
|
||||
|
||||
if let SettingsFile::Project((worktree_id, ref path)) = file
|
||||
&& let SettingsFile::Project((target_worktree_id, ref target_path)) = target_file
|
||||
|
||||
@@ -104,8 +104,15 @@ 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 pick_mut 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)]
|
||||
@@ -123,9 +130,15 @@ trait AnySettingField {
|
||||
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)>>;
|
||||
}
|
||||
|
||||
impl<T> AnySettingField for SettingField<T> {
|
||||
impl<T: PartialEq + Clone + Send + Sync + 'static> AnySettingField for SettingField<T> {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
@@ -144,6 +157,47 @@ impl<T> AnySettingField for SettingField<T> {
|
||||
.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;
|
||||
}
|
||||
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.clone()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
update_settings_file(current_file.clone(), cx, move |settings, _| {
|
||||
*(this.pick_mut)(settings) = value_to_set;
|
||||
})
|
||||
// todo(settings_ui): Don't log err
|
||||
.log_err();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
@@ -658,7 +712,7 @@ fn render_settings_item(
|
||||
cx: &mut Context<'_, SettingsWindow>,
|
||||
) -> Stateful<Div> {
|
||||
let (found_in_file, _) = setting_item.field.file_set_in(file.clone(), cx);
|
||||
let file_set_in = SettingsUiFile::from_settings(found_in_file);
|
||||
let file_set_in = SettingsUiFile::from_settings(found_in_file.clone());
|
||||
|
||||
h_flex()
|
||||
.id(setting_item.title)
|
||||
@@ -673,6 +727,24 @@ fn render_settings_item(
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.child(Label::new(SharedString::new_static(setting_item.title)))
|
||||
.when_some(
|
||||
setting_item
|
||||
.field
|
||||
.reset_to_default_fn(&file, &found_in_file, cx),
|
||||
|this, reset_to_default| {
|
||||
this.child(
|
||||
IconButton::new("reset-to-default-btn", IconName::Undo)
|
||||
.icon_color(Color::Muted)
|
||||
.size(ButtonSize::Compact)
|
||||
.on_click({
|
||||
move |_, _, cx| {
|
||||
reset_to_default(cx);
|
||||
}
|
||||
})
|
||||
.tooltip(Tooltip::text("Reset to default")),
|
||||
)
|
||||
},
|
||||
)
|
||||
.when_some(
|
||||
file_set_in.filter(|file_set_in| file_set_in != &file),
|
||||
|this, file_set_in| {
|
||||
|
||||
@@ -279,7 +279,7 @@ impl Component for Icon {
|
||||
)
|
||||
}
|
||||
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
|
||||
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
|
||||
Some(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
@@ -314,6 +314,30 @@ impl Component for Icon {
|
||||
),
|
||||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"All Icons",
|
||||
vec![single_example(
|
||||
"All Icons",
|
||||
h_flex()
|
||||
.image_cache(gpui::retain_all("all icons"))
|
||||
.flex_wrap()
|
||||
.gap_2()
|
||||
.children(<IconName as strum::IntoEnumIterator>::iter().map(
|
||||
|icon_name| {
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.border_1()
|
||||
.rounded_md()
|
||||
.px_2()
|
||||
.py_1()
|
||||
.border_color(Color::Muted.color(cx))
|
||||
.child(SharedString::new_static(icon_name.into()))
|
||||
.child(Icon::new(icon_name).into_any_element())
|
||||
},
|
||||
))
|
||||
.into_any_element(),
|
||||
)],
|
||||
),
|
||||
])
|
||||
.into_any_element(),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user