feat(mime_app): show related apps in open with dropdown in preview

This commit is contained in:
Michael Aaron Murphy 2026-06-11 18:45:49 +02:00
parent eda8291678
commit e8fdb28a24
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
3 changed files with 55 additions and 34 deletions

View file

@ -3180,7 +3180,7 @@ impl Application for App {
selected, selected,
.. ..
} => { } => {
let available_apps = self.mime_app_cache.get_apps_for_mime(&mime); let available_apps = self.mime_app_cache.get_apps_for_mime(&mime, true);
if let Some((app, _)) = available_apps.get(selected) { if let Some((app, _)) = available_apps.get(selected) {
if let Some(mut command) = if let Some(mut command) =
@ -5918,7 +5918,7 @@ impl Application for App {
}; };
let mut column = widget::list_column(); let mut column = widget::list_column();
let available_apps = self.mime_app_cache.get_apps_for_mime(mime); let available_apps = self.mime_app_cache.get_apps_for_mime(mime, true);
let item_height = 32.0; let item_height = 32.0;
let mut displayed_default = false; let mut displayed_default = false;
let mut last_kind = MimeAppMatch::Exact; let mut last_kind = MimeAppMatch::Exact;

View file

@ -252,7 +252,11 @@ impl MimeAppCache {
mime_app_cache mime_app_cache
} }
pub fn get_apps_for_mime(&self, mime_type: &Mime) -> Vec<(&Arc<MimeApp>, MimeAppMatch)> { pub fn get_apps_for_mime(
&self,
mime_type: &Mime,
include_other: bool,
) -> Vec<(&Arc<MimeApp>, MimeAppMatch)> {
let mut results = Vec::new(); let mut results = Vec::new();
let mut dedupe = FxHashSet::default(); let mut dedupe = FxHashSet::default();
@ -291,19 +295,21 @@ impl MimeAppCache {
} }
} }
results.extend({ if include_other {
let mut apps = self results.extend({
.apps() let mut apps = self
.iter() .apps()
.filter(|mime_app| !mime_app.no_display()) .iter()
.filter(|&mime_app| dedupe.insert(&mime_app.id)) .filter(|mime_app| !mime_app.no_display())
.map(|mime_app| (mime_app, MimeAppMatch::Other)) .filter(|&mime_app| dedupe.insert(&mime_app.id))
.collect::<Vec<_>>(); .map(|mime_app| (mime_app, MimeAppMatch::Other))
apps.sort_by(|(a, _), (b, _)| { .collect::<Vec<_>>();
crate::localize::LANGUAGE_SORTER.compare(&a.name, &b.name) apps.sort_by(|(a, _), (b, _)| {
crate::localize::LANGUAGE_SORTER.compare(&a.name, &b.name)
});
apps
}); });
apps }
});
results results
} }
@ -411,15 +417,28 @@ impl MimeAppCache {
// Sort cached apps for this mime by default precedence. // Sort cached apps for this mime by default precedence.
for default in defaults.into_iter().flatten() { for default in defaults.into_iter().flatten() {
let default = default.strip_suffix(".desktop").unwrap_or(default.as_ref()); let default = default.strip_suffix(".desktop").unwrap_or(default.as_ref());
let mut found_any = false;
apps.retain(|app| { apps.retain(|app| {
let found = app.id.as_str() == default; let found = app.id.as_str() == default;
if found { if found {
app.is_default.write().unwrap().insert(default.into()); app.is_default
.write()
.unwrap()
.insert(mime.essence_str().into());
cache.push(app.clone()); cache.push(app.clone());
found_any = true;
} }
!found !found
}); });
if !found_any && let Some(app) = self.apps.iter().find(|app| app.id == default) {
app.is_default
.write()
.unwrap()
.insert(mime.essence_str().into());
cache.push(app.clone());
}
} }
// Sort remaining apps by name // Sort remaining apps by name

View file

@ -2368,23 +2368,24 @@ impl Item {
))); )));
let mut settings = Vec::new(); let mut settings = Vec::new();
if let Some(mime_app_cache) = mime_app_cache_opt { if let Some(mime_app_cache) = mime_app_cache_opt {
let mime_apps = mime_app_cache.get(&self.mime); let mime_apps = mime_app_cache.get_apps_for_mime(&self.mime, false);
if !mime_apps.is_empty() { if !mime_apps.is_empty() {
let (names, icons) = mime_apps
.iter()
.map(|(app, _)| (Cow::Owned(app.name.clone()), app.icon()))
.collect::<(Vec<_>, Vec<_>)>();
settings.push( settings.push(
widget::settings::item::builder(fl!("open-with")).control( widget::settings::item::builder(fl!("open-with")).control(
Element::from( Element::from(
widget::dropdown( widget::dropdown(
mime_apps names,
.iter() mime_apps.iter().position(|(x, _)| x.is_default(&self.mime)),
.map(|app| Cow::Owned(app.name.clone()))
.collect::<Vec<Cow<'static, str>>>(),
mime_apps.iter().position(|x| x.is_default(&self.mime)),
move |index| index, move |index| index,
) )
.icons(Cow::Owned(mime_app_cache.icons(&self.mime))), .icons(Cow::Owned(icons)),
) )
.map(|index| { .map(move |index| {
let mime_app = &mime_apps[index]; let mime_app = &mime_apps[index].0;
Message::SetOpenWith(self.mime.clone(), mime_app.id.clone()) Message::SetOpenWith(self.mime.clone(), mime_app.id.clone())
}), }),
), ),
@ -6451,24 +6452,25 @@ impl Tab {
.and_then(|(mime, _)| mime.parse::<Mime>().ok()) .and_then(|(mime, _)| mime.parse::<Mime>().ok())
&& let Some(mime_app_cache) = mime_app_cache_opt && let Some(mime_app_cache) = mime_app_cache_opt
{ {
let mime_apps = mime_app_cache.get(&mime); let mime_apps = mime_app_cache.get_apps_for_mime(&mime, false);
if !mime_apps.is_empty() { if !mime_apps.is_empty() {
let mime_closure = mime.clone(); let mime_closure = mime.clone();
let (names, icons) = mime_apps
.iter()
.map(|(app, _)| (Cow::Owned(app.name.clone()), app.icon()))
.collect::<(Vec<_>, Vec<_>)>();
settings.push( settings.push(
widget::settings::item::builder(fl!("open-with")).control( widget::settings::item::builder(fl!("open-with")).control(
Element::from( Element::from(
widget::dropdown( widget::dropdown(
mime_apps names,
.iter() mime_apps.iter().position(|(x, _)| x.is_default(&mime)),
.map(|app| Cow::Owned(app.name.clone()))
.collect::<Vec<Cow<'static, str>>>(),
mime_apps.iter().position(|x| x.is_default(&mime)),
move |index| (index, mime_closure.clone()), move |index| (index, mime_closure.clone()),
) )
.icons(Cow::Owned(mime_app_cache.icons(&mime))), .icons(Cow::Owned(icons)),
) )
.map(|(index, mime)| { .map(move |(index, mime)| {
let mime_app = &mime_apps[index]; let mime_app = &mime_apps[index].0;
Message::SetOpenWith(mime, mime_app.id.clone()) Message::SetOpenWith(mime, mime_app.id.clone())
}), }),
), ),