From 9236c87842d581806e58fcf1b6003fccf5b2409c Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Fri, 14 Dec 2012 19:04:34 +0100 Subject: [PATCH] UserInterface: add info box support using the info command, you can display an arbitrary string in a tooltip box. --- src/commands.cc | 16 ++++++++ src/ncurses.cc | 92 +++++++++++++++++++++++++++++++++++-------- src/ncurses.hh | 9 +++-- src/remote.cc | 29 ++++++++++++++ src/remote.hh | 6 +++ src/user_interface.hh | 6 +++ 6 files changed, 138 insertions(+), 20 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index 6a357a02..f47c1574 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -512,6 +512,9 @@ public: void menu_select(int) override {} void menu_hide() override {} + void info_show(const String&, const DisplayCoord&, MenuStyle) override {} + void info_hide() override {} + DisplayCoord dimensions() override { return { 0, 0 }; } private: @@ -599,6 +602,18 @@ void menu(const CommandParameters& params, Context& context) }, context); } +void info(const CommandParameters& params, Context& context) +{ + ParametersParser parser(params, { }); + + if (parser.positional_count() > 1) + throw wrong_argument_count(); + + context.ui().info_hide(); + if (parser.positional_count() > 0) + context.ui().info_show(parser[0], {0,0}, MenuStyle::Inline); +} + void try_catch(const CommandParameters& params, Context& context) { if (params.size() != 3) @@ -721,6 +736,7 @@ void register_commands() cm.register_command("exec", exec_string); cm.register_command("eval", eval_string); cm.register_command("menu", menu); + cm.register_command("info", info); cm.register_command("try", try_catch); cm.register_command("def", define_command); diff --git a/src/ncurses.cc b/src/ncurses.cc index d4fc8e39..a5fa47a9 100644 --- a/src/ncurses.cc +++ b/src/ncurses.cc @@ -117,7 +117,7 @@ NCursesUI::~NCursesUI() signal(SIGINT, SIG_DFL); } -static void redraw(WINDOW* menu_win) +static void redraw(WINDOW* menu_win, WINDOW* info_win) { wnoutrefresh(stdscr); if (menu_win) @@ -125,15 +125,20 @@ static void redraw(WINDOW* menu_win) redrawwin(menu_win); wnoutrefresh(menu_win); } + if (info_win) + { + redrawwin(info_win); + wnoutrefresh(info_win); + } doupdate(); } using Utf8Policy = utf8::InvalidBytePolicy::Pass; using Utf8Iterator = utf8::utf8_iterator; -void addutf8str(Utf8Iterator begin, Utf8Iterator end) +void addutf8str(WINDOW* win, Utf8Iterator begin, Utf8Iterator end) { assert(begin <= end); while (begin != end) - addch(*begin++); + waddch(win, *begin++); } void NCursesUI::update_dimensions() @@ -166,7 +171,7 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer, if (content[content.length()-1] == '\n' and content.char_length() - 1 < m_dimensions.column - col_index) { - addutf8str(Utf8Iterator(content.begin()), Utf8Iterator(content.end())-1); + addutf8str(stdscr, Utf8Iterator(content.begin()), Utf8Iterator(content.end())-1); addch(' '); } else @@ -174,7 +179,7 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer, Utf8Iterator begin(content.begin()), end(content.end()); if (end - begin > m_dimensions.column - col_index) end = begin + (m_dimensions.column - col_index); - addutf8str(begin, end); + addutf8str(stdscr, begin, end); col_index += end - begin; } } @@ -200,10 +205,10 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer, if (m_dimensions.column - m_status_line.char_length() > status_len + 1) { move((int)m_dimensions.line, (int)(m_dimensions.column - status_len)); - addutf8str(Utf8Iterator(mode_line.begin()), + addutf8str(stdscr, Utf8Iterator(mode_line.begin()), Utf8Iterator(mode_line.end())); } - redraw(m_menu_win); + redraw(m_menu_win, m_info_win); } struct getch_iterator @@ -277,18 +282,18 @@ void NCursesUI::draw_status() move((int)m_dimensions.line, 0); clrtoeol(); if (m_status_cursor == -1) - addutf8str(m_status_line.begin(), m_status_line.end()); + addutf8str(stdscr, m_status_line.begin(), m_status_line.end()); else { auto cursor_it = utf8::advance(m_status_line.begin(), m_status_line.end(), (int)m_status_cursor); auto end = m_status_line.end(); - addutf8str(m_status_line.begin(), cursor_it); + addutf8str(stdscr, m_status_line.begin(), cursor_it); set_attribute(A_REVERSE, 1); addch((cursor_it == end) ? ' ' : utf8::codepoint(cursor_it)); set_attribute(A_REVERSE, 0); if (cursor_it != end) - addutf8str(utf8::next(cursor_it), end); + addutf8str(stdscr, utf8::next(cursor_it), end); } } @@ -297,7 +302,7 @@ void NCursesUI::print_status(const String& status, CharCount cursor_pos) m_status_line = status; m_status_cursor = cursor_pos; draw_status(); - redraw(m_menu_win); + redraw(m_menu_win, m_info_win); } void NCursesUI::menu_show(const memoryview& choices, @@ -326,14 +331,14 @@ void NCursesUI::menu_show(const memoryview& choices, int columns = (style == MenuStyle::Prompt) ? (max_x / (int)longest) : 1; int lines = std::min(10, (int)ceilf((float)m_choices.size()/columns)); - m_menu_pos = { anchor.line+1, anchor.column }; - if (m_menu_pos.line + lines >= max_y) - m_menu_pos.line = anchor.line - lines; - m_menu_size = { lines, columns == 1 ? longest : max_x }; + DisplayCoord pos = { anchor.line+1, anchor.column }; + if (pos.line + lines >= max_y) + pos.line = anchor.line - lines; + DisplayCoord size = { lines, columns == 1 ? longest : max_x }; m_menu = new_menu(&m_items[0]); - m_menu_win = newwin((int)m_menu_size.line, (int)m_menu_size.column, - (int)m_menu_pos.line, (int)m_menu_pos.column); + 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); @@ -370,6 +375,59 @@ void NCursesUI::menu_hide() m_choices.clear(); } +static DisplayCoord compute_needed_size(const String& str) +{ + DisplayCoord res{1,0}; + CharCount line_len = 0; + for (Utf8Iterator begin{str.begin()}, end{str.end()}; begin != end; ++begin) + { + if (*begin == '\n') + { + line_len = 0; + ++res.line; + } + else + { + ++line_len; + res.column = std::max(res.column, line_len); + } + } + return res; +} + +void NCursesUI::info_show(const String& content, const DisplayCoord& anchor, MenuStyle style) +{ + assert(m_info_win == nullptr); + + int max_x,max_y; + getmaxyx(stdscr, max_y, max_x); + max_x -= (int)anchor.column; + + DisplayCoord size = compute_needed_size(content); + if (style == MenuStyle::Prompt) + size.column = max_x; + + DisplayCoord pos = { anchor.line+1, anchor.column }; + if (pos.line + size.line >= max_y) + pos.line = anchor.line - size.line; + + m_info_win = newwin((int)size.line, (int)size.column, + (int)pos.line, (int)pos.column); + + wbkgd(m_info_win, COLOR_PAIR(get_color_pair(Color::Black, Color::Yellow))); + wmove(m_info_win, 0, 0); + addutf8str(m_info_win, Utf8Iterator(content.begin()), + Utf8Iterator(content.end())); +} + +void NCursesUI::info_hide() +{ + if (not m_info_win) + return; + delwin(m_info_win); + m_info_win = nullptr; +} + DisplayCoord NCursesUI::dimensions() { return m_dimensions; diff --git a/src/ncurses.hh b/src/ncurses.hh index 080316cb..b845fdf2 100644 --- a/src/ncurses.hh +++ b/src/ncurses.hh @@ -31,6 +31,10 @@ public: void menu_select(int selected) override; void menu_hide() override; + void info_show(const String& content, + const DisplayCoord& anchor, MenuStyle style) override; + void info_hide() override; + DisplayCoord dimensions() override; private: friend void on_term_resize(int); @@ -47,11 +51,10 @@ private: std::vector m_items; std::vector m_choices; - DisplayCoord m_menu_pos; - DisplayCoord m_menu_size; - int m_menu_fg; int m_menu_bg; + + WINDOW* m_info_win = nullptr; }; } diff --git a/src/remote.cc b/src/remote.cc index 72528a38..207cafc2 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -15,6 +15,8 @@ enum class RemoteUIMsg MenuShow, MenuSelect, MenuHide, + InfoShow, + InfoHide, Draw }; @@ -201,6 +203,22 @@ void RemoteUI::menu_hide() msg.write(RemoteUIMsg::MenuHide); } +void RemoteUI::info_show(const String& content, + const DisplayCoord& anchor, MenuStyle style) +{ + Message msg(m_socket); + msg.write(RemoteUIMsg::InfoShow); + msg.write(content); + msg.write(anchor); + msg.write(style); +} + +void RemoteUI::info_hide() +{ + Message msg(m_socket); + msg.write(RemoteUIMsg::InfoHide); +} + void RemoteUI::draw(const DisplayBuffer& display_buffer, const String& mode_line) { @@ -276,6 +294,17 @@ void RemoteClient::process_next_message() case RemoteUIMsg::MenuHide: m_ui->menu_hide(); break; + case RemoteUIMsg::InfoShow: + { + auto choices = read(m_socket); + auto anchor = read(m_socket); + auto style = read(m_socket); + m_ui->info_show(choices, anchor, style); + break; + } + case RemoteUIMsg::InfoHide: + m_ui->info_hide(); + break; case RemoteUIMsg::Draw: { DisplayBuffer display_buffer = read(m_socket); diff --git a/src/remote.hh b/src/remote.hh index aa0a31a3..a838f259 100644 --- a/src/remote.hh +++ b/src/remote.hh @@ -16,10 +16,16 @@ public: ~RemoteUI(); void print_status(const String& status, CharCount cursor_pos) override; + void menu_show(const memoryview& choices, const DisplayCoord& anchor, MenuStyle style) override; void menu_select(int selected) override; void menu_hide() override; + + void info_show(const String& content, + const DisplayCoord& anchor, MenuStyle style) override; + void info_hide() override; + void draw(const DisplayBuffer& display_buffer, const String& mode_line) override; diff --git a/src/user_interface.hh b/src/user_interface.hh index 3d474322..185619e0 100644 --- a/src/user_interface.hh +++ b/src/user_interface.hh @@ -24,10 +24,16 @@ class UserInterface : public SafeCountable public: virtual ~UserInterface() {} virtual void print_status(const String& status, CharCount cursor_pos = -1) = 0; + virtual void menu_show(const memoryview& choices, const DisplayCoord& anchor, MenuStyle style) = 0; virtual void menu_select(int selected) = 0; virtual void menu_hide() = 0; + + virtual void info_show(const String& content, + const DisplayCoord& anchor, MenuStyle style) = 0; + virtual void info_hide() = 0; + virtual void draw(const DisplayBuffer& display_buffer, const String& mode_line) = 0; virtual DisplayCoord dimensions() = 0;