Dotfiles/desktopenvs/hyprdrive/horizon-dock/apps.py

78 lines
2.9 KiB
Python

"""All-apps data source (AstalApps, same library astal-menu's appdrawer uses) plus
favorite-app persistence — a plain JSON list of desktop-entry IDs under
~/.local/state/horizon-dock/favorites.json (see paths.py for why STATE_DIR, not
CONFIG_DIR)."""
from __future__ import annotations
import json
import gi
gi.require_version("AstalApps", "0.1")
from gi.repository import AstalApps # noqa: E402
from paths import FAVORITES_FILE, ensure_dirs
class AppSource:
def __init__(self) -> None:
self._apps = AstalApps.Apps()
self._favorites: list[str] = self._load_favorites()
self._wm_index: dict[str, str] = {}
# -- listing -------------------------------------------------------------
def all_apps(self) -> list:
"""Every installed app, alphabetical by name."""
apps = list(self._apps.get_list())
apps.sort(key=lambda a: (a.get_name() or "").lower())
return apps
def icon_for_window(self, window: dict) -> str:
"""Map an open window's wm class to its installed app's icon — same
wm-class/executable/name index astal-menu/ui/taskbar.py builds for its
own taskbar, rebuilt lazily here on first use."""
if not self._wm_index:
for app in self._apps.get_list():
for key in (app.get_wm_class(), app.get_executable(), app.get_name()):
if key:
self._wm_index.setdefault(key.lower(), app.get_icon_name())
cls = (window.get("class") or "").lower()
return self._wm_index.get(cls) or cls or "application-x-executable"
def favorite_apps(self) -> list:
"""Favorited apps, in the order they were pinned. Entries whose app has
since been uninstalled are silently dropped (not un-favorited — the
desktop file may just be temporarily missing, e.g. mid-update)."""
by_entry = {a.get_entry(): a for a in self._apps.get_list()}
return [by_entry[e] for e in self._favorites if e in by_entry]
@staticmethod
def launch(app) -> None:
app.launch()
# -- favorites persistence ------------------------------------------------
def is_favorite(self, app) -> bool:
return app.get_entry() in self._favorites
def toggle_favorite(self, app) -> None:
entry = app.get_entry()
if not entry:
return
if entry in self._favorites:
self._favorites.remove(entry)
else:
self._favorites.append(entry)
self._save_favorites()
def _load_favorites(self) -> list[str]:
try:
data = json.loads(FAVORITES_FILE.read_text())
return [e for e in data if isinstance(e, str)]
except (FileNotFoundError, json.JSONDecodeError):
return []
def _save_favorites(self) -> None:
ensure_dirs()
FAVORITES_FILE.write_text(json.dumps(self._favorites, indent=2) + "\n")