improv: lazy load app icons for mime apps

This commit is contained in:
Hojjat 2026-06-09 16:34:20 -06:00
parent c6c9eac97e
commit 4ac289f40a
3 changed files with 24 additions and 26 deletions

View file

@ -5992,7 +5992,7 @@ impl Application for App {
widget::mouse_area( widget::mouse_area(
widget::button::custom( widget::button::custom(
widget::row::with_children([ widget::row::with_children([
icon(app.icon.clone()).size(32).into(), icon(app.icon()).size(32).into(),
if app.is_default() && !displayed_default { if app.is_default() && !displayed_default {
displayed_default = true; displayed_default = true;
widget::text::body(fl!( widget::text::body(fl!(

View file

@ -182,7 +182,8 @@ pub struct MimeApp {
pub path: Option<PathBuf>, pub path: Option<PathBuf>,
pub name: String, pub name: String,
pub exec: Option<String>, pub exec: Option<String>,
pub icon: widget::icon::Handle, icon_name: Box<str>,
icon: std::sync::OnceLock<widget::icon::Handle>,
is_default: Arc<AtomicBool>, is_default: Arc<AtomicBool>,
pub no_display: bool, pub no_display: bool,
} }
@ -201,6 +202,19 @@ impl MimeApp {
pub fn is_default(&self) -> bool { pub fn is_default(&self) -> bool {
self.is_default.load(atomic::Ordering::SeqCst) self.is_default.load(atomic::Ordering::SeqCst)
} }
pub fn icon(&self) -> widget::icon::Handle {
self.icon
.get_or_init(|| {
let name = &*self.icon_name;
if name.starts_with('/') {
cosmic::widget::icon::from_path(PathBuf::from(name))
} else {
cosmic::widget::icon::from_name(name).size(32).handle()
}
})
.clone()
}
} }
// This allows usage of MimeApp in a dropdown // This allows usage of MimeApp in a dropdown
@ -213,7 +227,6 @@ impl AsRef<str> for MimeApp {
pub struct MimeAppCache { pub struct MimeAppCache {
apps: Vec<Arc<MimeApp>>, apps: Vec<Arc<MimeApp>>,
cache: FxHashMap<Mime, Vec<Arc<MimeApp>>>, cache: FxHashMap<Mime, Vec<Arc<MimeApp>>>,
icons: FxHashMap<Mime, Box<[widget::icon::Handle]>>,
terminals: Vec<Arc<MimeApp>>, terminals: Vec<Arc<MimeApp>>,
} }
@ -222,7 +235,6 @@ impl MimeAppCache {
let mut mime_app_cache = Self { let mut mime_app_cache = Self {
apps: Vec::new(), apps: Vec::new(),
cache: FxHashMap::default(), cache: FxHashMap::default(),
icons: FxHashMap::default(),
terminals: Vec::new(), terminals: Vec::new(),
}; };
mime_app_cache.reload(); mime_app_cache.reload();
@ -243,7 +255,6 @@ impl MimeAppCache {
self.apps.clear(); self.apps.clear();
self.cache.clear(); self.cache.clear();
self.icons.clear();
self.terminals.clear(); self.terminals.clear();
let mut list = cosmic_mime_apps::List::default(); let mut list = cosmic_mime_apps::List::default();
@ -263,14 +274,8 @@ impl MimeAppCache {
path: Some(desktop_entry.path.clone()), path: Some(desktop_entry.path.clone()),
name: name.into(), name: name.into(),
exec: desktop_entry.exec().map(String::from), exec: desktop_entry.exec().map(String::from),
icon: { icon_name: desktop_entry.icon().unwrap_or_default().into(),
let icon = desktop_entry.icon().unwrap_or_default(); icon: std::sync::OnceLock::new(),
if icon.starts_with('/') {
cosmic::widget::icon::from_path(PathBuf::from(icon))
} else {
cosmic::widget::icon::from_name(icon).size(32).handle()
}
},
is_default: Arc::new(AtomicBool::new(false)), is_default: Arc::new(AtomicBool::new(false)),
no_display: desktop_entry no_display: desktop_entry
.mime_type() .mime_type()
@ -359,15 +364,6 @@ impl MimeAppCache {
tracing::debug!(target: "mime-apps", mime = mime.essence_str(), apps = ?(cache.iter().map(|app| &*app.id).collect::<Vec<&str>>()), "mime defaults found") tracing::debug!(target: "mime-apps", mime = mime.essence_str(), apps = ?(cache.iter().map(|app| &*app.id).collect::<Vec<&str>>()), "mime defaults found")
} }
// Copy icons to special cache
//TODO: adjust dropdown API so this is no longer needed
self.icons.extend(self.cache.iter().map(|(mime, apps)| {
(
mime.clone(),
apps.iter().map(|app| app.icon.clone()).collect(),
)
}));
let elapsed = start.elapsed(); let elapsed = start.elapsed();
tracing::info!(target: "mime-apps", "loaded mime app cache in {elapsed:?}"); tracing::info!(target: "mime-apps", "loaded mime app cache in {elapsed:?}");
} }
@ -380,8 +376,10 @@ impl MimeAppCache {
self.cache.get(key).map_or(&[], Vec::as_slice) self.cache.get(key).map_or(&[], Vec::as_slice)
} }
pub fn icons(&self, key: &Mime) -> &[widget::icon::Handle] { pub fn icons(&self, key: &Mime) -> Vec<widget::icon::Handle> {
self.icons.get(key).map_or(&[], Box::as_ref) self.cache
.get(key)
.map_or_else(Vec::new, |apps| apps.iter().map(|app| app.icon()).collect())
} }
fn get_default_terminal(&self) -> Option<String> { fn get_default_terminal(&self) -> Option<String> {

View file

@ -2381,7 +2381,7 @@ impl Item {
mime_apps.iter().position(|x| x.is_default()), mime_apps.iter().position(|x| x.is_default()),
move |index| index, move |index| index,
) )
.icons(Cow::Borrowed(mime_app_cache.icons(&self.mime))), .icons(Cow::Owned(mime_app_cache.icons(&self.mime))),
) )
.map(|index| { .map(|index| {
let mime_app = &mime_apps[index]; let mime_app = &mime_apps[index];
@ -6465,7 +6465,7 @@ impl Tab {
mime_apps.iter().position(|x| x.is_default()), mime_apps.iter().position(|x| x.is_default()),
move |index| (index, mime_closure.clone()), move |index| (index, mime_closure.clone()),
) )
.icons(Cow::Borrowed(mime_app_cache.icons(&mime))), .icons(Cow::Owned(mime_app_cache.icons(&mime))),
) )
.map(|(index, mime)| { .map(|(index, mime)| {
let mime_app = &mime_apps[index]; let mime_app = &mime_apps[index];