From 994025f0c8b37272c719c1b29e05c512dd18cab1 Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Wed, 17 Jun 2026 22:11:25 +0100 Subject: [PATCH] feat(rofi): real icons, zebra rows, content-fit menus across all themes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- config.rasi | 163 ++++++++++++++++++++++++++++++++++ modules/home/rofi.nix | 123 ++++++++++++++----------- modules/home/theme.nix | 1 + themes/summer-day/rofi.rasi | 9 +- themes/summer-night/rofi.rasi | 9 +- 5 files changed, 252 insertions(+), 53 deletions(-) create mode 100644 config.rasi diff --git a/config.rasi b/config.rasi new file mode 100644 index 0000000..9db2cd0 --- /dev/null +++ b/config.rasi @@ -0,0 +1,163 @@ +/** + * Rofi Everforest Theme + * Based on wofi everforest config + */ + +* { + /* Everforest color definitions */ + bg-dim: #232a2e; + bg0: #2d353b; + bg1: #343f44; + bg2: #3d484d; + bg3: #475258; + bg4: #4f585e; + bg5: #56635f; + bg-visual: #543a48; + bg-red: #514045; + bg-green: #425047; + bg-blue: #3a515d; + bg-yellow: #4d4c43; + fg: #d3c6aa; + red: #e67e80; + orange: #e69875; + yellow: #dbbc7f; + green: #a7c080; + aqua: #83c092; + blue: #7fbbb3; + purple: #d699b6; + grey0: #7a8478; + grey1: #859289; + grey2: #9da9a0; + + /* Font settings */ + font: "Iosevka Nerd Font 17"; + + /* General styling */ + background-color: transparent; + text-color: @fg; +} + +configuration { + /* Basic settings */ + drun-display-format: "{name}"; + case-sensitive: false; + location: 0; + disable-history: false; + hide-scrollbar: true; + display-drun: "Search"; + + /* Image settings */ + drun-icon-theme: "Papirus-Dark"; + show-icons: true; + icon-size: 40; +} + +window { + /* Window styling based on wofi config */ + transparency: "real"; + background-color: @fg; + text-color: @bg0; + border-bottom: 5px; + border-color: #7d6a40; + border-radius: 15px; + width: 40%; + height: 50%; + location: center; + anchor: center; + x-offset: 0; + y-offset: 0; +} + +mainbox { + background-color: transparent; + children: [ inputbar, listview ]; + spacing: 0px; + padding: 0px; +} + +inputbar { + children: [ prompt, entry ]; + background-color: @green; + text-color: @bg0; + expand: false; + border-bottom: 5px; + border-color: #556a35; + border-radius: 10px; + margin: 15px; + padding: 10px; + spacing: 15px; +} + +prompt { + enabled: true; + background-color: inherit; + text-color: inherit; +} + +entry { + background-color: inherit; + text-color: inherit; + cursor: text; + placeholder: "Type to search..."; + placeholder-color: @bg0; +} + +listview { + background-color: transparent; + columns: 1; + lines: 8; + spacing: 0px; + cycle: true; + dynamic: true; + layout: vertical; + padding: 20px; +} + +element { + background-color: transparent; + text-color: @bg0; + orientation: horizontal; + border-radius: 10px; + padding: 10px; + spacing: 15px; +} + +element-icon { + background-color: transparent; + text-color: inherit; + size: 40px; + cursor: inherit; +} + +element-text { + background-color: transparent; + text-color: inherit; + highlight: bold; + cursor: inherit; + vertical-align: 0.5; + horizontal-align: 0.0; +} + +element selected { + background-color: @bg0; + text-color: @fg; + border-bottom: 5px; + border-color: #161a1d; + border-radius: 10px; +} + +element-text selected { + text-color: @fg; +} + +element-icon selected { + text-color: @fg; +} + +scrollbar { + width: 0px; + background-color: transparent; + handle-width: 0px; + handle-color: transparent; + border: 0px; +} diff --git a/modules/home/rofi.nix b/modules/home/rofi.nix index 86e39f8..af676cb 100644 --- a/modules/home/rofi.nix +++ b/modules/home/rofi.nix @@ -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+); 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"; diff --git a/modules/home/theme.nix b/modules/home/theme.nix index 8221f19..6ff3a33 100644 --- a/modules/home/theme.nix +++ b/modules/home/theme.nix @@ -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; diff --git a/themes/summer-day/rofi.rasi b/themes/summer-day/rofi.rasi index 5ae6c11..423062c 100644 --- a/themes/summer-day/rofi.rasi +++ b/themes/summer-day/rofi.rasi @@ -29,7 +29,8 @@ window { border-color: @yellow; border-radius: 15px; width: 40%; - height: 50%; + /* No fixed height — size to content (fixed-num-lines:false) so every + * entry shows without scrolling and there's no empty space below. */ location: center; anchor: center; x-offset: 0; @@ -90,6 +91,12 @@ element { spacing: 15px; } +/* Zebra striping: every other row lifts with a faint light wash over the + * dark window (bg0 at ~12% alpha). The selected row below still wins. */ +element alternate.normal { + background-color: #fdf6e31f; +} + element-icon { background-color: transparent; text-color: inherit; diff --git a/themes/summer-night/rofi.rasi b/themes/summer-night/rofi.rasi index a268ad7..d362df7 100644 --- a/themes/summer-night/rofi.rasi +++ b/themes/summer-night/rofi.rasi @@ -29,7 +29,8 @@ window { border-color: @yellow; border-radius: 15px; width: 40%; - height: 50%; + /* No fixed height — size to content (fixed-num-lines:false) so every + * entry shows without scrolling and there's no empty space below. */ location: center; anchor: center; x-offset: 0; @@ -90,6 +91,12 @@ element { spacing: 15px; } +/* Zebra striping: every other row lifts with a faint dark wash over the + * cream window (bg0 at ~12% alpha). The selected row below still wins. */ +element alternate.normal { + background-color: #2d353b1f; +} + element-icon { background-color: transparent; text-color: inherit;