Pass title to UserInterface::info_show

Move unicode box generation and assistant code as
a NcursesUI implementation detail.
This commit is contained in:
Maxime Coste 2013-10-10 22:51:16 +01:00
parent 93f6a2ee43
commit 3c959cee99
6 changed files with 167 additions and 150 deletions

View File

@ -543,7 +543,7 @@ public:
void menu_select(int) override {} void menu_select(int) override {}
void menu_hide() override {} void menu_hide() override {}
void info_show(const String&, DisplayCoord, ColorPair, MenuStyle) override {} void info_show(const String&, const String&, DisplayCoord, ColorPair, MenuStyle) override {}
void info_hide() override {} void info_hide() override {}
void draw(const DisplayBuffer&, const DisplayLine&, const DisplayLine&) override {} void draw(const DisplayBuffer&, const DisplayLine&, const DisplayLine&) override {}
@ -642,88 +642,17 @@ void menu(CommandParameters params, Context& context)
}); });
} }
static String assist(String message, CharCount maxWidth)
{
static const std::vector<String> assistant =
{ " ╭──╮ ",
" │ │ ",
" @ @ ╭",
" ││ ││ │",
" ││ ││ ╯",
" │╰─╯│ ",
" ╰───╯ ",
" " };
const CharCount maxBubbleWidth = maxWidth - assistant[0].char_length() - 6;
CharCount bubbleWidth = 0;
std::vector<String> lines;
{
using Utf8It = utf8::utf8_iterator<String::iterator>;
Utf8It word_begin{message.begin()};
Utf8It word_end{word_begin};
Utf8It end{message.end()};
CharCount col = 0;
String line;
while (word_begin != end)
{
do
{
++word_end;
} while (word_end != end and not is_blank(*word_end) and not is_eol(*word_end));
col += word_end - word_begin;
if (col > maxBubbleWidth or *word_begin == '\n')
{
bubbleWidth = std::max(bubbleWidth, line.char_length());
lines.push_back(std::move(line));
line = "";
col = 0;
}
if (*word_begin != '\n')
line += String{word_begin.base(), word_end.base()};
word_begin = word_end;
}
if (not line.empty())
{
bubbleWidth = std::max(bubbleWidth, line.char_length());
lines.push_back(std::move(line));
}
}
String result;
LineCount lineCount{std::max<int>(assistant.size()-1, lines.size() + 2)};
for (LineCount i = 0; i < lineCount; ++i)
{
result += assistant[std::min((int)i, (int)assistant.size()-1)];
if (i == 0)
result += "╭─" + String(Codepoint{L''}, bubbleWidth) + "─╮";
else if (i < lines.size() + 1)
{
auto& line = lines[(int)i - 1];
const CharCount padding = std::max(bubbleWidth - line.char_length(), 0_char);
result += "" + line + String(' ', padding) + "";
}
else if (i == lines.size() + 1)
result += "╰─" + String(Codepoint{L''}, bubbleWidth) + "─╯";
result += "\n";
}
return result;
}
void info(CommandParameters params, Context& context) void info(CommandParameters params, Context& context)
{ {
ParametersParser parser(params, { { "anchor", true }, { "assist", false } }, ParametersParser parser(params, { { "anchor", true }, { "title", true } },
ParametersParser::Flags::None, 0, 1); ParametersParser::Flags::None, 0, 1);
context.ui().info_hide(); context.ui().info_hide();
if (parser.positional_count() > 0) if (parser.positional_count() > 0)
{ {
MenuStyle style = MenuStyle::Prompt; MenuStyle style = MenuStyle::Prompt;
DisplayCoord dimensions = context.ui().dimensions(); DisplayCoord pos = context.ui().dimensions();
DisplayCoord pos = { dimensions.line, 0 }; pos.column -= 1;
if (parser.has_option("anchor")) if (parser.has_option("anchor"))
{ {
style = MenuStyle::Inline; style = MenuStyle::Inline;
@ -738,8 +667,8 @@ void info(CommandParameters params, Context& context)
throw runtime_error("anchor param must be one of [left, right, cursor]"); throw runtime_error("anchor param must be one of [left, right, cursor]");
pos = context.window().display_position(it); pos = context.window().display_position(it);
} }
const String& message = parser.has_option("assist") ? assist(parser[0], dimensions.column) : parser[0]; const String& title = parser.has_option("title") ? parser.option_value("title") : "";
context.ui().info_show(message, pos, get_color("Information"), style); context.ui().info_show(title, parser[0], pos, get_color("Information"), style);
} }
} }

View File

