From ccfd1aecc1a56e1d5782ff575c3a0e2e6f7e7d2c Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Sun, 12 Jul 2026 02:44:56 +0545 Subject: [PATCH] feat(cache): implement icon theme aware caching for notification icons and cleanup on theme change --- src/window/notification/unified_cache.py | 81 ++++++++++++++++++++++- src/window/panel/components/globalmenu.py | 2 +- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/window/notification/unified_cache.py b/src/window/notification/unified_cache.py index 96515af..dec5982 100644 --- a/src/window/notification/unified_cache.py +++ b/src/window/notification/unified_cache.py @@ -3,7 +3,7 @@ import time import uuid from collections import OrderedDict -from fabric.utils import GdkPixbuf, get_relative_path, logger, os +from fabric.utils import GdkPixbuf, get_relative_path, Gtk, logger, os import shared.data as data @@ -14,6 +14,28 @@ _MEMORY_PIXBUF_CACHE: OrderedDict[str, GdkPixbuf.Pixbuf] = OrderedDict() _MEMORY_CACHE_MAX = 64 +def _get_icon_theme_name(): + """Get the current GTK icon theme name for cache invalidation""" + try: + settings = Gtk.Settings.get_default() + if settings: + return getattr(settings, "gtk_icon_theme_name", "") or "" + except Exception: + pass + # Fallback: read from settings.ini + try: + settings_path = os.path.expanduser("~/.config/gtk-3.0/settings.ini") + if os.path.exists(settings_path): + with open(settings_path) as f: + for line in f: + line = line.strip() + if line.startswith("gtk-icon-theme-name="): + return line.split("=", 1)[1].strip() + except Exception: + pass + return "" + + def ensure_cache_dir(): """Ensure unified notification cache directory exists""" os.makedirs(UNIFIED_NOTIFICATION_CACHE_DIR, exist_ok=True) @@ -36,10 +58,14 @@ def get_unified_cache_key(source_data, size=None, app_name=None): if source_data.startswith("file://"): source_data = source_data[7:] - # Create hash from file path and size + # Create hash from file path, size, and icon theme hash_input = source_data if size: hash_input += f"_{size[0]}x{size[1]}" + # Include icon theme so cache invalidates on theme change + theme = _get_icon_theme_name() + if theme: + hash_input += f"|{theme}" return hashlib.md5(hash_input.encode()).hexdigest()[:8] else: @@ -194,7 +220,58 @@ def get_fallback_icon(size=(48, 48)): return None +# Theme tracking for cache invalidation +_CURRENT_THEME_FILE = os.path.join(data.CACHE_DIR, ".current_icon_theme") + + +def _get_stored_theme(): + """Get the previously stored icon theme name""" + try: + if os.path.exists(_CURRENT_THEME_FILE): + with open(_CURRENT_THEME_FILE) as f: + return f.read().strip() + except Exception: + pass + return "" + + +def _store_theme(name): + """Store the current icon theme name""" + try: + os.makedirs(os.path.dirname(_CURRENT_THEME_FILE), exist_ok=True) + with open(_CURRENT_THEME_FILE, "w") as f: + f.write(name) + except Exception: + pass + + +def cleanup_theme_changed_caches(): + """Clear notification pixbuf caches if icon theme changed since last run""" + current_theme = _get_icon_theme_name() + stored_theme = _get_stored_theme() + + if current_theme and stored_theme and current_theme != stored_theme: + logger.info( + f"[Cache] Icon theme changed: {stored_theme} -> {current_theme}, clearing notification caches" + ) + cleanup_cache() # clear all pixbuf caches + # Also clear the icon resolver cache since icon names may map differently + try: + from utils.icon_resolver import ICON_CACHE_FILE + + if os.path.exists(ICON_CACHE_FILE): + os.unlink(ICON_CACHE_FILE) + logger.info("[Cache] Cleared icon resolver cache") + except Exception: + pass + + # Store current theme for next run + if current_theme: + _store_theme(current_theme) + + # Initialize cache on module load ensure_cache_dir() cleanup_old_cache_files() +cleanup_theme_changed_caches() verify_cache_persistence() diff --git a/src/window/panel/components/globalmenu.py b/src/window/panel/components/globalmenu.py index 2467946..fe06993 100644 --- a/src/window/panel/components/globalmenu.py +++ b/src/window/panel/components/globalmenu.py @@ -7,7 +7,7 @@ from fabric.widgets.label import Label from shared.dialogs.about import AboutApp, get_about_window from shared.window.dropdown import ModusDropdown, dropdown_divider, dropdowns -from services.globalmenu import get_global_menu_service +from globalmenu.service import get_global_menu_service from utils.app_name_resolver import format_window from utils.roam import modus_service from utils.utils import setup_cursor_hover