I have a menu I made in Curses in which the user can use the 'w' and 's' keys or up and down arrows on the keyboard to move up and down in the Curses menu.
Once the Enter key is pressed, the system command 'ls' is called.
The problem I am having is when the user tries to move to or from the 'Option 0' line, they have to press the arrow key twice. I only want them to have to press Enter once just like they can with the other options.
Is there a better way I can write the below so that the user only has to press the arrow keys once to move to a different option?
if position == 0
draw_info menu, 'You selected option 0'
input = menu.getch
if input == 13 # Curses recognizes 13 as Enter being pressed
Curses.close_screen
system "clear" or system "cls"
system 'ls'
puts "\n\nPress Enter to continue."
Here is my code:
require 'curses'
include Curses
Curses.init_screen
Curses.curs_set(0) # Invisible cursor
Curses.start_color
Curses.noecho # echo or noecho to display user input
Curses.cbreak # do not buffer commands until Enter is pressed
Curses.raw # disable interpretation of keyboard input
Curses.nonl
Curses.stdscr.nodelay = 1
begin
# Building a static window
def draw_menu(menu, active_index=nil)
["This is option 0.", "This is option 1.", "This is option 2.", "This is option 3."].each_with_index do |element, index|
# "w" for word array
# It's a shortcut for arrays
menu.setpos(index + 1, 1)
menu.attrset(index == active_index ? A_STANDOUT : A_NORMAL)
menu.addstr("#{index} - %-10s" % element) # %-Xs makes sure array words line up evenly if you place index after element
# you can change 17 to another number
end
menu.setpos(5, 1)
end
def draw_info(menu, text)
menu.setpos(6, 1) # sets the position of move up and down
# for example, menu.setpos(1, 10) moves to another
# location
menu.attrset(A_NORMAL)
menu.addstr text
end
position = 0
menu = Window.new(20, 70, 2, 2) # (height, width, top, left)
menu.keypad = true # enable keypad which allows arrow keys
#menu.box('|', '-')
draw_menu(menu, position)
while ch = menu.getch
stdscr.keypad = true
case ch
when KEY_UP, 'w'
#draw_info menu, 'move up'
position -= 1
when KEY_DOWN, 's'
#draw_info menu, 'move down'
position += 1
when 'x'
exit
end
position = 3 if position < 0
position = 0 if position > 3
draw_menu(menu, position)
if position == 0
draw_info menu, 'You selected option 0'
input = menu.getch
if input == 13 # Curses recognizes 13 as Enter being pressed
Curses.close_screen
system "clear" or system "cls"
system 'ls'
puts "\n\nPress Enter to continue."
end
elsif position == 1
draw_info menu, 'You selected option 1'
elsif position == 2
draw_info menu, 'You selected option 2'
else position == 3
draw_info menu, 'You selected option 3'
end
end
rescue => ex
Curses.close_screen
end
EDIT
This will allow the user to only have to hit the arrow key once but Curses.close_screen doesn't close the screen so I can run 'ls' properly.
require 'curses'
include Curses
Curses.init_screen
Curses.curs_set(0) # Invisible cursor
Curses.start_color
Curses.noecho # echo or noecho to display user input
Curses.cbreak # do not buffer commands until Enter is pressed
Curses.raw # disable interpretation of keyboard input
Curses.nonl
Curses.stdscr.nodelay = 1
begin
# Building a static window
def draw_menu(menu, active_index=nil)
["This is option 0.", "This is option 1.", "This is option 2.", "This is option 3."].each_with_index do |element, index|
# "w" for word array
# It's a shortcut for arrays
menu.setpos(index + 1, 1)
menu.attrset(index == active_index ? A_STANDOUT : A_NORMAL)
menu.addstr("#{index} - %-10s" % element) # %-Xs makes sure array words line up evenly if you place index after element
# you can change 17 to another number
end
menu.setpos(5, 1)
end
def draw_info(menu, text)
menu.setpos(6, 1) # sets the position of move up and down
# for example, menu.setpos(1, 10) moves to another
# location
menu.attrset(A_NORMAL)
menu.addstr text
end
position = 0
menu = Window.new(20, 70, 2, 2) # (height, width, top, left)
menu.keypad = true # enable keypad which allows arrow keys
#menu.box('|', '-')
draw_menu(menu, position)
while ch = menu.getch
stdscr.keypad = true
case ch
when KEY_UP, 'w'
#draw_info menu, 'move up'
position -= 1
when KEY_DOWN, 's'
#draw_info menu, 'move down'
position += 1
when 13
if position.zero?
# draw_info menu, "You hit enter."
Curses.close_screen
system "clear" or system "cls"
system 'ls'
puts "\n\nPress Enter to continue."
end
when 'x'
exit
end
position = 3 if position < 0
position = 0 if position > 3
draw_menu(menu, position)
draw_info menu, "#{position}"
end
rescue => ex
Curses.close_screen
end
Why not move the capture for the Enter into the case statement with the rest of the keys?
while ch = menu.getch
case ch
when KEY_UP, 'w'
position -= 1
when KEY_DOWN, 's'
position += 1
when 13
if position.zero?
Curses.close_screen
system "clear" or system "cls"
system 'ls'
puts "\n\nPress Enter to continue."
gets # waits for the user to press enter
end
when 'x'
exit
end
position = 3 if position < 0
position = 0 if position > 3
draw_menu(menu, position)
if position == 0
draw_info menu, 'You selected option 0'
elsif position == 1
draw_info menu, 'You selected option 1'
elsif position == 2
draw_info menu, 'You selected option 2'
else position == 3
draw_info menu, 'You selected option 3'
end
end
The reason you had to press 2 keys to get off of the first menu option is because when you 'entered' the first menu option (by making position
equal to 0
), you would then prompt for another character (through menu.getch
) that was outside of the main loop. So you needed to essentially 'cancel' off the first option, and then you could move about freely.
Doing it this way, you move onto the first menu option, continue with the stuff that all menu options needed to do, (the draw_info
calls) and then listen for another key in the main loop. Here, they can use an arrow key to move immediately off the option or press Enter. If they ever press Enter, you then check to see if they are on the correct menu item, if they are, then do your ls
call, otherwise just ignore the key and go on as usual.
Also that if/else
chain at the end could just be a single call:
draw_info menu, "You selected option #{position}"