@ -534,14 +534,112 @@ static DisplayCoord compute_pos(DisplayCoord anchor,
return pos; return pos;
} }
void NCursesUI::info_show(const String& content, DisplayCoord anchor, static std::vector<String> wrap_lines(const String& text, CharCount max_width)
ColorPair colors, MenuStyle style) {
enum CharCategory { Word, Blank, Eol };
static const auto categorize = [](Codepoint c) {
return is_blank(c) ? Blank
: is_eol(c) ? Eol : Word;
};
using Utf8It = utf8::utf8_iterator<String::const_iterator>;
Utf8It word_begin{text.begin()};
Utf8It word_end{word_begin};
Utf8It end{text.end()};
CharCount col = 0;
std::vector<String> lines;
String line;
while (word_begin != end)
{
CharCategory cat = categorize(*word_begin);
do
{
++word_end;
} while (word_end != end and categorize(*word_end) == cat);
col += word_end - word_begin;
if (col > max_width or *word_begin == '\n')
{
lines.push_back(std::move(line));
line = "";
col = 0;
}
if (*word_begin != '\n')
line += String{word_begin.base(), word_end.base()};
word_begin = word_end;
}
if (not line.empty())
lines.push_back(std::move(line));
return lines;
}
template<bool assist = true>
static String make_info_box(const String& title, const String& message,
CharCount max_width)
{
static const std::vector<String> assistant =
{ " ╭──╮ ",
" │ │ ",
" @ @ ╭",
" ││ ││ │",
" ││ ││ ╯",
" │╰─╯│ ",
" ╰───╯ ",
" " };
DisplayCoord assistant_size;
if (assist)
assistant_size = { (int)assistant.size(), assistant[0].char_length() };
const CharCount max_bubble_width = max_width - assistant_size.column - 6;
std::vector<String> lines = wrap_lines(message, max_bubble_width);
CharCount bubble_width = title.char_length() + 2;
for (auto& line : lines)
bubble_width = std::max(bubble_width, line.char_length());
String result;
auto lineCount = std::max<LineCount>(assistant_size.line-1, (int)lines.size() + 2);
for (LineCount i = 0; i < lineCount; ++i)
{
constexpr Codepoint dash{L''};
if (assist)
result += assistant[std::min((int)i, (int)assistant_size.line-1)];
if (i == 0)
{
if (title.empty())
result += "╭─" + String(dash, bubble_width) + "─╮";
else
{
CharCount dashCount = bubble_width - title.char_length() - 2_char;
CharCount leftCount = dashCount / 2;
CharCount rightCount= dashCount - leftCount;
result += "╭─" + String(dash, leftCount) + "" + title +"" + String(dash, rightCount) +"─╮";
}
}
else if (i < lines.size() + 1)
{
auto& line = lines[(int)i - 1];
const CharCount padding = std::max(bubble_width - line.char_length(), 0_char);
result += "" + line + String(' ', padding) + "";
}
else if (i == lines.size() + 1)
result += "╰─" + String(dash, bubble_width) + "─╯";
result += "\n";
}
return result;
}
void NCursesUI::info_show(const String& title, const String& content,
DisplayCoord anchor, ColorPair colors,
MenuStyle style)
{ {
kak_assert(m_info_win == nullptr); kak_assert(m_info_win == nullptr);
DisplayCoord size = compute_needed_size(content); const String& info_box = style == MenuStyle::Inline ?
if (style == MenuStyle::Prompt) content : make_info_box(title, content, m_dimensions.column);
size.column = window_size(stdscr).column - anchor.column;
DisplayCoord size = compute_needed_size(info_box);
DisplayCoord pos = compute_pos(anchor, size, m_menu_win); DisplayCoord pos = compute_pos(anchor, size, m_menu_win);
@ -550,7 +648,7 @@ void NCursesUI::info_show(const String& content, DisplayCoord anchor,
wbkgd(m_info_win, COLOR_PAIR(get_color_pair(colors))); wbkgd(m_info_win, COLOR_PAIR(get_color_pair(colors)));
int line = 0; int line = 0;
auto it = content.begin(), end = content.end(); auto it = info_box.begin(), end = info_box.end();
while (true) while (true)
{ {
wmove(m_info_win, line++, 0); wmove(m_info_win, line++, 0);

View File

@ -32,8 +32,9 @@ 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, DisplayCoord anchor, void info_show(const String& title, const String& content,
ColorPair colors, MenuStyle style) override; DisplayCoord anchor, ColorPair colors,
MenuStyle style) override;
void info_hide() override; void info_hide() override;
void set_input_callback(InputCallback callback) override; void set_input_callback(InputCallback callback) override;

View File

@ -33,21 +33,23 @@ void repeat_insert(Context& context, int)
context.client().repeat_last_insert(); context.client().repeat_last_insert();
} }
bool show_auto_info_ifn(const String& info, const Context& context) bool show_auto_info_ifn(const String& title, const String& info,
const Context& context)
{ {
if (not context.options()["autoinfo"].get<bool>() or not context.has_ui()) if (not context.options()["autoinfo"].get<bool>() or not context.has_ui())
return false; return false;
ColorPair col = get_color("Information"); ColorPair col = get_color("Information");
DisplayCoord pos = context.window().dimensions(); DisplayCoord pos = context.window().dimensions();
pos.column -= 1; pos.column -= 1;
context.ui().info_show(info, pos , col, MenuStyle::Inline); context.ui().info_show(title, info, pos , col, MenuStyle::Prompt);
return true; return true;
} }
template<typename Cmd> template<typename Cmd>
void on_next_key_with_autoinfo(const Context& context, Cmd cmd, const std::string& info) void on_next_key_with_autoinfo(const Context& context, Cmd cmd,
const String& title, const String& info)
{ {
const bool hide = show_auto_info_ifn(info, context); const bool hide = show_auto_info_ifn(title, info, context);
context.client().on_next_key([hide,cmd](Key key, Context& context) mutable { context.client().on_next_key([hide,cmd](Key key, Context& context) mutable {
if (hide) if (hide)
context.ui().info_hide(); context.ui().info_hide();
@ -152,19 +154,17 @@ void goto_commands(Context& context, int line)
break; break;
} }
} }
}, }, "goto",
"╭────────┤goto├───────╮\n" "g,k: buffer top \n"
"│ g,k: buffer top │\n" "l: line end \n"
"│ l: line end │\n" "h: line begin \n"
"│ h: line begin │\n" "j: buffer bottom\n"
"│ j: buffer bottom │\n" "e: buffer end \n"
"│ e: buffer end │\n" "t: window top \n"
"│ t: window top │\n" "b: window bottom\n"
"│ b: window bottom │\n" "c: window center\n"
"│ c: window center │\n" "a: last buffer \n"
"│ a: last buffer │\n" "f: file \n");
"│ f: file │\n"
"╰─────────────────────╯\n");
} }
} }
@ -200,16 +200,14 @@ void view_commands(Context& context, int param)
context.window().scroll( std::max<CharCount>(1, param)); context.window().scroll( std::max<CharCount>(1, param));
break; break;
} }
}, }, "view",
"╭─────────┤view├─────────╮\n" "v,c: center cursor \n"
"│ v,c: center cursor │\n" "t: cursor on top \n"
"│ t: cursor on top │\n" "b: cursor on bottom\n"
"│ b: cursor on bottom │\n" "h: scroll left \n"
"│ h: scroll left │\n" "j: scroll down \n"
"│ j: scroll down │\n" "k: scroll up \n"
"│ k: scroll up │\n" "l: scroll right \n");
"│ l: scroll right │\n"
"╰────────────────────────╯\n");
} }
void replace_with_char(Context& context, int) void replace_with_char(Context& context, int)
@ -222,10 +220,7 @@ void replace_with_char(Context& context, int)
auto restore_sels = on_scope_end([&]{ editor.select(std::move(sels)); }); auto restore_sels = on_scope_end([&]{ editor.select(std::move(sels)); });
editor.multi_select(std::bind(select_all_matches, _1, _2, Regex{"."})); editor.multi_select(std::bind(select_all_matches, _1, _2, Regex{"."}));
editor.insert(codepoint_to_str(key.key), InsertMode::Replace); editor.insert(codepoint_to_str(key.key), InsertMode::Replace);
}, }, "replace with char", "enter char to replace with\n");
"╭────┤replace with char├─────╮\n"
"│ enter char to replace with │\n"
"╰────────────────────────────╯\n");
} }
Codepoint to_lower(Codepoint cp) { return tolower(cp); } Codepoint to_lower(Codepoint cp) { return tolower(cp); }
@ -620,20 +615,18 @@ void select_object(Context& context, int param)
return context.editor().select(std::bind(select_surrounding, _1, _2, return context.editor().select(std::bind(select_surrounding, _1, _2,
sur.pair, level, flags)); sur.pair, level, flags));
} }
}, }, "select object",
"╭──────┤select object├───────╮\n" "b,(,): parenthesis block\n"
"│ b,(,): parenthesis block │\n" "B,{,}: braces block \n"
"│ B,{,}: braces block │\n" "r,[,]: brackets block \n"
"│ r,[,]: brackets block │\n" "<,>: angle block \n"
"│ <,>: angle block │\n" "\": double quote string\n"
"\": double quote string │\n" "': single quote string\n"
"│ ': single quote string │\n" "w: word \n"
"│ w: word │\n" "W: WORD \n"
"│ W: WORD │\n" "s: sentence \n"
"│ s: sentence │\n" "p: paragraph \n"
"│ p: paragraph │\n" "i: indent \n");
"│ i: indent │\n"
"╰────────────────────────────╯\n");
} }
template<Key::NamedKey key> template<Key::NamedKey key>
@ -704,10 +697,7 @@ void select_to_next_char(Context& context, int param)
std::bind(flags & SelectFlags::Reverse ? select_to_reverse : select_to, std::bind(flags & SelectFlags::Reverse ? select_to_reverse : select_to,
_1, _2, key.key, param, flags & SelectFlags::Inclusive), _1, _2, key.key, param, flags & SelectFlags::Inclusive),
flags & SelectFlags::Extend ? SelectMode::Extend : SelectMode::Replace); flags & SelectFlags::Extend ? SelectMode::Extend : SelectMode::Replace);
}, }, "select to next char","enter char to select to");
"╭──┤select to next char├──╮\n"
"│ enter char to select to │\n"
"╰─────────────────────────╯\n");
} }
void start_or_end_macro_recording(Context& context, int) void start_or_end_macro_recording(Context& context, int)
@ -719,10 +709,7 @@ void start_or_end_macro_recording(Context& context, int)
if (key.modifiers == Key::Modifiers::None and if (key.modifiers == Key::Modifiers::None and
key.key >= 'a' and key.key <= 'z') key.key >= 'a' and key.key <= 'z')
context.client().start_recording(key.key); context.client().start_recording(key.key);
}, }, "record macro", "enter macro name ");
"╭──┤record macro├──╮\n"
"│ enter macro name │\n"
"╰──────────────────╯\n");
} }
void replay_macro(Context& context, int count) void replay_macro(Context& context, int count)
@ -745,10 +732,7 @@ void replay_macro(Context& context, int count)
do { exec_keys(keys, context); } while (--count > 0); do { exec_keys(keys, context); } while (--count > 0);
} }
} }
}, }, "replay macro", "enter macro name");
"╭──┤replay macro├──╮\n"
"│ enter macro name │\n"
"╰──────────────────╯\n");
} }
template<Direction direction> template<Direction direction>

View File

@ -222,8 +222,9 @@ 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, DisplayCoord anchor, void info_show(const String& title, const String& content,
ColorPair colors, MenuStyle style) override; DisplayCoord anchor, ColorPair colors,
MenuStyle style) override;
void info_hide() override; void info_hide() override;
void draw(const DisplayBuffer& display_buffer, void draw(const DisplayBuffer& display_buffer,
@ -281,11 +282,13 @@ void RemoteUI::menu_hide()
msg.write(RemoteUIMsg::MenuHide); msg.write(RemoteUIMsg::MenuHide);
} }
void RemoteUI::info_show(const String& content, DisplayCoord anchor, void RemoteUI::info_show(const String& title, const String& content,
ColorPair colors, MenuStyle style) DisplayCoord anchor, ColorPair colors,
MenuStyle style)
{ {
Message msg(m_socket_watcher.fd()); Message msg(m_socket_watcher.fd());
msg.write(RemoteUIMsg::InfoShow); msg.write(RemoteUIMsg::InfoShow);
msg.write(title);
msg.write(content); msg.write(content);
msg.write(anchor); msg.write(anchor);
msg.write(colors); msg.write(colors);
@ -396,11 +399,12 @@ void RemoteClient::process_next_message()
break; break;
case RemoteUIMsg::InfoShow: case RemoteUIMsg::InfoShow:
{ {
auto choices = read<String>(socket); auto title = read<String>(socket);
auto content = read<String>(socket);
auto anchor = read<DisplayCoord>(socket); auto anchor = read<DisplayCoord>(socket);
auto colors = read<ColorPair>(socket); auto colors = read<ColorPair>(socket);
auto style = read<MenuStyle>(socket); auto style = read<MenuStyle>(socket);
m_ui->info_show(choices, anchor, colors, style); m_ui->info_show(title, content, anchor, colors, style);
break; break;
} }
case RemoteUIMsg::InfoHide: case RemoteUIMsg::InfoHide:

View File

@ -33,8 +33,9 @@ public:
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, DisplayCoord anchor, virtual void info_show(const String& title, const String& content,
ColorPair colors, MenuStyle style) = 0; DisplayCoord anchor, ColorPair colors,
MenuStyle style) = 0;
virtual void info_hide() = 0; virtual void info_hide() = 0;
virtual void draw(const DisplayBuffer& display_buffer, virtual void draw(const DisplayBuffer& display_buffer,