-
Notifications
You must be signed in to change notification settings - Fork 1
/
menu.sh
155 lines (141 loc) · 4.71 KB
/
menu.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/bin/bash
#
# MENU
#
# Description
# Display an interactive shell menu and select with arrow keys and Enter.
#
# Input variables
# $1 The header/prompt you wish to display
# $2 The variable name indicating the menu array you have defined
#
# Notes
# Enter, Tab or Space will select the entry
# ESC or Q will cancel
# Ctrl+C will quit the script as usual
#
# Return values
# $menu_status = The success/error status with 0=success and 1=error
# $menu_selected = The selected entry is set to the key of the menu array
# $menu_msg = If there was an error, this will hold the message
#
# Styling
# If you wish to override the default styles, define any of these variables before calling the menu function.
# $menu_marker = You can override the default marker character
# $menu_fg = You can override the default highlighted entries' text color
# $menu_bg = You can override the default highlighted entries' background color
# $menu_padding = Set to 1 to add a blank space between menu entries
#
menu() {
# Main Menu function
main_menu() {
local LANG default_marker default_highlight_fg default_highlight_bg i input key selected
local menu_array menu_fg menu_bg menu_marker header
LANG=en_US.UTF-8 # Support UTF-8 chars
default_marker=$(echo -e "\u27A4") # Marker character: ➤
default_highlight_fg="\e[38;5;15m" # White text
default_highlight_bg="\e[48;5;4m" # Blue background
# Check to make sure a menu array was passed in
if [[ -z "$1" ]]; then
echo "Error: No menu header submitted!" >&2
return 1
fi
# Define header message
header="$1"
shift
if [[ -z "$1" ]]; then
echo "Error: No menu submitted!" >&2
return 1
fi
# Define submitted menu
if [[ -v $2 ]]; then
# There are multiple strings passed, so set the array to all of them
local menu_array=("$@")
else
# If only one remaining string was passed, check to see if it points to an existing array variable
if declare -p "${1}" 2>/dev/null | grep -q '^declare \-a'; then
# An array by that name does exist
local tmp=$1[@] # Do a trick to fetch it (compatible with older Bash versions)
local menu_array=("${!tmp}")
else
local menu_array=("$1")
fi
fi
# Initial selected item
selected=0
# Set hightlight colors
if [[ ! -v menu_fg ]]; then
menu_fg="$default_highlight_fg"
fi
if [[ ! -v menu_bg ]]; then
menu_bg="$default_highlight_bg"
fi
# Display the menu with the selected item highlighted
display_menu() {
printf "\033[H\n%b\n\n" "$header" # \033[H is needed to redraw menu
if [[ -z "$menu_marker" ]]; then
menu_marker="$default_marker"
fi
for i in "${!menu_array[@]}"; do
if [ "$i" -eq "$selected" ]; then
printf "%b%b %s %s\e[0m\n" "$menu_fg" "$menu_bg" "$menu_marker" "${menu_array[$i]}"
else
echo " ${menu_array[$i]}"
fi
test "$menu_padding" == 1 && echo
done
}
# Main loop
while true; do
display_menu >&2
# Read a single character (Up, Down and ESC share a comon prefix, and have different suffixes)
IFS= read -rsn1 input
#printf "\n==%x==\n\n" "'$input"
# Check arrow keys
if [[ $input == $'\x1b' ]]; then
# Read next part of input string
read -rsn2 -t 0.1 key
#printf "\n==%x==\n\n" "'$key"
if [[ -z "$key" ]]; then
# Escape was pressed and no additional input was detected
echo "Canceled"
return 1
elif [[ $key == '[A' ]]; then
# Up arrow
if [ "$selected" -eq 0 ]; then # Wrap around from top to bottom
selected=$((${#menu_array[@]} -1))
else
((selected--))
fi
elif [[ $key == '[B' ]]; then
# Down arrow
((selected++))
if [ "$selected" -eq ${#menu_array[@]} ]; then # Wrap around from bottom to top
selected=0
fi
fi
elif [[ $input == $'\x09' || $input == ' ' || $input == $'\x0' ]]; then
# Tab, space, or enter
return $selected
elif [[ $input == 'q' ]]; then
# 'q'
echo "Canceled"
return 1
fi
done
}
# This wrapper function handles clearing screen and output handling
tput clear 2>/dev/null || clear
export menu_msg menu_selected menu_status
menu_msg="$(main_menu "$@")" # Call main_menu() function
menu_selected=$? # Return value is the selected menu entry (0-based)
if [[ -z "$menu_msg" ]]; then
# Success
menu_status=0
return 0
else
# Error
menu_status=1
return 1
fi
}