Compare commits
2 Commits
84fec63204
...
ec7247bc77
| Author | SHA1 | Date | |
|---|---|---|---|
|
ec7247bc77
|
|||
|
e1d041f010
|
@ -145,6 +145,7 @@ in {
|
|||||||
wiremix # Audio control
|
wiremix # Audio control
|
||||||
swww
|
swww
|
||||||
waypaper
|
waypaper
|
||||||
|
wtype # For elephant
|
||||||
|
|
||||||
# GTK apps (look good and work well with xdg portals)
|
# GTK apps (look good and work well with xdg portals)
|
||||||
nautilus # Fallback file chooser used by xdg-desktop-portal-gnome
|
nautilus # Fallback file chooser used by xdg-desktop-portal-gnome
|
||||||
@ -208,6 +209,10 @@ in {
|
|||||||
default = ["desktopapplications"];
|
default = ["desktopapplications"];
|
||||||
};
|
};
|
||||||
empty = ["desktopapplications"];
|
empty = ["desktopapplications"];
|
||||||
|
|
||||||
|
selection_wrap = true;
|
||||||
|
hide_quick_activation = true;
|
||||||
|
actions_as_menu = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
themes."cattpuccin-mocha" = let
|
themes."cattpuccin-mocha" = let
|
||||||
@ -657,8 +662,109 @@ in {
|
|||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Move values to config option and set in home/christoph/niri.nix
|
# TODO: Move values to config option and set in home/christoph/niri.nix
|
||||||
binds = with config.lib.niri.actions; {
|
binds = with config.lib.niri.actions; let
|
||||||
|
sessionMenu = mylib.rofi.mkMenu {
|
||||||
|
prompt = "Session";
|
||||||
|
layers = [
|
||||||
|
{
|
||||||
|
" Poweroff" = "poweroff";
|
||||||
|
" Reboot" = "reboot";
|
||||||
|
" Lock" = "loginctl lock-session";
|
||||||
|
# " Reload Hyprpanel" = "systemctl --user restart hyprpanel.service";
|
||||||
|
# " Reload Hyprland" = "hyprctl reload";
|
||||||
|
# " Exit Hyprland" = "hyprctl dispatch exit";
|
||||||
|
" Exit Niri" = "niri msg action quit";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
prompts = ["Select Session Action"];
|
||||||
|
rofiCmd = "walker -d";
|
||||||
|
};
|
||||||
|
wallpaperMenu = mylib.rofi.mkMenu {
|
||||||
|
prompt = "Wallpaper";
|
||||||
|
layers = [
|
||||||
|
"eza -1 ${config.paths.nixflake}/wallpapers"
|
||||||
|
];
|
||||||
|
prompts = ["Select Wallpaper"];
|
||||||
|
# Use waypaper instead of swww directly, so the chosen wallpaper will be restored after reboot
|
||||||
|
command = "waypaper --wallpaper ${config.paths.nixflake}/wallpapers/$OPTION0";
|
||||||
|
rofiCmd = "walker -d";
|
||||||
|
};
|
||||||
|
# niriMenu = mylib.rofi.mkMenu {
|
||||||
|
# prompt = "Niri";
|
||||||
|
# layers = [
|
||||||
|
# {
|
||||||
|
# " Take Region Screenshot" = "niri msg action screenshot -p false";
|
||||||
|
# " Take Window Screenshot" = "niri msg action screenshot-window -p false -d true";
|
||||||
|
# " Take Full-Screen Screenshot" = "niri msg action screenshot-screen -p false -d true";
|
||||||
|
# }
|
||||||
|
# ];
|
||||||
|
# prompts = ["Execute Niri Action"];
|
||||||
|
# rofiCmd = "walker -d";
|
||||||
|
# };
|
||||||
|
globalMenu = mylib.rofi.mkMenu {
|
||||||
|
prompt = "Global";
|
||||||
|
layers = [
|
||||||
|
{
|
||||||
|
" Control Session" = "${sessionMenu}/bin/rofi-menu-Session";
|
||||||
|
" Change Wallpaper" = "${wallpaperMenu}/bin/rofi-menu-Wallpaper";
|
||||||
|
# " Niri Actions" = "${niriMenu}/bin/rofi-menu-Niri";
|
||||||
|
" View Keybindings" = "niri msg action show-hotkey-overlay";
|
||||||
|
# TODO: What else? SSH menu?
|
||||||
|
}
|
||||||
|
];
|
||||||
|
prompts = ["Select Action"];
|
||||||
|
rofiCmd = "walker -d";
|
||||||
|
};
|
||||||
|
# No lectures anymore :) - Kept as example
|
||||||
|
# lecturesMenu = mylib.rofi.mkMenu {
|
||||||
|
# prompt = "Lecture";
|
||||||
|
# layers = [
|
||||||
|
# "eza -1 -D ~/Notes/TU"
|
||||||
|
# "eza -1 ~/Notes/TU/$OPTION0/Lecture | grep '.pdf'"
|
||||||
|
# ];
|
||||||
|
# prompts = [
|
||||||
|
# "Select Lecture"
|
||||||
|
# "Select Deck"
|
||||||
|
# ];
|
||||||
|
# command = "xdg-open ~/Notes/TU/$OPTION0/Lecture/$OPTION1";
|
||||||
|
# rofiCmd = "walker -d";
|
||||||
|
# };
|
||||||
|
in {
|
||||||
|
# DMenu
|
||||||
|
"Mod+Shift+A" = {
|
||||||
|
action = spawn "walker" "-m" "providerlist";
|
||||||
|
hotkey-overlay = {title = "Toggle the launcher.";};
|
||||||
|
};
|
||||||
|
"Mod+A" = {
|
||||||
|
action = spawn "walker" "-m" "desktopapplications";
|
||||||
|
hotkey-overlay = {title = "Toggle the application launcher.";};
|
||||||
|
};
|
||||||
|
"Mod+C" = {
|
||||||
|
action = spawn "walker" "-m" "clipboard";
|
||||||
|
hotkey-overlay = {title = "Show clipboard history.";};
|
||||||
|
};
|
||||||
|
"Mod+Escape" = {
|
||||||
|
action = spawn "${sessionMenu}/bin/rofi-menu-Session";
|
||||||
|
hotkey-overlay = {title = "Toggle the session menu.";};
|
||||||
|
};
|
||||||
|
"Mod+W" = {
|
||||||
|
action = spawn "${wallpaperMenu}/bin/rofi-menu-Wallpaper";
|
||||||
|
hotkey-overlay = {title = "Open wallpaper menu.";};
|
||||||
|
};
|
||||||
|
"Mod+D" = {
|
||||||
|
action = spawn "${globalMenu}/bin/rofi-menu-Global";
|
||||||
|
hotkey-overlay = {title = "Open global menu.";};
|
||||||
|
};
|
||||||
|
|
||||||
# Applications
|
# Applications
|
||||||
|
"Mod+Ctrl+W" = {
|
||||||
|
action = spawn "waypaper";
|
||||||
|
hotkey-overlay = {title = "Open waypaper.";};
|
||||||
|
};
|
||||||
|
"Mod+Shift+W" = {
|
||||||
|
action = spawn "waypaper" "--random";
|
||||||
|
hotkey-overlay = {title = "Select random wallpaper.";};
|
||||||
|
};
|
||||||
"Mod+T" = {
|
"Mod+T" = {
|
||||||
action = spawn "kitty";
|
action = spawn "kitty";
|
||||||
hotkey-overlay = {title = "Spawn Kitty.";};
|
hotkey-overlay = {title = "Spawn Kitty.";};
|
||||||
@ -691,52 +797,24 @@ in {
|
|||||||
action = spawn "neovide" "${config.paths.dotfiles}/flake.nix";
|
action = spawn "neovide" "${config.paths.dotfiles}/flake.nix";
|
||||||
hotkey-overlay = {title = "Edit the NixFlake.";};
|
hotkey-overlay = {title = "Edit the NixFlake.";};
|
||||||
};
|
};
|
||||||
"Mod+W" = {
|
|
||||||
action = spawn "waypaper";
|
|
||||||
hotkey-overlay = {title = "Open wallpaper chooser.";};
|
|
||||||
};
|
|
||||||
"Mod+Shift+W" = {
|
|
||||||
action = spawn "waypaper" "--random";
|
|
||||||
hotkey-overlay = {title = "Select random wallpaper.";};
|
|
||||||
};
|
|
||||||
|
|
||||||
"Mod+A" = {
|
|
||||||
action = spawn "walker" "-m" "desktopapplications";
|
|
||||||
hotkey-overlay = {title = "Toggle the application launcher.";};
|
|
||||||
};
|
|
||||||
"Mod+Shift+A" = {
|
|
||||||
action = spawn "walker" "-m" "providerlist";
|
|
||||||
hotkey-overlay = {title = "Toggle the launcher.";};
|
|
||||||
};
|
|
||||||
"Mod+Escape" = let
|
|
||||||
powerMenu = mylib.rofi.mkSimpleMenu rec {
|
|
||||||
prompt = "Session";
|
|
||||||
attrs = {
|
|
||||||
" Poweroff" = "poweroff";
|
|
||||||
" Reboot" = "reboot";
|
|
||||||
" Lock" = "loginctl lock-session";
|
|
||||||
# " Reload Hyprpanel" = "systemctl --user restart hyprpanel.service";
|
|
||||||
# " Reload Hyprland" = "hyprctl reload";
|
|
||||||
# " Exit Hyprland" = "hyprctl dispatch exit";
|
|
||||||
};
|
|
||||||
command = "walker -d -p ${prompt}";
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
action = spawn "${powerMenu}/bin/rofi-menu-Session";
|
|
||||||
hotkey-overlay = {title = "Toggle the session menu.";};
|
|
||||||
};
|
|
||||||
"Mod+C" = {
|
|
||||||
action = spawn "walker" "-m" "clipboard";
|
|
||||||
hotkey-overlay = {title = "Show clipboard history.";};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
"Mod+S" = {
|
"Mod+S" = {
|
||||||
action.screenshot-window = {write-to-disk = true;};
|
action.screenshot-window = {
|
||||||
|
write-to-disk = true;
|
||||||
|
show-pointer = false;
|
||||||
|
};
|
||||||
hotkey-overlay = {title = "Take a screenshot of the current window.";};
|
hotkey-overlay = {title = "Take a screenshot of the current window.";};
|
||||||
};
|
};
|
||||||
|
"Mod+Ctrl+S" = {
|
||||||
|
action.screenshot-screen = {
|
||||||
|
write-to-disk = true;
|
||||||
|
show-pointer = false;
|
||||||
|
};
|
||||||
|
hotkey-overlay = {title = "Take a screenshot of the current screen.";};
|
||||||
|
};
|
||||||
"Mod+Shift+S" = {
|
"Mod+Shift+S" = {
|
||||||
action.screenshot = {show-pointer = true;};
|
action.screenshot = {show-pointer = false;};
|
||||||
hotkey-overlay = {title = "Take a screenshot of a region.";};
|
hotkey-overlay = {title = "Take a screenshot of a region.";};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
195
lib/rofi.nix
195
lib/rofi.nix
@ -3,79 +3,164 @@
|
|||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}: rec {
|
}: {
|
||||||
# Receives attrs like:
|
# Receives attrs like:
|
||||||
# {
|
# {
|
||||||
# "Poweroff" = "poweroff";
|
# "Poweroff" = "poweroff";
|
||||||
# "Reload Hyprland" = "hyprctl reload";
|
# "Reload Hyprland" = "hyprctl reload";
|
||||||
# }
|
# }
|
||||||
mkSimpleMenu = let
|
# mkSimpleMenu = let
|
||||||
# Makes a string like ''"Poweroff" "Reload Hyprland"''
|
# # Makes a string like ''"Poweroff" "Reload Hyprland"''
|
||||||
unpack-options = attrs: "\"${lib.concatStringsSep "\" \"" (builtins.attrNames attrs)}\"";
|
# unpack-options = attrs: "\"${lib.concatStringsSep "\" \"" (builtins.attrNames attrs)}\"";
|
||||||
|
#
|
||||||
|
# mkCase = option: action: "else if test \"${option}\" = $OPTION\n set ACTION \"${action}\"";
|
||||||
|
#
|
||||||
|
# cases = attrs:
|
||||||
|
# attrs
|
||||||
|
# |> builtins.mapAttrs mkCase
|
||||||
|
# |> builtins.attrValues
|
||||||
|
# |> builtins.concatStringsSep "\n";
|
||||||
|
# in
|
||||||
|
# {
|
||||||
|
# prompt,
|
||||||
|
# attrs,
|
||||||
|
# command ? ''rofi -dmenu -i'',
|
||||||
|
# }:
|
||||||
|
# pkgs.writeScriptBin "rofi-menu-${prompt}" ''
|
||||||
|
# #! ${pkgs.fish}/bin/fish
|
||||||
|
#
|
||||||
|
# # OPTIONS contains all possible values Rofi will display
|
||||||
|
# set OPTIONS ${unpack-options attrs}
|
||||||
|
#
|
||||||
|
# # We choose a single OPTION using Rofi
|
||||||
|
# set OPTION (echo -e (string join "\n" $OPTIONS) | ${command} -p "${prompt}")
|
||||||
|
#
|
||||||
|
# # Check if the chosen OPTION is a valid choice from OPTIONS
|
||||||
|
# if not contains $OPTION $OPTIONS
|
||||||
|
# exit
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # Set a command to execute based on the chosen OPTION
|
||||||
|
# if false
|
||||||
|
# exit # Easier to generate with this
|
||||||
|
# ${cases attrs}
|
||||||
|
# else
|
||||||
|
# exit
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # Execute the command
|
||||||
|
# eval $ACTION
|
||||||
|
# '';
|
||||||
|
|
||||||
mkCase = option: action: "else if test \"${option}\" = $OPTION\n set ACTION \"${action}\"";
|
# Rofi/Dmenu menu generator.
|
||||||
|
#
|
||||||
cases = attrs:
|
# Each element in `layers` is one of:
|
||||||
attrs
|
# - attrset { "Label" = "value"; } # static options: selected value → $OPTIONn
|
||||||
|> builtins.mapAttrs mkCase
|
# - string "shell-cmd" # dynamic options from command: selected text → $OPTIONn
|
||||||
|> builtins.attrValues
|
# # may reference $OPTION0, $OPTION1, ... from earlier layers
|
||||||
|> builtins.concatStringsSep "\n";
|
#
|
||||||
in
|
# The "command" is the last action, it can reference $OPTION0, $OPTION1, ...
|
||||||
{
|
# If no "command" is given and the last layer is a static attrset, its selected value is evaluated directly.
|
||||||
|
#
|
||||||
|
# The "prompts" list are optional per-layer prompt strings (falls back to "prompt" if not provided).
|
||||||
|
#
|
||||||
|
# vpn.fish equivalent:
|
||||||
|
# mkMenu {
|
||||||
|
# prompt = "vpn";
|
||||||
|
# layers = [
|
||||||
|
# "cat /etc/rofi-vpns"
|
||||||
|
# { "start" = "start"; "stop" = "stop"; "status" = "status"; }
|
||||||
|
# ];
|
||||||
|
# command = "systemctl $OPTION1 $OPTION0.service";
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# lectures.fish equivalent:
|
||||||
|
# mkMenu {
|
||||||
|
# prompt = "lecture";
|
||||||
|
# layers = [
|
||||||
|
# "eza -1 -D ~/Notes/TU"
|
||||||
|
# "eza -1 ~/Notes/TU/$OPTION0/Lecture | grep '.pdf'"
|
||||||
|
# ];
|
||||||
|
# command = "xdg-open ~/Notes/TU/$OPTION0/Lecture/$OPTION1";
|
||||||
|
# }
|
||||||
|
mkMenu = {
|
||||||
prompt,
|
prompt,
|
||||||
attrs,
|
layers,
|
||||||
command ? ''rofi -dmenu -p " ${prompt} " -i'',
|
prompts ? [],
|
||||||
}:
|
command ? null,
|
||||||
pkgs.writeScriptBin "rofi-menu-${prompt}" ''
|
rofiCmd ? "rofi -dmenu -i",
|
||||||
#! ${pkgs.fish}/bin/fish
|
}: let
|
||||||
|
isStaticLayer = layer: builtins.isAttrs layer && !(layer ? options);
|
||||||
|
isDynamicLayer = layer: builtins.isString layer;
|
||||||
|
|
||||||
# OPTIONS contains all possible values Rofi will display
|
escStr = s: builtins.replaceStrings [''"'' "\\"] [''\"'' "\\\\"] s;
|
||||||
set OPTIONS ${unpack-options attrs}
|
|
||||||
|
|
||||||
# We choose a single OPTION using Rofi
|
layerPrompt = i:
|
||||||
set OPTION (echo -e (string join "\n" $OPTIONS) | ${command})
|
if i < builtins.length prompts
|
||||||
|
then lib.elemAt prompts i
|
||||||
|
else prompt;
|
||||||
|
|
||||||
# Check if the chosen OPTION is a valid choice from OPTIONS
|
# Static layer: attrset of label -> value
|
||||||
if not contains $OPTION $OPTIONS
|
# Displays labels in rofi; maps selected label to its value -> $OPTIONi
|
||||||
|
mkStaticLayer = i: attrs: let
|
||||||
|
lp = layerPrompt i;
|
||||||
|
labels = builtins.attrNames attrs;
|
||||||
|
optsStr = "\"${lib.concatStringsSep "\" \"" (map escStr labels)}\"";
|
||||||
|
mkCase = label: value: "else if test \"${escStr label}\" = $_LABEL${toString i}\n set OPTION${toString i} \"${escStr value}\"";
|
||||||
|
casesStr =
|
||||||
|
builtins.concatStringsSep "\n"
|
||||||
|
(builtins.attrValues (builtins.mapAttrs mkCase attrs));
|
||||||
|
in {
|
||||||
|
script = ''
|
||||||
|
set _OPTS${toString i} ${optsStr}
|
||||||
|
set _LABEL${toString i} (echo -e (string join "\n" $_OPTS${toString i}) | ${rofiCmd} -p "${lp}")
|
||||||
|
if not contains $_LABEL${toString i} $_OPTS${toString i}
|
||||||
exit
|
exit
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set a command to execute based on the chosen OPTION
|
|
||||||
if false
|
if false
|
||||||
exit # Easier to generate with this
|
exit
|
||||||
${cases attrs}
|
${casesStr}
|
||||||
else
|
else
|
||||||
exit
|
exit
|
||||||
end
|
end
|
||||||
|
|
||||||
# Execute the command
|
|
||||||
eval $ACTION
|
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# TODO: I want to generate the containers menu using the actionsA and actionsB attrs:
|
# Dynamic layer: shell command string whose output is piped to rofi
|
||||||
# - actionsA will be generated from the stuff in oci-containers.containers
|
# Selected text -> $OPTIONi; may reference earlier $OPTIONn variables
|
||||||
# - actionsB will be set statically for start, stop, status
|
mkDynamicLayer = i: cmd: let
|
||||||
|
lp = layerPrompt i;
|
||||||
|
in {
|
||||||
|
script = ''
|
||||||
|
set OPTION${toString i} (${cmd} | ${rofiCmd} -p "${lp}")
|
||||||
|
if test -z $OPTION${toString i}
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Receives attrs like:
|
mkLayer = i: layer:
|
||||||
# {
|
if isStaticLayer layer
|
||||||
# optionA = "exa -1 -D ~/Notes/TU";
|
then mkStaticLayer i layer
|
||||||
# optionB = "exa -1 -D ~/Notes/TU/$OPTIONA/Lecture | grep \".pdf\"";
|
else if isDynamicLayer layer
|
||||||
# commandB = "xdg-open ~/Notes/TU/$OPTIONA/Lecture/$OPTIONB";
|
then mkDynamicLayer i layer
|
||||||
# }
|
else throw "mkMenu: layer ${toString i} has invalid type";
|
||||||
#
|
|
||||||
# Keys:
|
layerResults = lib.imap0 mkLayer layers; # Map with 0-based index
|
||||||
# - optionA, optionB # Command that generates Rofi options:
|
layerScripts = map (r: r.script) layerResults;
|
||||||
# exa -1 -D ~/Notes/TU
|
lastLayer = lib.last layers;
|
||||||
# cat /etc/rofi-vpns
|
|
||||||
# - commandA, commandB # Action to execute after sth. was chosen (mutually excl. with command)
|
finalCmd =
|
||||||
# - actionsA, actionsB # Configure actions by lookup (mutually excl. with command):
|
if command != null
|
||||||
# actionsB = {"status" = "systemctl status..."}
|
then command
|
||||||
# - colorA, colorB # Configure highlighting conditions:
|
else if isStaticLayer lastLayer
|
||||||
# colorA = {"red" = "systemctl ... | grep ..."};
|
then "$OPTION${toString (builtins.length layers - 1)}"
|
||||||
#
|
else throw "mkMenu: 'command' must be set when the last layer is not a static attrset";
|
||||||
# Use $OPTIONA and $OPTIONB to use the options chosen by option<A/B>-command and rofi
|
|
||||||
# Use $EVALA and $EVALB to use the outputs generated by command<A/B>
|
|
||||||
mkMenu = let
|
|
||||||
in
|
in
|
||||||
prompt: attrs: "";
|
pkgs.writeScriptBin "rofi-menu-${prompt}" ''
|
||||||
|
#! ${pkgs.fish}/bin/fish
|
||||||
|
|
||||||
|
${lib.concatStringsSep "\n" layerScripts}
|
||||||
|
eval "${finalCmd}"
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user