feat: add a function to force svg lookup
This commit is contained in:
parent
51eebd05f1
commit
c737c3f6a4
2 changed files with 89 additions and 27 deletions
28
src/lib.rs
28
src/lib.rs
|
|
@ -88,6 +88,7 @@ pub fn list_themes() -> Vec<&'static str> {
|
||||||
pub struct LookupBuilder<'a> {
|
pub struct LookupBuilder<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
cache: bool,
|
cache: bool,
|
||||||
|
force_svg: bool,
|
||||||
scale: u16,
|
scale: u16,
|
||||||
size: u16,
|
size: u16,
|
||||||
theme: &'a str,
|
theme: &'a str,
|
||||||
|
|
@ -174,6 +175,24 @@ impl<'a> LookupBuilder<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// By default [`find`] will prioritize Png over Svg icon.
|
||||||
|
/// Use this if you need to prioritize Svg icons. This could be useful
|
||||||
|
/// if you need a modifiable icon, to match a user theme for instance.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```rust
|
||||||
|
/// # fn main() {
|
||||||
|
/// use freedesktop_icons::lookup;
|
||||||
|
///
|
||||||
|
/// let icon = lookup("firefox")
|
||||||
|
/// .force_svg()
|
||||||
|
/// .find();
|
||||||
|
/// # }
|
||||||
|
pub fn force_svg(mut self) -> Self {
|
||||||
|
self.force_svg = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute the current lookup
|
/// Execute the current lookup
|
||||||
/// if no icon is found in the current theme fallback to
|
/// if no icon is found in the current theme fallback to
|
||||||
/// `/usr/share/icons/hicolor` theme and then to `/usr/share/pixmaps`.
|
/// `/usr/share/icons/hicolor` theme and then to `/usr/share/pixmaps`.
|
||||||
|
|
@ -186,6 +205,7 @@ impl<'a> LookupBuilder<'a> {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
cache: false,
|
cache: false,
|
||||||
|
force_svg: false,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
size: 24,
|
size: 24,
|
||||||
theme: "hicolor",
|
theme: "hicolor",
|
||||||
|
|
@ -212,12 +232,12 @@ impl<'a> LookupBuilder<'a> {
|
||||||
// Then lookup in the given theme
|
// Then lookup in the given theme
|
||||||
THEMES.get(self.theme).and_then(|icon_theme| {
|
THEMES.get(self.theme).and_then(|icon_theme| {
|
||||||
let icon = icon_theme
|
let icon = icon_theme
|
||||||
.try_get_icon(self.name, self.size, self.scale)
|
.try_get_icon(self.name, self.size, self.scale, self.force_svg)
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
// Fallback to the parent themes recursively
|
// Fallback to the parent themes recursively
|
||||||
icon_theme.inherits().into_iter().find_map(|parent| {
|
icon_theme.inherits().into_iter().find_map(|parent| {
|
||||||
THEMES.get(parent).and_then(|parent| {
|
THEMES.get(parent).and_then(|parent| {
|
||||||
parent.try_get_icon(self.name, self.size, self.scale)
|
parent.try_get_icon(self.name, self.size, self.scale, self.force_svg)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -225,10 +245,10 @@ impl<'a> LookupBuilder<'a> {
|
||||||
THEMES
|
THEMES
|
||||||
.get("hicolor")
|
.get("hicolor")
|
||||||
// Fallback to 'hicolor'
|
// Fallback to 'hicolor'
|
||||||
.and_then(|hicolor| hicolor.try_get_icon(self.name, self.size, self.scale))
|
.and_then(|hicolor| hicolor.try_get_icon(self.name, self.size, self.scale, self.force_svg))
|
||||||
})
|
})
|
||||||
// Last chance, try to find the icon in "/usr/share/pixmaps"
|
// Last chance, try to find the icon in "/usr/share/pixmaps"
|
||||||
.or_else(|| try_build_icon_path(self.name, "/usr/share/pixmaps"));
|
.or_else(|| try_build_icon_path(self.name, "/usr/share/pixmaps", self.force_svg));
|
||||||
|
|
||||||
if self.cache {
|
if self.cache {
|
||||||
self.store(self.theme, icon)
|
self.store(self.theme, icon)
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,14 @@ pub struct Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
pub fn try_get_icon(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
pub fn try_get_icon(&self, name: &str, size: u16, scale: u16, force_svg: bool) -> Option<PathBuf> {
|
||||||
self.try_get_icon_exact_size(name, size, scale)
|
self.try_get_icon_exact_size(name, size, scale, force_svg)
|
||||||
.or_else(|| self.try_get_icon_closest_size(name, size, scale))
|
.or_else(|| self.try_get_icon_closest_size(name, size, scale, force_svg))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_get_icon_exact_size(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
fn try_get_icon_exact_size(&self, name: &str, size: u16, scale: u16, force_svg: bool) -> Option<PathBuf> {
|
||||||
self.match_size(size, scale)
|
self.match_size(size, scale)
|
||||||
.find_map(|path| try_build_icon_path(name, path))
|
.find_map(|path| try_build_icon_path(name, path, force_svg))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_size(&self, size: u16, scale: u16) -> impl Iterator<Item=PathBuf> + '_ {
|
fn match_size(&self, size: u16, scale: u16) -> impl Iterator<Item=PathBuf> + '_ {
|
||||||
|
|
@ -41,10 +41,10 @@ impl Theme {
|
||||||
.map(|dir| self.path().join(dir))
|
.map(|dir| self.path().join(dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_get_icon_closest_size(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
fn try_get_icon_closest_size(&self, name: &str, size: u16, scale: u16, force_svg: bool) -> Option<PathBuf> {
|
||||||
self.closest_match_size(size, scale)
|
self.closest_match_size(size, scale)
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|path| try_build_icon_path(name, path))
|
.find_map(|path| try_build_icon_path(name, path, force_svg))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn closest_match_size(&self, size: u16, scale: u16) -> Vec<PathBuf> {
|
fn closest_match_size(&self, size: u16, scale: u16) -> Vec<PathBuf> {
|
||||||
|
|
@ -61,25 +61,47 @@ impl Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn try_build_icon_path<P: AsRef<Path>>(name: &str, path: P) -> Option<PathBuf> {
|
pub(super) fn try_build_icon_path<P: AsRef<Path>>(name: &str, path: P, force_svg: bool) -> Option<PathBuf> {
|
||||||
|
if force_svg {
|
||||||
|
try_build_svg(name, path.as_ref())
|
||||||
|
} else {
|
||||||
|
try_build_png(name, path.as_ref())
|
||||||
|
.or(try_build_svg(name, path.as_ref()))
|
||||||
|
.or(try_build_xmp(name, path.as_ref()))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_build_svg<P: AsRef<Path>>(name: &str, path: P) -> Option<PathBuf> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let svg = path.join(format!("{name}.svg"));
|
||||||
|
if svg.exists() {
|
||||||
|
Some(svg)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_build_png<P: AsRef<Path>>(name: &str, path: P) -> Option<PathBuf> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let png = path.join(format!("{name}.png"));
|
let png = path.join(format!("{name}.png"));
|
||||||
if png.exists() {
|
if png.exists() {
|
||||||
return Some(png);
|
Some(png)
|
||||||
}
|
} else {
|
||||||
|
|
||||||
let svg = path.join(format!("{name}.svg"));
|
|
||||||
if svg.exists() {
|
|
||||||
return Some(svg);
|
|
||||||
}
|
|
||||||
let xmp = path.join(format!("{name}.xmp"));
|
|
||||||
|
|
||||||
if xmp.exists() {
|
|
||||||
return Some(xmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_build_xmp<P: AsRef<Path>>(name: &str, path: P) -> Option<PathBuf> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let xmp = path.join(format!("{name}.xmp"));
|
||||||
|
if xmp.exists() {
|
||||||
|
Some(xmp)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Iter through the base paths and get all theme directories
|
// Iter through the base paths and get all theme directories
|
||||||
pub(super) fn get_all_themes() -> Result<BTreeMap<String, Theme>> {
|
pub(super) fn get_all_themes() -> Result<BTreeMap<String, Theme>> {
|
||||||
|
|
@ -126,6 +148,8 @@ impl Debug for Theme {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use speculoos::prelude::*;
|
||||||
use crate::THEMES;
|
use crate::THEMES;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -133,7 +157,25 @@ mod test {
|
||||||
let theme = THEMES.get("Adwaita").unwrap();
|
let theme = THEMES.get("Adwaita").unwrap();
|
||||||
println!(
|
println!(
|
||||||
"{:?}",
|
"{:?}",
|
||||||
theme.try_get_icon_exact_size("edit-delete-symbolic", 24, 1)
|
theme.try_get_icon_exact_size("edit-delete-symbolic", 24, 1, false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_get_png_first() {
|
||||||
|
let theme = THEMES.get("hicolor").unwrap();
|
||||||
|
let icon = theme.try_get_icon_exact_size("blueman", 24, 1, true);
|
||||||
|
assert_that!(icon)
|
||||||
|
.is_some()
|
||||||
|
.is_equal_to(PathBuf::from("/usr/share/icons/hicolor/scalable/apps/blueman.svg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_get_svg_first() {
|
||||||
|
let theme = THEMES.get("hicolor").unwrap();
|
||||||
|
let icon = theme.try_get_icon_exact_size("blueman", 24, 1, false);
|
||||||
|
assert_that!(icon)
|
||||||
|
.is_some()
|
||||||
|
.is_equal_to(PathBuf::from("/usr/share/icons/hicolor/22x22/apps/blueman.png"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue