Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter directory when loading #169

Merged
merged 4 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading