From dc7a317cb8c8f2d4061183cf28670e9904db67a4 Mon Sep 17 00:00:00 2001 From: Muhammad Daffa Ma'arif Date: Sun, 25 Oct 2026 07:59:38 +0700 Subject: [PATCH 1/2] feat(rice-switch): redesign the menu as a frosted row list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework the rice-switch theme from the ground up while keeping the row list: neutral frosted glass with a hairline border, a header with a "Switch Desktop" title and a search pill, two-line rows (name, then compositor · shell), a leading dot column marking the current rice, bold instead of underlined search matches, and a centred footer that says switching logs out. No icons, no emoji. Visual only: selection, cancel and validation are unchanged. Fixes #80 --- bin/rice-switch | 13 ++++-- common/rofi/rice-switch.rasi | 80 +++++++++++++++++++++--------------- tests/rice-switch.sh | 6 ++- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/bin/rice-switch b/bin/rice-switch index f96f542..5b04cf3 100755 --- a/bin/rice-switch +++ b/bin/rice-switch @@ -93,6 +93,8 @@ gui_menu() { current="$(current_rice)" local rice_names=() plain_rows="" rofi_rows="" active="" + local gutter_on='● ' + local gutter_off='● ' for d in "$DOTFILES_DIR"/rices/*/; do [[ -d "$d" ]] || continue local rname title subtitle @@ -103,8 +105,13 @@ gui_menu() { [[ "$rname" == "$current" ]] && active="${#rice_names[@]}" rice_names+=("$rname") plain_rows+="$title${subtitle:+ — $subtitle}"$'\n' - rofi_rows+="$(markup_escape "$title")" - [[ -n "$subtitle" ]] && rofi_rows+=$'\n'"$(markup_escape "$subtitle")" + # A leading column holds a dot for the current rice. Both lines start + # with the same monospace glyph (invisible where unused), so the name + # and the subtitle stay aligned whatever the proportional font does. + local dot="$gutter_off" + [[ "$rname" == "$current" ]] && dot="$gutter_on" + rofi_rows+="$dot$(markup_escape "$title")" + [[ -n "$subtitle" ]] && rofi_rows+=$'\n'"$gutter_off$(markup_escape "$subtitle")" rofi_rows+=$'\x1f' done @@ -121,7 +128,7 @@ gui_menu() { # separated by \x1f instead of newlines local args=(-dmenu -i -no-custom -format i -markup-rows -sep $'\x1f' -eh 2 -l "${#rice_names[@]}" -p "󰍉" -theme "$theme" - -mesg "Current: $(markup_escape "$current") · ↵ switch and log out · esc cancel") + -mesg "Switching logs you out · ↵ switch · esc cancel") [[ -n "$active" ]] && args+=(-a "$active" -selected-row "$active") choice=$(printf '%s' "$rofi_rows" | rofi "${args[@]}") || return 0 elif command -v fuzzel >/dev/null 2>&1; then diff --git a/common/rofi/rice-switch.rasi b/common/rofi/rice-switch.rasi index eff7133..0f4f5bf 100644 --- a/common/rofi/rice-switch.rasi +++ b/common/rofi/rice-switch.rasi @@ -1,7 +1,6 @@ -/* Rice switcher (bin/rice-switch): frosted panel in the style of macOS Spotlight. - * Translucent enough for the compositor's blur, opaque enough to read without it. - * Only the accent comes from the palette (colors.rasi). */ -@import "colors.rasi" +/* Rice switcher (bin/rice-switch): a frosted row list in the style of macOS. + * Neutral glass on purpose; translucent enough for the compositor's blur and + * opaque enough to read without it. */ configuration { show-icons: false; @@ -11,15 +10,15 @@ configuration { } * { - font: "Inter 11"; + font: "Inter 10.5"; - glass: rgba(28, 28, 32, 70%); - stroke: rgba(255, 255, 255, 13%); + glass: rgba(30, 30, 34, 66%); + stroke: rgba(255, 255, 255, 14%); hairline: rgba(255, 255, 255, 7%); + field: rgba(255, 255, 255, 8%); text: rgba(255, 255, 255, 92%); - text-dim: rgba(235, 235, 245, 50%); + text-dim: rgba(235, 235, 245, 52%); hover: rgba(255, 255, 255, 10%); - accent: @selected-fg; background-color: transparent; text-color: @text; @@ -29,36 +28,54 @@ window { transparency: "real"; location: center; anchor: center; - width: 520px; - padding: 8px; + width: 540px; + padding: 20px 14px 14px 14px; border: 1px; border-color: @stroke; - border-radius: 18px; + border-radius: 24px; background-color: @glass; } mainbox { - spacing: 6px; - children: [ "inputbar", "listview", "message" ]; + spacing: 12px; + children: [ "header", "listview", "message" ]; +} + +header { + orientation: horizontal; + expand: false; + spacing: 16px; + padding: 0 6px; + children: [ "textbox-title", "inputbar" ]; +} + +textbox-title { + expand: true; + content: "Switch Desktop"; + font: "Inter Display Semi-Bold 16"; + vertical-align: 0.5; + horizontal-align: 0.0; + text-color: @text; } inputbar { - padding: 10px 12px 12px 12px; - spacing: 12px; - border: 0 0 1px 0; - border-color: @hairline; + expand: false; + width: 200px; + padding: 7px 12px; + spacing: 8px; + border-radius: 10px; + background-color: @field; children: [ "prompt", "entry" ]; } prompt { - font: "JetBrainsMono Nerd Font 15"; + font: "JetBrainsMono Nerd Font 11"; text-color: @text-dim; vertical-align: 0.5; } entry { - font: "Inter Display 15"; - placeholder: "Switch rice"; + placeholder: "Search"; placeholder-color: @text-dim; cursor: text; vertical-align: 0.5; @@ -71,12 +88,14 @@ listview { cycle: true; scrollbar: false; spacing: 2px; - padding: 2px 0; + padding: 8px 0 0 0; + border: 1px 0 0 0; + border-color: @hairline; } element { - padding: 8px 12px; - border-radius: 11px; + padding: 9px 14px; + border-radius: 12px; cursor: pointer; } @@ -88,22 +107,19 @@ element selected.active { element-text { text-color: inherit; vertical-align: 0.5; + highlight: bold; cursor: pointer; } -element normal.active, -element selected.active, -element alternate.active { - text-color: @accent; -} - message { - padding: 8px 12px 4px 12px; + padding: 10px 0 2px 0; border: 1px 0 0 0; border-color: @hairline; } +/* the footer; textbox-title above sets its own colour and alignment */ textbox { - font: "Inter 9.5"; + font: "Inter 9"; text-color: @text-dim; + horizontal-align: 0.5; } diff --git a/tests/rice-switch.sh b/tests/rice-switch.sh index c5f5282..d1620b5 100755 --- a/tests/rice-switch.sh +++ b/tests/rice-switch.sh @@ -5,8 +5,8 @@ # 3. A chosen row still reaches the switch. # 4. Empty or path-like rice names are rejected. # 5. Menu content: one two-line row per rice (DISPLAY_NAME, then COMPOSITOR · -# SHELL_NAME), the current rice marked active, an out-of-range index -# ignored, theme fallback. +# SHELL_NAME), a dot and the active mark on the current rice, an +# out-of-range index ignored, theme fallback. set -euo pipefail REPO="$(cd "$(dirname "$0")/.." && pwd)" @@ -83,6 +83,8 @@ rices="$(find "$REPO/rices" -mindepth 1 -maxdepth 1 -type d | wc -l)" [[ "$rows" -eq "$rices" ]] || fail "expected $rices rows, got $rows" grep -qF 'end4' "$TMP/rofi.stdin" || fail "end4 row has no plain title: $(cat "$TMP/rofi.stdin")" grep -qF '>Hyprland · end4-pC Material 3' "$TMP/rofi.stdin" || fail "end4 row has no subtitle" +[[ "$(grep -o 'alpha="80%">●' "$TMP/rofi.stdin" | wc -l)" == 1 ]] || fail "expected one visible dot, for the current rice" +grep -qF 'alpha="80%">● end4' "$TMP/rofi.stdin" || fail "the dot is not on the end4 row" end4_index="$(find "$REPO/rices" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | LC_ALL=C sort | grep -nx end4 | cut -d: -f1)" grep -A1 -x -- '-a' "$TMP/rofi.args" | tail -n1 | grep -qx "$((end4_index - 1))" || fail "current rice not marked active: $(cat "$TMP/rofi.args")" grep -A1 -x -- '-theme' "$TMP/rofi.args" | tail -n1 | grep -q 'rice-switch.rasi$' || fail "rice-switch.rasi not used" From 4e399f4730735a956c11d425eb3f914334feca61 Mon Sep 17 00:00:00 2001 From: Muhammad Daffa Ma'arif Date: Sun, 25 Oct 2026 08:02:26 +0700 Subject: [PATCH 2/2] style(rice-switch): use str for the title, compare the dot count as an integer --- bin/rice-switch | 1 + common/rofi/rice-switch.rasi | 2 +- tests/rice-switch.sh | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/rice-switch b/bin/rice-switch index 5b04cf3..88d72e5 100755 --- a/bin/rice-switch +++ b/bin/rice-switch @@ -94,6 +94,7 @@ gui_menu() { local rice_names=() plain_rows="" rofi_rows="" active="" local gutter_on='● ' + # alpha 1%: Pango's alpha range starts at 1, so 0% is not a valid value local gutter_off='● ' for d in "$DOTFILES_DIR"/rices/*/; do [[ -d "$d" ]] || continue diff --git a/common/rofi/rice-switch.rasi b/common/rofi/rice-switch.rasi index 0f4f5bf..f464175 100644 --- a/common/rofi/rice-switch.rasi +++ b/common/rofi/rice-switch.rasi @@ -51,7 +51,7 @@ header { textbox-title { expand: true; - content: "Switch Desktop"; + str: "Switch Desktop"; font: "Inter Display Semi-Bold 16"; vertical-align: 0.5; horizontal-align: 0.0; diff --git a/tests/rice-switch.sh b/tests/rice-switch.sh index d1620b5..cec025a 100755 --- a/tests/rice-switch.sh +++ b/tests/rice-switch.sh @@ -83,7 +83,7 @@ rices="$(find "$REPO/rices" -mindepth 1 -maxdepth 1 -type d | wc -l)" [[ "$rows" -eq "$rices" ]] || fail "expected $rices rows, got $rows" grep -qF 'end4' "$TMP/rofi.stdin" || fail "end4 row has no plain title: $(cat "$TMP/rofi.stdin")" grep -qF '>Hyprland · end4-pC Material 3' "$TMP/rofi.stdin" || fail "end4 row has no subtitle" -[[ "$(grep -o 'alpha="80%">●' "$TMP/rofi.stdin" | wc -l)" == 1 ]] || fail "expected one visible dot, for the current rice" +[[ "$(grep -o 'alpha="80%">●' "$TMP/rofi.stdin" | wc -l)" -eq 1 ]] || fail "expected one visible dot, for the current rice" grep -qF 'alpha="80%">● end4' "$TMP/rofi.stdin" || fail "the dot is not on the end4 row" end4_index="$(find "$REPO/rices" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | LC_ALL=C sort | grep -nx end4 | cut -d: -f1)" grep -A1 -x -- '-a' "$TMP/rofi.args" | tail -n1 | grep -qx "$((end4_index - 1))" || fail "current rice not marked active: $(cat "$TMP/rofi.args")"