Switches sat with the default FILL vertical alignment, so in taller rows (e.g. the Bluetooth Power switch in the expanded card's header) they stretched to the row height and rendered as a squared/vertical blob. Give every Gtk.Switch valign=CENTER so it keeps its fixed pill height regardless of the row. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XUWCXM4KhjRkwheaA3X7bP |
||
|---|---|---|
| .. | ||
| backend | ||
| lib | ||
| modules | ||
| services | ||
| style | ||
| ui | ||
| .gitignore | ||
| README.md | ||
| appservices.py | ||
| main.py | ||
| module_base.py | ||
| paths.py | ||
| registry.py | ||
| settings.py | ||
| theme.py | ||
| window.py | ||
README.md
astal-menu
A touch-friendly GTK4 popup control centre for the hyprlua desktop, replacing
nwg-dock and nwg-drawer. Triggered from the EWW top bar (or Super+D).
Layout: a 2×2 quad grid of feature modules over a full-width application drawer. Any quad can expand to overlay the other three; the drawer can expand to the bottom of the screen. A single margined root box letterboxes the menu identically in every state.
Stack
- Frontend: Python + PyGObject + GTK4, as a
wlr-layer-shellsurface (gtk4-layer-shell). Chosen over Lua because lgi/Astal-Lua only support GTK3. - Location map: a static OSM image stitched from tiles (
backend/staticmap.py, Pillow). libshumate does not paint tiles in this environment (the officialshumate-demoshows the same blank map), though tile downloads work — hence the static fallback. - Services: Astal GObject libraries via introspection —
AstalNetwork,AstalBluetooth,AstalApps— plus our own IP-geolocation singleton. - Backends: Python/Bash scripts in
backend/(JSON on stdout), run async viaGio.Subprocessso nothing blocks the UI. - Theme:
style/_colors.css(@define-color, generated from~/Dotfiles/colors.confbyapply-theme.sh) +style/style.css.
Running
main.py is single-instance. The autostart launches a hidden resident daemon;
verbs are forwarded over D-Bus:
scripts/astal-menu-start.sh # resident daemon (hidden); sets LD_PRELOAD
scripts/menu-toggle.sh # --toggle
scripts/menu-toggle.sh appdrawer # open with the app drawer expanded
astal-menu-start.sh must LD_PRELOAD libgtk4-layer-shell (it loads after
libwayland under PyGObject otherwise).
Adding a module
- Create
modules/<name>.pyexposing a top-levelSPEC = ModuleSpec(...)whosebuild(ctx)returns aModuleInstance(compact=…, expanded=…, …).compactshows in the 2×2 cell; a distinctexpandedwidget enables the expand button (they must be separate instances — GTK widgets have one parent).- Declare per-feature toggles via
features=[Feature("id", "Label", default)]; read them withctx.feature("id"). A disabled quad never callsbuild(). - Shared state (network, bluetooth, location) is on
ctx.services.
- Append its
SPECtoALL_SPECSinregistry.py. Nothing else changes.
Files
main.py app + single-instance IPC window.py layer-shell + letterbox
registry.py module list (extension seam) settings.py JSON toggles/order
module_base.py ModuleSpec / ModuleInstance / ModuleContext
appservices.py shared Astal services + location
ui/ quadcard, quadgrid, appdrawer
modules/ location, weather, bluetooth, network
services/ location (geolocation singleton)
lib/ ansi (SGR→TextTag), proc (async subprocess)
backend/ geolocate.py, weather.sh, network.sh
style/ _colors.css, style.css