feat(rofi): real icons, zebra rows, content-fit menus across all themes

Reworked the menu look, palette-driven so it lands on every theme:

  - Real Papirus icons in place of Nerd Font glyphs: a row() helper emits
    rofi's per-row icon protocol, and the five static menus (root, Tools,
    System, Power, Capture) map each entry to a freedesktop icon name
    (verified present in Papirus). Entry text is now clean ("Apps"), the
    case-matching is unchanged.
  - Larger icons via a new t.ui.iconSize knob (36px, generated theme); the
    summer whole-swaps keep their 40px identity.
  - Rounded, roomy rows with subtle zebra striping: alternate rows lift to
    @surface (a faint bg0 wash on the summer cream/slate windows), the
    selected row to @accent — using rofi's normal/alternate/selected row
    parities.
  - Content-fit windows: fixed-num-lines=false so a 6-entry menu stops
    reserving 8 rows of empty space (the launcher still fills + scrolls).
    Dropped the fixed height:50% from the summer .rasi so they size to
    content too — which also unhid their clipped Power/Keybindings rows.
  - Title-cased the menu prompts.

Verified by building the HM config and rendering the generated theme plus
both summer whole-swaps headlessly (sway + grim): icons resolve, zebra
shows, every menu hugs its rows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-06-17 22:11:25 +01:00
parent b0b2a4d732
commit 994025f0c8
5 changed files with 252 additions and 53 deletions

View File

