mirror of
https://github.com/danbulant/icon
synced 2026-05-19 04:08:36 +00:00
improved caching for icons
This commit is contained in:
parent
49f93a9c3e
commit
5d382f8110
2 changed files with 71 additions and 26 deletions
|
|
@ -1,18 +1,24 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::{
|
||||||
|
ffi::OsString,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct IconFile {
|
pub struct IconFile {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
pub name: OsString,
|
||||||
pub file_type: FileType,
|
pub file_type: FileType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IconFile {
|
impl IconFile {
|
||||||
pub fn from_path(path: &Path) -> Option<IconFile> {
|
pub fn from_path(path: &Path) -> Option<IconFile> {
|
||||||
let file_type = FileType::from_path_ext(path)?;
|
let file_type = FileType::from_path_ext(path)?;
|
||||||
|
let name = path.file_stem()?.to_os_string();
|
||||||
|
|
||||||
Some(IconFile {
|
Some(IconFile {
|
||||||
path: path.to_owned(),
|
path: path.to_owned(),
|
||||||
file_type,
|
file_type,
|
||||||
|
name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
89
src/theme.rs
89
src/theme.rs
|
|
@ -5,7 +5,7 @@ use freedesktop_entry_parser::low_level::{EntryIter, SectionBytes};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
/// Main struct to locate icon files.
|
/// Main struct to locate icon files.
|
||||||
///
|
///
|
||||||
|
|
@ -84,6 +84,16 @@ impl Icons {
|
||||||
pub fn find_standalone_icon(&self, icon_name: &str) -> Option<IconFile> {
|
pub fn find_standalone_icon(&self, icon_name: &str) -> Option<IconFile> {
|
||||||
self.standalone_icons.get(icon_name).cloned()
|
self.standalone_icons.get(icon_name).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset all caches in all loaded themes.
|
||||||
|
///
|
||||||
|
/// This is useful if you know that icons have changed on disk and want to force a reload,
|
||||||
|
/// or when benchmarking.
|
||||||
|
pub fn reset_cache(&self) {
|
||||||
|
for theme in self.themes.values() {
|
||||||
|
theme.reset_cache();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Icons {
|
impl Default for Icons {
|
||||||
|
|
@ -111,11 +121,12 @@ impl Theme {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_cache(&self) {
|
||||||
|
self.info.index.reset_cache();
|
||||||
|
}
|
||||||
|
|
||||||
// find an icon in this theme only, not checking parents.
|
// find an icon in this theme only, not checking parents.
|
||||||
fn find_icon_here(&self, icon_name: &str, size: u32, scale: u32) -> Option<IconFile> {
|
fn find_icon_here(&self, icon_name: &str, size: u32, scale: u32) -> Option<IconFile> {
|
||||||
const EXTENSIONS: [&str; 3] = ["png", "xmp", "svg"];
|
|
||||||
let file_names = EXTENSIONS.map(|ext| format!("{icon_name}.{ext}"));
|
|
||||||
|
|
||||||
let base_dirs = &self.info.base_dirs;
|
let base_dirs = &self.info.base_dirs;
|
||||||
|
|
||||||
let sub_dirs = &self.info.index.directories;
|
let sub_dirs = &self.info.index.directories;
|
||||||
|
|
@ -126,17 +137,9 @@ impl Theme {
|
||||||
|
|
||||||
for base_dir in base_dirs {
|
for base_dir in base_dirs {
|
||||||
for sub_dir in exact_sub_dirs.clone() {
|
for sub_dir in exact_sub_dirs.clone() {
|
||||||
for file_name in &file_names {
|
if let Some(file) = sub_dir.lookup_icon(base_dir, icon_name) {
|
||||||
let path = base_dir
|
// exact match!
|
||||||
.join(sub_dir.directory_name.as_str())
|
return Some(file);
|
||||||
.join(file_name);
|
|
||||||
|
|
||||||
if path.exists() {
|
|
||||||
if let Some(file) = IconFile::from_path(&path) {
|
|
||||||
// exact match!
|
|
||||||
return Some(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,16 +155,9 @@ impl Theme {
|
||||||
let distance = sub_dir.size_distance(size, scale);
|
let distance = sub_dir.size_distance(size, scale);
|
||||||
|
|
||||||
if distance < min_dist {
|
if distance < min_dist {
|
||||||
for file_name in &file_names {
|
if let Some(file) = sub_dir.lookup_icon(base_dir, icon_name) {
|
||||||
let path = base_dir
|
min_dist = distance;
|
||||||
.join(sub_dir.directory_name.as_str())
|
best_icon = Some(file);
|
||||||
.join(file_name);
|
|
||||||
if path.exists() {
|
|
||||||
if let Some(file) = IconFile::from_path(&path) {
|
|
||||||
min_dist = distance;
|
|
||||||
best_icon = Some(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -233,6 +229,12 @@ impl ThemeIndex {
|
||||||
Ok(index)
|
Ok(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_cache(&self) {
|
||||||
|
for dir in &self.directories {
|
||||||
|
dir.reset_cache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse(bytes: &[u8]) -> Result<Self, ThemeParseError> {
|
pub fn parse(bytes: &[u8]) -> Result<Self, ThemeParseError> {
|
||||||
let mut entry: EntryIter = freedesktop_entry_parser::low_level::parse_entry(bytes);
|
let mut entry: EntryIter = freedesktop_entry_parser::low_level::parse_entry(bytes);
|
||||||
|
|
||||||
|
|
@ -310,6 +312,9 @@ pub struct DirectoryIndex {
|
||||||
pub max_size: u32,
|
pub max_size: u32,
|
||||||
pub min_size: u32,
|
pub min_size: u32,
|
||||||
pub threshold: u32,
|
pub threshold: u32,
|
||||||
|
/// Used as cache during icon lookups
|
||||||
|
pub known_icons: RwLock<Vec<IconFile>>,
|
||||||
|
pub known_basedirs: RwLock<Vec<PathBuf>>,
|
||||||
// pub additional_values: HashMap<String, String>,
|
// pub additional_values: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,9 +358,43 @@ impl DirectoryIndex {
|
||||||
max_size,
|
max_size,
|
||||||
min_size,
|
min_size,
|
||||||
threshold,
|
threshold,
|
||||||
|
known_icons: Default::default(),
|
||||||
|
known_basedirs: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset_cache(&self) {
|
||||||
|
self.known_icons.write().unwrap().clear();
|
||||||
|
self.known_basedirs.write().unwrap().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_cache(&self, base_dir: &PathBuf) {
|
||||||
|
if self.known_basedirs.read().unwrap().contains(base_dir) {
|
||||||
|
return; // already loaded for this base dir
|
||||||
|
}
|
||||||
|
|
||||||
|
self.known_basedirs.write().unwrap().push(base_dir.clone());
|
||||||
|
|
||||||
|
let dir = base_dir.join(&self.directory_name);
|
||||||
|
if dir.is_dir() {
|
||||||
|
let icons = &mut *self.known_icons.write().unwrap();
|
||||||
|
for entry in dir.read_dir().unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
if entry.file_type().unwrap().is_file() {
|
||||||
|
if let Some(icon) = IconFile::from_path(&entry.path()) {
|
||||||
|
icons.push(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_icon(&self, base_dir: &PathBuf, icon_name: &str) -> Option<IconFile> {
|
||||||
|
self.load_cache(base_dir);
|
||||||
|
let icons = self.known_icons.read().unwrap();
|
||||||
|
icons.iter().find(|ico| ico.name == icon_name).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
fn size_distance(&self, icon_size: u32, icon_scale: u32) -> u32 {
|
fn size_distance(&self, icon_size: u32, icon_scale: u32) -> u32 {
|
||||||
let size = icon_size * icon_scale;
|
let size = icon_size * icon_scale;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue