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

74 lines
2.3 KiB
Python

"""The contract every quad module implements.
Adding a module = drop a file in modules/ that defines a top-level `SPEC`
(a ModuleSpec), then append its import to registry.py. Nothing else needs to
change: enable/disable, feature toggles, the card chrome, and the expand/collapse
plumbing are all provided generically.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Callable, Optional
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk # noqa: E402
@dataclass
class Feature:
"""A per-quad on/off switch surfaced in the card's settings popover."""
id: str
label: str
default: bool = True
@dataclass
class ModuleInstance:
"""What a module's build() returns."""
compact: Gtk.Widget # shown in the 2x2 cell
expanded: Optional[Gtk.Widget] = None # shown when the quad is expanded
scroll_expanded: bool = True # wrap the expanded view in a scroller
on_show: Optional[Callable[[], None]] = None
on_hide: Optional[Callable[[], None]] = None
destroy: Optional[Callable[[], None]] = None
@dataclass
class ModuleSpec:
id: str
title: str
icon: str # nerd-font glyph
build: Callable[["ModuleContext"], ModuleInstance]
default_enabled: bool = True
features: list[Feature] = field(default_factory=list)
class ModuleContext:
"""Handed to a module's build(). Scopes settings to the module and exposes the
expand/collapse requests so a module can drive the layout without knowing it."""
def __init__(self, spec: ModuleSpec, settings, services,
request_expand: Callable[[str], None],
request_collapse: Callable[[], None]):
self.spec = spec
self.settings = settings
self.services = services
self._request_expand = request_expand
self._request_collapse = request_collapse
def expand(self) -> None:
self._request_expand(self.spec.id)
def collapse(self) -> None:
self._request_collapse()
# feature toggles, scoped to this module
def feature(self, feature_id: str, default: bool = True) -> bool:
return self.settings.feature(self.spec.id, feature_id, default)
def on_settings_changed(self, cb: Callable[[], None]) -> None:
self.settings.subscribe(cb)