From 0ae3256251604083d7913ce25676b53aa21fafd0 Mon Sep 17 00:00:00 2001 From: eythaann Date: Wed, 8 Jan 2025 03:17:23 -0500 Subject: [PATCH] enh(icons): avoid overriding of system icon pack --- .../modules/launcher/infra/index.tsx | 37 +++-- .../restoration_and_migrations/mod.rs | 2 +- src/background/seelen_weg/icon_extractor.rs | 29 ++-- src/background/state/application/icons.rs | 127 ++++++++++-------- src/background/state/application/mod.rs | 32 +++-- src/background/state/infrastructure.rs | 6 +- static/themes/default/theme.launcher.css | 8 ++ 7 files changed, 150 insertions(+), 91 deletions(-) diff --git a/src/apps/seelen_rofi/modules/launcher/infra/index.tsx b/src/apps/seelen_rofi/modules/launcher/infra/index.tsx index f9312703..79e934dd 100644 --- a/src/apps/seelen_rofi/modules/launcher/infra/index.tsx +++ b/src/apps/seelen_rofi/modules/launcher/infra/index.tsx @@ -1,9 +1,9 @@ -import { SeelenCommand } from '@seelen-ui/lib'; +import { IconPackManager, SeelenCommand } from '@seelen-ui/lib'; import { invoke } from '@tauri-apps/api/core'; import { getCurrentWindow } from '@tauri-apps/api/window'; -import { Checkbox, Tooltip } from 'antd'; +import { Checkbox, Spin, Tooltip } from 'antd'; import { motion } from 'framer-motion'; -import { KeyboardEventHandler, useRef, useState } from 'react'; +import { KeyboardEventHandler, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; @@ -16,6 +16,7 @@ import { Item } from './Item'; import { RunnerSelector } from './RunnerSelector'; export function Launcher() { + const [loading, setLoading] = useState(true); const [showHelp, setShowHelp] = useState(true); const [showHistory, setShowHistory] = useState(false); const [_command, _setCommand] = useState(''); @@ -40,6 +41,16 @@ export function Launcher() { } }); + useEffect(() => { + async function loadIcons() { + for (const app of apps) { + await IconPackManager.extractIcon(app); + } + } + // we load all icons one by one first to avoid load all icons at the same time and block the UI + loadIcons().finally(() => setLoading(false)); + }); + const command = _command.trim().toLowerCase(); const selectedRunner = runners[usingRunnerIdx]; const selectedHistory = selectedRunner ? history[selectedRunner.id] || [] : []; @@ -106,13 +117,19 @@ export function Launcher() {
- {apps.map((item) => ( -
diff --git a/src/background/restoration_and_migrations/mod.rs b/src/background/restoration_and_migrations/mod.rs index 56afbe12..00d861ff 100644 --- a/src/background/restoration_and_migrations/mod.rs +++ b/src/background/restoration_and_migrations/mod.rs @@ -50,7 +50,7 @@ impl RestorationAndMigration { create_if_needed("themes")?; create_if_needed("layouts")?; create_if_needed("placeholders")?; - create_if_needed("icons/system")?; + create_if_needed("icons")?; create_if_needed("wallpapers")?; create_if_needed("plugins")?; create_if_needed("widgets")?; diff --git a/src/background/seelen_weg/icon_extractor.rs b/src/background/seelen_weg/icon_extractor.rs index ff2367e2..417bf616 100644 --- a/src/background/seelen_weg/icon_extractor.rs +++ b/src/background/seelen_weg/icon_extractor.rs @@ -26,6 +26,7 @@ use crate::error_handler::Result; use crate::modules::start::application::START_MENU_MANAGER; use crate::modules::uwp::UwpManager; use crate::state::application::FULL_STATE; +use crate::trace_lock; use crate::utils::constants::SEELEN_COMMON; use crate::windows_api::WindowsApi; @@ -249,8 +250,9 @@ pub fn extract_and_save_icon_from_file>(path: T) -> Result>(path: T) -> Result>(path: T) -> Result>(app_umid: T) -> Result { let app_umid = app_umid.as_ref(); - let state = FULL_STATE.load(); - if let Some(icon) = state.get_icon_by_key(app_umid) { + let icon_manager_mutex = FULL_STATE.load().icon_packs().clone(); + if let Some(icon) = trace_lock!(icon_manager_mutex).get_icon_by_key(app_umid) { return Ok(icon); } @@ -312,8 +315,9 @@ pub fn extract_and_save_icon_umid>(app_umid: T) -> Result .ok_or("No shortcut found for umid")?; let extracted = extract_and_save_icon_from_file(&shortcut)?; let filename = PathBuf::from(extracted.file_name().unwrap()); - state.add_system_icon(app_umid, &filename); - state.write_system_icon_pack()?; + let mut icon_manager = trace_lock!(icon_manager_mutex); + icon_manager.add_system_icon(app_umid, &filename); + icon_manager.write_system_icon_pack()?; return Ok(extracted); } @@ -324,8 +328,9 @@ pub fn extract_and_save_icon_umid>(app_umid: T) -> Result .icons_path() .join("system") .join(&relative_path); - state.add_system_icon(app_umid, &relative_path); - state.write_system_icon_pack()?; std::fs::copy(app_icon, &image_path)?; + let mut icon_manager = trace_lock!(icon_manager_mutex); + icon_manager.add_system_icon(app_umid, &relative_path); + icon_manager.write_system_icon_pack()?; Ok(image_path) } diff --git a/src/background/state/application/icons.rs b/src/background/state/application/icons.rs index ef328c87..1be89d98 100644 --- a/src/background/state/application/icons.rs +++ b/src/background/state/application/icons.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, fs::OpenOptions, path::{Path, PathBuf}, }; @@ -11,13 +12,73 @@ use crate::{ error_handler::Result, seelen::get_app_handle, trace_lock, utils::constants::SEELEN_COMMON, }; -use super::FullState; +use super::{FullState, FULL_STATE}; + +#[derive(Debug, Clone, Default)] +pub struct IconPacksManager(HashMap); + +impl IconPacksManager { + pub fn list(&self) -> Vec<&IconPack> { + self.0.values().collect_vec() + } + + pub fn owned_list(&self) -> Vec { + self.0.values().cloned().collect_vec() + } + + pub fn add_system_icon(&mut self, key: &str, icon: &Path) { + let system_pack = self.0.get_mut("system").unwrap(); + let key = key.trim_start_matches(r"\\?\").to_string(); + system_pack.apps.insert(key, icon.to_owned()); + } + + /// Get icon pack by app user model id, filename or path + pub fn get_icon_by_key(&self, key: &str) -> Option { + let filename = PathBuf::from(key) + .file_name() + .map(|p| p.to_string_lossy().to_string()); + + let using = FULL_STATE.load().settings().icon_packs.clone(); + for icon_pack in using.into_iter().rev() { + if let Some(icon_pack) = self.0.get(&icon_pack) { + let maybe_icon = icon_pack.apps.get(key).or_else(|| match filename.as_ref() { + Some(filename) => icon_pack.apps.get(filename), + None => None, + }); + if let Some(icon) = maybe_icon { + let full_path = SEELEN_COMMON + .icons_path() + .join(&icon_pack.info.filename) + .join(icon); + if full_path.exists() { + return Some(full_path); + } + } + } + } + None + } + + pub fn write_system_icon_pack(&self) -> Result<()> { + let folder = SEELEN_COMMON.icons_path().join("system"); + std::fs::create_dir_all(&folder)?; + let file_path = folder.join("metadata.yml"); + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(&file_path)?; + let system_pack = self.0.get("system").unwrap(); + serde_yaml::to_writer(&mut file, system_pack)?; + Ok(()) + } +} impl FullState { pub fn emit_icon_packs(&self) -> Result<()> { get_app_handle().emit( SeelenEvent::StateIconPacksChanged, - trace_lock!(self.icon_packs()).values().collect_vec(), + trace_lock!(self.icon_packs()).list(), )?; Ok(()) } @@ -32,6 +93,8 @@ impl FullState { pub(super) fn load_icons_packs(&mut self) -> Result<()> { let entries = std::fs::read_dir(SEELEN_COMMON.icons_path())?; + let mut icon_packs_manager = trace_lock!(self.icon_packs); + for entry in entries.flatten() { let path = entry.path(); if path.is_dir() { @@ -39,7 +102,8 @@ impl FullState { match icon_pack { Ok(mut icon_pack) => { icon_pack.info.filename = entry.file_name().to_string_lossy().to_string(); - trace_lock!(self.icon_packs) + icon_packs_manager + .0 .insert(icon_pack.info.filename.clone(), icon_pack); } Err(err) => { @@ -50,65 +114,18 @@ impl FullState { } // add default icon pack if not exists - if trace_lock!(self.icon_packs).get("system").is_none() { + if icon_packs_manager.0.contains_key("system") { let mut icon_pack = IconPack::default(); icon_pack.info.display_name = "System".to_string(); icon_pack.info.author = "System".to_string(); icon_pack.info.description = "Icons from Windows and Program Files".to_string(); icon_pack.info.filename = "system".to_string(); - trace_lock!(self.icon_packs).insert(icon_pack.info.filename.clone(), icon_pack); - self.write_system_icon_pack()?; + icon_packs_manager + .0 + .insert(icon_pack.info.filename.clone(), icon_pack); + icon_packs_manager.write_system_icon_pack()?; } Ok(()) } - - pub fn write_system_icon_pack(&self) -> Result<()> { - let folder = SEELEN_COMMON.icons_path().join("system"); - let file_path = folder.join("metadata.yml"); - std::fs::create_dir_all(&folder)?; - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(&file_path)?; - let icons = trace_lock!(self.icon_packs); - let system_pack = icons.get("system").unwrap(); - self.skip_modification(file_path); - serde_yaml::to_writer(&mut file, system_pack)?; - Ok(()) - } - - pub fn add_system_icon(&self, key: &str, icon: &Path) { - let mut icons = trace_lock!(self.icon_packs); - let system_pack = icons.get_mut("system").unwrap(); - let key: String = key.trim_start_matches(r"\\?\").to_string(); - system_pack.apps.insert(key, icon.to_owned()); - } - - /// Get icon pack by app user model id, filename or path - pub fn get_icon_by_key(&self, key: &str) -> Option { - let filename = PathBuf::from(key) - .file_name() - .map(|p| p.to_string_lossy().to_string()); - - for icon_pack in self.settings.icon_packs.iter().rev() { - if let Some(icon_pack) = trace_lock!(self.icon_packs).get(icon_pack) { - let maybe_icon = icon_pack.apps.get(key).or_else(|| match filename.as_ref() { - Some(filename) => icon_pack.apps.get(filename), - None => None, - }); - if let Some(icon) = maybe_icon { - let full_path = SEELEN_COMMON - .icons_path() - .join(&icon_pack.info.filename) - .join(icon); - if full_path.exists() { - return Some(full_path); - } - } - } - } - None - } } diff --git a/src/background/state/application/mod.rs b/src/background/state/application/mod.rs index 3f1122de..98201bdb 100644 --- a/src/background/state/application/mod.rs +++ b/src/background/state/application/mod.rs @@ -9,6 +9,7 @@ mod widgets; use arc_swap::ArcSwap; use getset::Getters; +use icons::IconPacksManager; use itertools::Itertools; use lazy_static::lazy_static; use notify_debouncer_full::{ @@ -18,7 +19,7 @@ use notify_debouncer_full::{ }; use parking_lot::Mutex; use seelen_core::state::{ - IconPack, Plugin, PluginId, Profile, WegItems, Widget, WidgetId, WindowManagerLayout, + Plugin, PluginId, Profile, WegItems, Widget, WidgetId, WindowManagerLayout, }; use std::{ collections::{HashMap, HashSet, VecDeque}, @@ -54,7 +55,7 @@ pub struct FullState { pub settings: Settings, pub settings_by_app: VecDeque, pub themes: HashMap, - pub icon_packs: Arc>>, + pub icon_packs: Arc>, pub placeholders: HashMap, pub layouts: HashMap, pub weg_items: WegItems, @@ -75,7 +76,7 @@ impl FullState { settings: Settings::default(), settings_by_app: VecDeque::new(), themes: HashMap::new(), - icon_packs: Arc::new(Mutex::new(HashMap::new())), + icon_packs: Arc::new(Mutex::new(IconPacksManager::default())), placeholders: HashMap::new(), layouts: HashMap::new(), weg_items: WegItems::default(), @@ -117,12 +118,23 @@ impl FullState { } fn process_changes(&mut self, changed: &HashSet) -> Result<()> { - if changed - .iter() - .any(|p| p.starts_with(SEELEN_COMMON.icons_path()) && p.ends_with("metadata.yml")) - { - log::info!("Icons Packs changed"); - self.load_icons_packs()?; + let mut is_changing_icons_metadata = false; + let mut is_only_changing_system_icons = true; + + for path in changed.iter() { + if path.starts_with(SEELEN_COMMON.icons_path()) && path.ends_with("metadata.yml") { + is_changing_icons_metadata = true; + if !path.ends_with("system\\metadata.yml") { + is_only_changing_system_icons = false; + } + } + } + + if is_changing_icons_metadata { + if !is_only_changing_system_icons { + log::info!("Icons Packs changed"); + self.load_icons_packs()?; + } self.emit_icon_packs()?; } @@ -209,7 +221,7 @@ impl FullState { fn start_listeners(&mut self) -> Result<()> { log::trace!("Starting Seelen UI Files Watcher"); let mut debouncer = new_debouncer( - Duration::from_millis(500), + Duration::from_millis(100), None, |result: DebounceEventResult| match result { Ok(events) => { diff --git a/src/background/state/infrastructure.rs b/src/background/state/infrastructure.rs index 32a8f545..29355b98 100644 --- a/src/background/state/infrastructure.rs +++ b/src/background/state/infrastructure.rs @@ -14,9 +14,9 @@ use super::{ #[tauri::command(async)] pub fn state_get_icon_packs() -> Vec { - let icon_packs = FULL_STATE.load().icon_packs.clone(); - let icon_packs = trace_lock!(icon_packs); - icon_packs.values().cloned().collect_vec() + let mutex = FULL_STATE.load().icon_packs().clone(); + let icon_packs = trace_lock!(mutex); + icon_packs.owned_list() } #[tauri::command(async)] diff --git a/static/themes/default/theme.launcher.css b/static/themes/default/theme.launcher.css index 33a912d1..84ed8ca4 100644 --- a/static/themes/default/theme.launcher.css +++ b/static/themes/default/theme.launcher.css @@ -75,6 +75,14 @@ body { justify-content: flex-start; overflow: auto; height: 100%; + + .launcher-loading { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + } } .launcher-footer {