如何防止在 Curses 导航菜单中输入两次?

How to prevent having to get input twice in Curses navigation menu?

我在 Curses 中制作了一个菜单,用户可以使用 'w' 和 's' 键或键盘上的上下箭头在 Curses 菜单中上下移动。

一旦按下回车键,就会调用系统命令'ls'。

我遇到的问题是,当用户尝试移入或移出 'Option 0' 行时,他们必须按两次箭头键。我只希望他们按一次 Enter 键,就像使用其他选项一样。

有没有更好的方法我可以写下面的内容,这样用户只需按一次箭头键就可以移动到不同的选项?

  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."

这是我的代码:

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

编辑

这将允许用户只需按一次箭头键,但 Curses.close_screen 不会关闭屏幕,因此我可以 运行 'ls' 正确。

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

您可以使用 ungetch 函数将键码推回输入 FIFO

curses.ungetch(ch)

Push ch so the next getch() will return it. Note: Only one ch can be pushed before getch() is called.

实际上,可移植应用程序只能依靠推回一个字符。使用 ncurses 有一个限制,但它不止一个。

为什么不将 Enter 的捕获与其余键一起移动到 case 语句中?

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

您必须按 2 个键才能退出第一个菜单选项的原因是当您 'entered' 第一个菜单选项时(通过使 position 等于 0) ,然后您将提示输入主循环之外的另一个字符(通过 menu.getch)。所以你基本上需要 'cancel' 关闭第一个选项,然后你就可以自由移动了。

这样做,您将移动到第一个菜单选项,继续执行所有菜单选项需要执行的操作(draw_info 调用),然后在主循环中监听另一个键。在这里,他们可以使用箭头键立即离开选项或按 Enter。如果他们按 Enter 键,然后您检查他们是否在正确的菜单项上,如果是,则执行您的 ls 调用,否则忽略该键并照常继续。

最后的 if/else 链可能只是一个调用:

draw_info menu, "You selected option #{position}"