-
Notifications
You must be signed in to change notification settings - Fork 5
/
windows.sh
250 lines (222 loc) · 7.12 KB
/
windows.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#!/usr/bin/env bash
#set -euo pipefail
#IFS=$'\n\t'
# little helpers for terminal print control and key input
ESC=$( printf "\033")
cursor_blink_on() { printf "$ESC[?25h"; }
cursor_blink_off() { printf "$ESC[?25l"; }
cursor_to() { printf "$ESC[$1;${2:-1}H"; }
print_option() { printf "$1 "; }
print_selected_on() { printf "$ESC[7m"; }
print_selected_off() { printf "$ESC[27m"; }
get_cursor_row() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; }
get_cursor_column() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${COL}; }
repl() { printf '%.0s'"$1" $(seq 1 "$2"); }
key_input() {
local key=""
local extra=""
local escKey=`echo -en "\033"`
local upKey=`echo -en "\033[A"`
local downKey=`echo -en "\033[B"`
read -s -n1 key 2> /dev/null >&2
while read -s -n1 -t .0001 extra 2> /dev/null >&2 ; do
key="$key$extra"
done
if [[ $key = $upKey ]]; then
echo "up"
elif [[ $key = $downKey ]]; then
echo "down"
elif [[ $key = $escKey ]]; then
echo "esc"
elif [[ $key = "" ]]; then
echo "enter"
fi
}
function refresh_window {
# формат вызова
# refresh_window y x height width shift "@"
local MaxWindowWidth
local left_x
local top_y
local ReturnKey=""
local temp
local -a ms
local -a menu_items
local height
local i=0
local shift_y
ms=( "$@" )
left_x=${ms[1]}
top_y=${ms[0]}
MaxWindowWidth=${ms[3]}
menu_items=( "${ms[@]:5}" )
height=${ms[2]}
shift_y=${ms[4]}
cursor_to $(($top_y )) $(($left_x))
printf "┌"
repl "─" $(( $MaxWindowWidth + 3 ))
printf "┐"
for ((i=0;i<${height};i++))
do
cursor_to $(($top_y + ${i} + 1)) $(($left_x))
print_option "│ ${menu_items[${i}+${shift_y}]}"
repl " " $(( ${MaxWindowWidth}-${#menu_items[${i}+${shift_y}]} ))
printf "│"
done
cursor_to $(($top_y + ${i} +1 )) $(($left_x))
printf "└"
repl "─" $(( $MaxWindowWidth + 3 ))
printf "┘"
}
function vertical_menu {
# формат вызова
# vertical_menu y x height width "@"
# если x = center - то центрирование по горизонтали
# если y = center - то центрирование по вертикали
# = current - выводим меню в текущей строке
# если height = 0 - не устанавливать высоты (она будет посчитана автоматически)
# = число - установить высоту окна равную числу. Пункты меню будут скролироваться
# width = число. Если строка будет больше этого числа - то ширина будет расширена до него
# если среди пунктов меню встречается слово default со знаком =, то это значит установка пункта меню по умолчанию
# этот пункт меню будет выбранным при выводе меню
# vertical_menu y x height width "default=2" "First Item" "Second Item" "Third Item"
local MaxWindowWidth
local left_x
local top_y
local ReturnKey=""
local -a ms
local -a menu_items
local size
local lines
local columns
local current_y
local skip_lines=0
local height
local shift_y=0
size=$(stty size)
lines=${size% *}
columns=${size#* }
# Обработка и удаление аргумента default, если он присутствует
local default_selected_index=0
local new_menu_items=()
for arg in "$@"; do
if [[ $arg == default=* ]]; then
default_selected_index=${arg#default=}
else
new_menu_items+=("$arg")
fi
done
ms=("${new_menu_items[@]:0:4}")
menu_items=("${new_menu_items[@]:4}")
left_x=${ms[1]}
top_y=${ms[0]}
MaxWindowWidth=${ms[3]}
current_y=$(get_cursor_row)
if ((${ms[2]} == 0)); then
height=${#menu_items[@]}
else
# если требуемая высота больше чем количество пунктов меню, уменьшаем ее
if ((${ms[2]} > ${#menu_items[@]})); then
height=${#menu_items[@]}
else
height=${ms[2]}
fi
fi
#find the width of the window
for el in "${menu_items[@]}"; do
if ((${MaxWindowWidth} < ${#el})); then
MaxWindowWidth=${#el}
fi
done
((MaxWindowWidth = ${MaxWindowWidth} + 2))
if [[ ${ms[1]} == "center" ]]; then
((left_x = (${columns} - ${MaxWindowWidth} - 6) / 2))
fi
if [[ ${ms[0]} == "center" ]]; then
((top_y = (${lines} - ${height} - 2) / 2))
fi
if [[ ${ms[0]} == "current" ]]; then
# если меню не поместится - надо сдвинуть экран
((skip_lines = 0))
if (((${current_y} + ${height}+1) > ${lines})); then
((skip_lines = ${current_y} + ${height} - ${lines} + 2))
echo -en ${ESC}"[${skip_lines}S"
fi
((top_y = ${current_y} - ${skip_lines}))
((current_y = top_y))
fi
refresh_window ${top_y} ${left_x} ${height} ${MaxWindowWidth} ${shift_y} "${menu_items[@]}"
# ensure cursor and input echoing back on upon a ctrl+c during read -s
trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
cursor_blink_off
local selected=${default_selected_index}
local previous_selected=${default_selected_index}
while true; do
# print options by overwriting the last lines
cursor_to $(($top_y + $previous_selected + 1)) $(($left_x))
print_option "│ ${menu_items[$previous_selected + ${shift_y}]}"
repl " " $(($MaxWindowWidth - ${#menu_items[$previous_selected + ${shift_y}]}))
printf "│"
cursor_to $(($top_y + $selected + 1)) $(($left_x))
printf "│ "
print_selected_on
printf " ${menu_items[${selected} + ${shift_y}]}"
repl " " $(($MaxWindowWidth - ${#menu_items[$selected + ${shift_y}]}))
print_selected_off
printf " │"
# user key control
ReturnKey=$(key_input)
case ${ReturnKey} in
enter) break ;;
esc)
selected=255
break
;;
up)
previous_selected=${selected}
((selected--))
if [[ ${selected} -lt 0 ]]; then
if ((${shift_y} > 0)); then
((shift_y--))
refresh_window ${top_y} ${left_x} ${height} ${MaxWindowWidth} ${shift_y} "${menu_items[@]}"
fi
selected=0
fi
;;
down)
previous_selected=${selected}
((selected++))
if [[ ${selected} -ge ${height} ]]; then
if (((${shift_y} + ${selected}) < ${#menu_items[@]})); then
((shift_y++))
refresh_window ${top_y} ${left_x} ${height} ${MaxWindowWidth} ${shift_y} "${menu_items[@]}"
fi
selected=${previous_selected}
fi
;;
esac
done
printf "\n"
cursor_blink_on
cursor_to ${current_y} 1
if [[ ${ms[0]} == "current" ]]; then
# очистить выведенное меню
echo -en ${ESC}"[0J"
fi
((selected += ${shift_y}))
return ${selected}
}
function fn_bui_setup_get_env()
{
# save the home dir
local _script_name=${BASH_SOURCE[0]}
local _script_dir=${_script_name%/*}
if [[ "$_script_name" == "$_script_dir" ]]
then
# _script name has no path
_script_dir="."
fi
# convert to absolute path
_script_dir=$(cd $_script_dir; pwd -P)
export BUI_HOME=$_script_dir
}