diff --git a/CHANGELOG.md b/CHANGELOG.md index 46329203..c6310d58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Use path edit as file to save [#160](https://github.com/fluxxcode/egui-file-dialog/pull/160) - Updated sysinfo to version `0.32` [#161](https://github.com/fluxxcode/egui-file-dialog/pull/161) - Made default egui fonts an optional feature `default_fonts` [#163](https://github.com/fluxxcode/egui-file-dialog/pull/163) (thanks [@StarStarJ](https://github.com/StarStarJ)!) +- Filter directory when loading to improve performance [#169](https://github.com/fluxxcode/egui-file-dialog/pull/169) ## 2024-10-01 - v0.7.0 - egui update and QoL changes ### 🚨 Breaking Changes diff --git a/src/data/directory_content.rs b/src/data/directory_content.rs index a9f615d2..65fd470a 100644 --- a/src/data/directory_content.rs +++ b/src/data/directory_content.rs @@ -128,61 +128,30 @@ impl DirectoryContent { config: &FileDialogConfig, path: &Path, include_files: bool, + show_hidden: bool, + file_filter: Option<&FileFilter>, ) -> io::Result { Ok(Self { - content: load_directory(config, path, include_files)?, + content: load_directory(config, path, include_files, show_hidden, file_filter)?, }) } - /// Checks if the given directory entry is visible with the applied filters. - fn is_entry_visible( - dir_entry: &DirectoryEntry, - show_hidden: bool, - search_value: &str, - file_filter: Option<&FileFilter>, - ) -> bool { - if !search_value.is_empty() - && !dir_entry - .file_name() - .to_lowercase() - .contains(&search_value.to_lowercase()) - { - return false; - } - - if !show_hidden && dir_entry.is_hidden() { - return false; - } - - if let Some(file_filter) = file_filter { - if dir_entry.is_file() && !(file_filter.filter)(dir_entry.as_path()) { - return false; - } - } - - true - } - pub fn filtered_iter<'s>( &'s self, - show_hidden: bool, search_value: &'s str, - file_filter: Option<&'s FileFilter>, ) -> impl Iterator + 's { self.content .iter() - .filter(move |p| Self::is_entry_visible(p, show_hidden, search_value, file_filter)) + .filter(|p| apply_search_value(p, search_value)) } pub fn filtered_iter_mut<'s>( &'s mut self, - show_hidden: bool, search_value: &'s str, - file_filter: Option<&'s FileFilter>, ) -> impl Iterator + 's { self.content .iter_mut() - .filter(move |p| Self::is_entry_visible(p, show_hidden, search_value, file_filter)) + .filter(|p| apply_search_value(p, search_value)) } pub fn reset_multi_selection(&mut self) { @@ -207,11 +176,21 @@ impl DirectoryContent { } } +fn apply_search_value(entry: &DirectoryEntry, value: &str) -> bool { + value.is_empty() + || entry + .file_name() + .to_lowercase() + .contains(&value.to_lowercase()) +} + /// Loads the contents of the given directory. fn load_directory( config: &FileDialogConfig, path: &Path, include_files: bool, + show_hidden: bool, + file_filter: Option<&FileFilter>, ) -> io::Result> { let paths = fs::read_dir(path)?; @@ -229,6 +208,16 @@ fn load_directory( continue; } + if !show_hidden && entry.is_hidden() { + continue; + } + + if let Some(file_filter) = file_filter { + if entry.is_file() && !(file_filter.filter)(entry.as_path()) { + continue; + } + } + result.push(entry); } Err(_) => continue, diff --git a/src/file_dialog.rs b/src/file_dialog.rs index 9c24911d..9604f002 100644 --- a/src/file_dialog.rs +++ b/src/file_dialog.rs @@ -1183,6 +1183,7 @@ impl FileDialog { ) .clicked() { + self.refresh(); ui.close_menu(); } }); @@ -1861,7 +1862,7 @@ impl FileDialog { if let Some(i) = select_filter { self.selected_file_filter = i; self.selected_item = None; - self.directory_content.reset_multi_selection(); + self.refresh(); } } @@ -1915,7 +1916,6 @@ impl FileDialog { .auto_shrink([false, false]) .show(ui, |ui| { let mut data = std::mem::take(&mut self.directory_content); - let file_filter = self.get_selected_file_filter().cloned(); // If the multi selection should be reset, excluding the currently // selected primary item @@ -1924,11 +1924,7 @@ impl FileDialog { // The primary selected item is used for item a. let mut batch_select_item_b: Option = None; - for item in data.filtered_iter_mut( - self.config.storage.show_hidden, - &self.search_value.clone(), - file_filter.as_ref(), - ) { + for item in data.filtered_iter_mut(&self.search_value.clone()) { let file_name = item.file_name(); let mut primary_selected = false; @@ -2025,11 +2021,7 @@ impl FileDialog { // Reset the multi selection except the currently selected primary item if reset_multi_selection { - for item in data.filtered_iter_mut( - self.config.storage.show_hidden, - &self.search_value.clone(), - file_filter.as_ref(), - ) { + for item in data.filtered_iter_mut(&self.search_value) { if let Some(selected_item) = &self.selected_item { if selected_item.path_eq(item) { continue; @@ -2071,18 +2063,10 @@ impl FileDialog { ) { // Get the position of item a and item b let pos_a = directory_content - .filtered_iter( - self.config.storage.show_hidden, - &self.search_value, - self.get_selected_file_filter(), - ) + .filtered_iter(&self.search_value) .position(|p| p.path_eq(item_a)); let pos_b = directory_content - .filtered_iter( - self.config.storage.show_hidden, - &self.search_value, - self.get_selected_file_filter(), - ) + .filtered_iter(&self.search_value) .position(|p| p.path_eq(item_b)); // If both items where found inside the directory entry, mark every item between @@ -2104,11 +2088,7 @@ impl FileDialog { } for item in directory_content - .filtered_iter_mut( - self.config.storage.show_hidden, - &self.search_value, - self.get_selected_file_filter(), - ) + .filtered_iter_mut(&self.search_value) .enumerate() .filter(|(i, _)| i > &min && i < &max) .map(|(_, p)| p) @@ -2287,11 +2267,7 @@ impl FileDialog { if FileDialogKeyBindings::any_pressed(ctx, &keybindings.select_all, true) && self.mode == DialogMode::SelectMultiple { - for item in self.directory_content.filtered_iter_mut( - self.config.storage.show_hidden, - &self.search_value, - self.get_selected_file_filter().cloned().as_ref(), - ) { + for item in self.directory_content.filtered_iter_mut(&self.search_value) { item.selected = true; } } @@ -2407,11 +2383,7 @@ impl FileDialog { /// Gets a filtered iterator of the directory content of this object. fn get_dir_content_filtered_iter(&self) -> impl Iterator { - self.directory_content.filtered_iter( - self.config.storage.show_hidden, - &self.search_value, - self.get_selected_file_filter(), - ) + self.directory_content.filtered_iter(&self.search_value) } /// Opens the dialog to create a new folder. @@ -2637,24 +2609,15 @@ impl FileDialog { let mut directory_content = std::mem::take(&mut self.directory_content); let search_value = std::mem::take(&mut self.search_value); - let file_filter = self.get_selected_file_filter().cloned(); let index = directory_content - .filtered_iter( - self.config.storage.show_hidden, - &search_value, - file_filter.as_ref(), - ) + .filtered_iter(&search_value) .position(|p| p.path_eq(item)); if let Some(index) = index { if index != 0 { if let Some(item) = directory_content - .filtered_iter_mut( - self.config.storage.show_hidden, - &search_value, - file_filter.as_ref(), - ) + .filtered_iter_mut(&search_value) .nth(index.saturating_sub(1)) { self.select_item(item); @@ -2681,23 +2644,14 @@ impl FileDialog { let mut directory_content = std::mem::take(&mut self.directory_content); let search_value = std::mem::take(&mut self.search_value); - let file_filter = self.get_selected_file_filter().cloned(); let index = directory_content - .filtered_iter( - self.config.storage.show_hidden, - &search_value, - file_filter.as_ref(), - ) + .filtered_iter(&search_value) .position(|p| p.path_eq(item)); if let Some(index) = index { if let Some(item) = directory_content - .filtered_iter_mut( - self.config.storage.show_hidden, - &search_value, - file_filter.as_ref(), - ) + .filtered_iter_mut(&search_value) .nth(index.saturating_add(1)) { self.select_item(item); @@ -2719,11 +2673,7 @@ impl FileDialog { let mut directory_content = std::mem::take(&mut self.directory_content); if let Some(item) = directory_content - .filtered_iter_mut( - self.config.storage.show_hidden, - &self.search_value.clone(), - self.get_selected_file_filter().cloned().as_ref(), - ) + .filtered_iter_mut(&self.search_value.clone()) .next() { self.select_item(item); @@ -2740,11 +2690,7 @@ impl FileDialog { let mut directory_content = std::mem::take(&mut self.directory_content); if let Some(item) = directory_content - .filtered_iter_mut( - self.config.storage.show_hidden, - &self.search_value.clone(), - self.get_selected_file_filter().cloned().as_ref(), - ) + .filtered_iter_mut(&self.search_value.clone()) .last() { self.select_item(item); @@ -2905,16 +2851,21 @@ impl FileDialog { fn load_directory_content(&mut self, path: &Path) -> io::Result<()> { self.directory_error = None; - self.directory_content = - match DirectoryContent::from_path(&self.config, path, self.show_files) { - Ok(content) => content, - Err(err) => { - self.directory_content.clear(); - self.selected_item = None; - self.directory_error = Some(err.to_string()); - return Err(err); - } - }; + self.directory_content = match DirectoryContent::from_path( + &self.config, + path, + self.show_files, + self.config.storage.show_hidden, + self.get_selected_file_filter(), + ) { + Ok(content) => content, + Err(err) => { + self.directory_content.clear(); + self.selected_item = None; + self.directory_error = Some(err.to_string()); + return Err(err); + } + }; self.create_directory_dialog.close(); self.scroll_to_selection = true;