From 84e83a8bbc7e3cd0878a256ba3e906135e87061d Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sat, 19 Jan 2019 22:06:28 +1100 Subject: Make search apply to the filtered list in system/software selection menus, and cache stuff used for searching in software selection menu --- src/frontend/mame/ui/selgame.cpp | 105 +++++++++++++++---------------- src/frontend/mame/ui/selsoft.cpp | 129 ++++++++++++++++++++++----------------- src/frontend/mame/ui/selsoft.h | 23 ++++++- 3 files changed, 143 insertions(+), 114 deletions(-) diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp index 43deb8ae81b..9ae7cb64666 100644 --- a/src/frontend/mame/ui/selgame.cpp +++ b/src/frontend/mame/ui/selgame.cpp @@ -506,43 +506,57 @@ void menu_select_game::populate(float &customtop, float &custombottom) if (!isfavorite()) { m_populated_favorites = false; + m_displaylist.clear(); + machine_filter const *const flt(m_persistent_data.filter_data().get_current_filter()); + + // if search is not empty, find approximate matches if (!m_search.empty()) { - // if search is not empty, find approximate matches populate_search(); + if (flt) + { + for (auto it = m_searchlist.begin(); (m_searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it) + { + if (flt->apply(it->second)) + m_displaylist.emplace_back(it->second); + } + } + else + { + std::transform( + m_searchlist.begin(), + std::next(m_searchlist.begin(), (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH)), + std::back_inserter(m_displaylist), + [] (auto const &entry) { return entry.second; }); + } } else { - // reset search string - m_search.clear(); - m_displaylist.clear(); - // if filter is set on category, build category list - machine_filter const *const flt(m_persistent_data.filter_data().get_current_filter()); std::vector const &sorted(m_persistent_data.sorted_list()); if (!flt) std::copy(sorted.begin(), sorted.end(), std::back_inserter(m_displaylist)); else flt->apply(sorted.begin(), sorted.end(), std::back_inserter(m_displaylist)); + } - // iterate over entries - int curitem = 0; - for (ui_system_info const &elem : m_displaylist) - { - if (old_item_selected == -1 && elem.driver->name == reselect_last::driver()) - old_item_selected = curitem; - - bool cloneof = strcmp(elem.driver->parent, "0"); - if (cloneof) - { - int cx = driver_list::find(elem.driver->parent); - if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) - cloneof = false; - } + // iterate over entries + int curitem = 0; + for (ui_system_info const &elem : m_displaylist) + { + if (old_item_selected == -1 && elem.driver->name == reselect_last::driver()) + old_item_selected = curitem; - item_append(elem.driver->type.fullname(), "", (cloneof) ? (flags_ui | FLAG_INVERT) : flags_ui, (void *)elem.driver); - curitem++; + bool cloneof = strcmp(elem.driver->parent, "0"); + if (cloneof) + { + int cx = driver_list::find(elem.driver->parent); + if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) + cloneof = false; } + + item_append(elem.driver->type.fullname(), "", (cloneof) ? (flags_ui | FLAG_INVERT) : flags_ui, (void *)elem.driver); + curitem++; } } else @@ -996,24 +1010,11 @@ void menu_select_game::populate_search() } } - // sort according to edit distance and put up to 200 in the menu + // sort according to edit distance std::stable_sort( m_searchlist.begin(), m_searchlist.end(), [] (auto const &lhs, auto const &rhs) { return lhs.first < rhs.first; }); - uint32_t flags_ui = FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW; - for (int curitem = 0; (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH) > curitem; ++curitem) - { - game_driver const &drv(*m_searchlist[curitem].second.get().driver); - bool cloneof = strcmp(drv.parent, "0") != 0; - if (cloneof) - { - int const cx = driver_list::find(drv.parent); - if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) - cloneof = false; - } - item_append(drv.type.fullname(), "", !cloneof ? flags_ui : (FLAG_INVERT | flags_ui), (void *)&drv); - } } //------------------------------------------------- @@ -1228,30 +1229,22 @@ render_texture *menu_select_game::get_icon_texture(int linenum, void *selectedre void menu_select_game::inkey_export() { std::vector list; - if (!m_search.empty()) + if (m_populated_favorites) { - for (int curitem = 0; (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH); ++curitem) - list.push_back(m_searchlist[curitem].second.get().driver); + // iterate over favorites + mame_machine_manager::instance()->favorite().apply( + [&list] (ui_software_info const &info) + { + assert(info.driver); + if (info.startempty) + list.push_back(info.driver); + }); } else { - if (m_populated_favorites) - { - // iterate over favorites - mame_machine_manager::instance()->favorite().apply( - [&list] (ui_software_info const &info) - { - assert(info.driver); - if (info.startempty) - list.push_back(info.driver); - }); - } - else - { - list.reserve(m_displaylist.size()); - for (ui_system_info const &info : m_displaylist) - list.emplace_back(info.driver); - } + list.reserve(m_displaylist.size()); + for (ui_system_info const &info : m_displaylist) + list.emplace_back(info.driver); } menu::stack_push(ui(), container(), std::move(list)); diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp index 5ee05186028..26635c6d31a 100644 --- a/src/frontend/mame/ui/selsoft.cpp +++ b/src/frontend/mame/ui/selsoft.cpp @@ -75,6 +75,23 @@ bool compare_software(ui_software_info const &a, ui_software_info const &b) } // anonymous namespace +menu_select_software::search_item::search_item(ui_software_info const &s) + : software(s) + , ucs_shortname(ustr_from_utf8(normalize_unicode(s.shortname, unicode_normalization_form::D, true))) + , ucs_longname(ustr_from_utf8(normalize_unicode(s.longname, unicode_normalization_form::D, true))) + , penalty(1.0) +{ +} + +void menu_select_software::search_item::set_penalty(std::u32string const &search) +{ + // TODO: search alternate title as well + penalty = util::edit_distance(search, ucs_shortname); + if (penalty) + penalty = (std::min)(penalty, util::edit_distance(search, ucs_longname)); +} + + //------------------------------------------------- // ctor //------------------------------------------------- @@ -89,6 +106,8 @@ menu_select_software::menu_select_software(mame_ui_manager &mui, render_containe , m_filters() , m_filter_type(software_filter::ALL) , m_swinfo() + , m_searchlist() + , m_displaylist() { reselect_last::reselect(false); @@ -237,13 +256,20 @@ void menu_select_software::populate(float &customtop, float &custombottom) m_has_empty_start = true; int old_software = -1; + // FIXME: why does it do this relatively expensive operation every time? machine_config config(m_driver, machine().options()); for (device_image_interface &image : image_interface_iterator(config.root_device())) - if (image.filename() == nullptr && image.must_be_loaded()) + { + if (!image.filename() && image.must_be_loaded()) { m_has_empty_start = false; break; } + } + + // start with an empty list + m_displaylist.clear(); + filter_map::const_iterator const flt(m_filters.find(m_filter_type)); // no active search if (m_search.empty()) @@ -252,34 +278,44 @@ void menu_select_software::populate(float &customtop, float &custombottom) if (m_has_empty_start) item_append("[Start empty]", "", flags_ui, (void *)&m_swinfo[0]); - m_displaylist.clear(); - filter_map::const_iterator const it(m_filters.find(m_filter_type)); - if (m_filters.end() == it) + if (m_filters.end() == flt) std::copy(std::next(m_swinfo.begin()), m_swinfo.end(), std::back_inserter(m_displaylist)); else - it->second->apply(std::next(m_swinfo.begin()), m_swinfo.end(), std::back_inserter(m_displaylist)); + flt->second->apply(std::next(m_swinfo.begin()), m_swinfo.end(), std::back_inserter(m_displaylist)); + } + else + { + find_matches(); - // iterate over entries - for (size_t curitem = 0; curitem < m_displaylist.size(); ++curitem) + if (m_filters.end() == flt) + { + std::transform( + m_searchlist.begin(), + std::next(m_searchlist.begin(), (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH)), + std::back_inserter(m_displaylist), + [] (search_item const &entry) { return entry.software; }); + } + else { - if (reselect_last::software() == "[Start empty]" && !reselect_last::driver().empty()) - old_software = 0; - else if (m_displaylist[curitem].get().shortname == reselect_last::software() && m_displaylist[curitem].get().listname == reselect_last::swlist()) - old_software = m_has_empty_start ? curitem + 1 : curitem; - - item_append( - m_displaylist[curitem].get().longname, m_displaylist[curitem].get().devicetype, - m_displaylist[curitem].get().parentname.empty() ? flags_ui : (FLAG_INVERT | flags_ui), (void *)&m_displaylist[curitem].get()); + for (auto it = m_searchlist.begin(); (m_searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it) + { + if (flt->second->apply(it->software)) + m_displaylist.emplace_back(it->software); + } } } - else - { - find_matches(m_search.c_str(), MAX_VISIBLE_SEARCH); - for (int curitem = 0; m_searchlist[curitem] != nullptr; ++curitem) - item_append(m_searchlist[curitem]->longname, m_searchlist[curitem]->devicetype, - m_searchlist[curitem]->parentname.empty() ? flags_ui : (FLAG_INVERT | flags_ui), - (void *)m_searchlist[curitem]); + // iterate over entries + for (size_t curitem = 0; curitem < m_displaylist.size(); ++curitem) + { + if (reselect_last::software() == "[Start empty]" && !reselect_last::driver().empty()) + old_software = 0; + else if (m_displaylist[curitem].get().shortname == reselect_last::software() && m_displaylist[curitem].get().listname == reselect_last::swlist()) + old_software = m_has_empty_start ? curitem + 1 : curitem; + + item_append( + m_displaylist[curitem].get().longname, m_displaylist[curitem].get().devicetype, + m_displaylist[curitem].get().parentname.empty() ? flags_ui : (FLAG_INVERT | flags_ui), (void *)&m_displaylist[curitem].get()); } item_append(menu_item_type::SEPARATOR, flags_ui); @@ -502,43 +538,25 @@ void menu_select_software::load_sw_custom_filters() // find approximate matches //------------------------------------------------- -void menu_select_software::find_matches(const char *str, int count) +void menu_select_software::find_matches() { - // allocate memory to track the penalty value - std::vector penalty(count, 1.0); - std::u32string const search(ustr_from_utf8(normalize_unicode(str, unicode_normalization_form::D, true))); - - int index = 0; - for ( ; index < m_displaylist.size(); ++index) + // ensure search list is populated + if (m_searchlist.empty()) { - // pick the best match between shortname and longname - // TODO: search alternate title as well - double curpenalty(util::edit_distance(search, ustr_from_utf8(normalize_unicode(m_displaylist[index].get().shortname, unicode_normalization_form::D, true)))); - if (curpenalty) - { - double const tmp(util::edit_distance(search, ustr_from_utf8(normalize_unicode(m_displaylist[index].get().longname, unicode_normalization_form::D, true)))); - curpenalty = (std::min)(curpenalty, tmp); - } - - // insert into the sorted table of matches - for (int matchnum = count - 1; matchnum >= 0; --matchnum) - { - // stop if we're worse than the current entry - if (curpenalty >= penalty[matchnum]) - break; + m_searchlist.reserve(m_swinfo.size()); + std::copy(m_swinfo.begin(), m_swinfo.end(), std::back_inserter(m_searchlist)); + } - // as long as this isn't the last entry, bump this one down - if (matchnum < count - 1) - { - penalty[matchnum + 1] = penalty[matchnum]; - m_searchlist[matchnum + 1] = m_searchlist[matchnum]; - } + // update search + const std::u32string ucs_search(ustr_from_utf8(normalize_unicode(m_search, unicode_normalization_form::D, true))); + for (search_item &entry : m_searchlist) + entry.set_penalty(ucs_search); - m_searchlist[matchnum] = &m_displaylist[index].get(); - penalty[matchnum] = curpenalty; - } - } - (index < count) ? m_searchlist[index] = nullptr : m_searchlist[count] = nullptr; + // sort according to edit distance + std::stable_sort( + m_searchlist.begin(), + m_searchlist.end(), + [] (search_item const &lhs, search_item const &rhs) { return lhs.penalty < rhs.penalty; }); } //------------------------------------------------- @@ -646,7 +664,6 @@ void menu_select_software::filter_selected() { if ((software_filter::FIRST <= m_filter_highlight) && (software_filter::LAST >= m_filter_highlight)) { - m_search.clear(); filter_map::const_iterator it(m_filters.find(software_filter::type(m_filter_highlight))); if (m_filters.end() == it) it = m_filters.emplace(software_filter::type(m_filter_highlight), software_filter::create(software_filter::type(m_filter_highlight), m_filter_data)).first; diff --git a/src/frontend/mame/ui/selsoft.h b/src/frontend/mame/ui/selsoft.h index 1becf695841..1bfdd9c1e48 100644 --- a/src/frontend/mame/ui/selsoft.h +++ b/src/frontend/mame/ui/selsoft.h @@ -15,6 +15,10 @@ #include "ui/selmenu.h" #include "ui/utils.h" +#include +#include +#include + namespace ui { @@ -29,6 +33,21 @@ private: using filter_map = std::map; using icon_cache = texture_lru; + struct search_item + { + search_item(ui_software_info const &s); + search_item(search_item const &) = default; + search_item(search_item &&) = default; + search_item &operator=(search_item const &) = default; + search_item &operator=(search_item &&) = default; + void set_penalty(std::u32string const &search); + + std::reference_wrapper software; + std::u32string ucs_shortname; + std::u32string ucs_longname; + double penalty; + }; + virtual void populate(float &customtop, float &custombottom) override; virtual void handle() override; @@ -51,7 +70,7 @@ private: virtual void inkey_export() override { throw false; } void build_software_list(); - void find_matches(const char *str, int count); + void find_matches(); void load_sw_custom_filters(); // handlers @@ -68,7 +87,7 @@ private: software_filter::type m_filter_type; std::vector m_swinfo; - ui_software_info const *m_searchlist[MAX_VISIBLE_SEARCH + 1]; + std::vector m_searchlist; std::vector > m_displaylist; }; -- cgit v1.2.3