78 lines
2.9 KiB
Python
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")
|