NCurses: hand coded menu implementation

This commit is contained in:
Maxime Coste 2013-03-14 19:19:33 +01:00
parent 65850ff1e8
commit 17b2d8c052
2 changed files with 61 additions and 39 deletions

View File

@ -99,9 +99,6 @@ NCursesUI::NCursesUI()
use_default_colors();
ESCDELAY=25;
m_menu_fg = get_color_pair({ Color::Blue, Color::Cyan });
m_menu_bg = get_color_pair({ Color::Cyan, Color::Blue });
signal(SIGWINCH, on_term_resize);
signal(SIGINT, on_sigint);
@ -321,13 +318,46 @@ void NCursesUI::print_status(const String& status, CharCount cursor_pos)
redraw();
}
void NCursesUI::draw_menu()
{
assert(m_menu_win);
auto menu_fg = get_color_pair({ Color::Blue, Color::Cyan });
auto menu_bg = get_color_pair({ Color::Cyan, Color::Blue });
wattron(m_menu_win, COLOR_PAIR(menu_bg));
wbkgdset(m_menu_win, COLOR_PAIR(menu_bg));
DisplayCoord menu_size = window_size(m_menu_win);
CharCount column_width = menu_size.column / m_menu_columns;
for (auto line = 0_line; line < menu_size.line; ++line)
{
wmove(m_menu_win, (int)line, 0);
for (int col = 0; col < m_menu_columns; ++col)
{
int choice_idx = (int)(m_menu_top_line + line) * m_menu_columns + col;
if (choice_idx >= m_choices.size())
break;
if (choice_idx == m_selected_choice)
wattron(m_menu_win, COLOR_PAIR(menu_fg));
auto& choice = m_choices[choice_idx];
auto begin = choice.begin();
auto end = utf8::advance(begin, choice.end(), column_width);
addutf8str(m_menu_win, begin, end);
for (auto pad = column_width - utf8::distance(begin, end); pad > 0; --pad)
waddch(m_menu_win, ' ');
wattron(m_menu_win, COLOR_PAIR(menu_bg));
}
wclrtoeol(m_menu_win);
}
redraw();
}
void NCursesUI::menu_show(const memoryview<String>& choices,
const DisplayCoord& anchor, MenuStyle style)
{
assert(m_menu == nullptr);
assert(m_menu_win == nullptr);
assert(m_choices.empty());
assert(m_items.empty());
DisplayCoord maxsize = window_size(stdscr);
maxsize.column -= anchor.column;
@ -336,57 +366,51 @@ void NCursesUI::menu_show(const memoryview<String>& choices,
CharCount longest = 0;
for (auto& choice : choices)
{
m_choices.push_back(choice.empty() ? " " : choice.substr(0_char, std::min((int)maxsize.column-1, 200)));
m_items.emplace_back(new_item(m_choices.back().c_str(), ""));
m_choices.push_back(choice.substr(0_char, std::min((int)maxsize.column-1, 200)));
longest = std::max(longest, m_choices.back().char_length());
}
m_items.push_back(nullptr);
longest += 1;
int columns = (style == MenuStyle::Prompt) ? (int)(maxsize.column / longest) : 1;
int lines = std::min(10, (int)ceilf((float)m_choices.size()/columns));
m_menu_columns = (style == MenuStyle::Prompt) ? (int)(maxsize.column / longest) : 1;
int lines = std::min(10, (int)ceilf((float)m_choices.size()/m_menu_columns));
DisplayCoord pos = { anchor.line+1, anchor.column };
if (pos.line + lines >= maxsize.line)
pos.line = anchor.line - lines;
DisplayCoord size = { lines, columns == 1 ? longest : maxsize.column };
DisplayCoord size = { lines, style == MenuStyle::Prompt ? maxsize.column : longest };
m_menu = new_menu(&m_items[0]);
m_selected_choice = 0;
m_menu_top_line = 0;
m_menu_win = newwin((int)size.line, (int)size.column,
(int)pos.line, (int)pos.column);
set_menu_win(m_menu, m_menu_win);
set_menu_format(m_menu, lines, columns);
set_menu_mark(m_menu, nullptr);
set_menu_fore(m_menu, COLOR_PAIR(m_menu_fg));
set_menu_back(m_menu, COLOR_PAIR(m_menu_bg));
post_menu(m_menu);
redraw();
draw_menu();
}
void NCursesUI::menu_select(int selected)
{
// last item in m_items is the nullptr, hence the - 1
if (selected >= 0 and selected < m_items.size() - 1)
if (selected < 0 or selected >= m_choices.size())
{
set_menu_fore(m_menu, COLOR_PAIR(m_menu_fg));
set_current_item(m_menu, m_items[selected]);
m_selected_choice = -1;
m_menu_top_line = 0;
}
else
set_menu_fore(m_menu, COLOR_PAIR(m_menu_bg));
redraw();
{
m_selected_choice = selected;
LineCount selected_line = m_selected_choice / m_menu_columns;
DisplayCoord menu_size = window_size(m_menu_win);
if (selected_line < m_menu_top_line)
m_menu_top_line = selected_line;
if (selected_line >= m_menu_top_line + menu_size.line)
m_menu_top_line = selected_line;
}
draw_menu();
}
void NCursesUI::menu_hide()
{
if (not m_menu)
if (not m_menu_win)
return;
unpost_menu(m_menu);
free_menu(m_menu);
for (auto item : m_items)
if (item)
free_item(item);
m_menu = nullptr;
m_items.clear();
m_choices.clear();
wredrawln(stdscr, (int)window_pos(m_menu_win).line, (int)window_size(m_menu_win).line);
delwin(m_menu_win);

View File

@ -2,7 +2,6 @@
#define ncurses_hh_INCLUDED
#include <ncurses.h>
#include <menu.h>
#include "user_interface.hh"
#include "display_buffer.hh"
@ -50,13 +49,12 @@ private:
CharCount m_status_cursor = -1;
void draw_status();
MENU* m_menu = nullptr;
WINDOW* m_menu_win = nullptr;
std::vector<ITEM*> m_items;
std::vector<String> m_choices;
int m_menu_fg;
int m_menu_bg;
int m_selected_choice = 0;
int m_menu_columns = 1;
LineCount m_menu_top_line = 0;
void draw_menu();
WINDOW* m_info_win = nullptr;