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_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 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)
{
ParametersParser parser(params, { { "anchor", true }, { "assist", false } },
ParametersParser parser(params, { { "anchor", true }, { "title", true } },
ParametersParser::Flags::None, 0, 1);
context.ui().info_hide();
if (parser.positional_count() > 0)
{
MenuStyle style = MenuStyle::Prompt;
DisplayCoord dimensions = context.ui().dimensions();
DisplayCoord pos = { dimensions.line, 0 };
DisplayCoord pos = context.ui().dimensions();
pos.column -= 1;
if (parser.has_option("anchor"))
{
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]");
pos = context.window().display_position(it);
}
const String& message = parser.has_option("assist") ? assist(parser[0], dimensions.column) : parser[0];
context.ui().info_show(message, pos, get_color("Information"), style);
const String& title = parser.has_option("title") ? parser.option_value("title") : "";
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;
}
void NCursesUI::info_show(const String& content, DisplayCoord anchor,
ColorPair colors, MenuStyle style)
static std::vector<String> wrap_lines(const String& text, CharCount max_width)
{
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);
DisplayCoord size = compute_needed_size(content);
if (style == MenuStyle::Prompt)
size.column = window_size(stdscr).column - anchor.column;
const String& info_box = style == MenuStyle::Inline ?
content : make_info_box(title, content, m_dimensions.column);
DisplayCoord size = compute_needed_size(info_box);
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)));
int line = 0;
auto it = content.begin(), end = content.end();
auto it = info_box.begin(), end = info_box.end();
while (true)
{
wmove(m_info_win, line++, 0);

View File

@ -32,8 +32,9 @@ public:
void menu_select(int selected) override;
void menu_hide() override;
void info_show(const String& content, DisplayCoord anchor,
ColorPair colors, MenuStyle style) override;
void info_show(const String& title, const String& content,
DisplayCoord anchor, ColorPair colors,
MenuStyle style) override;
void info_hide() 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();
}
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())
return false;
ColorPair col = get_color("Information");
DisplayCoord pos = context.window().dimensions();
pos.column -= 1;
context.ui().info_show(info, pos , col, MenuStyle::Inline);
context.ui().info_show(title, info, pos , col, MenuStyle::Prompt);
return true;
}
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 {
if (hide)
context.ui().info_hide();
@ -152,19 +154,17 @@ void goto_commands(Context& context, int line)
break;
}
}
},
"╭────────┤goto├───────╮\n"
"│ g,k: buffer top │\n"
"│ l: line end │\n"
"│ h: line begin │\n"
"│ j: buffer bottom │\n"
"│ e: buffer end │\n"
"│ t: window top │\n"
"│ b: window bottom │\n"
"│ c: window center │\n"
"│ a: last buffer │\n"
"│ f: file │\n"
"╰─────────────────────╯\n");
}, "goto",
"g,k: buffer top \n"
"l: line end \n"
"h: line begin \n"
"j: buffer bottom\n"
"e: buffer end \n"
"t: window top \n"
"b: window bottom\n"
"c: window center\n"
"a: last buffer \n"
"f: file \n");
}
}
@ -200,16 +200,14 @@ void view_commands(Context& context, int param)
context.window().scroll( std::max<CharCount>(1, param));
break;
}
},
"╭─────────┤view├─────────╮\n"
"│ v,c: center cursor │\n"
"│ t: cursor on top │\n"
"│ b: cursor on bottom │\n"
"│ h: scroll left │\n"
"│ j: scroll down │\n"
"│ k: scroll up │\n"
"│ l: scroll right │\n"
"╰────────────────────────╯\n");
}, "view",
"v,c: center cursor \n"
"t: cursor on top \n"
"b: cursor on bottom\n"
"h: scroll left \n"
"j: scroll down \n"
"k: scroll up \n"
"l: scroll right \n");
}
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)); });
editor.multi_select(std::bind(select_all_matches, _1, _2, Regex{"."}));
editor.insert(codepoint_to_str(key.key), InsertMode::Replace);
},
"╭────┤replace with char├─────╮\n"
"│ enter char to replace with │\n"
"╰────────────────────────────╯\n");
}, "replace with char", "enter char to replace with\n");
}
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,
sur.pair, level, flags));
}
},
"╭──────┤select object├───────╮\n"
"│ b,(,): parenthesis block │\n"
"│ B,{,}: braces block │\n"
"│ r,[,]: brackets block │\n"
"│ <,>: angle block │\n"
"\": double quote string │\n"
"│ ': single quote string │\n"
"│ w: word │\n"
"│ W: WORD │\n"
"│ s: sentence │\n"
"│ p: paragraph │\n"
"│ i: indent │\n"
"╰────────────────────────────╯\n");
}, "select object",
"b,(,): parenthesis block\n"
"B,{,}: braces block \n"
"r,[,]: brackets block \n"
"<,>: angle block \n"
"\": double quote string\n"
"': single quote string\n"
"w: word \n"
"W: WORD \n"
"s: sentence \n"
"p: paragraph \n"
"i: indent \n");
}
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,
_1, _2, key.key, param, flags & SelectFlags::Inclusive),
flags & SelectFlags::Extend ? SelectMode::Extend : SelectMode::Replace);
},
"╭──┤select to next char├──╮\n"
"│ enter char to select to │\n"
"╰─────────────────────────╯\n");
}, "select to next char","enter char to select to");
}
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
key.key >= 'a' and key.key <= 'z')
context.client().start_recording(key.key);
},
"╭──┤record macro├──╮\n"
"│ enter macro name │\n"
"╰──────────────────╯\n");
}, "record macro", "enter macro name ");
}
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);
}
}
},
"╭──┤replay macro├──╮\n"
"│ enter macro name │\n"
"╰──────────────────╯\n");
}, "replay macro", "enter macro name");
}
template<Direction direction>

View File

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

View File

@ -33,8 +33,9 @@ public:
virtual void menu_select(int selected) = 0;
virtual void menu_hide() = 0;
virtual void info_show(const String& content, DisplayCoord anchor,
ColorPair colors, MenuStyle style) = 0;
virtual void info_show(const String& title, const String& content,
DisplayCoord anchor, ColorPair colors,
MenuStyle style) = 0;
virtual void info_hide() = 0;
virtual void draw(const DisplayBuffer& display_buffer,