Merge pull request #1650 from hojjatabdollahi/hojjat/fix-open-dialog-mime

Fix mimetype based filtering in dialog
This commit is contained in:
Levi Portenier 2026-03-06 16:43:41 -07:00 committed by GitHub
commit bfb2c6d5b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 4 deletions

View file

@ -22,6 +22,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
pub enum Message { pub enum Message {
DialogMessage(DialogMessage), DialogMessage(DialogMessage),
DialogOpen(DialogKind), DialogOpen(DialogKind),
DialogOpenImages,
DialogResult(DialogResult), DialogResult(DialogResult),
} }
@ -105,6 +106,31 @@ impl Application for App {
return Task::batch(tasks); return Task::batch(tasks);
} }
} }
Message::DialogOpenImages => {
if self.dialog_opt.is_none() {
let (mut dialog, task) = Dialog::new(
DialogSettings::new().kind(DialogKind::OpenFile),
Message::DialogMessage,
Message::DialogResult,
);
let mut tasks = vec![task];
tasks.push(dialog.set_filters(
vec![
DialogFilter {
label: "Images".into(),
patterns: vec![DialogFilterPattern::Mime("image/*".into())],
},
DialogFilter {
label: "Any file".into(),
patterns: vec![DialogFilterPattern::Glob("*".into())],
},
],
Some(0),
));
self.dialog_opt = Some(dialog);
return Task::batch(tasks);
}
}
Message::DialogResult(result) => { Message::DialogResult(result) => {
self.dialog_opt = None; self.dialog_opt = None;
self.result_opt = Some(result); self.result_opt = Some(result);
@ -130,6 +156,13 @@ impl Application for App {
} }
column = column.push(button); column = column.push(button);
} }
{
let mut button = widget::button::standard("Open Image");
if self.dialog_opt.is_none() {
button = button.on_press(Message::DialogOpenImages);
}
column = column.push(button);
}
{ {
let mut button = widget::button::standard("Open Multiple Files"); let mut button = widget::button::standard("Open Multiple Files");
if self.dialog_opt.is_none() { if self.dialog_opt.is_none() {

View file

@ -24,6 +24,7 @@ use cosmic::{
segmented_button, segmented_button,
}, },
}; };
use mime_guess::{Mime, mime};
use notify_debouncer_full::{ use notify_debouncer_full::{
DebouncedEvent, Debouncer, RecommendedCache, new_debouncer, DebouncedEvent, Debouncer, RecommendedCache, new_debouncer,
notify::{self, RecommendedWatcher}, notify::{self, RecommendedWatcher},
@ -1887,7 +1888,6 @@ impl Application for App {
if let Some(filter_i) = self.filter_selected if let Some(filter_i) = self.filter_selected
&& let Some(filter) = self.filters.get(filter_i) && let Some(filter) = self.filters.get(filter_i)
{ {
// Parse globs (Mime implements PartialEq with &str, so no need to parse)
let mut parsed_globs = Vec::new(); let mut parsed_globs = Vec::new();
let mut mimes = Vec::new(); let mut mimes = Vec::new();
for pattern in &filter.patterns { for pattern in &filter.patterns {
@ -1900,15 +1900,25 @@ impl Application for App {
} }
} }
} }
DialogFilterPattern::Mime(value) => mimes.push(value.as_str()), DialogFilterPattern::Mime(value) => match value.parse::<Mime>() {
Ok(parsed) => mimes.push(parsed),
Err(err) => {
log::warn!("failed to parse mime {value:?}: {err}");
}
},
} }
} }
items.retain(|item| { items.retain(|item| {
// Directories are always shown // Directories are always shown
item.metadata.is_dir() item.metadata.is_dir()
// Check for mime type match (first because it is faster) || mimes.iter().any(|filter_mime| {
|| mimes.iter().copied().any(|mime| mime == item.mime) if filter_mime.subtype() == mime::STAR {
filter_mime.type_() == item.mime.type_()
} else {
*filter_mime == item.mime
}
})
// Check for glob match (last because it is slower) // Check for glob match (last because it is slower)
|| parsed_globs.iter().any(|glob| glob.matches(&item.name)) || parsed_globs.iter().any(|glob| glob.matches(&item.name))
}); });