Skip to content

Commit

Permalink
Filter directory when loading (fluxxcode#169)
Browse files Browse the repository at this point in the history
* Fix rustfmt errors

* Filter directory when loading

* Update changelog

* Cleanup text filter
  • Loading branch information
fluxxcode authored Oct 24, 2024
1 parent f74cdec commit c11ce40
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 115 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 25 additions & 36 deletions src/data/directory_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,61 +128,30 @@ impl DirectoryContent {
config: &FileDialogConfig,
path: &Path,
include_files: bool,
show_hidden: bool,
file_filter: Option<&FileFilter>,
) -> io::Result<Self> {
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<Item = &'s DirectoryEntry> + '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<Item = &'s mut DirectoryEntry> + '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) {
Expand All @@ -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<Vec<DirectoryEntry>> {
let paths = fs::read_dir(path)?;

Expand All @@ -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,
Expand Down
109 changes: 30 additions & 79 deletions src/file_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,7 @@ impl FileDialog {
)
.clicked()
{
self.refresh();
ui.close_menu();
}
});
Expand Down Expand Up @@ -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();
}
}

Expand Down Expand Up @@ -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
Expand All @@ -1924,11 +1924,7 @@ impl FileDialog {
// The primary selected item is used for item a.
let mut batch_select_item_b: Option<DirectoryEntry> = 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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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<Item = &DirectoryEntry> {
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.
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit c11ce40

Please sign in to comment.