Dotfiles/desktopenvs/hyprlua/astal-menu/settings.py

84 lines
3.0 KiB
Python

"""Persisted user settings: which quads are enabled, per-module feature toggles,
and quad ordering. Single JSON file, read at startup, written on every change.
Modules never import this directly for their own feature flags; they receive a
scoped view via ctx.feature(...) so the persistence format stays centralised.
"""
from __future__ import annotations
import json
from typing import Callable
from paths import CONFIG_DIR, SETTINGS_FILE, ensure_dirs
class Settings:
def __init__(self) -> None:
self._data: dict = {"quads": {}, "features": {}, "order": None, "favorites": []}
self._listeners: list[Callable[[], None]] = []
self.load()
# -- persistence -------------------------------------------------------
def load(self) -> None:
path = SETTINGS_FILE
if not path.exists():
# one-time migration from the old CONFIG_DIR location (pre state-dir)
legacy = CONFIG_DIR / "settings.json"
if legacy.exists():
path = legacy
try:
self._data.update(json.loads(path.read_text()))
except (FileNotFoundError, json.JSONDecodeError):
pass
def save(self) -> None:
ensure_dirs()
SETTINGS_FILE.write_text(json.dumps(self._data, indent=2))
for cb in list(self._listeners):
cb()
def subscribe(self, cb: Callable[[], None]) -> None:
self._listeners.append(cb)
# -- quad enable/disable ----------------------------------------------
def quad_enabled(self, module_id: str, default: bool = True) -> bool:
return bool(self._data["quads"].get(module_id, default))
def set_quad_enabled(self, module_id: str, value: bool) -> None:
self._data["quads"][module_id] = bool(value)
self.save()
# -- per-module feature toggles ---------------------------------------
def feature(self, module_id: str, feature_id: str, default: bool = True) -> bool:
return bool(self._data["features"].get(module_id, {}).get(feature_id, default))
def set_feature(self, module_id: str, feature_id: str, value: bool) -> None:
self._data["features"].setdefault(module_id, {})[feature_id] = bool(value)
self.save()
# -- favorites ---------------------------------------------------------
def favorites(self) -> list[str]:
return list(self._data.get("favorites", []))
def is_favorite(self, entry: str) -> bool:
return entry in self._data.get("favorites", [])
def toggle_favorite(self, entry: str) -> None:
if not entry:
return
favs = self._data.setdefault("favorites", [])
if entry in favs:
favs.remove(entry)
else:
favs.append(entry)
self.save()
# -- ordering ----------------------------------------------------------
def order(self) -> list[str] | None:
return self._data.get("order")
def set_order(self, order: list[str]) -> None:
self._data["order"] = order
self.save()