Ansi_menu
Wrote this trying NOT to use libraries like ‘(n)curses’ or ‘io/console’.
The key-handling became way to involved.. so I decided to use tty-reader for that.
require 'tty-reader'
# get cursor pos via sending Esc[6n
def pos
res = ''
$stdin.raw do |stdin|
$stdout << "\e[6n"
$stdout.flush
while (c = stdin.getc) != 'R'
res << c if c
end
end
# named matches, return hash
m = res.match /(?<ypos>\d+);(?<xpos>\d+)/
{:y => Integer(m[:ypos]), :x => Integer(m[:xpos])}
end
# menu
def select(menu)
rd = TTY::Reader.new
row, pos = pos()[:y] - menu.size, 0
menu.each_with_index {|item, idx|
puts '%s %s' % [(idx==0) ? '>' : ' ', item]
}
# lambdas
cur = ->(b) { print b ? "\e[?25h" : "\e[?25l"}
cur_goto = ->(row=0,col=0) { print "\e[#{row};#{col}H" }
pat = ->(r, c, txt) { cur_goto[r, c]; print txt }
res = -> { cur_goto[row + menu.size, 0]; cur[true] }
# keys
rd.on(:keydown, :keyup) do |ev|
pat[pos+row, 0, ' %s' % menu[pos]]
pos = (pos += (ev.key.name == :down) ? 1 : -1) % menu.size
pat[pos+row, 0, '>']
end
.on(:keyescape) { res[]; return nil }
.on(:keyreturn) { res[]; return menu[pos] }
cur[false]
# loop
loop { ch = rd.read_keypress }
end
# -----------------------------------------------------------------------------
# usage :
# move cursor => arrow keys (up / down)
# make selection => Return
# quit without selecting => Esc
arr = %w(one two three four five end)
puts 'Selected: %s' % [select(arr) || 'none']