Instantly share code, notes, and snippets.
Last active
November 8, 2022 08:11
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save hapylestat/58fa0e9b543f4eefd9588ce90c4b1fbc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# shellcheck disable=SC2155,SC2015,SC2183 | |
unselectedColor="\033[38;05;188m" | |
selectedColor="\033[38;05;232;48;05;188m" | |
resetColor="\033[m" | |
moveCursor() { | |
tput cup "$2" "$1" | |
} | |
moveCursorEX() { | |
echo -ne "\033[$(($2+1));$1f" | |
} | |
redrawMenuItems() { | |
local -n _menuItems=$1 | |
local startPos=$2 | |
local pos=$3 | |
local oldPos=$4 | |
# +1 comes from leading new line in the menu | |
local menuLen=$((${#_menuItems[@]} + 2)) | |
local menuOldPosY=$((startPos - (menuLen - oldPos))) | |
local menuNewPosY=$((startPos - (menuLen - pos))) | |
moveCursorEX "0" "${menuOldPosY}" | |
echo -ne "\t${unselectedColor}${oldPos}. ${_menuItems[${oldPos}]}${resetColor}" | |
moveCursorEX "0" "${menuNewPosY}" | |
echo -ne "\t${selectedColor}${pos}. ${_menuItems[${pos}]}${resetColor}" | |
moveCursorEX "0" "${startPos}" | |
} | |
drawMenu() { | |
local -n _menuItems=$1 | |
local menuPosition=$2 | |
local menuTitle="$3" | |
local menuwidth=${#menuTitle} | |
local menuLen=$((${#_menuItems[@]})) | |
echo -ne "\t${unselectedColor}"; printf '%*s' "${menuwidth}" | tr ' ' "="; echo -e "${resetColor}" | |
echo -ne "\t${unselectedColor}"; echo -e " $menuTitle ${resetColor}" | |
echo -ne "\t${unselectedColor}"; printf '%*s' "${menuwidth}" | tr ' ' "="; echo -e "${resetColor}" | |
echo "" | |
for i in $(seq 0 ${menuLen}); do | |
echo -ne "\t" | |
if [[ $i -eq ${menuPosition} ]]; then | |
echo -e "${selectedColor}$i. ${_menuItems[${i}]}${resetColor}" | |
else | |
if [[ -n "${_menuItems[${i}]}" ]]; then | |
echo -e "${unselectedColor}$i. ${_menuItems[${i}]}${resetColor}" | |
fi | |
fi | |
done | |
# leading new line | |
echo | |
} | |
getCurrentPos(){ | |
local _col; local _row | |
# shellcheck disable=SC2162 | |
IFS=';' read -sdR -p $'\E[6n' _row _col | |
echo "${_col} ${_row#*[}" | |
} | |
readKey(){ | |
read -rsN 1 _key | |
printf %d "'${_key}" # %x for hex | |
} | |
menu(){ | |
local menuItems=($1) | |
local menuTitle="$2" | |
local keyCode=(0) | |
local pos=0 | |
local oldPos=0 | |
drawMenu "menuItems" "${pos}" "${menuTitle}" | |
local startPosStr=$(getCurrentPos); | |
local startPos="${startPosStr#* }" | |
while [[ ${keyCode[0]} -ne 10 ]]; do | |
local keyCode=("$(readKey)") # byte 1 | |
if [[ ${keyCode[0]} -eq 27 ]]; then # escape character | |
local keyCode+=("$(readKey)") # byte 2 | |
if [[ ${keyCode[-1]} -ne 27 ]]; then # checking if user pressed actual | |
local keyCode+=("$(readKey)") # byte 3 | |
if [[ "51 50 48 52 53 54" =~ (^|[[:space:]])"${keyCode[2]}"($|[[:space:]]) ]]; then | |
while [[ ${keyCode[-1]} -ne 126 ]]; do | |
local keyCode+=("$(readKey)") | |
done | |
fi | |
if [[ "49" =~ (^|[[:space:]])"${keyCode[2]}"($|[[:space:]]) ]]; then | |
local keyCode+=("$(readKey)") # byte 4 | |
[[ ${keyCode[-1]} -ne 126 ]] && local keyCode+=("$(readKey)") # byte 5 | |
[[ ${keyCode[-1]} -eq 59 ]] && local keyCode+=("$(readKey)") # byte 5 check | |
[[ ${keyCode[-1]} -ne 126 ]] && local keyCode+=("$(readKey)") | |
fi | |
fi | |
fi | |
local oldPos=${pos} | |
case "${keyCode[*]}" in | |
"27 91 65") local pos=$((pos - 1));; # up | |
"27 91 66") local pos=$((pos + 1));; # down | |
"27 91 53 126") local pos=$((pos - 2));; # pgup | |
"27 91 54 126") local pos=$((pos + 2));; # pgdn | |
"27 91 72") local pos=0;; # home | |
"27 91 70") local pos=$((${#menuItems[*]} - 1));; # end | |
esac | |
[[ ${pos} -lt 0 ]] && local pos=0 | |
[[ ${pos} -ge ${#menuItems[*]} ]] && local pos=$((${#menuItems[*]} - 1)) | |
redrawMenuItems "menuItems" "${startPos}" "${pos}" "${oldPos}" | |
done | |
return "${pos}" | |
} | |
menu "Menu_Item_1 Menu_Item_2 Menu_Item_3 Menu_item_4 Menu_item_5" "Title Here" | |
case "$?" in | |
0) echo "selected 0";; | |
1) echo "selected 1";; | |
2) echo "selected 2";; | |
3) echo "selected 3";; | |
4) echo "selected 4";; | |
esac |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function moveCursor{ param($position) | |
$host.UI.RawUI.CursorPosition = $position | |
} | |
function RedrawMenuItems{ | |
param ([array]$menuItems, $oldMenuPos=0, $menuPosition=0, $currPos) | |
# +1 comes from leading new line in the menu | |
$menuLen = $menuItems.Count + 1 | |
$fcolor = $host.UI.RawUI.ForegroundColor | |
$bcolor = $host.UI.RawUI.BackgroundColor | |
$menuOldPos = New-Object System.Management.Automation.Host.Coordinates(0, ($currPos.Y - ($menuLen - $oldMenuPos))) | |
$menuNewPos = New-Object System.Management.Automation.Host.Coordinates(0, ($currPos.Y - ($menuLen - $menuPosition))) | |
moveCursor $menuOldPos | |
Write-Host "`t" -NoNewLine | |
Write-Host "$oldMenuPos. $($menuItems[$oldMenuPos])" -fore $fcolor -back $bcolor -NoNewLine | |
moveCursor $menuNewPos | |
Write-Host "`t" -NoNewLine | |
Write-Host "$menuPosition. $($menuItems[$menuPosition])" -fore $bcolor -back $fcolor -NoNewLine | |
moveCursor $currPos | |
} | |
function DrawMenu { param ([array]$menuItems, $menuPosition, $menuTitel) | |
$fcolor = $host.UI.RawUI.ForegroundColor | |
$bcolor = $host.UI.RawUI.BackgroundColor | |
$menuwidth = $menuTitel.length + 4 | |
Write-Host "`t" -NoNewLine; Write-Host ("=" * $menuwidth) -fore $fcolor -back $bcolor | |
Write-Host "`t" -NoNewLine; Write-Host " $menuTitel " -fore $fcolor -back $bcolor | |
Write-Host "`t" -NoNewLine; Write-Host ("=" * $menuwidth) -fore $fcolor -back $bcolor | |
Write-Host "" | |
for ($i = 0; $i -le $menuItems.length;$i++) { | |
Write-Host "`t" -NoNewLine | |
if ($i -eq $menuPosition) { | |
Write-Host "$i. $($menuItems[$i])" -fore $bcolor -back $fcolor -NoNewline | |
Write-Host "" -fore $fcolor -back $bcolor | |
} else { | |
if ($($menuItems[$i])) { | |
Write-Host "$i. $($menuItems[$i])" -fore $fcolor -back $bcolor | |
} | |
} | |
} | |
# leading new line | |
Write-Host "" | |
} | |
function Menu { param ([array]$menuItems, $menuTitel = "MENU") | |
$vkeycode = 0 | |
$pos = 0 | |
$oldPos = 0 | |
DrawMenu $menuItems $pos $menuTitel | |
$currPos=$host.UI.RawUI.CursorPosition | |
While ($vkeycode -ne 13) { | |
$press = $host.ui.rawui.readkey("NoEcho,IncludeKeyDown") | |
$vkeycode = $press.virtualkeycode # https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes | |
Write-host "$($press.character)" -NoNewLine | |
$oldPos=$pos; | |
Switch ($vkeycode) { | |
33 {$pos = if ($pos - 2 -gt 0) {$pos - 2} Else {0}} #PgUP | |
34 {$pos = if ($pos + 2 -lt @($menuItems).length) {$pos + 2} Else {@($menuItems).length}} #PgDn | |
35 {$pos = @($menuItems).length} # End | |
36 {$pos = 0} # Up | |
38 {$pos--} # Arrow UP | |
40 {$pos++} # Arrow DOWN | |
} | |
if ($pos -lt 0) {$pos = 0} | |
if ($pos -ge $menuItems.length) {$pos = $menuItems.length -1} | |
RedrawMenuItems $menuItems $oldPos $pos $currPos | |
} | |
Write-Output $pos | |
} | |
$bad = "a", "b", "c" | |
$selection = Menu $bad "Title" | |
Switch ($selection){ | |
0 { Write-Output "a" } | |
1 { Write-Output "b" } | |
2 { Write-Output "c" } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, I love your PowerShell menu and want to use it in one of my scripts. During implementation i noticed, that PgUp/PgDn/End does not work as expected. I was able to fix this. Here is my modified code. I also added a variable for the page size for PgUp/PgDn and enabled rollover to be able to jump from the last entry to the first and vice versa.
Function Menu { param ([array]$menuItems, $menuTitel = "MENU")
}
I also have one problem - my menu has about 50 entries and it would be great if your script could make columns. I found a script that is able to do this, but it redraws the menu on every move whichis very annoying.
https://community.spiceworks.com/scripts/show/4785-create-menu-2-0-arrow-key-driven-powershell-menu-for-scripts
Do you think it would be possible for you to add columns to your script? That would be great!
Brgds Deas