Use shared-mime-info for mime detection
This commit is contained in:
parent
ac4626d9cf
commit
672821a5fc
4 changed files with 169 additions and 805 deletions
889
Cargo.lock
generated
889
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
src/tab.rs
30
src/tab.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue