UserInterface: add info box support
using the info command, you can display an arbitrary string in a tooltip box.
This commit is contained in:
parent
cccd0388eb
commit
9236c87842
|
@ -512,6 +512,9 @@ public:
|
||||||
void menu_select(int) override {}
|
void menu_select(int) override {}
|
||||||
void menu_hide() override {}
|
void menu_hide() override {}
|
||||||
|
|
||||||
|
void info_show(const String&, const DisplayCoord&, MenuStyle) override {}
|
||||||
|
void info_hide() override {}
|
||||||
|
|
||||||
DisplayCoord dimensions() override { return { 0, 0 }; }
|
DisplayCoord dimensions() override { return { 0, 0 }; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -599,6 +602,18 @@ void menu(const CommandParameters& params, Context& 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)
|
void try_catch(const CommandParameters& params, Context& context)
|
||||||
{
|
{
|
||||||
if (params.size() != 3)
|
if (params.size() != 3)
|
||||||
|
@ -721,6 +736,7 @@ void register_commands()
|
||||||
cm.register_command("exec", exec_string);
|
cm.register_command("exec", exec_string);
|
||||||
cm.register_command("eval", eval_string);
|
cm.register_command("eval", eval_string);
|
||||||
cm.register_command("menu", menu);
|
cm.register_command("menu", menu);
|
||||||
|
cm.register_command("info", info);
|
||||||
cm.register_command("try", try_catch);
|
cm.register_command("try", try_catch);
|
||||||
|
|
||||||
cm.register_command("def", define_command);
|
cm.register_command("def", define_command);
|
||||||
|
|
|
@ -117,7 +117,7 @@ NCursesUI::~NCursesUI()
|
||||||
signal(SIGINT, SIG_DFL);
|
signal(SIGINT, SIG_DFL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void redraw(WINDOW* menu_win)
|
static void redraw(WINDOW* menu_win, WINDOW* info_win)
|
||||||
{
|
{
|
||||||
wnoutrefresh(stdscr);
|
wnoutrefresh(stdscr);
|
||||||
if (menu_win)
|
if (menu_win)
|
||||||
|
@ -125,15 +125,20 @@ static void redraw(WINDOW* menu_win)
|
||||||
redrawwin(menu_win);
|
redrawwin(menu_win);
|
||||||
wnoutrefresh(menu_win);
|
wnoutrefresh(menu_win);
|
||||||
}
|
}
|
||||||
|
if (info_win)
|
||||||
|
{
|
||||||
|
redrawwin(info_win);
|
||||||
|
wnoutrefresh(info_win);
|
||||||
|
}
|
||||||
doupdate();
|
doupdate();
|
||||||
}
|
}
|
||||||
using Utf8Policy = utf8::InvalidBytePolicy::Pass;
|
using Utf8Policy = utf8::InvalidBytePolicy::Pass;
|
||||||
using Utf8Iterator = utf8::utf8_iterator<String::iterator, Utf8Policy>;
|
using Utf8Iterator = utf8::utf8_iterator<String::iterator, Utf8Policy>;
|
||||||
void addutf8str(Utf8Iterator begin, Utf8Iterator end)
|
void addutf8str(WINDOW* win, Utf8Iterator begin, Utf8Iterator end)
|
||||||
{
|
{
|
||||||
assert(begin <= end);
|
assert(begin <= end);
|
||||||
while (begin != end)
|
while (begin != end)
|
||||||
addch(*begin++);
|
waddch(win, *begin++);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCursesUI::update_dimensions()
|
void NCursesUI::update_dimensions()
|
||||||
|
@ -166,7 +171,7 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer,
|
||||||
if (content[content.length()-1] == '\n' and
|
if (content[content.length()-1] == '\n' and
|
||||||
content.char_length() - 1 < m_dimensions.column - col_index)
|
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(' ');
|
addch(' ');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -174,7 +179,7 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer,
|
||||||
Utf8Iterator begin(content.begin()), end(content.end());
|
Utf8Iterator begin(content.begin()), end(content.end());
|
||||||
if (end - begin > m_dimensions.column - col_index)
|
if (end - begin > m_dimensions.column - col_index)
|
||||||
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;
|
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)
|
if (m_dimensions.column - m_status_line.char_length() > status_len + 1)
|
||||||
{
|
{
|
||||||
move((int)m_dimensions.line, (int)(m_dimensions.column - status_len));
|
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()));
|
Utf8Iterator(mode_line.end()));
|
||||||
}
|
}
|
||||||
redraw(m_menu_win);
|
redraw(m_menu_win, m_info_win);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct getch_iterator
|
struct getch_iterator
|
||||||
|
@ -277,18 +282,18 @@ void NCursesUI::draw_status()
|
||||||
move((int)m_dimensions.line, 0);
|
move((int)m_dimensions.line, 0);
|
||||||
clrtoeol();
|
clrtoeol();
|
||||||
if (m_status_cursor == -1)
|
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
|
else
|
||||||
{
|
{
|
||||||
auto cursor_it = utf8::advance(m_status_line.begin(), m_status_line.end(),
|
auto cursor_it = utf8::advance(m_status_line.begin(), m_status_line.end(),
|
||||||
(int)m_status_cursor);
|
(int)m_status_cursor);
|
||||||
auto end = m_status_line.end();
|
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);
|
set_attribute(A_REVERSE, 1);
|
||||||
addch((cursor_it == end) ? ' ' : utf8::codepoint<Utf8Policy>(cursor_it));
|
addch((cursor_it == end) ? ' ' : utf8::codepoint<Utf8Policy>(cursor_it));
|
||||||
set_attribute(A_REVERSE, 0);
|
set_attribute(A_REVERSE, 0);
|
||||||
if (cursor_it != end)
|
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_line = status;
|
||||||
m_status_cursor = cursor_pos;
|
m_status_cursor = cursor_pos;
|
||||||
draw_status();
|
draw_status();
|
||||||
redraw(m_menu_win);
|
redraw(m_menu_win, m_info_win);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCursesUI::menu_show(const memoryview<String>& choices,
|
void NCursesUI::menu_show(const memoryview<String>& choices,
|
||||||
|
@ -326,14 +331,14 @@ void NCursesUI::menu_show(const memoryview<String>& choices,
|
||||||
int columns = (style == MenuStyle::Prompt) ? (max_x / (int)longest) : 1;
|
int columns = (style == MenuStyle::Prompt) ? (max_x / (int)longest) : 1;
|
||||||
int lines = std::min(10, (int)ceilf((float)m_choices.size()/columns));
|
int lines = std::min(10, (int)ceilf((float)m_choices.size()/columns));
|
||||||
|
|
||||||
m_menu_pos = { anchor.line+1, anchor.column };
|
DisplayCoord pos = { anchor.line+1, anchor.column };
|
||||||
if (m_menu_pos.line + lines >= max_y)
|
if (pos.line + lines >= max_y)
|
||||||
m_menu_pos.line = anchor.line - lines;
|
pos.line = anchor.line - lines;
|
||||||
m_menu_size = { lines, columns == 1 ? longest : max_x };
|
DisplayCoord size = { lines, columns == 1 ? longest : max_x };
|
||||||
|
|
||||||
m_menu = new_menu(&m_items[0]);
|
m_menu = new_menu(&m_items[0]);
|
||||||
m_menu_win = newwin((int)m_menu_size.line, (int)m_menu_size.column,
|
m_menu_win = newwin((int)size.line, (int)size.column,
|
||||||
(int)m_menu_pos.line, (int)m_menu_pos.column);
|
(int)pos.line, (int)pos.column);
|
||||||
set_menu_win(m_menu, m_menu_win);
|
set_menu_win(m_menu, m_menu_win);
|
||||||
set_menu_format(m_menu, lines, columns);
|
set_menu_format(m_menu, lines, columns);
|
||||||
set_menu_mark(m_menu, nullptr);
|
set_menu_mark(m_menu, nullptr);
|
||||||
|
@ -370,6 +375,59 @@ void NCursesUI::menu_hide()
|
||||||
m_choices.clear();
|
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()
|
DisplayCoord NCursesUI::dimensions()
|
||||||
{
|
{
|
||||||
return m_dimensions;
|
return m_dimensions;
|
||||||
|
|
|
@ -31,6 +31,10 @@ public:
|
||||||
void menu_select(int selected) override;
|
void menu_select(int selected) override;
|
||||||
void menu_hide() override;
|
void menu_hide() override;
|
||||||
|
|
||||||
|
void info_show(const String& content,
|
||||||
|
const DisplayCoord& anchor, MenuStyle style) override;
|
||||||
|
void info_hide() override;
|
||||||
|
|
||||||
DisplayCoord dimensions() override;
|
DisplayCoord dimensions() override;
|
||||||
private:
|
private:
|
||||||
friend void on_term_resize(int);
|
friend void on_term_resize(int);
|
||||||
|
@ -47,11 +51,10 @@ private:
|
||||||
std::vector<ITEM*> m_items;
|
std::vector<ITEM*> m_items;
|
||||||
std::vector<String> m_choices;
|
std::vector<String> m_choices;
|
||||||
|
|
||||||
DisplayCoord m_menu_pos;
|
|
||||||
DisplayCoord m_menu_size;
|
|
||||||
|
|
||||||
int m_menu_fg;
|
int m_menu_fg;
|
||||||
int m_menu_bg;
|
int m_menu_bg;
|
||||||
|
|
||||||
|
WINDOW* m_info_win = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ enum class RemoteUIMsg
|
||||||
MenuShow,
|
MenuShow,
|
||||||
MenuSelect,
|
MenuSelect,
|
||||||
MenuHide,
|
MenuHide,
|
||||||
|
InfoShow,
|
||||||
|
InfoHide,
|
||||||
Draw
|
Draw
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,6 +203,22 @@ void RemoteUI::menu_hide()
|
||||||
msg.write(RemoteUIMsg::MenuHide);
|
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,
|
void RemoteUI::draw(const DisplayBuffer& display_buffer,
|
||||||
const String& mode_line)
|
const String& mode_line)
|
||||||
{
|
{
|
||||||
|
@ -276,6 +294,17 @@ void RemoteClient::process_next_message()
|
||||||
case RemoteUIMsg::MenuHide:
|
case RemoteUIMsg::MenuHide:
|
||||||
m_ui->menu_hide();
|
m_ui->menu_hide();
|
||||||
break;
|
break;
|
||||||
|
case RemoteUIMsg::InfoShow:
|
||||||
|
{
|
||||||
|
auto choices = read<String>(m_socket);
|
||||||
|
auto anchor = read<DisplayCoord>(m_socket);
|
||||||
|
auto style = read<MenuStyle>(m_socket);
|
||||||
|
m_ui->info_show(choices, anchor, style);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RemoteUIMsg::InfoHide:
|
||||||
|
m_ui->info_hide();
|
||||||
|
break;
|
||||||
case RemoteUIMsg::Draw:
|
case RemoteUIMsg::Draw:
|
||||||
{
|
{
|
||||||
DisplayBuffer display_buffer = read<DisplayBuffer>(m_socket);
|
DisplayBuffer display_buffer = read<DisplayBuffer>(m_socket);
|
||||||
|
|
|
@ -16,10 +16,16 @@ public:
|
||||||
~RemoteUI();
|
~RemoteUI();
|
||||||
|
|
||||||
void print_status(const String& status, CharCount cursor_pos) override;
|
void print_status(const String& status, CharCount cursor_pos) override;
|
||||||
|
|
||||||
void menu_show(const memoryview<String>& choices,
|
void menu_show(const memoryview<String>& choices,
|
||||||
const DisplayCoord& anchor, MenuStyle style) override;
|
const DisplayCoord& anchor, MenuStyle style) override;
|
||||||
void menu_select(int selected) override;
|
void menu_select(int selected) override;
|
||||||
void menu_hide() 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,
|
void draw(const DisplayBuffer& display_buffer,
|
||||||
const String& mode_line) override;
|
const String& mode_line) override;
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,16 @@ class UserInterface : public SafeCountable
|
||||||
public:
|
public:
|
||||||
virtual ~UserInterface() {}
|
virtual ~UserInterface() {}
|
||||||
virtual void print_status(const String& status, CharCount cursor_pos = -1) = 0;
|
virtual void print_status(const String& status, CharCount cursor_pos = -1) = 0;
|
||||||
|
|
||||||
virtual void menu_show(const memoryview<String>& choices,
|
virtual void menu_show(const memoryview<String>& choices,
|
||||||
const DisplayCoord& anchor, MenuStyle style) = 0;
|
const DisplayCoord& anchor, MenuStyle style) = 0;
|
||||||
virtual void menu_select(int selected) = 0;
|
virtual void menu_select(int selected) = 0;
|
||||||
virtual void menu_hide() = 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,
|
virtual void draw(const DisplayBuffer& display_buffer,
|
||||||
const String& mode_line) = 0;
|
const String& mode_line) = 0;
|
||||||
virtual DisplayCoord dimensions() = 0;
|
virtual DisplayCoord dimensions() = 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user