Menu: support searching through choices with /

when hitting / while in a menu, a regex filter can be entered so that only
entries matching it are selectable, <esc> disable filtering while a second
<esc> close the menu as usual.
This commit is contained in:
Maxime Coste 2012-10-15 01:46:52 +02:00
parent 148466c659
commit 4be6882bd5

View File

@ -132,7 +132,8 @@ public:
Menu(Context& context, const memoryview<String>& choices, Menu(Context& context, const memoryview<String>& choices,
MenuCallback callback) MenuCallback callback)
: ClientMode(context.client()), : ClientMode(context.client()),
m_callback(callback), m_choice_count(choices.size()), m_selected(0) m_callback(callback), m_choices(choices.begin(), choices.end()),
m_selected(m_choices.begin())
{ {
DisplayCoord menu_pos{ context.window().dimensions().line, 0_char }; DisplayCoord menu_pos{ context.window().dimensions().line, 0_char };
context.ui().menu_show(choices, menu_pos, MenuStyle::Prompt); context.ui().menu_show(choices, menu_pos, MenuStyle::Prompt);
@ -140,53 +141,91 @@ public:
void on_key(const Key& key, Context& context) override void on_key(const Key& key, Context& context) override
{ {
if (key == Key::Down or auto match_filter = [this](const String& str) {
return boost::regex_match(str.begin(), str.end(), m_filter);
};
if (key == Key(Key::Modifiers::Control, 'm'))
{
context.ui().menu_hide();
context.ui().print_status("");
// save callback as reset_normal_mode will delete this
MenuCallback callback = std::move(m_callback);
int selected = m_selected - m_choices.begin();
reset_normal_mode();
callback(selected, context);
return;
}
else if (key == Key::Escape)
{
if (m_edit_filter)
{
m_edit_filter = false;
m_filter = boost::regex(".*");
m_filter_editor.reset("");
context.ui().print_status("");
}
else
{
context.ui().menu_hide();
reset_normal_mode();
}
}
else if (key == Key::Down or
key == Key(Key::Modifiers::Control, 'i') or key == Key(Key::Modifiers::Control, 'i') or
key == Key(Key::Modifiers::Control, 'n') or key == Key(Key::Modifiers::Control, 'n') or
key == Key(Key::Modifiers::None, 'j')) key == Key(Key::Modifiers::None, 'j'))
{ {
if (++m_selected >= m_choice_count) auto it = std::find_if(m_selected+1, m_choices.end(), match_filter);
m_selected = 0; if (it == m_choices.end())
context.ui().menu_select(m_selected); it = std::find_if(m_choices.begin(), m_selected+1, match_filter);
m_selected = it;
context.ui().menu_select(m_selected - m_choices.begin());
} }
if (key == Key::Up or else if (key == Key::Up or
key == Key::BackTab or key == Key::BackTab or
key == Key(Key::Modifiers::Control, 'p') or key == Key(Key::Modifiers::Control, 'p') or
key == Key(Key::Modifiers::None, 'k')) key == Key(Key::Modifiers::None, 'k'))
{ {
if (--m_selected < 0) ChoiceList::const_reverse_iterator selected(m_selected);
m_selected = m_choice_count-1; auto it = std::find_if(selected, m_choices.rend(), match_filter);
context.ui().menu_select(m_selected); if (it == m_choices.rend())
it = std::find_if(m_choices.rbegin(), selected, match_filter);
m_selected = it.base()-1;
context.ui().menu_select(m_selected - m_choices.begin());
} }
if (key == Key(Key::Modifiers::Control, 'm')) else if (key == '/' and not m_edit_filter)
{ {
context.ui().menu_hide(); m_edit_filter = true;
// save callback as reset_normal_mode will delete this
MenuCallback callback = std::move(m_callback);
int selected = m_selected;
reset_normal_mode();
callback(selected, context);
} }
if (key == Key::Escape) else if (m_edit_filter)
{ {
context.ui().menu_hide(); m_filter_editor.handle_key(key);
reset_normal_mode();
} auto search = ".*" + m_filter_editor.line() + ".*";
if (key.modifiers == Key::Modifiers::None and m_filter = boost::regex(search.begin(), search.end());
key.key >= '0' and key.key <= '9') auto it = std::find_if(m_selected, m_choices.end(), match_filter);
{ if (it == m_choices.end())
context.ui().menu_hide(); it = std::find_if(m_choices.begin(), m_selected, match_filter);
// save callback as reset_normal_mode will delete this m_selected = it;
MenuCallback callback = std::move(m_callback); context.ui().menu_select(m_selected - m_choices.begin());
reset_normal_mode();
callback(key.key - '0' - 1, context);
} }
if (m_edit_filter)
context.ui().print_status("/" + m_filter_editor.line(),
m_filter_editor.cursor_pos() + 1);
} }
private: private:
MenuCallback m_callback; MenuCallback m_callback;
int m_selected;
int m_choice_count; using ChoiceList = std::vector<String>;
const ChoiceList m_choices;
ChoiceList::const_iterator m_selected;
boost::regex m_filter = boost::regex(".*");
bool m_edit_filter = false;
LineEditor m_filter_editor;
}; };
class Prompt : public ClientMode class Prompt : public ClientMode