hyprshell_core_lib/
theme_icon_cache.rs

1// https://github.com/H3rmt/hyprshell/discussions/137#discussioncomment-12078216
2use std::collections::BTreeMap;
3use std::fs::read_dir;
4use std::path::{Path, PathBuf};
5use std::sync::{Mutex, MutexGuard, OnceLock};
6use tracing::debug;
7
8fn get_icon_map() -> &'static Mutex<BTreeMap<Box<str>, Box<Path>>> {
9    static MAP_LOCK: OnceLock<Mutex<BTreeMap<Box<str>, Box<Path>>>> = OnceLock::new();
10    MAP_LOCK.get_or_init(|| Mutex::new(BTreeMap::new()))
11}
12
13pub fn init_icon_map(icon_names: Vec<String>, search_path: Vec<PathBuf>) {
14    let mut map = get_icon_map().lock().expect("Failed to lock icon map");
15
16    debug!("found {} icons from theme", icon_names.len());
17    for icon in icon_names {
18        map.insert(
19            icon.into_boxed_str(),
20            Box::from(Path::new("")),
21        );
22    }
23
24    // gtk4 only reports 500 icons for candy-theme, scan through the filesystem
25    for path in search_path {
26        if path.exists() {
27            let paths = collect_files_recursive(&path);
28            debug!(
29                "found {} icons from filesystem in {path:?} paths",
30                paths.len()
31            );
32            for (icon, path) in paths {
33                map.insert(icon, path);
34            }
35        }
36    }
37}
38
39pub fn get_all_icons<'a>() -> MutexGuard<'a, BTreeMap<Box<str>, Box<Path>>> {
40    get_icon_map().lock().expect("Failed to lock icon map")
41}
42
43pub fn theme_has_icon_name(name: &str) -> bool {
44    let map = get_icon_map().lock().expect("Failed to lock icon map");
45    map.contains_key(&Box::from(name))
46}
47fn collect_files_recursive(dir: &Path) -> Vec<(Box<str>, Box<Path>)> {
48    let mut files = Vec::new();
49    let mut dirs_to_visit = vec![dir.to_path_buf()];
50
51    while let Some(current_dir) = dirs_to_visit.pop() {
52        if current_dir.is_dir() {
53            if let Ok(entries) = read_dir(&current_dir) {
54                for entry in entries.flatten() {
55                    let path = entry.path();
56                    if path.is_dir() {
57                        dirs_to_visit.push(path);
58                    } else {
59                        files.push((
60                            path.file_stem()
61                                .unwrap_or_default()
62                                .to_string_lossy()
63                                .into_owned()
64                                .into_boxed_str(),
65                            entry.path().into_boxed_path(),
66                        ));
67                    }
68                }
69            }
70        }
71    }
72
73    files
74}