Skip to content

Commit

Permalink
enh(icons): avoid overriding of system icon pack
Browse files Browse the repository at this point in the history
  • Loading branch information
eythaann committed Jan 8, 2025
1 parent a9699c7 commit 0ae3256
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 91 deletions.
37 changes: 27 additions & 10 deletions src/apps/seelen_rofi/modules/launcher/infra/index.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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('');
Expand All @@ -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] || [] : [];
Expand Down Expand Up @@ -106,13 +117,19 @@ export function Launcher() {
</div>
<Tooltip open={showHelp} title="Tab / Shift + Tab" placement="left">
<div className="launcher-body">
{apps.map((item) => (
<Item
key={item.path}
item={item}
hidden={!item.path.toLowerCase().includes(command)}
/>
))}
{loading ? (
<div className="launcher-loading">
<Spin size="large" />
</div>
) : (
apps.map((item) => (
<Item
key={item.path}
item={item}
hidden={!item.path.toLowerCase().includes(command)}
/>
))
)}
</div>
</Tooltip>
<div className="launcher-footer">
Expand Down
2 changes: 1 addition & 1 deletion src/background/restoration_and_migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")?;
Expand Down
29 changes: 17 additions & 12 deletions src/background/seelen_weg/icon_extractor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -249,8 +250,9 @@ pub fn extract_and_save_icon_from_file<T: AsRef<Path>>(path: T) -> Result<PathBu

let key = path.to_string_lossy().to_string();

let state = FULL_STATE.load();
if let Some(icon) = state.get_icon_by_key(&key) {
let mutex = FULL_STATE.load().icon_packs().clone();
let mut icon_manager = trace_lock!(mutex);
if let Some(icon) = icon_manager.get_icon_by_key(&key) {
return Ok(icon);
}

Expand All @@ -273,22 +275,23 @@ pub fn extract_and_save_icon_from_file<T: AsRef<Path>>(path: T) -> Result<PathBu
// try get icons for URLs
if is_url_file {
let icon = get_icon_from_url_file(path)?;
state.add_system_icon(&key, &icon_filename);
state.write_system_icon_pack()?;
icon.save(&icon_path)?;
icon_manager.add_system_icon(&key, &icon_filename);
icon_manager.write_system_icon_pack()?;
return Ok(icon_path);
}

// try get the icon directly from the file
if let Ok(icon) = get_icon_from_file(path) {
state.add_system_icon(&key, &icon_filename);
state.write_system_icon_pack()?;
icon.save(&icon_path)?;
icon_manager.add_system_icon(&key, &icon_filename);
icon_manager.write_system_icon_pack()?;
return Ok(icon_path);
}

// if the lnk don't have an icon, try to extract it from the target
if is_lnk_file {
drop(icon_manager);
let (target, _) = WindowsApi::resolve_lnk_target(path)?;
return extract_and_save_icon_from_file(&target);
}
Expand All @@ -300,8 +303,8 @@ pub fn extract_and_save_icon_from_file<T: AsRef<Path>>(path: T) -> Result<PathBu
pub fn extract_and_save_icon_umid<T: AsRef<str>>(app_umid: T) -> Result<PathBuf> {
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);
}

Expand All @@ -312,8 +315,9 @@ pub fn extract_and_save_icon_umid<T: AsRef<str>>(app_umid: T) -> Result<PathBuf>
.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);
}

Expand All @@ -324,8 +328,9 @@ pub fn extract_and_save_icon_umid<T: AsRef<str>>(app_umid: T) -> Result<PathBuf>
.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)
}
127 changes: 72 additions & 55 deletions src/background/state/application/icons.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
collections::HashMap,
fs::OpenOptions,
path::{Path, PathBuf},
};
Expand All @@ -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<String, IconPack>);

impl IconPacksManager {
pub fn list(&self) -> Vec<&IconPack> {
self.0.values().collect_vec()
}

pub fn owned_list(&self) -> Vec<IconPack> {
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<PathBuf> {
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(())
}
Expand All @@ -32,14 +93,17 @@ 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() {
let icon_pack = Self::load_icon_pack_from_dir(&path);
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) => {
Expand All @@ -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<PathBuf> {
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
}
}
Loading

0 comments on commit 0ae3256

Please sign in to comment.