Use shared-mime-info for mime detection

This commit is contained in:
Jeremy Soller 2024-03-04 11:16:25 -07:00
parent ac4626d9cf
commit 672821a5fc
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
4 changed files with 169 additions and 805 deletions

889
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,7 @@ shlex = { version = "1.3" }
tokio = { version = "1" } tokio = { version = "1" }
trash = "3.2.0" trash = "3.2.0"
xdg = { version = "2.5.2", optional = true } xdg = { version = "2.5.2", optional = true }
xdg-mime = "0.3"
# Internationalization # Internationalization
i18n-embed = { version = "0.14", features = [ i18n-embed = { version = "0.14", features = [
"fluent-system", "fluent-system",
@ -45,10 +46,6 @@ features = ["multi-window", "tokio", "winit"]
version = "0.2.1" version = "0.2.1"
features = ["serde"] features = ["serde"]
#TODO: clean up and send changes upstream
[dependencies.systemicons]
git = "https://github.com/jackpot51/systemicons"
[features] [features]
default = ["desktop", "wgpu"] default = ["desktop", "wgpu"]
desktop = ["libcosmic/desktop", "dep:freedesktop_entry_parser", "dep:xdg"] desktop = ["libcosmic/desktop", "dep:freedesktop_entry_parser", "dep:xdg"]

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use cosmic::widget::icon; use cosmic::widget::icon;
use mime_guess::Mime;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::{collections::HashMap, path::Path, sync::Mutex}; use std::{collections::HashMap, path::Path, sync::Mutex};
@ -8,49 +9,62 @@ pub const FALLBACK_MIME_ICON: &str = "text-x-generic";
#[derive(Debug, Eq, Hash, PartialEq)] #[derive(Debug, Eq, Hash, PartialEq)]
struct MimeIconKey { struct MimeIconKey {
//TODO: this stores icon data for every path, instead store per mime type mime: Mime,
path: String,
size: u16, size: u16,
} }
struct MimeIconCache { struct MimeIconCache {
cache: HashMap<MimeIconKey, Option<icon::Handle>>, cache: HashMap<MimeIconKey, Option<icon::Handle>>,
shared_mime_info: xdg_mime::SharedMimeInfo,
} }
impl MimeIconCache { impl MimeIconCache {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
cache: HashMap::new(), cache: HashMap::new(),
shared_mime_info: xdg_mime::SharedMimeInfo::new(),
} }
} }
pub fn get(&mut self, key: MimeIconKey) -> Option<icon::Handle> { pub fn get(&mut self, key: MimeIconKey) -> Option<icon::Handle> {
self.cache self.cache
.entry(key) .entry(key)
.or_insert_with_key(|key| match systemicons::get_icon(&key.path, key.size) { .or_insert_with_key(|key| {
Ok(icon_kind) => match icon_kind { for icon_name in self.shared_mime_info.lookup_icon_names(&key.mime) {
systemicons::Icon::Png(bytes) => Some(icon::from_raster_bytes(bytes)), if let Some(path) = icon::from_name(icon_name)
systemicons::Icon::Svg(bytes) => Some(icon::from_svg_bytes(bytes)), .prefer_svg(true)
}, .size(key.size)
Err(err) => { .path()
log::warn!("failed to get icon for {:?}: {:?}", key, err); {
None return Some(icon::from_path(path));
}
} }
None
}) })
.clone() .clone()
} }
} }
static MIME_ICON_CACHE: Lazy<Mutex<MimeIconCache>> = Lazy::new(|| Mutex::new(MimeIconCache::new())); static MIME_ICON_CACHE: Lazy<Mutex<MimeIconCache>> = Lazy::new(|| Mutex::new(MimeIconCache::new()));
pub fn mime_icon<P: AsRef<Path>>(path: P, size: u16) -> icon::Handle { pub fn mime_for_path<P: AsRef<Path>>(path: P) -> Mime {
//TODO: smarter path handling let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
let path = path // Try the shared mime info cache first
.as_ref() let guess = mime_icon_cache
.to_str() .shared_mime_info
.expect("failed to convert path to UTF-8") .guess_mime_type()
.to_owned(); .path(&path)
.guess();
if guess.uncertain() {
// If uncertain, try mime_guess. This could happen on platforms without shared-mime-info
mime_guess::from_path(&path).first_or_octet_stream()
} else {
guess.mime_type().clone()
}
}
pub fn mime_icon(mime: Mime, size: u16) -> icon::Handle {
let mut mime_icon_cache = MIME_ICON_CACHE.lock().unwrap(); let mut mime_icon_cache = MIME_ICON_CACHE.lock().unwrap();
match mime_icon_cache.get(MimeIconKey { path, size }) { match mime_icon_cache.get(MimeIconKey { mime, size }) {
Some(handle) => handle, Some(handle) => handle,
None => icon::from_name(FALLBACK_MIME_ICON).size(size).handle(), None => icon::from_name(FALLBACK_MIME_ICON).size(size).handle(),
} }

View file

@ -20,7 +20,7 @@ use cosmic::{
}, },
theme, widget, Element, theme, widget, Element,
}; };
use mime_guess::{mime, Mime, MimeGuess}; use mime_guess::{mime, Mime};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::{ use std::{
cell::Cell, cell::Cell,
@ -39,7 +39,7 @@ use crate::{
key_bind::KeyBind, key_bind::KeyBind,
menu, menu,
mime_app::{mime_apps, MimeApp}, mime_app::{mime_apps, MimeApp},
mime_icon::mime_icon, mime_icon::{mime_for_path, mime_icon},
mouse_area, mouse_area,
}; };
@ -232,12 +232,12 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec<Item> {
folder_icon(&path, sizes.list_condensed()), folder_icon(&path, sizes.list_condensed()),
) )
} else { } else {
let mime = mime_for_path(&path);
( (
//TODO: best fallback mime for files? mime.clone(),
MimeGuess::from_path(&path).first_or_octet_stream(), mime_icon(mime.clone(), sizes.grid()),
mime_icon(&path, sizes.grid()), mime_icon(mime.clone(), sizes.list()),
mime_icon(&path, sizes.list()), mime_icon(mime, sizes.list_condensed()),
mime_icon(&path, sizes.list_condensed()),
) )
}; };
@ -349,13 +349,15 @@ pub fn scan_trash(sizes: IconSizes) -> Vec<Item> {
folder_icon(&path, sizes.list()), folder_icon(&path, sizes.list()),
folder_icon(&path, sizes.list_condensed()), folder_icon(&path, sizes.list_condensed()),
), ),
trash::TrashItemSize::Bytes(_) => ( trash::TrashItemSize::Bytes(_) => {
//TODO: best fallback mime for files? let mime = mime_for_path(&path);
MimeGuess::from_path(&path).first_or_octet_stream(), (
mime_icon(&path, sizes.grid()), mime.clone(),
mime_icon(&path, sizes.list()), mime_icon(mime.clone(), sizes.grid()),
mime_icon(&path, sizes.list_condensed()), mime_icon(mime.clone(), sizes.list()),
), mime_icon(mime, sizes.list_condensed()),
)
}
}; };
items.push(Item { items.push(Item {