@@ -456,7 +456,11 @@ pub struct SettingsWindow {
navbar_entry: usize,
navbar_entries: Vec<NavBarEntry>,
navbar_scroll_handle: UniformListScrollHandle,
- search_matches: Vec<Vec<bool>>,
+ /// [page_index][page_item_index] will be false
+ /// when the item is filtered out either by searches
+ /// or by the current file
+ filter_table: Vec<Vec<bool>>,
+ has_query: bool,
content_handles: Vec<Vec<Entity<NonFocusableHandle>>>,
page_scroll_handle: ScrollHandle,
focus_handle: FocusHandle,
@@ -874,7 +878,8 @@ impl SettingsWindow {
navbar_scroll_handle: UniformListScrollHandle::default(),
search_bar,
search_task: None,
- search_matches: vec![],
+ filter_table: vec![],
+ has_query: false,
content_handles: vec![],
page_scroll_handle: ScrollHandle::new(),
focus_handle: cx.focus_handle(),
@@ -961,7 +966,8 @@ impl SettingsWindow {
fn visible_navbar_entries(&self) -> impl Iterator<Item = (usize, &NavBarEntry)> {
let mut index = 0;
let entries = &self.navbar_entries;
- let search_matches = &self.search_matches;
+ let search_matches = &self.filter_table;
+ let has_query = self.has_query;
std::iter::from_fn(move || {
while index < entries.len() {
let entry = &entries[index];
@@ -983,7 +989,7 @@ impl SettingsWindow {
let entry_index = index;
index += 1;
- if entry.is_root && !entry.expanded {
+ if entry.is_root && !entry.expanded && !has_query {
while index < entries.len() {
if entries[index].is_root {
break;
@@ -998,7 +1004,7 @@ impl SettingsWindow {
fn filter_matches_to_file(&mut self) {
let current_file = self.current_file.mask();
- for (page, page_filter) in std::iter::zip(&self.pages, &mut self.search_matches) {
+ for (page, page_filter) in std::iter::zip(&self.pages, &mut self.filter_table) {
let mut header_index = 0;
let mut any_found_since_last_header = true;
@@ -1039,9 +1045,10 @@ impl SettingsWindow {
self.search_task.take();
let query = self.search_bar.read(cx).text(cx);
if query.is_empty() || self.search_index.is_none() {
- for page in &mut self.search_matches {
+ for page in &mut self.filter_table {
page.fill(true);
}
+ self.has_query = false;
self.filter_matches_to_file();
cx.notify();
return;
@@ -1055,7 +1062,7 @@ impl SettingsWindow {
match_indices: impl Iterator<Item = usize>,
cx: &mut Context<SettingsWindow>,
) {
- for page in &mut this.search_matches {
+ for page in &mut this.filter_table {
page.fill(false);
}
@@ -1065,10 +1072,11 @@ impl SettingsWindow {
header_index,
item_index,
} = search_index.key_lut[match_index];
- let page = &mut this.search_matches[page_index];
+ let page = &mut this.filter_table[page_index];
page[header_index] = true;
page[item_index] = true;
}
+ this.has_query = true;
this.filter_matches_to_file();
this.open_first_nav_page();
cx.notify();
@@ -1153,8 +1161,8 @@ impl SettingsWindow {
}));
}
- fn build_search_matches(&mut self) {
- self.search_matches = self
+ fn build_filter_table(&mut self) {
+ self.filter_table = self
.pages
.iter()
.map(|page| vec![true; page.items.len()])
@@ -1254,7 +1262,7 @@ impl SettingsWindow {
}
sub_page_stack_mut().clear();
// PERF: doesn't have to be rebuilt, can just be filled with true. pages is constant once it is built
- self.build_search_matches();
+ self.build_filter_table();
self.update_matches(cx);
cx.notify();
@@ -1590,15 +1598,16 @@ impl SettingsWindow {
.root_item(entry.is_root)
.toggle_state(this.is_navbar_entry_selected(ix))
.when(entry.is_root, |item| {
- item.expanded(entry.expanded).on_toggle(cx.listener(
- move |this, _, window, cx| {
- this.toggle_navbar_entry(ix);
- window.focus(
- &this.navbar_entries[ix].focus_handle,
- );
- cx.notify();
- },
- ))
+ item.expanded(entry.expanded || this.has_query)
+ .on_toggle(cx.listener(
+ move |this, _, window, cx| {
+ this.toggle_navbar_entry(ix);
+ window.focus(
+ &this.navbar_entries[ix].focus_handle,
+ );
+ cx.notify();
+ },
+ ))
})
.on_click(
cx.listener(move |this, _, window, cx| {
@@ -1715,7 +1724,7 @@ impl SettingsWindow {
.iter()
.enumerate()
.filter_map(move |(item_index, item)| {
- self.search_matches[page_idx][item_index].then_some((item_index, item))
+ self.filter_table[page_idx][item_index].then_some((item_index, item))
})
}
@@ -2398,80 +2407,6 @@ mod test {
fn navbar_entry(&self) -> usize {
self.navbar_entry
}
-
- fn new_builder(window: &mut Window, cx: &mut Context<Self>) -> Self {
- let mut this = Self::new(None, window, cx);
- this.navbar_entries.clear();
- this.pages.clear();
- this
- }
-
- fn build(mut self, cx: &App) -> Self {
- self.build_navbar(cx);
- self.build_search_matches();
- self.build_search_index();
- self
- }
-
- fn add_page(
- mut self,
- title: &'static str,
- build_page: impl Fn(SettingsPage) -> SettingsPage,
- ) -> Self {
- let page = SettingsPage {
- title,
- items: Vec::default(),
- };
-
- self.pages.push(build_page(page));
- self
- }
-
- fn search(&mut self, search_query: &str, window: &mut Window, cx: &mut Context<Self>) {
- self.search_task.take();
- self.search_bar.update(cx, |editor, cx| {
- editor.set_text(search_query, window, cx);
- });
- self.update_matches(cx);
- }
-
- fn assert_search_results(&self, other: &Self) {
- // page index could be different because of filtered out pages
- #[derive(Debug, PartialEq)]
- struct EntryMinimal {
- is_root: bool,
- title: &'static str,
- }
- pretty_assertions::assert_eq!(
- other
- .visible_navbar_entries()
- .map(|(_, entry)| EntryMinimal {
- is_root: entry.is_root,
- title: entry.title,
- })
- .collect::<Vec<_>>(),
- self.visible_navbar_entries()
- .map(|(_, entry)| EntryMinimal {
- is_root: entry.is_root,
- title: entry.title,
- })
- .collect::<Vec<_>>(),
- );
- assert_eq!(
- self.current_page().items.iter().collect::<Vec<_>>(),
- other
- .visible_page_items()
- .map(|(_, item)| item)
- .collect::<Vec<_>>()
- );
- }
- }
-
- impl SettingsPage {
- fn item(mut self, item: SettingsPageItem) -> Self {
- self.items.push(item);
- self
- }
}
impl PartialEq for NavBarEntry {
@@ -2485,21 +2420,6 @@ mod test {
}
}
- impl SettingsPageItem {
- fn basic_item(title: &'static str, description: &'static str) -> Self {
- SettingsPageItem::SettingItem(SettingItem {
- files: USER,
- title,
- description,
- field: Box::new(SettingField {
- pick: |settings_content| &settings_content.auto_update,
- pick_mut: |settings_content| &mut settings_content.auto_update,
- }),
- metadata: None,
- })
- }
- }
-
fn register_settings(cx: &mut App) {
settings::init(cx);
theme::init(theme::LoadThemes::JustBase, cx);
@@ -2575,7 +2495,8 @@ mod test {
navbar_entry: selected_idx.expect("Must have a selected navbar entry"),
navbar_entries: Vec::default(),
navbar_scroll_handle: UniformListScrollHandle::default(),
- search_matches: vec![],
+ filter_table: vec![],
+ has_query: false,
content_handles: vec![],
search_task: None,
page_scroll_handle: ScrollHandle::new(),
@@ -2596,7 +2517,7 @@ mod test {
search_index: None,
};
- settings_window.build_search_matches();
+ settings_window.build_filter_table();
settings_window.build_navbar(cx);
for expanded_page_index in expanded_pages {
for entry in &mut settings_window.navbar_entries {
@@ -2783,108 +2704,4 @@ mod test {
> Appearance & Behavior
"
);
-
- #[gpui::test]
- fn test_basic_search(cx: &mut gpui::TestAppContext) {
- let cx = cx.add_empty_window();
- let (actual, expected) = cx.update(|window, cx| {
- register_settings(cx);
-
- let expected = cx.new(|cx| {
- SettingsWindow::new_builder(window, cx)
- .add_page("General", |page| {
- page.item(SettingsPageItem::SectionHeader("General settings"))
- .item(SettingsPageItem::basic_item("test title", "General test"))
- })
- .build(cx)
- });
-
- let actual = cx.new(|cx| {
- SettingsWindow::new_builder(window, cx)
- .add_page("General", |page| {
- page.item(SettingsPageItem::SectionHeader("General settings"))
- .item(SettingsPageItem::basic_item("test title", "General test"))
- })
- .add_page("Theme", |page| {
- page.item(SettingsPageItem::SectionHeader("Theme settings"))
- })
- .build(cx)
- });
-
- actual.update(cx, |settings, cx| settings.search("gen", window, cx));
-
- (actual, expected)
- });
-
- cx.cx.run_until_parked();
-
- cx.update(|_window, cx| {
- let expected = expected.read(cx);
- let actual = actual.read(cx);
- expected.assert_search_results(&actual);
- })
- }
-
- #[gpui::test]
- fn test_search_render_page_with_filtered_out_navbar_entries(cx: &mut gpui::TestAppContext) {
- let cx = cx.add_empty_window();
- let (actual, expected) = cx.update(|window, cx| {
- register_settings(cx);
-
- let actual = cx.new(|cx| {
- SettingsWindow::new_builder(window, cx)
- .add_page("General", |page| {
- page.item(SettingsPageItem::SectionHeader("General settings"))
- .item(SettingsPageItem::basic_item(
- "Confirm Quit",
- "Whether to confirm before quitting Zed",
- ))
- .item(SettingsPageItem::basic_item(
- "Auto Update",
- "Automatically update Zed",
- ))
- })
- .add_page("AI", |page| {
- page.item(SettingsPageItem::basic_item(
- "Disable AI",
- "Whether to disable all AI features in Zed",
- ))
- })
- .add_page("Appearance & Behavior", |page| {
- page.item(SettingsPageItem::SectionHeader("Cursor")).item(
- SettingsPageItem::basic_item(
- "Cursor Shape",
- "Cursor shape for the editor",
- ),
- )
- })
- .build(cx)
- });
-
- let expected = cx.new(|cx| {
- SettingsWindow::new_builder(window, cx)
- .add_page("Appearance & Behavior", |page| {
- page.item(SettingsPageItem::SectionHeader("Cursor")).item(
- SettingsPageItem::basic_item(
- "Cursor Shape",
- "Cursor shape for the editor",
- ),
- )
- })
- .build(cx)
- });
-
- actual.update(cx, |settings, cx| settings.search("cursor", window, cx));
-
- (actual, expected)
- });
-
- cx.cx.run_until_parked();
-
- cx.update(|_window, cx| {
- let expected = expected.read(cx);
- let actual = actual.read(cx);
- expected.assert_search_results(&actual);
- })
- }
}