@@ -41,9 +41,9 @@ let
nomarchy-menu = pkgs.writeShellScriptBin "nomarchy-menu" ''
# Nomarchy menu dispatcher thin presentation layer over
# `rofi -dmenu`; actions delegate to nomarchy-theme-sync, systemctl,
# hyprctl and friends. Icons are nf-md glyphs (Pango falls back to a
# Nerd Font for them). `nomarchy-menu` with no argument shows the
# module picker.
# hyprctl and friends. Menu entries carry real icons from the theme's
# icon set (Papirus) via rofi's per-row icon protocol see row() below.
# `nomarchy-menu` with no argument shows the module picker.
urlencode() {
local s="$1" out="" ch i
@@ -58,16 +58,22 @@ let
printf '%s' "$out"
}
# Emit one dmenu row with a themed icon: `row "Label" icon-name`. The NUL
# + 0x1f field separator is rofi's per-row icon protocol; printed straight
# to the pipe because bash can't store NUL in a variable. Rendered when
# rofi has show-icons (on globally; passed explicitly on the icon menus).
row() { printf '%s\0icon\x1f%s\n' "$1" "$2"; }
case "''${1:-}" in
power)
choice=$(printf '%s\n' \
"󰌾 Lock" \
"󰍃 Logout" \
"󰤄 Suspend" \
"󰒲 Hibernate" \
"󰜉 Reboot" \
"󰐥 Shutdown" \
| rofi -dmenu -p power) || exit 0
choice=$( {
row "Lock" system-lock-screen
row "Logout" system-log-out
row "Suspend" system-suspend
row "Hibernate" system-hibernate
row "Reboot" system-reboot
row "Shutdown" system-shutdown
} | rofi -dmenu -show-icons -p Power) || exit 0
case "$choice" in
*Lock) loginctl lock-session ;;
*Logout) hyprctl dispatch exit ;;
@@ -134,12 +140,12 @@ let
exec blueman-manager ;;
capture)
choice=$(printf '%s\n' \
"󰆟 Region clipboard" \
"󰆟 Region file" \
"󰍹 Full screen clipboard" \
"󰍹 Full screen file" \
| rofi -dmenu -p capture) || exit 0
choice=$( {
row "Region clipboard" applets-screenshooter
row "Region file" applets-screenshooter
row "Full screen clipboard" camera-photo
row "Full screen file" camera-photo
} | rofi -dmenu -show-icons -p Capture) || exit 0
dir="$HOME/Pictures/Screenshots"
file="$dir/$(date +%Y%m%d-%H%M%S).png"
case "$choice" in
@@ -183,16 +189,16 @@ let
tools)
# Tools submenu utilities you invoke. Each leaf is also reachable
# directly (SUPER+CTRL+<mnemonic>); this just keeps the root short.
choice=$(printf '%s\n' \
"󰃬 Calculator" \
"󰅌 Clipboard" \
"󰒨 Emoji" \
"󰈞 Files" \
"󰖟 Web search" \
"󰄄 Capture" \
"󰭹 Ask Claude" \
"󰁍 Back" \
| rofi -dmenu -p tools) || exit 0
choice=$( {
row "Calculator" accessories-calculator
row "Clipboard" edit-paste
row "Emoji" face-smile-big
row "Files" system-file-manager
row "Web search" system-search
row "Capture" applets-screenshooter
row "Ask Claude" internet-chat
row "Back" go-previous
} | rofi -dmenu -show-icons -p Tools) || exit 0
case "$choice" in
*Calc*) exec "$0" calc ;;
*Clipboard*) exec "$0" clipboard ;;
@@ -208,18 +214,17 @@ let
# System submenu connectivity and machine state. Snapshots and the
# power-profile picker self-gate (BTRFS+snapper / laptop on ppd), so
# the submenu shrinks to match the hardware, like the Waybar modules.
entries=(
"󰖩 Network"
"󰂯 Bluetooth"
"󰂛 Do Not Disturb"
)
command -v btrfs-assistant >/dev/null 2>&1 && entries+=("󰋚 Snapshots")
bats=(/sys/class/power_supply/BAT*)
if [ -e "''${bats[0]}" ] && command -v powerprofilesctl >/dev/null 2>&1; then
entries+=("󰓅 Power profile")
fi
entries+=("󰁍 Back")
choice=$(printf '%s\n' "''${entries[@]}" | rofi -dmenu -p system) || exit 0
choice=$( {
row "Network" network-wireless
row "Bluetooth" bluetooth
row "Do Not Disturb" notification-disabled
command -v btrfs-assistant >/dev/null 2>&1 && row "Snapshots" timeshift
if [ -e "''${bats[0]}" ] && command -v powerprofilesctl >/dev/null 2>&1; then
row "Power profile" preferences-system-power
fi
row "Back" go-previous
} | rofi -dmenu -show-icons -p System) || exit 0
case "$choice" in
*Network*) exec "$0" network ;;
*Bluetooth*) exec "$0" bluetooth ;;
@@ -233,14 +238,14 @@ let
# Root picker grouped into category submenus so the top level stays
# short and scannable. Apps/Theme/Power/Keybindings are frequent enough
# to keep at the top; everything else lives under Tools or System.
choice=$(printf '%s\n' \
"󰀻 Apps" \
"󰏘 Theme" \
"󰦬 Tools" \
"󰒓 System" \
"󰐥 Power" \
"󰋖 Keybindings" \
| rofi -dmenu -p menu) || exit 0
choice=$( {
row "Apps" applications-all
row "Theme" preferences-desktop-theme
row "Tools" applications-utilities
row "System" preferences-system
row "Power" system-shutdown
row "Keybindings" preferences-desktop-keyboard
} | rofi -dmenu -show-icons -p Menu) || exit 0
case "$choice" in
*Apps*) exec rofi -show drun ;;
*Theme*) exec "$0" theme ;;
@@ -283,6 +288,10 @@ in
drun-display-format = "{name}";
display-drun = "apps";
display-run = "run";
# Size the window to the actual number of rows instead of always
# reserving `lines` (8) — so a 6-entry menu doesn't leave empty space,
# while the app launcher still fills + scrolls past `lines`.
fixed-num-lines = false;
};
# Whole-swap themes bring their own rofi.rasi; otherwise the theme
@@ -335,22 +344,34 @@ in
columns = 1;
dynamic = true;
scrollbar = false;
spacing = px 2;
spacing = px 4; # a little air so the rounded zebra rows separate
padding = mkLiteral "8px 0px 0px 0px";
};
# Rounded, roomy rows with subtle zebra striping: alternate rows
# lift to @surface, the selected row to @accent. normal/alternate/
# selected are rofi's row parities (the `.normal` suffix is the
# match-state, untouched by our menus).
"element" = {
padding = mkLiteral "8px 12px";
padding = mkLiteral "10px 14px";
border-radius = px t.ui.rounding;
spacing = px 8;
spacing = px 12; # gap between the (larger) icon and the label
};
"element selected" = {
"element normal.normal" = {
background-color = mkLiteral "@bg";
text-color = mkLiteral "@fg";
};
"element alternate.normal" = {
background-color = mkLiteral "@surface";
text-color = mkLiteral "@fg";
};
"element selected.normal" = {
background-color = mkLiteral "@accent";
text-color = mkLiteral "@bg";
};
"element-icon" = {
background-color = mkLiteral "transparent";
size = mkLiteral "1em";
size = px t.ui.iconSize; # real icons (Papirus), not 1em font glyphs
};
"element-text" = {
background-color = mkLiteral "transparent";

View File

@@ -38,6 +38,7 @@ let
gapsOut = 12;
borderSize = 2;
rounding = 10;
iconSize = 36; # rofi launcher + menu icon size (px) for the generated theme
activeOpacity = 1.0;
inactiveOpacity = 0.95;
terminalOpacity = 0.96;