From d9d3507da3c0e81b81ea5190c958deed9e84c1df Mon Sep 17 00:00:00 2001 From: q Date: Sun, 30 Dec 2018 11:16:39 +0200 Subject: [PATCH] shortcuts for select-option --- shell/select-option.sh | 150 ++++++++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 53 deletions(-) diff --git a/shell/select-option.sh b/shell/select-option.sh index 26b6578..6afa4c7 100755 --- a/shell/select-option.sh +++ b/shell/select-option.sh @@ -7,11 +7,23 @@ function select_option { printf -v nformat "%%%dd. " ${#optcount} local max_opt=0 local multiselected=() + local choices=() + local shortcuts=() + local idx=0 + local clean_opt for opt; do - if [[ "${#opt}" -gt $max_opt ]]; then - max_opt=${#opt} - fi multiselected+=(0) + if [[ "$opt" =~ ^\[.\]* ]]; then # choice starts with [x] + shortcuts+=( "${opt:1:1}" ) + choices+=( "${opt:3}" ) + else + shortcuts+=( "${opt:0:1}" ) + choices+=( "$opt" ) + fi + if [[ "${#choices[$idx]}" -gt $max_opt ]]; then + max_opt="${#choices[$idx]}" + fi + ((idx++)) done # little helpers for terminal print control and key input @@ -20,20 +32,25 @@ function select_option { cursor_blink_off() { printf "$ESC[?25l"; } cursor_to() { printf "$ESC[$1;${2:-1}H"; } if [[ $SELECT_MONOCHROME -eq 1 ]]; then - print_option() { printf " $ESC[30m%s $ESC[0m %s $ESC[30m%s$ESC[0m " "$2" "$1" "$3"; } - print_selected() { printf " $ESC[30m%s$ESC[0m{ $ESC[1m%s$ESC[0m }$ESC[30m%s$ESC[0m " "$2" "$1" "$3"; } + print_option() { printf " $ESC[30m%s $ESC[0m%s %s $ESC[30m%s$ESC[0m$ESC[K" "$1" "$2" "$3" "$4"; } + print_selected() { printf " $ESC[30m%s $ESC[0m%s{ $ESC[1m%s$ESC[0m } $ESC[30m%s$ESC[0m$ESC[K" "$1" "$2" "$3" "$4"; } print_multiselected() { printf "$ESC[30;1m*$ESC[0m"; } else - print_option() { printf " $ESC[30m%s $ESC[33m %s $ESC[0m $ESC[30m%s$ESC[0m " "$2" "$1" "$3"; } - print_selected() { printf " $ESC[30m%s$ESC[35;1m{$ESC[37;1m %s $ESC[0;35;1m}$ESC[0;30m%s$ESC[0m " "$2" "$1" "$3"; } + print_option() { printf " $ESC[30m%s $ESC[33m%s %s$ESC[0m $ESC[30m%s$ESC[0m$ESC[K" "$1" "$2" "$3" "$4"; } + print_selected() { printf " $ESC[30m%s $ESC[35;1m%s{ $ESC[37;1m%s$ESC[0;35;1m } $ESC[0;30m%s$ESC[0m$ESC[K" "$1" "$2" "$3" "$4"; } print_multiselected() { printf "$ESC[32;1m*$ESC[0m"; } fi get_cursor_row() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; } key_input() { read -s -n1 key1 2>/dev/null >&2 read -s -n2 -t 0.1 key2 2>/dev/null >&2 - if [[ "$key1" =~ ^[0-9]+$ ]]; then - echo $key1; return; - fi + local idx=$selected + while true; do + idx=$(( ($idx + 1) % $optcount )) + if [[ "$key1" = "${shortcuts[$idx]}" ]]; then + echo $idx; return; + fi + if [[ $idx -eq $selected ]]; then break; fi + done if [[ $key1$key2 = $ESC[A ]]; then echo up; return; fi if [[ $key1$key2 = $ESC[B ]]; then echo down; return; fi if [[ $key1$key2 = $ESC[C ]]; then echo left; return; fi @@ -64,15 +81,29 @@ function select_option { cursor_blink_off local selected=0 + local bottomlength=$(( 10 + $max_opt )) + local multiselect_pos=6 + if [[ $SELECT_NUMBERS -eq 1 ]]; then + bottomlength=$(( $bottomlength + 3 )) + multiselect_pos=$(( $multiselect_pos + 3 )) + fi + if [[ $SELECT_SHORTCUTS -eq 1 ]]; then + bottomlength=$(( $bottomlength + 3 )) + multiselect_pos=$(( $multiselect_pos + 3 )) + fi while true; do # print options by overwriting the last lines local idx=0 local nidx + local shortcut cursor_to $startrow - printf " $ESC[30m%s$ESC[0m" "$toprow" + if [ $selected -lt 0 ]; then selected=$(($# - 1)); fi + if [ $selected -ge $# ]; then selected=0; fi + printf " $ESC[30m%s$ESC[0m$ESC[K " "$toprow" for opt; do + opt="${choices[$idx]}" local lborder="${lborderchars:$idx:1}" if [ -z "$lborder" ]; then lborder=" "; fi local rborder="${rborderchars:$(( $optcount - $idx - 1)):1}" @@ -80,37 +111,40 @@ function select_option { if [[ $SELECT_UNDERSCORES -eq 1 ]]; then opt=${opt//_/ } fi - if [[ $SELECT_NUMBERS -eq 1 ]]; then + if [[ $SELECT_NUMBERS -eq 1 ]]; then printf -v nidx "$nformat" $(( idx + 1 )) fi + if [[ $SELECT_SHORTCUTS -eq 1 ]]; then + printf -v shortcut "%s) " "${shortcuts[$idx]}" + fi printf -v padopt "%-${max_opt}s" "$opt" cursor_to $(($startrow + $idx + 1)) if [ $idx -eq $selected ]; then - print_selected "$nidx$padopt" "$lborder" "$rborder" + print_selected "$lborder" "$nidx$shortcut" "$padopt" "$rborder" else - print_option "$nidx$padopt" "${lborder}" "$rborder" + print_option "$lborder" "$nidx$shortcut" "$padopt" "$rborder" fi if (( ${multiselected[$idx]} )); then - cursor_to $(($startrow + $idx + 1)) 5 + cursor_to $(($startrow + $idx + 1)) $multiselect_pos print_multiselected fi ((idx++)) done cursor_to $(($startrow + $idx + 1)) - local bottomlength=$(( 8 + $max_opt + ${#nidx} )) - printf "$ESC[30m%${bottomlength}s$ESC[0m" "$bottomrow" + printf "$ESC[K$ESC[30m%${bottomlength}s$ESC[0m " "$bottomrow" # user key control local user_input=`key_input` case "$user_input" in enter) break;; esc) reset_display; exit 1;; - up) ((selected--)); - if [ $selected -lt 0 ]; then selected=$(($# - 1)); fi;; - down) ((selected++)); - if [ $selected -ge $# ]; then selected=0; fi;; - multiselect) if (( $SELECT_MULTI )); then multiselected[$selected]=$(( 1 - ${multiselected[$selected]} )); fi;; - [0-9]) selected=$(( $user_input - 1 )) ;; + up) ((selected--));; + down) ((selected++));; + multiselect) if (( $SELECT_MULTI )); then + multiselected[$selected]=$(( 1 - ${multiselected[$selected]} )); + ((selected++)); + fi;; + ?) selected=$user_input;; esac done @@ -123,7 +157,7 @@ function select_option { if [[ -n "$SELECT_TOFILE" ]]; then printf "" > "$SELECT_TOFILE"; fi for opt; do if (( ${multiselected[$idx]} )); then - if [[ -n "$SELECT_TOFILE" ]]; then echo "$opt" >> "$SELECT_TOFILE"; fi + if [[ -n "$SELECT_TOFILE" ]]; then echo "${choices[$idx]}" >> "$SELECT_TOFILE"; fi returnvalue=$(( $returnvalue + 2**$idx )) fi ((idx++)) @@ -132,12 +166,7 @@ function select_option { # Single choice returnvalue=$selected if [[ -n "$SELECT_TOFILE" ]]; then - for opt; do - if [ $idx -eq $selected ]; then - echo "$opt" > "$SELECT_TOFILE" - fi - ((idx++)) - done + echo "${choices[$selected]}" > "$SELECT_TOFILE" fi fi @@ -151,43 +180,49 @@ _help() { echo "Select Option [Pure BASH] Adapted from: https://unix.stackexchange.com/questions/146570/arrow-key-enter-menu - Usage: $SELF [-n] [--nc] [-o File] [-_] list of options - -n Numbered list + Usage: $SELF [-nms_] [--nc] [-o File] list of options + -n Show item number -m Multiple choice selection. Note that only 7 options can be encoded reliably in return value. Use File output for more options. + -s Show shortcuts --nc No colors -o File Save the choice(s) in a file. -_ Print spaces instead of underscores, workaround for - shell arguments with spaces. + shell arguments containing spaces. + --package Special option to base64 encode this program, to be + used in your own programs User interface: Enter Select option Esc / Ctrl-c Exit x Multi-selection + other keys Shortcut, defaults to first letter of choice Returns: Single selection: - (10 + index) of selection (first option = 10) - 1 if ctrl-c or ESC pressed - If output file is defined, choice is printed there. + - (10 + index) of selection (first option = 10) + - 1 if ctrl-c or ESC pressed + - If output file is defined, choice is printed there. Multiple selection: - (10 + 2**index) encoded multiple values (no options = 10). See example. - 1 if ctrl-c or ESC pressed - All choices separately in the output File. + - (10 + 2**index) encoded multiple values (no options = 10). See example. + - 1 if ctrl-c or ESC pressed + - All choices separately in the output File. Example: Single choice: - $SELF -n one two three - EC=\$? - [[ \$EC -lt 10 ]] && { echo Exited..; } || { echo You selected \$(( \$EC - 9 )); } + > $SELF -n one two three + > EC=\$? + > [[ \$EC -lt 10 ]] && { echo Exited..; } || { echo You selected \$(( \$EC - 9 )); } + Single choice with shortcuts, press 1 2 3 or c to move in the menu: + > $SELF -s [1]One [2]Two [3]Three [c]Cancel Multi choice: - $SELF -m one two three four five six seven -o choice.txt - C=\$?; C=\$(( \$C - 10 )) - if (( \$C == 0 )); then printf 'No selection'; else printf 'Selections: '; fi - for b in 7 6 5 4 3 2 1 0; do - if (( \$C >= 2**\$b )); then printf '%d, ' \$(( \$b + 1 )); C=\$(( \$C - 2**\$b )); fi - done; printf '\\n' + > $SELF -m one two three four five six seven -o choice.txt + > C=\$?; C=\$(( \$C - 10 )) + > if (( \$C == 0 )); then printf 'No selection'; else printf 'Selections: '; fi + > for b in 7 6 5 4 3 2 1 0; do + > if (( \$C >= 2**\$b )); then printf '%d, ' \$(( \$b + 1 )); C=\$(( \$C - 2**\$b )); fi + > done; printf '\\n' " exit @@ -203,6 +238,7 @@ _package_self() { SELECT_MONOCHROME=0 +SELECT_SHORTCUTS=0 SELECT_NUMBERS=0 SELECT_UNDERSCORES=0 SELECT_MULTI=0 @@ -210,14 +246,22 @@ SELECT_TOFILE="" for (( i=1; i<=$#; i++ )); do value=${!i} j=$(( i + 1 )) - [[ "$value" = "-h" ]] && _help [[ "$value" = "--help" ]] && _help [[ "$value" = "--nc" ]] && { SELECT_MONOCHROME=1; continue; } - [[ "$value" = "-m" ]] && { SELECT_MULTI=1; continue; } - [[ "$value" = "-n" ]] && { SELECT_NUMBERS=1; continue; } - [[ "$value" = "-_" ]] && { SELECT_UNDERSCORES=1; continue; } - [[ "$value" = "-o" ]] && { SELECT_TOFILE="${!j}"; ((i++)); continue; } [[ "$value" = "--package" ]] && { _package_self; exit; } + [[ "${value}" = "-"* ]] && { + [[ "$value" =~ -.*h ]] && { _help; } + [[ "$value" =~ -.*m ]] && { SELECT_MULTI=1; } + [[ "$value" =~ -.*s ]] && { SELECT_SHORTCUTS=1; } + [[ "$value" =~ -.*n ]] && { SELECT_NUMBERS=1; } + [[ "$value" =~ -.*_ ]] && { SELECT_UNDERSCORES=1; } + [[ "$value" =~ -.*o ]] && { + SELECT_TOFILE="${!j}"; ((i++)); + [[ -z "$SELECT_TOFILE" ]] && { echo Output file name missing; exit 1; } + } + continue + } + opts+=( "$value" ) done