DEV Community

Cover image for TUI select fn
John Robertson
John Robertson

Posted on

TUI select fn

Alt Text

Those of you familiar with bash's select builtin know that the automated layout of choices is a nice feature. Last week while prototyping some SQL I became frustrated with select, mostly because you must type in a number and then press the Enter key. Remembering the TUI interfaces of the 1980's, I set about writing hkselect, a hotkey driven replacement for select. Here are the main features:

  • Automated layout of choices.
  • Assign item hotkey with &.
  • Automatic hotkey assignment if & is missing.
  • Bind escaped keys to callback functions.
  • Transparently caches layout information and composed labels.

Full project is here

Below is an example bash program with a functioning hotkey TUI that includes callbacks for F1, F2, and F3:

# Comments from hkselect() implementation in bash++
function hkselect
################################################
# Approximate replacement for bash's builtin `select'.
# hkselect() does not require the user to press enter key
# to finalize choice.
# Usage: hkselect [command_flags] arg1 ...
#
# User choices are supplied as args to the function, and
# the hotkey may be indicated by placing an `&' in
# the preceding character, e.g.
# press&kplease
# where `k' is the hotkey. If a hotkey is not indicated,
# then one will be assigned automatically.
#
# Command flags:
# -b 'keystr|funcname|arg1 arg2 ...' Bind escape-based key (F1-F12, Arrows, ...) to a function
# -B global_arr_name Array of -b arguments
# -p left_pad Left margin padding count
# -r 'rebuke string' String to print when user pressed invalid key
# -s 'subtitle string' Subtitle to print below choices
# -w max_width Limit the width of choices layout
#
# Global variables:
# HKSELKEY - key the user pressed when making a successful choice
# REPLY - zero based index of the arg whose hotkey was pressed
#
# RETURN: upon return the value of HKSELKEY will contain the key which
# the user pressed, and REPLY will contain the 0-based index of the
# matching argument passed when hkselect() was called
view raw bash++ hosted with ❤ by GitHub
#!/bin/bash
##############################################################
# Script to demonstrate use of hkselect() function in bash++
#
# John Robertson <john@rrci.com>
# Initial release: Tue Mar 30 11:02:44 EDT 2021
#
# Halt on error, no globbing, no unbound variables
set -efu
# import bash++ facilities
source ../bash++
# Flag so we know when a bound key was pressed
is_boundKey=0
function FnKey_cb
###############################################################
# This function is bound to function keys
{
1>&2 echo -en "\nI can see you've pressed $1\nPress return to continue "
read
# Note that a bound key callback cause hkselect() to return.
is_boundKey=1
}
function cleanup
################################################
# Cleanup on exit
{
# Restore cursor
tput cvvis
# Reset any text attributes
tput sgr0
}
###################################
### Execution starts here #########
###################################
# cleanup on exit
trap cleanup 0
# Data might come from a dynamic source, like a file or pipe or ...
declare -a DATA_CHOICES=([0]=one [1]=two [2]=three)
# Strings we'll use repeatedly
PAD=" "
REBUKE="${PAD}$(tput bold)Please make a selection, or press a function key.$(tput sgr0)"
SUBTITLE="${PAD}You will write awesome programs with $(tput bold)bash++$(tput sgr0)"
# Loop processing user keystrokes
while true; do
# Clear the terminal
tput clear
# Print banner
1>&2 echo -e "\n${PAD}$(tput rev)Welcome to the hkselect example program$(tput sgr0)"
1>&2 echo -e "${PAD}$(tput bold)Press a function key!$(tput sgr0)"
# Wait for user to press a valid key
hkselect \
-r "$REBUKE"\
-s "$SUBTITLE"\
-p 4 \
-b $(tput kf1)'|FnKey_cb|F1' \
-b $(tput kf2)'|FnKey_cb|F2' \
-b $(tput kf3)'|FnKey_cb|F3' \
"${DATA_CHOICES[@]}"\
'&Shout hurray!' 'E&xit'
# Handle known hotkeys first
case $HKSELKEY in
s|S)
1>&2 echo -en 'Hurray!\nPress return to continue '
read discard
;;
x|X) break;;
$'\e') # User pressed escape
echo
break
;;
*) # Could be an automatically assigned hotkey
if (( !is_boundKey )); then
# If we get to here, then REPLY contains the zero based index of the item chosen by the user
1>&2 echo -en "Thank you for selecting '${DATA_CHOICES[$REPLY]}'.\nPress return to continue "
read
fi
;;
esac
is_boundKey=0
done
view raw hkselect.sh hosted with ❤ by GitHub

Top comments (0)