use super::icon::Icon; use crate::config; use anyhow::Result; use gtk::prelude::*; use std::cell::RefCell; use std::collections::BTreeMap; use std::fs::File; use std::io::BufReader; use std::path::PathBuf; #[derive(Clone, Debug)] pub struct IconsContext { pub context: String, pub icons: Vec, pub is_system: bool, } pub struct IconsModel { pub system_icons: RefCell>, pub shipped_icons: RefCell>, } impl IconsModel { pub fn new() -> Self { let system_icons = RefCell::new(BTreeMap::new()); let shipped_icons = RefCell::new(BTreeMap::new()); let model = Self { system_icons, shipped_icons }; model.init(); model } fn get_icons(&self) -> Vec { // Fusion the system and shipped icons into one big thing let mut system_icons: Vec = self .system_icons .borrow() .clone() .into_iter() .map(|(_, icons_context)| icons_context.icons) .flatten() .collect(); let shipped_icons: Vec = self .shipped_icons .borrow() .clone() .into_iter() .map(|(_, icons_context)| icons_context.icons) .flatten() .collect(); system_icons.extend_from_slice(&shipped_icons); system_icons } pub fn get_icon_byname(&self, icon_name: &str) -> Option { let found_icons: Vec = self .get_icons() .into_iter() .filter(|icon| { // filter out unneeded icons icon.name == icon_name }) .collect(); Some(found_icons.get(0)?.clone()) } pub fn filter(&self, terms: Vec<&str>) -> Vec { self.get_icons() .into_iter() .filter(|icon| { // filter out unneeded icons terms.iter().any(|k| icon.should_display(k)) }) .collect() } fn init(&self) { if let Err(err) = self.init_shipped_icons() { error!("Failed to init the shipped icons?? {}", err); } if let Err(err) = self.init_system_icons() { error!("Failed to init system icons?? {}", err) } } fn init_shipped_icons(&self) -> Result<()> { let dev_kit_file: PathBuf = [config::PKGDATADIR, "icons_dev_kit.json"].iter().collect(); let file = File::open(dev_kit_file)?; let reader = BufReader::new(file); let dev_kit_icons: Vec = serde_json::from_reader(reader)?; dev_kit_icons.iter().for_each(|icon| { self.shipped_icons .borrow_mut() .entry(icon.context.clone()) .and_modify(|current_icons| { current_icons.icons.push(icon.clone()); }) .or_insert(IconsContext { icons: vec![icon.clone()], context: icon.context.clone(), is_system: icon.is_system, }); }); Ok(()) } fn init_system_icons(&self) -> Result<()> { // Load default theme icons let theme = gtk::IconTheme::get_default().unwrap(); let legacy_contexts = vec!["Legacy"]; let icons_blacklist = vec![ "gesture-rotate-clockwise-symbolic", "gesture-pinch-symbolic", "gesture-two-finger-swipe-left-symbolic", "gesture-two-finger-swipe-right-symbolic", "gesture-stretch-symbolic", "gesture-rotate-anticlockwise-symbolic", ]; for context in theme.list_contexts().iter() { let context_str = context.as_str(); if legacy_contexts.contains(&context_str) { continue; } let mut icons: Vec = theme .list_icons(Some(context_str)) .into_iter() .filter(|icon_name| { // filter out unneeded icons !icons_blacklist.contains(&icon_name.as_str()) && icon_name.ends_with("-symbolic") && !icon_name.contains('.') }) .map(|icon_name| Icon { name: icon_name.as_str().to_string(), is_system: true, tags: Vec::new(), context: context_str.to_string(), }) .collect(); icons.sort_by(|a, b| a.name.cmp(&b.name)); if icons.is_empty() { continue; } let context = context_str.to_string(); self.system_icons.borrow_mut().insert( context.clone(), IconsContext { icons, is_system: true, context, }, ); } Ok(()) } }